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 | 大型生产 | 企业级 | 学习成本高 |
💡 小技巧
- 使用 Alpine 基础镜像:体积小,安全更新快。
- 日志输出到 stderr:便于 Docker 原生日志收集。
- 使用命名卷:
docker volume create dns-data。 - 固定镜像版本:避免意外升级导致问题。
- 健康检查必须配置:自动检测服务故障。
📖 扩展阅读