首页/云/Docker 容器速查/ENTRYPOINT 结合 tini 做 PID1
镜像与构建107 条

ENTRYPOINT 结合 tini 做 PID1

在镜像里把 tini 作为入口:它负责信号转发与子进程回收,与 docker run --init 等效但写进镜像、随处生效。

Docker 容器速查云更新于 2026-10-13

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 1tini 成为 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 或编排平台负责。

易错点

  • 漏写 -- 分隔符:tini 把应用的参数当成自己的选项解析,容器启动即报未知参数
  • 路径写错:不同发行版 tini 安装位置不同,换基础镜像后启动失败,装完先确认实际路径再写 ENTRYPOINT
  • 只装 tini 没改 ENTRYPOINT:进程树里依旧没有 init,信号与僵尸进程问题照旧存在
  • 误以为 tini 能解决所有进程问题:应用若 fork 到后台并退出,容器照样结束,这属于应用的启动方式问题

数据来源与参考

  • tini 项目文档(github.com/krallin/tini)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌