当节点磁盘打满时如何优雅迁移容器

解读

国内生产环境常见“磁盘打满”场景:

  1. 镜像层堆积(旧版本未清理)
  2. 未挂载外部卷的容器写入大量日志
  3. 私有 Registry 与节点混部,镜像同步占满系统盘
  4. 云主机只给 20~40 GiB 系统盘,未单独挂数据盘

面试官想验证:

  • 能否在不中断业务的前提下把容器漂到空闲节点
  • 是否熟悉 Docker 存储驱动、日志驱动、卷管理
  • 对“优雅”二字的理解:服务发现、连接保持、数据一致性、回滚预案

知识点

  1. Docker 存储驱动差异:overlay2 需 inode 与磁盘空间双重余量,devicemapper 直接-vg 打满即拒绝写入
  2. 容器可写层与卷(volume、bind mount)生命周期解耦
  3. docker inspect 查看 GraphDriver.Data 确认占用来源
  4. 日志驱动 json-file 的 max-size、max-file 轮转参数
  5. Swarm 模式下的 drain 节点与 rolling update 机制
  6. CRIU 技术边界:Docker 默认不开启 checkpoint/restore,国内多数公司用滚动重启代替热迁移
  7. 国内云厂商 ESSD、云盘快照、在线扩容 特性,可在 OS 不重启情况下扩容系统盘
  8. 强制清理命令:docker system prune -a、docker volume prune,但风险高,需先确认无未挂载数据库卷

答案

回答时分“应急止血”与“优雅迁移”两步,给出可落地的 SOP,并强调风险控制。

  1. 应急止血(30 秒内决策)
    a. 立即 du -h /var/lib/docker 定位最大子目录
    b. 若是日志,执行 echo "" > $(docker inspect --format='{{.LogPath}}' 容器ID) 快速截断,业务无损
    c. 若是镜像层,执行 docker image prune -a --filter "until=24h" 释放空间,保留最近一天镜像防回滚失败
    d. 若系统盘剩余 <5%,临时挂载云盘扩容或做软链接 /var/lib/docker → /data/docker,扩容后必须重启 Docker 守护进程

  2. 优雅迁移(保证 SLA)
    前提:业务已用 Swarm 或 Kubernetes,容器无状态,持久卷放在 NAS/云盘
    a. 在 Swarm 管理节点执行
    docker node update --availability drain 目标节点
    该节点上的副本会被平滑驱逐,新实例在空闲节点拉起,旧容器等待 stop-grace-period(默认 10s) 自然退出
    b. 若容器挂载了本地 volume,需提前
    docker run --rm -v 源卷:/from -v 远程NAS:/to alpine cp -a /from /to/$(hostname)
    然后 docker service update --mount-add type=volume,source=新卷,target=/data 服务名
    c. 迁移后做 三次健康检查

    • docker service ps 服务名 观察 NEW 任务状态 Running
    • 业务探活接口 200 OK
    • 监控告警恢复
      d. 原节点磁盘清理完毕,再 docker node update --availability active 重新上线
  3. 回滚预案
    若新实例启动失败,立即
    docker service scale 服务名=原副本数+1
    并在原节点 docker start 旧容器(日志已截断,可临时复活),保证业务不断

  4. 后续根治

    • 把 /var/lib/docker 和 /var/log 挂到独立云盘,系统盘只放 OS
    • 在 /etc/docker/daemon.json 统一日志驱动:
      {"log-driver":"json-file","log-opts":{"max-size":"50m","max-file":"3"}}
    • 在 CI 阶段加 docker-slim多阶段构建,镜像体积下降 60%
    • 用 crontab+Prometheus 监控磁盘余量 <15% 就报警,并自动触发 prune

拓展思考

  1. 如果业务容器有状态且未用外部卷,能否直接打 tar 包把可写层拷走?
    答:overlay2 下可写层位于 /var/lib/docker/overlay2/容器ID/diff,可 tar + rsync 到目标节点,再用 docker create + docker cp 导入,但需保证存储驱动一致,且容器停写,否则文件句柄不一致导致数据损坏。生产环境不推荐,应强制用外部卷或 StatefulSet。

  2. 国内金融合规要求审计日志不能截断,如何既释放空间又保留日志?
    答:把 json-file 改为 local 驱动并开启压缩,或 sidecar 方式用 filebeat 实时采到 Kafka,本地保留 2 小时后自动轮转,满足《银行业信息系统灾难恢复规范》中“日志不可缺失、本地可短暂清理”条款。

  3. 当集群所有节点磁盘同时告急,Swarm 无法调度,如何兜底?
    答:

    • 立即在镜像仓库做 GC:registry garbage-collect config.yml,删除未引用的 blob
    • 启用 镜像预热策略:只保留最近两个版本,CI 自动打标签并触发清理脚本
    • 临时把副本数缩为 0 的非核心服务下线,为核心服务腾出空间,保证黄金交易链路不断

通过以上结构化回答,既展示了快速止血的实战能力,也体现了对高可用、合规、长期治理的深度思考,符合国内中大型企业对 Docker 岗位的技术期待。