适用:自建大型集群配套的镜像构建 / 节点 Docker 守护进程 假设你已读过 Docker操作手册.md 和 DockerCompose操作手册.md 镜像供应链单独成篇,见 镜像供应链安全手册.md
目录
- 生产前提:你真的需要 Docker 吗
- 守护进程生产配置
- 存储驱动与磁盘
- 日志驱动
- 资源限制与隔离
- Buildx 多架构构建流水线
- 构建缓存最佳实践
- Dockerfile 生产规约
- Rootless / Podman 兼容
- Docker Compose 在生产的边界
- 节点 Docker 故障处理
- 生产化 Checklist
1. 生产前提:你真的需要 Docker 吗
K8s 1.24+ 默认运行时是 containerd,不再是 dockershim。50+ 节点集群强烈建议:
| 场景 | 建议运行时 |
|---|---|
| K8s worker 节点 | containerd(轻、官方主推) |
| 构建节点 / CI | docker(buildx 体验好)或 buildkit 独立部署 |
| 开发机 | docker(生态最熟) |
下文"Docker 守护进程"主要针对构建机器和仍用 docker 作运行时的节点。
2. 守护进程生产配置
/etc/docker/daemon.json:
{
"data-root": "/var/lib/docker",
"storage-driver": "overlay2",
"live-restore": true,
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "5",
"compress": "true",
"labels": "service,env"
},
"default-ulimits": {
"nofile": { "Name": "nofile", "Hard": 1048576, "Soft": 1048576 },
"nproc": { "Name": "nproc", "Hard": 65535, "Soft": 65535 }
},
"default-address-pools": [
{ "base": "172.30.0.0/16", "size": 24 }
],
"registry-mirrors": [
"https://harbor.internal.example.com"
],
"insecure-registries": [],
"allow-nondistributable-artifacts": [],
"max-concurrent-downloads": 10,
"max-concurrent-uploads": 5,
"max-download-attempts": 5,
"no-new-privileges": true,
"userland-proxy": false,
"metrics-addr": "127.0.0.1:9323",
"experimental": false,
"default-runtime": "runc",
"exec-opts": ["native.cgroupdriver=systemd"]
}
关键项解释
live-restore: true:升级 dockerd 不杀容器,生产必开default-address-pools:避免默认172.17.0.0/16与企业内网冲突(这是常见事故源)exec-opts: native.cgroupdriver=systemd:与 K8s/kubelet 一致,避免 cgroup 驱动错配userland-proxy: false:高 QPS 下用 iptables NAT 性能更好no-new-privileges: true:默认安全基线metrics-addr:暴露 Prometheus 指标data-root:默认/var/lib/docker,数据多时换大盘 独立 LV
修改后:
sudo systemctl daemon-reload
sudo systemctl restart docker
docker info | grep -E "Storage Driver|Cgroup|Live Restore"
3. 存储驱动与磁盘
3.1 选 overlay2
90% 场景用 overlay2(默认)。要求:
- 内核 ≥ 4.0(推荐 ≥ 5.x)
- 后端文件系统 ext4 或 xfs(xfs 必须
ftype=1)
mkfs.xfs -n ftype=1 /dev/sdb1
3.2 数据目录独立挂载
/var/lib/docker 单独挂一块大盘(推荐 SSD),LVM 方便扩容:
pvcreate /dev/sdb
vgcreate vg-docker /dev/sdb
lvcreate -L 500G -n lv-docker vg-docker
mkfs.xfs -n ftype=1 /dev/vg-docker/lv-docker
mkdir -p /var/lib/docker
echo "/dev/vg-docker/lv-docker /var/lib/docker xfs defaults 0 0" >> /etc/fstab
mount -a
3.3 监控 & 清理
定期清理(cron):
# /etc/cron.d/docker-prune
30 3 * * * root docker system prune -af --filter "until=168h" >> /var/log/docker-prune.log 2>&1
构建节点不要乱清,会丢 buildx 缓存。专门隔离构建机和运行机。
监控指标:
docker_state_total{state="running"}
node_filesystem_avail_bytes{mountpoint="/var/lib/docker"} / node_filesystem_size_bytes{mountpoint="/var/lib/docker"}
4. 日志驱动
4.1 json-file(默认,最稳)
容器化生产 99% 用这个,必须配 max-size 和 max-file,否则会撑爆磁盘。已在第 2 节配过。
4.2 集中日志(推荐两种方式)
方式 A:journald → Vector / Fluent Bit
{ "log-driver": "journald" }
宿主机 journald 由 Vector 转发到 Loki / ES。
方式 B:保持 json-file,sidecar 收集
K8s 默认这样,DaemonSet 跑 Fluent Bit 收 /var/log/containers/*.log。
不推荐
gelf/fluentd直连 driver:网络抖动时 docker 写日志会阻塞,进而阻塞容器 stdout,曾导致重大事故。
5. 资源限制与隔离
K8s 场景由 K8s 处理;裸 docker 场景:
docker run -d --name app \
--cpus=2 \
--memory=2g --memory-swap=2g \
--pids-limit=1000 \
--ulimit nofile=65535:65535 \
--read-only \
--tmpfs /tmp:rw,size=64m,mode=1777 \
--cap-drop=ALL --cap-add=NET_BIND_SERVICE \
--security-opt=no-new-privileges \
--user 10001:10001 \
myapp:1.0
6. Buildx 多架构构建流水线
6.1 启用 buildx + 容器 builder
# 创建专用 builder(脱离默认 docker driver,能用所有 buildkit 特性)
docker buildx create --name multi --driver docker-container --use \
--driver-opt network=host \
--buildkitd-flags '--allow-insecure-entitlement security.insecure'
docker buildx inspect --bootstrap
6.2 远程共享 builder(CI 多机加速)
CI 集群跑一个 BuildKit 服务,所有 runner 共享缓存:
# 单独跑 buildkitd
docker run -d --name buildkitd --privileged \
-p 1234:1234 \
moby/buildkit:latest --addr tcp://0.0.0.0:1234
# 各 runner 用 remote driver
docker buildx create --name remote --driver remote tcp://buildkit.ci.internal:1234 --use
6.3 多架构构建(amd64 + arm64)
# 注册 binfmt(每台机器一次)
docker run --privileged --rm tonistiigi/binfmt --install all
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t harbor.internal/app/myapp:1.2.3-${GIT_SHA::8} \
--push .
6.4 Cache 远程化(CI 必备)
docker buildx build \
--platform linux/amd64,linux/arm64 \
--cache-from=type=registry,ref=harbor.internal/app/myapp:buildcache \
--cache-to=type=registry,ref=harbor.internal/app/myapp:buildcache,mode=max \
-t harbor.internal/app/myapp:1.2.3 --push .
模式:
mode=min:只缓存最终层mode=max:缓存所有中间层(推荐,命中率高,但占用大)
GitHub Actions 用 cache-from=type=gha,cache-to=type=gha,mode=max。
7. 构建缓存最佳实践
7.1 分层缓存命中规则
层缓存命中条件:指令 + 上下文文件 hash 相同。任何上层失效会传染下层。
错误示例:
COPY . /app
RUN npm ci # 改任何源码都会重装依赖
正确:
COPY package.json package-lock.json ./
RUN npm ci # 仅 package.json 改时重装
COPY . .
RUN npm run build
7.2 BuildKit 高级特性
# syntax=docker/dockerfile:1.7
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
# Cache mount:跨构建复用 npm 缓存
RUN --mount=type=cache,target=/root/.npm \
npm ci
FROM deps AS build
COPY . .
RUN --mount=type=cache,target=/app/node_modules/.cache \
npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=deps /app/node_modules ./node_modules
USER node
CMD ["node","dist/server.js"]
Go 项目类似:
# syntax=docker/dockerfile:1.7
FROM golang:1.22 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download
COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/app ./cmd/app
FROM gcr.io/distroless/static
COPY --from=build /out/app /app
USER nonroot:nonroot
ENTRYPOINT ["/app"]
7.3 secret mount(永远不要 ARG 传密钥)
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
npm ci
docker buildx build --secret id=npmrc,src=$HOME/.npmrc -t myapp:1.0 .
8. Dockerfile 生产规约
| 规则 | 说明 |
|---|---|
| 基础镜像 pin 到 sha256 | FROM node:20-alpine@sha256:... 防供应链 tag 偷换 |
| 多阶段,最终镜像不带构建工具 | 通用 |
| 用 distroless / alpine / scratch | 减少攻击面与体积 |
| 非 root 用户 | USER 10001 或 distroless 自带 nonroot |
| 不写敏感信息 | 用 secret mount / 运行时注入 |
| 显式 EXPOSE 和 ENTRYPOINT/CMD | 避免运行时歧义 |
| 加 HEALTHCHECK 或 K8s probe(推荐 K8s 侧) | 运行时健康 |
| 加 OCI labels | org.opencontainers.image.source / revision / version / created |
| 加 .dockerignore | 必须有 |
OCI labels 推荐:
ARG GIT_SHA
ARG VERSION
ARG BUILD_DATE
LABEL org.opencontainers.image.source="https://git.example.com/team/myapp" \
org.opencontainers.image.revision="$GIT_SHA" \
org.opencontainers.image.version="$VERSION" \
org.opencontainers.image.created="$BUILD_DATE" \
org.opencontainers.image.licenses="Apache-2.0" \
org.opencontainers.image.vendor="Example Inc."
构建时:
docker buildx build \
--build-arg GIT_SHA=$(git rev-parse HEAD) \
--build-arg VERSION=$(git describe --tags --always) \
--build-arg BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ) \
-t harbor.internal/app/myapp:$(git rev-parse --short HEAD) \
--push .
9. Rootless / Podman 兼容
9.1 Rootless Docker
合规要求高的环境用 rootless:
dockerd-rootless-setuptool.sh install
systemctl --user enable --now docker
限制:
- 不能直接绑 < 1024 端口(可改
net.ipv4.ip_unprivileged_port_start) - 性能略低
- 需要 newuidmap / subuid / subgid 配置
9.2 Podman 兼容
K8s containerd + 开发机 Podman 是越来越常见的组合。Podman CLI 兼容 docker:
alias docker=podman
podman build -t myapp:1.0 .
差异:
- 没有守护进程(fork 模型)
- 默认 rootless
- 用
podman generate kube可输出 K8s YAML
10. Docker Compose 在生产的边界
10.1 结论先行
多机生产不要用 Compose。 Compose 设计目标是"单机栈"。
允许使用的场景:
- 单机内部工具(CI runner / 跳板机服务)
- 边缘节点(断网环境,单台跑应用)
- 临时演示 / PoC
不要用于:
- 多机生产应用集群(用 K8s)
- 关键业务数据库(要么 K8s + Operator,要么纯 VM + 配置管理)
10.2 单机生产 Compose 的最低要求
services:
app:
image: harbor.internal/app/myapp:1.2.3-abc1234 # 不用 latest,含 sha 摘要更佳
pull_policy: always
restart: unless-stopped
init: true # 防 zombie
read_only: true
tmpfs: ["/tmp:size=64m"]
user: "10001:10001"
cap_drop: [ALL]
security_opt:
- no-new-privileges:true
deploy:
resources:
limits: { cpus: "2", memory: 2G }
reservations: { memory: 512M }
healthcheck:
test: ["CMD","wget","-qO-","http://localhost:8080/healthz"]
interval: 15s
timeout: 3s
retries: 5
start_period: 30s
logging:
driver: json-file
options:
max-size: "100m"
max-file: "5"
labels: "service,env"
tag: "{{.Name}}/{{.ID}}"
labels:
service: myapp
env: prod
secrets:
- source: db_password
target: /run/secrets/db_password
mode: 0400
secrets:
db_password:
file: /etc/secrets/db_password # 文件权限 0600,root only
10.3 secret 的正确用法
secrets: 字段把 secret 作为文件挂进容器(/run/secrets/<name>),比环境变量安全(不会出现在 docker inspect、ps 输出、子进程环境)。
应用读:
with open("/run/secrets/db_password") as f:
password = f.read().strip()
10.4 单机近似零宕机
Compose 没有滚动语义,但可以:
# 双 Compose 项目 + 前置 nginx 切流
docker compose -p app-blue -f compose.yaml --env-file .env.blue up -d
docker compose -p app-green -f compose.yaml --env-file .env.green up -d
# 验证 green 健康后,nginx upstream 切到 green
# 然后停 blue
docker compose -p app-blue down
10.5 备份
# 备份命名卷
docker run --rm \
-v db-data:/data:ro \
-v $(pwd)/backup:/backup \
alpine tar czf /backup/db-$(date +%F).tar.gz -C / data
数据库类必须用应用级备份(mysqldump / pg_dump),文件级 tar 不保证一致性。
11. 节点 Docker 故障处理
11.1 常见现象与排查
| 现象 | 排查 | ||
|---|---|---|---|
dockerd 卡死,容器无法操作 | journalctl -u docker --since "1 hour ago";看 goroutine kill -SIGUSR1 $(pidof dockerd) 后看日志;多数是日志驱动阻塞或 containerd 异常 | ||
| 磁盘满 | docker system df;du -sh /var/lib/docker/containers/*/*.log(日志没限大小);du -sh /var/lib/docker/overlay2 | ||
failed to register layer | overlay2 损坏,停 docker、备份 /var/lib/docker、清理或换盘 | ||
| 网络不通 / IP 冲突 | 检查 default-address-pools 是否与企业网段冲突;iptables -t nat -L -n | ||
| 高 CPU sys 占用 | 老内核的 cgroup v1 + 大量短任务;升内核或开 cgroup v2 | ||
| 容器频繁 OOM | `docker inspect | jq '.[0].State';查 dmesg | grep -i kill`;调 limits |
11.2 关键运维命令
# 守护进程信息
docker info
docker version
# dockerd debug 数据
kill -SIGUSR1 $(pidof dockerd) # 输出 goroutine stack 到日志
journalctl -u docker --since "30 min ago"
# 容器级
docker stats --no-stream
docker top <container>
docker inspect <container> | jq '.[0].State'
docker events --since 1h # 守护进程事件
# 强清理(小心!只在确认无误时)
docker container prune
docker image prune -a
docker volume prune
docker system prune -a --volumes # 全清,谨慎
11.3 保留现场
故障时先保留再清理:
# 容器内 core dump / 文件
docker cp <id>:/path/to/log /tmp/
# 容器配置快照
docker inspect <id> > /tmp/inspect.json
# 整个守护进程数据目录(大!)
tar czf /tmp/docker-state.tgz /var/lib/docker/containers /var/lib/docker/overlay2/<layer-id>
12. 生产化 Checklist
守护进程
-
daemon.json配齐:live-restore、log-opts、ulimits、address-pools、storage-driver - cgroup 驱动 = systemd(与 K8s 一致)
-
/var/lib/docker独立大盘 SSD - dockerd 监控接 Prometheus(metrics-addr)
- 审计 / 合规要求 → rootless 或 Podman
镜像
- 所有 FROM 用 alpine / distroless / 公司基础镜像
- 多阶段,最终镜像最小化
- 非 root 用户
- Pin 到 sha256(高安全要求场景)
- OCI labels 完整
- 永不 latest,tag 含 git-sha
-
.dockerignore完整
构建
- Buildx + container/remote driver
- 多架构(amd64 + arm64,按需)
- cache-to/cache-from 远程缓存
- secret 用
--mount=type=secret,绝不 ARG - 构建机器与运行机器分开
- 镜像签名 + SBOM + 漏洞扫描(见 镜像供应链安全手册.md)
运行
- 资源 limit 必填(CPU / mem / pids)
- cap drop ALL,按需 add
- no-new-privileges
- read-only + tmpfs /tmp
- healthcheck(K8s 场景由 probe 替代)
- 日志大小限制
- 重启策略明确
Compose(如使用)
- 仅限单机场景
- secrets 用文件方式
- 镜像 tag 不变更(immutable)
- 数据卷有应用级备份
- healthcheck + restart 策略
故障预案
- dockerd 卡死的 SIGUSR1 步骤进 runbook
- 磁盘清理脚本 + 报警
- 节点 IP 段冲突自检
- 升级 docker 演练(live-restore 验证)