强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Dnsmasq 服务搭建完全教程 / 第 10 章:Docker 部署

第 10 章:Docker 部署

10.1 为什么容器化部署 Dnsmasq

优势说明
隔离性Dnsmasq 运行在独立环境中,不干扰宿主机
可移植性配置文件随镜像打包,一键部署
版本管理使用镜像标签管理不同版本
快速恢复容器崩溃后秒级重启
配置管理Docker Compose / K8s 统一管理
挑战说明
网络模式需要 host 模式才能正常工作 DHCP
端口占用53 端口可能被其他服务占用
TFTP文件映射需要额外配置

10.2 基本 Docker 部署

10.2.1 使用官方镜像

# Docker Hub 上流行的 Dnsmasq 镜像
# jpillora/dnsmasq - 最常用的镜像

docker run -d \
  --name dnsmasq \
  --cap-add=NET_ADMIN \
  --net=host \
  -v /etc/dnsmasq.conf:/etc/dnsmasq.conf \
  -v /etc/dnsmasq.d:/etc/dnsmasq.d \
  jpillora/dnsmasq:latest

10.2.2 自定义 Dockerfile

# Dockerfile.dnsmasq
FROM alpine:3.19

RUN apk add --no-cache dnsmasq

# 创建配置目录
RUN mkdir -p /etc/dnsmasq.d /var/lib/misc

# 复制自定义配置
COPY dnsmasq.conf /etc/dnsmasq.conf
COPY conf.d/ /etc/dnsmasq.d/

# 暴露端口
EXPOSE 53/udp 53/tcp 67/udp 69/udp

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
  CMD nslookup google.com 127.0.0.1 || exit 1

# 以前台模式运行
CMD ["dnsmasq", "--no-daemon", "--log-facility=-"]

构建并运行:

# 构建镜像
docker build -t my-dnsmasq -f Dockerfile.dnsmasq .

# 运行容器
docker run -d \
  --name dnsmasq \
  --cap-add=NET_ADMIN \
  --net=host \
  -v /path/to/config:/etc/dnsmasq.d \
  -v /path/to/hosts:/etc/dnsmasq.hosts \
  -v /path/to/leases:/var/lib/misc \
  my-dnsmasq

10.2.3 网络模式选择

网络模式DNSDHCP说明
host推荐,Dnsmasq 直接使用宿主机网络
bridge⚠️DHCP 需要额外配置
macvlanDnsmasq 获得独立 IP
# host 模式(推荐)
docker run -d --name dnsmasq --net=host my-dnsmasq

# bridge 模式(仅 DNS,不推荐 DHCP)
docker run -d --name dnsmasq \
  -p 53:53/udp \
  -p 53:53/tcp \
  my-dnsmasq

# macvlan 模式(独立 IP)
docker network create -d macvlan \
  --subnet=192.168.1.0/24 \
  --gateway=192.168.1.1 \
  -o parent=eth1 \
  macvlan_net

docker run -d --name dnsmasq \
  --net=macvlan_net \
  --ip=192.168.1.2 \
  my-dnsmasq

10.3 Docker Compose 部署

10.3.1 基本 Docker Compose 配置

# docker-compose.yml
version: '3.8'

services:
  dnsmasq:
    image: jpillora/dnsmasq:latest
    container_name: dnsmasq
    restart: unless-stopped
    network_mode: host
    cap_add:
      - NET_ADMIN
    volumes:
      - ./config/dnsmasq.conf:/etc/dnsmasq.conf:ro
      - ./config/dnsmasq.d:/etc/dnsmasq.d:ro
      - ./config/hosts:/etc/dnsmasq.hosts:ro
      - ./data/leases:/var/lib/misc
      - ./logs:/var/log/dnsmasq
    environment:
      - HTTP_USER=admin
      - HTTP_PASS=changeme
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
# 启动
docker compose up -d

# 查看日志
docker compose logs -f dnsmasq

# 重载配置
docker compose exec dnsmasq kill -HUP 1

# 重启
docker compose restart dnsmasq

10.3.2 带 Web UI 的完整配置

# docker-compose.yml
version: '3.8'

services:
  dnsmasq:
    image: jpillora/dnsmasq:latest
    container_name: dnsmasq
    restart: unless-stopped
    network_mode: host
    cap_add:
      - NET_ADMIN
    volumes:
      - ./config/dnsmasq.conf:/etc/dnsmasq.conf:ro
      - ./config/dnsmasq.d:/etc/dnsmasq.d:ro
      - ./config/hosts:/etc/dnsmasq.hosts:ro
      - ./data/leases:/var/lib/misc
      - ./logs:/var/log/dnsmasq
    environment:
      # jpillora/dnsmasq 内置 Web UI
      - HTTP_USER=admin
      - HTTP_PASS=secure_password_here
    ports:
      - "8080:8080"   # Web UI

networks:
  default:
    name: dnsmasq-net

10.3.3 生产环境 Docker Compose

# docker-compose.prod.yml
version: '3.8'

services:
  dnsmasq:
    image: jpillora/dnsmasq:latest
    container_name: dnsmasq
    restart: always
    network_mode: host
    cap_add:
      - NET_ADMIN
      - NET_RAW
    volumes:
      # 配置文件(只读挂载)
      - ./config/dnsmasq.conf:/etc/dnsmasq.conf:ro
      - ./config/dnsmasq.d:/etc/dnsmasq.d:ro
      - ./config/hosts:/etc/dnsmasq.hosts:ro
      
      # 数据目录(读写)
      - dnsmasq-leases:/var/lib/misc
      - dnsmasq-logs:/var/log/dnsmasq
      
      # 系统文件(只读)
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    environment:
      - TZ=Asia/Shanghai
      - HTTP_USER=admin
      - HTTP_PASS=${ADMIN_PASSWORD:-changeme}
    
    # 资源限制
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 128M
        reservations:
          memory: 32M
    
    # 日志配置
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "5"
    
    # 健康检查
    healthcheck:
      test: ["CMD", "nslookup", "baidu.com", "127.0.0.1"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

volumes:
  dnsmasq-leases:
  dnsmasq-logs:

10.4 Dnsmasq 配置(容器内)

10.4.1 容器内配置文件

# config/dnsmasq.conf
# 适用于 Docker 容器的配置

# 监听地址
listen-address=0.0.0.0
bind-interfaces

# DNS 配置
port=53
cache-size=500
domain=home.lan
expand-hosts

# 上游 DNS
server=223.5.5.5
server=8.8.8.8
no-resolv

# hosts 文件
addn-hosts=/etc/dnsmasq.hosts

# 日志
log-queries
log-facility=/var/log/dnsmasq/dnsmasq.log

# 加载子目录配置
conf-dir=/etc/dnsmasq.d/,*.conf
# config/dnsmasq.d/dhcp.conf
# DHCP 配置(使用 host 网络模式时)

interface=eth1
dhcp-range=192.168.1.100,192.168.1.200,255.255.255.0,24h
dhcp-option=option:router,192.168.1.1
dhcp-option=option:dns-server,192.168.1.1
dhcp-authoritative

10.4.2 热重载配置

# 方法 1:发送 SIGHUP
docker exec dnsmasq kill -HUP 1

# 方法 2:Docker Compose exec
docker compose exec dnsmasq kill -HUP 1

# 方法 3:重建容器(配置文件变更时)
docker compose down
docker compose up -d

# 方法 4:使用 Docker 的 --watch(Docker Compose v2.22+)
# docker compose watch

10.5 容器 DNS 配置

10.5.1 为 Docker 容器提供 DNS

# 方案 1:所有容器使用 Dnsmasq 作为 DNS

# /etc/docker/daemon.json
{
  "dns": ["192.168.1.1"],
  "dns-search": ["home.lan"]
}

# 重启 Docker
sudo systemctl restart docker
# 方案 2:单个 Compose 项目指定 DNS
services:
  webapp:
    image: nginx
    dns:
      - 192.168.1.1    # Dnsmasq 服务器
    dns_search:
      - home.lan

10.5.2 Docker 内部 DNS 集成

# 使用 Docker 自定义网络,指定 DNS
version: '3.8'

services:
  dnsmasq:
    image: jpillora/dnsmasq:latest
    container_name: dnsmasq
    network_mode: host
    cap_add:
      - NET_ADMIN
    volumes:
      - ./config:/etc/dnsmasq.d:ro

  # 应用容器使用自定义 DNS
  webapp:
    image: nginx
    dns:
      - 192.168.1.1
    networks:
      - app-net

  api:
    image: node:18
    dns:
      - 192.168.1.1
    networks:
      - app-net

networks:
  app-net:
    driver: bridge

10.5.3 容器名称解析

# Docker Compose 中容器之间自动解析服务名
# 如果需要自定义 DNS,可以在 Dnsmasq 中添加

# config/dnsmasq.d/docker-services.conf
address=/webapp.home.lan/172.17.0.2
address=/api.home.lan/172.17.0.3
address=/db.home.lan/172.17.0.4

10.6 DHCP 在 Docker 中的注意事项

10.6.1 DHCP 必须使用 host 网络模式

# DHCP 需要直接访问物理网络接口
# bridge 模式下 DHCP 请求无法正确传递

# 推荐配置
docker run -d --name dnsmasq \
  --net=host \
  --cap-add=NET_ADMIN \
  my-dnsmasq

10.6.2 替代方案:使用 macvlan

# 如果不能使用 host 模式,使用 macvlan
docker network create -d macvlan \
  --subnet=192.168.1.0/24 \
  --gateway=192.168.1.1 \
  -o parent=eth1 \
  lan-net

docker run -d --name dnsmasq \
  --net=lan-net \
  --ip=192.168.1.2 \
  --cap-add=NET_ADMIN \
  my-dnsmasq

10.7 日志与监控

10.7.1 容器日志管理

# docker-compose.yml
services:
  dnsmasq:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "5"
    
    # 或使用外部日志驱动
    # logging:
    #   driver: "syslog"
    #   options:
    #     syslog-address: "tcp://log-server:514"

10.7.2 Prometheus 监控集成

# 使用 Dnsmasq exporter
version: '3.8'

services:
  dnsmasq:
    image: jpillora/dnsmasq:latest
    network_mode: host
    cap_add:
      - NET_ADMIN

  # Prometheus Dnsmasq Exporter
  dnsmasq-exporter:
    image: google/dnsmasq_exporter:latest
    command:
      - "--dnsmasq.address=http://127.0.0.1:53"
    ports:
      - "9153:9153"
    restart: unless-stopped

10.8 Kubernetes 部署

10.8.1 DaemonSet 部署

# k8s/dnsmasq-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: dnsmasq
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: dnsmasq
  template:
    metadata:
      labels:
        app: dnsmasq
    spec:
      hostNetwork: true
      dnsPolicy: Default
      containers:
        - name: dnsmasq
          image: jpillora/dnsmasq:latest
          ports:
            - containerPort: 53
              protocol: UDP
            - containerPort: 53
              protocol: TCP
          securityContext:
            capabilities:
              add:
                - NET_ADMIN
          volumeMounts:
            - name: config
              mountPath: /etc/dnsmasq.conf
              subPath: dnsmasq.conf
              readOnly: true
            - name: config-dir
              mountPath: /etc/dnsmasq.d
              readOnly: true
          resources:
            requests:
              memory: "32Mi"
              cpu: "50m"
            limits:
              memory: "128Mi"
              cpu: "200m"
      volumes:
        - name: config
          configMap:
            name: dnsmasq-config
        - name: config-dir
          configMap:
            name: dnsmasq-confd

10.8.2 ConfigMap 配置

# k8s/dnsmasq-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: dnsmasq-config
  namespace: kube-system
data:
  dnsmasq.conf: |
    listen-address=0.0.0.0
    port=53
    cache-size=500
    server=223.5.5.5
    server=8.8.8.8
    no-resolv
    log-queries
    log-facility=-
    conf-dir=/etc/dnsmasq.d/,*.conf

10.9 完整部署示例

项目目录结构

dnsmasq-docker/
├── docker-compose.yml
├── Dockerfile
├── .env
├── config/
│   ├── dnsmasq.conf
│   ├── hosts
│   └── dnsmasq.d/
│       ├── 01-upstream.conf
│       ├── 02-dhcp.conf
│       └── 03-custom.conf
├── data/
│   └── leases/
├── logs/
└── scripts/
    ├── backup.sh
    └── update-adblock.sh
# .env
ADMIN_PASSWORD=secure_password_here
TIMEZONE=Asia/Shanghai
# 启动
docker compose up -d

# 验证
dig @127.0.0.1 www.baidu.com

# 查看日志
docker compose logs -f dnsmasq

# 更新配置后重载
docker compose exec dnsmasq kill -HUP 1

# 停止
docker compose down

10.10 小结

场景网络模式DHCP推荐
主机 DNS 服务host⭐ 推荐
仅 DNS 缓存bridge简单场景
独立网络macvlan多网段
关键配置说明
--net=hostDHCP 必须使用 host 模式
--cap-add=NET_ADMINDHCP 需要网络管理权限
配置文件只读挂载:ro 防止容器内修改
日志持久化挂载日志目录或使用 Docker 日志驱动

10.11 扩展阅读