Docker 容器管理:从创建到销毁的完整生命周期

容器的生命周期管理,是 Docker 日常操作里最基础也最频繁的部分。说实话,我刚接触 Docker 那阵子,光记命令就费了不少劲,后来发现其实就那么几个操作——创建、启动、停止、删除、进入、看日志、限制资源。搞懂了这些,无论多复杂的批处理流程都能控制住。

核心概念:容器本质是一个运行中的进程隔离环境。你创建它时,它还是一个“待机”状态;只有 start 后进程才会跑起来。这一点我见过不少新手搞混,以为 create 就等于启动了。

容器生命周期管理核心流程 docker create docker start 运行中容器 docker stop docker rm docker exec docker logs 资源限制 创建≠启动 create 只拉取/准备镜像 执行容器内服务 docker exec -it CONTAINER bash 日志实时查看 docker logs -f CONTAINER 资源限制参数 --memory=2g --cpus=1.5

容器的创建与启动:先“制造”再“点火”

创建容器,我通常用 docker create 配合镜像名和启动命令。为什么不用 docker run 一步到位?因为有时候你需要先准备好容器环境,再择机启动。举个例子:

docker create --name qc-pipeline \
  -v /data/inputs:/workspace/inputs \
  -v /data/outputs:/workspace/outputs \
  ubuntu:22.04 /bin/bash -c "python3 run_qc.py"

这条命令创建了一个名为 qc-pipeline 的容器,挂载了两个数据卷,但此时容器并未运行。启动它也很简单:

docker start qc-pipeline

我的习惯:在批次质控流程里,我倾向于先 create 再 start,这样可以在启动前检查挂载路径是否正确,避免一跑起来就报错。

如果需求简单,直接用 docker run 一步到位也行。它会自动 pull 镜像、创建容器、启动容器,一条命令搞定。比如:

docker run --name batch-qc -d --rm \
  -v $(pwd)/data:/data \
  qc-image:latest

注意这里的 --rm 参数——容器停止后自动删除。我之前在自动化流程里忘加这个,跑完一批测试后磁盘被几百个停止状态的容器占满了。嗯,踩过坑你就记住了。

停止与删除:不要留垃圾

容器停止可以用 docker stop,它会发 SIGTERM 信号给主进程,等待超时(默认10秒)后强制 SIGKILL。如果你确认容器内进程已经处理完,想立刻停掉:

docker kill qc-pipeline

删除容器我建议分两步:先停,再删。当然也可以用 docker rm -f 强制删除运行中的容器。但这样做可能会有数据丢失的风险。我曾经有一次在删除前没等质控结果写盘,结果那批样本的中间结果全丢了……从那以后我再也不敢强删正在写数据的容器。

操作 命令 说明
优雅停止 docker stop CONTAINER 发 SIGTERM,等超时后强制
强制停止 docker kill CONTAINER 直接发 SIGKILL
删除容器 docker rm CONTAINER 只能删已停止的
强制删除 docker rm -f CONTAINER 删运行中的容器(小心)

进入容器:调试和配置的入口

当你需要查看容器内部情况、修改配置或手动执行命令时,docker exec 是标配。比如以交互式终端进入正在运行的容器:

docker exec -it qc-pipeline /bin/bash

如果容器里没有 bash(比如 alpine 镜像),可以用 sh:

docker exec -it qc-pipeline sh

我个人经常用这个命令排查问题——比如批次质控脚本没输出日志,我就进去检查 Python 包有没有装全、路径对不对。注意 -it 参数:-i 保持标准输入打开,-t 分配伪终端。少了它,很多交互式命令没法用。

注意:exec 进入的容器必须处于运行状态。如果容器已经停止,你需要先启动它,或者用 docker commit 将其保存为新镜像再启动排查。

日志查看:容器内部的“黑匣子”

看我日志,就像看飞行数据记录仪一样,能还原容器里发生了什么。docker logs 默认返回标准输出和标准错误。实时跟踪最新日志:

docker logs -f qc-pipeline

加上 --tail 只看最近几行:

docker logs --tail 100 qc-pipeline

时间戳有时候也很关键——特别是要跟外部调度系统对齐时:

docker logs -t qc-pipeline

我在做批次回溯时,会把每批容器的日志按文件名保存下来:

docker logs batch_20240301 > /logs/20240301/batch.log 2>&1

这样万一后面发现某个样本的质控异常,翻日志就能定位到问题环节。

资源限制:别让一个容器拖垮整个节点

在质控流程里,经常有多批次容器同时跑。不管制资源的话,一个写 Python 死循环的脚本就能把 CPU 吃满,其他容器跟着遭殃。我建议在创建容器时就限制好内存和 CPU:

docker run --name test-qc \
  --memory=4g \
  --memory-swap=4g \
  --cpus=2 \
  --cpuset-cpus=0,1 \
  qc-image:latest

参数说明:

  • --memory:最大可用内存,单位 b/k/m/g。超出会触发 OOM kill。
  • --memory-swap:总内存+交换分区大小。设置为与 memory 相同则禁用交换。
  • --cpus:允许使用的 CPU 核数,可以是小数(如 1.5)。
  • --cpuset-cpus:绑定到特定物理 CPU 核心(如 0,1)。

实战技巧:对于 IO 密集型的质控步骤,还可以限制磁盘 IO:--device-read-bps /dev/sda:50mb --device-write-bps /dev/sda:50mb。我有一回就是忘了限制写带宽,结果四个容器同时写盘,把 NAS 打挂了……

查看容器当前资源使用情况,用 docker stats:

docker stats qc-pipeline

它会实时显示 CPU%、内存、网络 IO、磁盘 IO。我常常开着它对多个容器做对比,判断哪个流程更吃资源。

实践要点:我这几年总结的几个习惯

  1. 给容器起有意义的名称——不要用自动生成的随机名,用 --name 带上批次号或流程标识。
  2. 批量清理已停止的容器:docker container prune 一键清除所有停止状态的容器。
  3. 日志统一管理:Docker 默认日志驱动是 json-file,日志文件会越积越大。我习惯加 --log-opt max-size=10m --log-opt max-file=3 限制日志轮转。
  4. 资源限制一定要设——哪怕你觉得自己写代码很稳,也要防一手数据异常导致的全流程雪崩。

我个人最常用的一条组合拳:docker run -d --restart=on-failure:3 --memory=2g --cpus=1 name:tag。这样容器崩溃后最多自动重启 3 次,既能容错又不会无限重试把资源耗尽。

好了,容器的生命周期管理就这些。你只要把 create、start、stop、rm、exec、logs 和资源限制这几个操作练熟,绝大部分批次质控流程的容器化部署都不会有障碍。下次遇到容器跑着跑着没反应的情况,先别慌,docker exec -it 进去看一眼,再 docker logs 查下输出,多半能找到线索。