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

BIND DNS 服务器搭建完全教程 / 第 13 章:Docker 容器化部署

本章概述

容器化部署 BIND 可以简化环境管理、提高可移植性。本章讲解使用 Docker 和 Docker Compose 部署 BIND,包括配置管理、日志收集、健康检查等。


13.1 基础 Dockerfile

13.1.1 使用 Alpine Linux(推荐)

# Dockerfile
FROM alpine:3.19

# 安装 BIND
RUN apk add --no-cache bind bind-tools

# 创建目录
RUN mkdir -p /etc/bind \
    /var/bind/{primary,secondary,dynamic} \
    /var/log/named \
    && chown -R named:named /var/bind /var/log/named

# 暴露端口
EXPOSE 53/udp 53/tcp 953/tcp

# 健康检查
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
    CMD dig @127.0.0.1 . NS > /dev/null 2>&1 || exit 1

# 启动命令
CMD ["/usr/sbin/named", "-c", "/etc/bind/named.conf", "-f", "-g", "-u", "named"]

注意-f 参数让 named 在前台运行(Docker 要求),-g 输出日志到 stderr。

13.1.2 使用 Ubuntu 基础镜像

# Dockerfile.ubuntu
FROM ubuntu:22.04

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    bind9 bind9utils dnsutils && \
    rm -rf /var/lib/apt/lists/*

RUN mkdir -p /var/cache/bind/{primary,secondary,dynamic} \
    /var/log/named && \
    chown -R bind:bind /var/cache/bind /var/log/named

EXPOSE 53/udp 53/tcp 953/tcp

HEALTHCHECK --interval=30s --timeout=5s \
    CMD dig @127.0.0.1 . NS > /dev/null 2>&1 || exit 1

CMD ["/usr/sbin/named", "-c", "/etc/bind/named.conf", "-f", "-g", "-u", "bind"]

13.2 Docker Compose 配置

13.2.1 基本配置

# docker-compose.yml
version: '3.8'

services:
  dns:
    build: .
    container_name: bind9-dns
    restart: unless-stopped
    ports:
      - "53:53/udp"
      - "53:53/tcp"
      # rndc 管理端口(仅限主机访问)
      - "127.0.0.1:953:953/tcp"
    volumes:
      # 配置文件
      - ./config/named.conf:/etc/bind/named.conf:ro
      - ./config/zones:/etc/bind/zones:ro
      # 区域文件(可写,支持动态更新)
      - dns-data:/var/cache/bind
      # 日志
      - ./logs:/var/log/named
      # 根提示文件
      - ./config/db.root:/etc/bind/db.root:ro
    environment:
      - TZ=Asia/Shanghai
    networks:
      - dns-network
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    read_only: false
    tmpfs:
      - /run
      - /tmp

volumes:
  dns-data:
    driver: local

networks:
  dns-network:
    driver: bridge

13.2.2 目录结构

dns-server/
├── docker-compose.yml
├── Dockerfile
├── config/
│   ├── named.conf
│   ├── db.root
│   ├── zones/
│   │   ├── primary/
│   │   │   └── example.com.zone
│   │   └── secondary/
│   └── keys/
│       └── update.key
└── logs/
    ├── default.log
    ├── query.log
    └── security.log

13.3 配置文件管理

13.3.1 named.conf

// config/named.conf
options {
    directory "/var/cache/bind";
    
    listen-on port 53 { any; };
    listen-on-v6 port 53 { any; };
    
    recursion yes;
    allow-recursion { 192.168.0.0/16; 10.0.0.0/8; 172.16.0.0/12; };
    allow-query { any; };
    
    forwarders {
        8.8.8.8;
        1.1.1.1;
    };
    forward first;
    
    version "not disclosed";
    dnssec-validation auto;
    
    max-cache-size 256m;
    max-cache-ttl 3600;
    max-ncache-ttl 900;
    
    minimal-responses yes;
    
    pid-file "/run/named.pid";
    session-keyfile "/run/session.key";
};

logging {
    channel default_log {
        stderr;
        severity info;
        print-time yes;
        print-severity yes;
        print-category yes;
    };
    channel query_log {
        file "/var/log/named/query.log" versions 3 size 50m;
        severity dynamic;
        print-time yes;
    };
    category default { default_log; };
    category queries { query_log; };
    category lame-servers { null; };
    category edns-disabled { null; };
};

zone "." {
    type hint;
    file "/etc/bind/db.root";
};

zone "example.com" {
    type primary;
    file "/etc/bind/zones/primary/example.com.zone";
    allow-transfer { none; };
};

zone "internal.corp" {
    type primary;
    file "/var/cache/bind/primary/internal.corp.zone";
    allow-update { key update-key; };
};

13.3.2 热重载配置

# 使用 Docker exec 重载配置
docker exec bind9-dns rndc reload

# 使用 Docker Compose
docker compose exec dns rndc reload

# 完全重启
docker compose restart dns

13.4 高可用 Compose 配置

13.4.1 主从架构

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

services:
  dns-primary:
    build: .
    container_name: bind9-primary
    restart: unless-stopped
    ports:
      - "53:53/udp"
      - "53:53/tcp"
    volumes:
      - ./config-primary/named.conf:/etc/bind/named.conf:ro
      - ./zones/primary:/var/cache/bind/primary
      - ./logs/primary:/var/log/named
      - ./config/db.root:/etc/bind/db.root:ro
    networks:
      dns-network:
        ipv4_address: 172.20.0.10
    healthcheck:
      test: ["CMD", "dig", "@127.0.0.1", ".", "NS"]
      interval: 30s
      timeout: 5s
      retries: 3

  dns-secondary:
    build: .
    container_name: bind9-secondary
    restart: unless-stopped
    ports:
      - "54:53/udp"
      - "54:53/tcp"
    volumes:
      - ./config-secondary/named.conf:/etc/bind/named.conf:ro
      - ./zones/secondary:/var/cache/bind/secondary
      - ./logs/secondary:/var/log/named
      - ./config/db.root:/etc/bind/db.root:ro
    depends_on:
      - dns-primary
    networks:
      dns-network:
        ipv4_address: 172.20.0.11

networks:
  dns-network:
    ipam:
      config:
        - subnet: 172.20.0.0/24

13.5 日志收集

13.5.1 Docker 原生日志

# docker-compose.yml
services:
  dns:
    logging:
      driver: "json-file"
      options:
        max-size: "50m"
        max-file: "5"

13.5.2 使用 Syslog 驱动

services:
  dns:
    logging:
      driver: "syslog"
      options:
        syslog-address: "tcp://logserver:514"
        tag: "bind9"

13.5.3 集中日志(Loki + Promtail)

# docker-compose.yml
services:
  dns:
    # ... 其他配置 ...
    labels:
      - "logging=loki"
      - "loki.job=bind9"

  promtail:
    image: grafana/promtail:latest
    volumes:
      - ./logs:/var/log/named
      - ./promtail-config.yml:/etc/promtail/config.yml
    command: -config.file=/etc/promtail/config.yml
# promtail-config.yml
server:
  http_listen_port: 9080

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: bind9
    static_configs:
      - targets: [localhost]
        labels:
          job: bind9
          __path__: /var/log/named/*.log

13.6 DNS 服务器编排(Docker Swarm / Kubernetes)

13.6.1 Docker Swarm 部署

# 初始化 Swarm
docker swarm init

# 部署 Stack
docker stack deploy -c docker-compose.yml dns

13.6.2 Kubernetes ConfigMap

# k8s-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: bind-config
data:
  named.conf: |
    options {
        directory "/var/cache/bind";
        recursion yes;
        allow-recursion { 10.0.0.0/8; };
        listen-on port 53 { any; };
    };
    
    zone "example.com" {
        type primary;
        file "/etc/bind/zones/example.com.zone";
    };
  example.com.zone: |
    $TTL 3600
    @   IN  SOA  ns1.example.com. admin.example.com. (
            2026051001 3600 900 1209600 86400
        )
        IN  NS  ns1.example.com.
    ns1 IN  A   10.0.0.10
    www IN  A   10.0.0.20
# k8s-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: bind9
spec:
  replicas: 2
  selector:
    matchLabels:
      app: bind9
  template:
    metadata:
      labels:
        app: bind9
    spec:
      containers:
        - name: bind9
          image: bind9:latest
          ports:
            - containerPort: 53
              protocol: UDP
            - containerPort: 53
              protocol: TCP
          volumeMounts:
            - name: config
              mountPath: /etc/bind/named.conf
              subPath: named.conf
            - name: zones
              mountPath: /etc/bind/zones/example.com.zone
              subPath: example.com.zone
          livenessProbe:
            exec:
              command: ["dig", "@127.0.0.1", ".", "NS"]
            initialDelaySeconds: 10
            periodSeconds: 30
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "512Mi"
              cpu: "500m"
      volumes:
        - name: config
          configMap:
            name: bind-config
        - name: zones
          configMap:
            name: bind-config

13.7 最佳实践

13.7.1 镜像构建

实践 说明
使用 Alpine 镜像小(~15MB)
固定版本 apk add bind=9.18.x
多阶段构建 减少最终镜像大小
安全扫描 docker scan bind9

13.7.2 运行时安全

services:
  dns:
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE    # 允许绑定 53 端口
    read_only: true          # 只读文件系统
    security_opt:
      - no-new-privileges    # 禁止提权
    tmpfs:
      - /run:rw,size=10M
      - /tmp:rw,size=100M

13.7.3 网络优化

services:
  dns:
    sysctls:
      - net.core.rmem_max=8388608
      - net.core.wmem_max=8388608

13.8 常见问题

问题 原因 解决方案
端口冲突 宿主机已有 DNS 服务 修改端口映射
权限不足 绑定 53 端口需要权限 cap_add: NET_BIND_SERVICE
日志丢失 容器重启 挂载日志卷
配置不生效 文件只读挂载后更新 重启容器或使用 volume
性能差 默认网络模式 使用 host 网络模式

13.9 本章小结

部署方式 适用场景 优点 缺点
单容器 开发测试 简单 无高可用
Compose 小型生产 配置即代码 手动扩缩容
Swarm 中型生产 自动扩缩容 配置复杂
Kubernetes 大型生产 企业级 学习成本高

💡 小技巧

  1. 使用 Alpine 基础镜像:体积小,安全更新快。
  2. 日志输出到 stderr:便于 Docker 原生日志收集。
  3. 使用命名卷docker volume create dns-data
  4. 固定镜像版本:避免意外升级导致问题。
  5. 健康检查必须配置:自动检测服务故障。

📖 扩展阅读