3.3 容器生命周期与进程模型


3.3 容器生命周期与进程模型

摘要:容器的生老病死在进程层面都有确切对应:run 是创建加启动,stop 是发信号加等待,pause 是冻结 cgroup。本节拆解每条生命周期命令的真实动作,讲透 PID 1 的信号困境与僵尸进程问题,并处理"一启动就退出""杀不掉"两类高频故障。

学习目标

  1. 说出 run/start/stop/kill/pause/rm 各命令在进程层面的动作
  2. 解释容器存活与前台进程的绑定关系,判断"跑完即退"是否异常
  3. 理解 PID 1 的信号处理责任与僵尸进程成因
  4. 排查容器启动即退与无法停止两类故障

每条命令都是一次进程操作

把生命周期命令翻译成进程语言:

  • docker run = 创建进程(配 namespace、cgroup、overlay 挂载)+ 立即启动
  • docker start / stop:对已存在容器的启动与终止
  • docker stop = 向 PID 1 发 SIGTERM,默认等 10 秒,超时补 SIGKILL
  • docker kill = 跳过商量直接 SIGKILL(可 -s 指定信号)
  • docker pause = 冻结该 cgroup 内全部进程(SIGSTOP 级别),进程还在,只是不调度
  • docker rm = 删除容器(连带可写层目录),运行中容器需 -f

所以"容器活着"的确切含义是:容器内的 1 号进程活着。1 号退出,容器即退出。这条规则解释了无数新手困惑。

跑完即退不一定是坏了

docker run ubuntu echo hello

输出 hello 后容器退出。这不是故障:echo 是前台进程,执行完自然退出,容器随之结束。反过来,如果 Dockerfile 里写了 CMD service nginx start,service 把 nginx 派生到后台就返回,1 号进程退出,容器立刻死亡——这就是"为什么容器里起服务不能用后台方式"的根源。容器的正确姿势是前台常驻:CMD ["nginx", "-g", "daemon off;"],1 号就是 nginx 本尊。

排查"一启动就退出"的标准流程:

docker logs <容器> docker inspect <容器> --format '{{.State.ExitCode}} {{.State.Error}}'

日志与退出码几乎总能给出答案:配置文件路径不对(退出码 1)、依赖的数据库连不上(应用自毁)、或者如上所述 1 号进程本身就不是常驻进程。

stop 的 10 秒:PID 1 的信号责任

docker stop 发 SIGTERM 给 1 号进程,期待它处理信号、清理资源、退出。前提是信号真的能到达你的应用。两种典型断路:

断路一:shell 形式的 CMD。 CMD python server.py 实际 1 号是 /bin/sh,sh 不转发信号给子进程,你的应用永远收不到 SIGTERM,只能等 10 秒后被 SIGKILL 强杀,来不及落盘与断连。解法是 exec 数组形式,2.2 节已经立过规矩。

断路二:应用没装信号处理器。 收到 SIGTERM 按默认行为立即退出,连接中的请求被硬切。成熟框架都提供优雅退出钩子,把处理器接上。

PID 1 的另一职责:收割僵尸。 容器内 1 号进程是所有孤儿进程的养父,内核把孤儿托付给它,期待它 wait 回收。如果你的应用不调用 wait(多数业务程序不做这件事),它派生的子进程退出后就变成僵尸,积累到 pids 上限,容器就再也无法创建新进程。官方基础镜像里那些 --init 参数、tini 小工具干的就是这个:当专业 1 号,替你转发信号加收割僵尸:

docker run -d --init myapp

一行参数换来正确的信号语义与干净的进程表,生产环境值得默认加上。

docker stop 的完整时序

杀不掉的容器与清理残局

偶尔遇到容器卡在 Removing 状态杀不掉,通常与宿主机存储或内核状态有关。处置顺序:

# 先看真实进程还在不在 ps -ef | grep <容器id> # 1 号还在:直接对真实 PID 动手 kill -9 <真实PID> # 进程已死但容器状态卡住:等几秒再试 rm -f docker rm -f <容器>

批量清理现场用组合拳(注意先确认再执行):

# 清理全部停止的容器 docker container prune -f # 清理悬挂镜像 docker image prune -f

pause 的一个实用场景:宿主机要做内存压力测试或快照,docker pause 冻结容器、不杀进程,测完 docker unpause 原样恢复。与 stop 的区别在于恢复成本——pause 是暂停音乐,stop 是关机重启。

restart 策略:让容器学会自愈

docker run -d --restart=on-failure:5 myapp

三种策略:no(默认,死了不管)、always(守护进程启动也拉起)on-failure:N(仅非零退出码重启,最多 N 次)。注意 restart 治标不治本:应用有 bug 反复崩溃,重启策略只会制造"看起来活着"的假象,根因还得靠日志与退出码排查。真正的自愈要在编排层(第 6 章)做健康检查驱动的重启。

本节要点回顾

  • 容器活着 = 1 号进程活着,一切生命周期命令本质是进程操作
  • 服务必须前台运行,后台起服务等于宣布容器死刑
  • stop 的优雅程度取决于信号通路:exec 数组形式 + 信号处理器 + 可选 --init
  • 僵尸进程是 1 号的失职,--init 或 tini 一行解决
  • restart 策略是止血带不是药,自愈要靠健康检查

运行时层拆完。接下来向上走到容器与外界连接的那一层——网络。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U