init 字段解决什么
Compose 的 init: true 让容器把 PID 1 交给一个轻量 init 进程(tini),由它转发信号并回收孤儿进程,等价于 docker run --init。当容器入口是 shell 脚本、并会不断 fork 子进程时,这个字段能避免 docker stop 时信号被吞,以及僵尸进程累积。
基本信息
| 字段 | init: true |
|---|---|
| 等价写法 | docker run --init |
| 作用 | 信号转发 + 孤儿进程回收 |
| 适用版本 | Compose 文件格式 3.x 起,Compose V2 均支持 |
| 默认行为 | 不启用,PID 1 由镜像入口命令承担 |
典型使用场景
- 入口是 shell 脚本、脚本又会拉起多个后台子进程的服务
- 长时间运行且频繁创建子进程的任务型容器
- docker stop 后容器要等到超时才退出,怀疑信号没有被转发
- 容器内出现僵尸进程(ps 状态为 Z)时作为低成本修复
- 无法修改镜像入口,又需要正确的信号处理能力
Compose 写法示例
Compose 写法示例
services:
worker:
image: app/worker:1.4.2
init: true # 由 init 进程接管 PID 1
command: sh -c './worker.sh & wait' # shell 包装型入口最需要它运行期核对
运行期核对
docker top worker # 观察进程树,init 应排在最前
docker inspect -f '{{.HostConfig.Init}}' worker # true 表示已启用
docker stop -t 5 worker # 验证能否快速优雅退出启用与不启用对照
| 维度 | init: true | 不启用 |
|---|---|---|
| PID 1 | 轻量 init 进程 | 镜像入口命令,常是 shell |
| 信号转发 | 由 init 负责转发 | shell 未写 trap 就不转发 |
| 孤儿进程回收 | 自动回收 | 累积为僵尸进程 |
| 停止容器 | 收到信号后正常退出 | 可能等到超时才被强杀 |
应用自身已正确实现 PID 1 职责(直接以 exec 形式启动、内部处理信号)时可以不启用;入口是脚本类服务时加上更省心。
ENTRYPOINT 用 JSON 数组形式能让应用直接成为 PID 1,比 shell 包装更干净,也能减少对 init 的依赖。
init 只保证转发与回收这条路径正确;应用本身没有处理 SIGTERM 时进程仍会被强杀,这类情况要从应用侧改。
除 init 字段外,也可以在镜像里用 dumb-init 之类的轻量 init 作为入口包装,效果等价;选择哪种取决于基础镜像生态与团队既有做法。