Docker 学习笔记(五):生产实践与安全

写在前面

本文是 Docker 系列收官篇。前面的内容让容器"能跑",这篇讲让容器"在生产环境跑得稳、跑得安全":日志、监控、健康检查、资源限制、优雅关闭、安全加固,以及 Docker vs K8s 的选择。


一、生产环境清单

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
一个生产级容器应用应该有:
  ✓ 健康检查(让编排器知道是否健康)
  ✓ 资源限制(防止单容器吃光资源)
  ✓ 日志方案(收集、聚合)
  ✓ 监控(指标、告警)
  ✓ 重启策略(崩溃自动恢复)
  ✓ 优雅关闭(处理完请求再停)
  ✓ 非 root 用户
  ✓ 镜像扫描(无漏洞)
  ✓ 密钥管理(密钥不进镜像)

二、健康检查

1
2
3
# Dockerfile 里定义健康检查
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1
1
2
3
4
5
6
7
8
9
# docker-compose.yml
services:
  app:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 10s     # 启动宽限期
1
2
3
4
5
状态:starting → healthy → unhealthy
  健康检查失败 → unhealthy
  编排器(Docker Swarm / K8s)根据状态决定是否重启/摘流量

  应用要提供 /health 端点(检查自身 + 依赖)

三、资源限制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# docker-compose.yml
services:
  app:
    deploy:
      resources:
        limits:
          cpus: "1.5"         # 最多 1.5 核
          memory: 512M        # 最多 512M 内存
        reservations:         # 保底
          cpus: "0.5"
          memory: 128M
1
2
3
4
5
# docker run 限制
docker run --cpus="1.5" --memory="512m" --memory-swap="1g" myapp

# OOM:容器内存超限会被内核 OOM Kill(重启)
# CPU 限流:超 cpu 配额会被节流(慢,但不杀)
1
2
3
4
为什么要限制:
  不限制 → 一个容器内存泄漏/死循环,吃光宿主机
        → 影响其他容器("吵闹的邻居")
  限制   → 隔离资源,单容器故障不扩散

四、重启策略

1
2
3
4
5
docker run --restart=no          # 不重启(默认)
docker run --restart=on-failure  # 非正常退出才重启
docker run --restart=on-failure:5  # 最多重启 5 次
docker run --restart=always      # 总是重启(包括手动 stop?stop 例外)
docker run --restart=unless-stopped  # 总是重启,除非手动停止(推荐)
1
2
3
4
unless-stopped 最常用:
  崩溃 → 自动重启
  手动 stop → 不重启
  宿主机重启 → 容器跟着起来

五、优雅关闭

容器收到停止信号(SIGTERM),应处理完当前请求再退出:

1
2
# 应用要监听 SIGTERM
# .NET/Java/Go 等运行时会处理,但要正确配置
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
docker stop 默认:
  1. 发 SIGTERM
  2. 等待 10 秒(grace period)
  3. 没退出就 SIGKILL(强杀,可能丢数据)

docker stop -t 30 web   # 等 30 秒

应用要做:
  ✓ 监听 SIGTERM
  ✓ 停止接收新请求
  ✓ 处理完已有请求
  ✓ 释放资源(关闭连接、刷盘)
  ✓ 退出

  否则:强杀 → 请求中断、数据丢失

六、日志管理

1
2
3
4
5
6
7
Docker 日志两种:
  1. 容器 stdout/stderr(应用日志)
     docker logs web
     日志驱动(logging driver)决定怎么处理

  2. 容器内文件日志
     要挂载出来或用 agent 收集
1
2
3
4
5
6
7
8
# 配置日志驱动 + 日志轮转(防日志撑爆磁盘)
services:
  app:
    logging:
      driver: json-file
      options:
        max-size: "10m"       # 单文件最大 10M
        max-file: "3"         # 保留 3 个
1
2
3
4
5
6
7
8
日志驱动:
  json-file(默认)— 存文件
  fluentd / syslog / journald — 转发到日志系统
  none — 不存

生产方案:
  应用日志输出到 stdout → Docker 收集 → 转发到 ELK/Loki
  (容器十二要素应用原则:日志当事件流,输出到 stdout)

七、监控

1
2
3
# 内置:实时资源占用
docker stats
# CONTAINER  CPU %  MEM USAGE / LIMIT  MEM %  NET I/O  BLOCK I/O
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
生产监控栈:
  cAdvisor    — 采集容器指标(CPU/内存/网络/磁盘)
  Prometheus  — 存储时序指标
  Grafana     — 可视化看板
  Node Exporter — 宿主机指标

  应用内部指标 → Prometheus client 暴露 /metrics
  容器指标 → cAdvisor
  日志 → ELK/Loki
  告警 → Alertmanager

八、安全加固

8.1 最小权限

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
services:
  app:
    user: "1000:1000"          # 非 root 运行
    read_only: true            # 根文件系统只读
    tmpfs:
      - /tmp                   # 需要写的目录用 tmpfs
    cap_drop:
      - ALL                    # 丢弃所有 Linux capabilities
    cap_add:
      - NET_BIND_SERVICE       # 只加需要的
    security_opt:
      - no-new-privileges      # 禁止提权

8.2 镜像安全

1
2
3
4
5
6
✓ 用可信基础镜像(官方、固定版本 tag)
✓ 镜像扫描:docker scout / trivy / clair
  扫描已知漏洞(CVE)
✓ 用 distroless/scratch(减小攻击面)
✓ 多阶段构建(不带源码/构建工具)
✓ 不用 root

8.3 密钥管理

1
2
3
4
5
6
7
❌ 密钥写进镜像 / 环境变量明文(会泄露)
✓ Docker Secret(Swarm)/ K8s Secret
✓ 挂载文件(只读,不进镜像、不进 env)
✓ 外部密钥管理(Vault、云 KMS)

  环境变量也不安全(inspect 能看到、子进程能读)
  生产用 Secret 文件挂载或外部 KMS

8.4 更强的隔离

1
2
3
4
5
6
7
默认容器共享宿主机内核 → 内核漏洞可能逃逸
更强隔离方案:
  gVisor     — 用户态内核,拦截系统调用(Google)
  Kata Containers — 轻量虚拟机跑容器(VM 级隔离)
  Firecracker — 微 VM(AWS,极快启动)

  多租户、高安全场景用这些替代默认 runc

九、Docker vs Kubernetes

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
Docker / Docker Compose:
  ✓ 单机
  ✓ 简单、上手快
  ✓ 本地开发、CI、小项目
  ✗ 不能跨机器、故障自愈弱、无滚动更新

Kubernetes:
  ✓ 多机集群、自动调度
  ✓ 自愈(容器挂了自动重启/迁移)
  ✓ 滚动更新、回滚
  ✓ 服务发现、负载均衡、扩缩容
  ✓ 配置/密钥管理
  ✗ 复杂、运维成本高

选择:
  单机 / 小项目 / 本地     → Docker Compose
  多机 / 生产 / 微服务      → Kubernetes
  中间过渡                 → Docker Swarm(简单,但已式微)

  生产正规军:Docker 打镜像 → K8s 编排运行
  (Docker 是构建/打包工具,K8s 是运行/编排平台)

十、CI/CD 集成

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
典型流程(结合本站 GitHub Actions 自动部署文章):

  开发者 push 代码
    → GitHub Actions 触发
    → docker build 构建镜像
    → docker push 到镜像仓库
    → 服务器 docker pull + docker compose up -d
    (或 K8s kubectl apply)

  Docker 让"构建一次,到处运行"成为可能
  是 CI/CD 流水线的核心组件

十一、小结

  • 生产清单:健康检查、资源限制、重启策略、日志、监控、优雅关闭、安全
  • 健康检查:HEALTHCHECK + /health 端点
  • 资源限制:limits/reservations,防止"吵闹邻居"
  • 优雅关闭:监听 SIGTERM,处理完请求再退出
  • 日志:输出 stdout,日志驱动转发,轮转防爆盘
  • 监控:cAdvisor + Prometheus + Grafana
  • 安全:非 root、只读根、cap_drop、镜像扫描、密钥管理、gVisor
  • Docker vs K8s:Docker 打包构建,K8s 编排运行

系列总结

Docker 五篇完结:

  1. 基础与原理:容器 vs 虚拟机、namespace/cgroups/UnionFS
  2. Dockerfile:多阶段构建、镜像瘦身、安全
  3. Compose:多服务编排、服务发现、依赖管理
  4. 网络与存储:bridge 自定义网络、Volume/Bind/tmpfs、无状态原则
  5. 生产实践:健康检查、资源限制、日志监控、安全加固、Docker vs K8s

核心心法:容器让应用与运行环境解耦——一次构建,到处运行。Docker 负责打包和单机,K8s 负责多机编排。理解容器的隔离(namespace)、限制(cgroups)、分层(UnionFS),就理解了云原生的根基。