Docker中使用gosu的方法详解
作者:雲帝
在Docker镜像中启动服务时,我们通常不希望以root身份运行主进程,sudogosu是一个非常轻量的工具,功能类似sudo,但不会产生额外进程,非常适合在Docker容器中用来切换用户,本文给大家介绍Docker中使用gosu的方法,感兴趣的朋友一起看看吧
Docker中使用gosu
为什么要用 gosu?
在 Docker 镜像中启动服务时,我们通常不希望以 root 身份运行主进程。
问题:
- 构建镜像时通常需要 root 权限;
- 启动时希望进程使用普通用户运行;
- 容器中使用
sudo会产生额外的中间进程,导致信号转发不正常; - 最终可能出现僵尸进程、容器无法优雅退出等问题。
gosu 是一个非常轻量的工具,功能类似 sudo,但不会产生额外进程,非常适合在 Docker 容器中用来切换用户。
注意事项
- 创建专用普通用户,不要直接使用 root。
- 在 entrypoint 中使用 gosu 切换用户,不要用 sudo。
- 使用 exec 启动主进程,保证 PID=1 是应用本身。
- 正确接收 SIGTERM 信号,避免僵尸进程。
- 不要在 Docker 中滥用 sudo。
sudo vs gosu:真实对比
使用 gosu
docker run --rm gosu/alpine gosu root ps aux
输出:
PID USER TIME COMMAND
1 root 0:00 ps aux
ps命令的 PID 是 1,容器内只有一个进程。- 这样当宿主机发送
SIGTERM信号时,应用能直接接收到。
使用 sudo
docker run --rm ubuntu:trusty sudo ps aux
输出:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 46012 1772 ? Rs 12:05 0:00 sudo ps aux
root 6 0.0 0.0 15568 1140 ? R 12:05 0:00 ps aux
- 容器内出现两个进程,PID=1 被
sudo占用。 - 应用进程的 PID 不为 1,无法直接接收信号。
- 当执行
docker stop时,信号发送给的是sudo,而不是应用进程,可能导致应用残留为僵尸进程。
对比:
| 项目 | gosu | sudo |
|---|---|---|
| 进程数 | 1 | 2 |
| PID=1 | 应用进程 | sudo |
| 信号转发 | 正常 | 不正常 |
| 僵尸进程风险 | 低 | 高 |
| 适合容器使用 | 是 | 否 |
Dockerfile 安装 gosu
在 Dockerfile 中添加如下步骤即可:
FROM ubuntu:22.04 # 安装 gosu RUN apt-get update && apt-get install -y gosu curl ca-certificates && rm -rf /var/lib/apt/lists/* # 创建普通用户 RUN groupadd -r tempuser && useradd -r -g tempuser tempuser # 拷贝入口脚本 COPY entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/entrypoint.sh # 验证 gosu 是否工作 RUN gosu nobody true ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
entrypoint.sh 的写法
entrypoint.sh 是容器启动脚本。
#!/usr/bin/env bash
set -e
# 使用 gosu 切换用户身份
if [ "$1" = "start-app" ]; then
echo "Running application as tempuser..."
exec gosu tempuser /app/my_server
fi
# 如果有其他命令,直接执行
exec "$@"注意:exec 会让当前 shell 被新进程取代,保证应用进程的 PID=1。
构建 & 运行容器
docker build -t myapp . docker run --rm -p 8080:8080 myapp start-app
容器内:
/app/my_server的 PID = 1- 收到
docker stop的信号时,能正确退出 - 不会残留
sudo或 shell 僵尸进程
信号量与僵尸进程问题
容器启动时 PID=1 的进程非常特殊,它负责接收来自宿主机的信号(例如 SIGTERM、SIGINT)。
- 如果 PID=1 是
sudo,信号不会传给应用; - 如果 PID=1 是应用进程本身,就能优雅退出;
- 使用
exec+gosu可以保证 PID=1 是你的应用; - 避免使用 shell 包一层后
&运行后台进程的错误做法。
常见问题
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 容器退出时残留僵尸进程 | PID=1 不是应用进程 | 使用 exec + gosu |
| docker stop 不生效 | 信号被 sudo 拦截 | 不使用 sudo,PID=1 改为应用 |
| gosu 报权限错误 | 二进制无执行权限 | chmod +x /usr/local/bin/gosu |
| 切换用户失败 | 用户不存在 | 使用 useradd 创建对应用户 |
参考资料
- gosu 官方 GitHub
- Docker 官方最佳实践
- Docker 官方最佳实践中文
- Why use exec in entrypoint?(Stack Overflow)
- CSDN docker与gosu
到此这篇关于Docker中使用gosu的文章就介绍到这了,更多相关docker使用gosu内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
