dumb-init vs tini vs wait - 小镇
dumb-init vs tini vs wait
你应该是想比较 dumb-init vs tini vs wait(shell 内置 wait),以及 PID 1 场景下子进程僵尸回收是否影响父进程。
先给结论:
-
dumb-init / tini 都是容器里的 PID 1 init 进程,用于信号转发 + 僵尸进程回收。
-
wait 只是 shell 等待子进程退出的同步机制,不是 init,不负责 orphan reaping。
-
子进程僵尸不被回收:
-
不会直接阻塞父进程运行
-
但是会消耗 PID 表项,长期可能导致
fork()失败,最终影响父进程或整个容器。
-
-
在 Kubernetes 中,推荐使用 tini(Docker 默认 init)或者 dumb-init。
1. dumb-init vs tini vs wait 对比
| 特性 | dumb-init | tini | wait |
|---|---|---|---|
| 类型 | 小型 init | 小型 init | shell builtin |
| 用途 | 容器 PID 1 | 容器 PID 1 | 等待子进程 |
| 是否可作为 PID 1 | ✅ | ✅ | ❌ |
| 信号转发 | ✅ | ✅ | ❌ |
| SIGTERM 优雅退出 | ✅ | ✅ | 部分依赖 shell |
| 僵尸进程回收 | ✅ | ✅ | ❌ |
| orphan 子进程接管 | ✅ | ✅ | ❌ |
| 子进程退出状态传播 | ✅ | ✅ | ✅ |
| 资源占用 | 极低 | 极低 | 无额外 |
| 代码复杂度 | Python/C | C | shell |
| Docker 官方支持 | 否 | ✅ Docker --init 默认 | 否 |
| Kubernetes 常用 | 较多 | 较多 | 不推荐 |
2. 容器为什么需要 tini/dumb-init?
Linux 里面:
父进程|+-- child|+-- grand child
如果 child 死掉:
PID 1|+-- orphan grand child
Linux 会把 orphan 收养给 PID 1:
PID 1|+-- orphan
然后 PID 1 必须:
waitpid(-1, ...)
回收它。
否则:
PID 1|+-- zombie
出现:
ps auxroot 20 Z worker <defunct>
3. dumb-init / tini 工作模型
例如:
container|PID 1tini|+-- nginx|+-- worker
启动:
tini|exec nginx
收到:
docker stop|vSIGTERM|vtini
tini:
转发 SIGTERM|vnginx
同时:
waitpid()
waitpid()
waitpid()
回收 zombie。
4. dumb-init vs tini 差异
tini
Docker 官方推荐:
docker run --init
实际上:
docker-init|tini
特点:
-
C 实现
-
极小
-
专注 init
-
Kubernetes 常见
dumb-init
结构:
dumb-init|exec app
额外能力:
session 模式
例如:
dumb-init -- python app.py
可以处理:
SIGTERM
SIGINT
SIGKILL
并转发给整个 process group。
适合:
-
Python
-
Java
-
Node.js
-
shell wrapper
5. wait 和 tini/dumb-init 最大区别
假设:
Dockerfile:
CMD ["bash","start.sh"]
start.sh:
#!/bin/bashpython worker.py &wait
进程:
PID 1 bash|+-- python worker
这里:
wait
只是:
bash等待python退出
它不会:
-
接管 orphan
-
reaping zombie
-
转发信号
例如:
python|+-- child
python crash:
child|orphan
最后:
PID 1 bash
成为收养者。
但是 bash 的 wait 不负责:
waitpid(-1)
所以 zombie 可能留下。
6. 僵尸子进程会影响父进程吗?
短期
通常:
parent|+-- zombie
父进程:
继续运行
例如:
PID 1 nginxPID 100 worker Z
nginx 还能工作。
长期
影响:
1. 消耗 PID
Linux 有 PID 上限:
/proc/sys/kernel/pid_max
大量 zombie:
PID table full
导致:
fork: Resource temporarily unavailable
例如:
Java:
Unable to create native thread
Python:
OSError: [Errno 11] Resource temporarily unavailable
2. 影响容器重启
Kubernetes:
Pod|PID 1|zombie x 10000
可能:
container still running
但是:
liveness probe fail
最终 kill。
7. PID 1 特殊性
Linux 对 PID 1 有特殊处理。
普通进程:
default SIGTERM handler
PID 1:
如果没有注册 handler
默认忽略很多信号
例如:
你的程序:
PID 1 python app.py
执行:
docker stop
发送:
SIGTERM
但是:
Python PID1:
没有处理
可能:
30秒后 SIGKILL
导致:
-
无优雅退出
-
数据丢失
tini:
docker stop|tini|SIGTERM|app
解决。
8. Kubernetes 推荐结构
推荐:
container|PID 1 tini|application|workers
例如:
Dockerfile:
FROM alpineRUN apk add tiniENTRYPOINT ["/sbin/tini","--"]CMD ["./server"]
或者:
Docker:
docker run --init image
9. dumb-init vs tini 选择
| 场景 | 推荐 |
|---|---|
| Docker 官方环境 | tini |
| Kubernetes | tini |
| Python/Node shell wrapper | dumb-init |
| 需要 signal process group | dumb-init |
| 最简单 init | tini |
| distroless | tini 静态二进制 |
总结
| 问题 | 答案 |
|---|---|
| dumb-init/tini 是什么? | 容器 PID1 init |
| wait 能代替吗? | 不能 |
| 僵尸影响父进程吗? | 不会立即影响,但长期耗尽 PID |
| 为什么 Kubernetes 需要? | 处理 SIGTERM + zombie reaping |
| tini 和 dumb-init 谁好? | 大多数情况 tini;复杂 shell 场景 dumb-init |
| 父进程退出 zombie 会怎样? | 被 PID1 接管并回收 |
对于 Kubernetes,实际最佳实践通常是:
PID 1|tini|your application
而不是:
PID 1|bash|application