tini 与 PID 1 的关系
容器的 PID 1 除运行主进程外,还负责把信号转发给子进程、回收孤儿进程。很多应用并不是按 PID1 的语义写的,直接当 PID1 会出现 docker stop 后迟迟不退、僵尸进程堆积的问题。tini 是一个体积极小的 init,把它写进镜像入口,就能让每个容器具备这两项能力,效果与 docker run --init 相同,区别在于写进镜像后在任何运行环境都自动生效。
基本信息
| 用途 | 以轻量 init 身份充当容器 PID 1 |
|---|---|
| 安装 | apk add --no-cache tini 或 apt-get install -y tini |
| 入口写法 | ENTRYPOINT ["/sbin/tini", "--"] 后接 CMD |
| 运行时等价物 | docker run --init |
| 路径差异 | Alpine 常为 /sbin/tini,Debian/Ubuntu 常为 /usr/bin/tini |
典型使用场景
- 应用自身不处理 SIGTERM,希望 docker stop 能触发优雅退出
- 容器内会派生子进程(如 shell 启动的守护脚本)需要回收
- 做公司统一基础镜像,向所有下游镜像注入一致的 init 行为
- 编排平台不暴露 --init 参数,只能靠镜像内部解决
- 要求本地开发与线上运行行为完全一致
镜像内用法
镜像内用法
FROM alpine:3.20
RUN apk add --no-cache tini
WORKDIR /app
COPY server /app/server
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["/app/server"]
# Debian 系请先确认路径:dpkg -L tini | grep bin验证方式
验证方式
docker run -d --name web app/api:1.4.2 # 镜像自带 tini,运行时无需加参数
docker exec web ps -o pid,ppid,comm # PID 1 应为 tini,应用是其子进程
docker stop -t 30 web # 观察停止是否及时
docker logs web | tail -20 # 确认应用收到信号并输出退出日志关键点核对
| 项 | 说明 | 核对方式 |
|---|---|---|
| -- 分隔符 | tini 之后的所有参数都归属被托管进程 | 缺少时 tini 会把应用参数当成自己的选项 |
| PID 1 | tini 成为 1 号进程 | 在容器内用 ps 查看进程树 |
| 信号转发 | SIGTERM 由 tini 传递给子进程 | docker stop 后看应用优雅退出日志 |
| 僵尸回收 | tini 负责 reap orphan | 长跑后 ps 输出中不应有 defunct 残留 |
镜像内已经使用 tini 时再叠加 docker run --init 会多一层 init,进程树里能看到两级,排查进程关系时不要误判为异常。
ENTRYPOINT 一定要用 exec 列表形式;若写成 shell 形式,shell 会占住 PID 1,tini 就失去了作用。
tini 只做信号转发与子进程回收,不提供守护能力:应用崩溃退出后它不会拉起应用,重启仍由容器的 restart policy 或编排平台负责。