Linux 中 rootless Podman 调用离线 Docker Compose 二进制文件
1. 适用场景
本文适用于以下环境:
- Linux 服务器不能直接访问互联网;
- 使用普通用户运行 rootless Podman,全程不使用
sudo podman; - 使用 Docker Compose 独立二进制文件作为
podman compose的外部 Compose provider; - 示例安装位置为
~/.local/bin/docker-compose; - 文中版本号仅作示例,部署时可替换为经过验证且与目标环境兼容的版本。
podman compose本身是一个包装命令。它会调用外部 Compose provider,再由该 provider 通过 rootless Podman API socket 管理容器。
2. 最终调用关系
普通用户执行 podman compose
↓
Podman 调用 ~/.local/bin/docker-compose
↓
docker-compose 连接 rootless Podman socket
↓
管理该普通用户自己的容器、镜像、网络和数据卷
rootless Podman 与 root 用户的 Podman 存储相互独立。因此,不要混用以下两种方式:
podman ...
sudo podman ...
3. 在联网电脑上准备离线文件
3.1 确认服务器 CPU 架构
在服务器执行:
uname -m
常见对应关系:
uname -m 输出 |
Compose 文件架构 |
|---|---|
x86_64 |
linux-x86_64 |
aarch64、arm64 |
linux-aarch64 或 linux-arm64 |
例如服务器输出 x86_64,应准备:
docker-compose-linux-x86_64
3.2 下载二进制文件
在可以联网的电脑上,从 Docker Compose Releases 页面下载目标版本对应的 Linux 二进制文件。
本文示例文件:
docker-compose-linux-x86_64
如发布页面提供校验文件,应同时下载并核对 SHA256。也可以先在联网电脑计算哈希并记录:
shasum -a 256 docker-compose-linux-x86_64
Linux 上可使用:
sha256sum docker-compose-linux-x86_64
将二进制文件通过 U 盘、内网文件服务器或 scp 复制到服务器。
例如通过 SSH 将文件复制到服务器当前用户的主目录:
scp docker-compose-linux-x86_64 your-user@server.example.com:~/
4. 以普通用户安装 Docker Compose
以下命令均以目标普通用户执行,不加 sudo。
创建用户级命令目录:
mkdir -p ~/.local/bin
移动并重命名二进制文件:
mv ~/docker-compose-linux-x86_64 ~/.local/bin/docker-compose
增加执行权限:
chmod 755 ~/.local/bin/docker-compose
也可以使用:
chmod +x ~/.local/bin/docker-compose
两者在该场景下通常都能正常使用,但含义略有区别:
chmod 755明确设置为所有者可读写执行,其他用户可读执行;chmod +x仅在现有权限基础上增加执行位,不重设其他权限。
检查文件类型和权限:
file ~/.local/bin/docker-compose
ls -l ~/.local/bin/docker-compose
验证二进制文件可以运行:
~/.local/bin/docker-compose version
预期输出类似:
Docker Compose version v5.3.0
如果出现 Exec format error,通常是下载的 CPU 架构不匹配。
5. 配置用户级 PATH
确认当前 PATH 是否包含 ~/.local/bin:
echo "$PATH"
command -v docker-compose
如果找不到 docker-compose,将下面内容加入 ~/.profile:
export PATH="$HOME/.local/bin:$PATH"
让配置立即生效:
. ~/.profile
再次验证:
command -v docker-compose
docker-compose version
预期路径类似:
/home/your-user/.local/bin/docker-compose
6. 启用 rootless Podman API socket
检查用户级 socket:
systemctl --user status podman.socket
启用并立即启动:
systemctl --user enable --now podman.socket
检查 socket 文件:
ls -l "${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/podman/podman.sock"
查看实际监听地址:
systemctl --user show podman.socket -p Listen
典型地址为:
unix:///run/user/1000/podman/podman.sock
其中 1000 是当前用户 UID,可通过下面命令查看:
id -u
可选:退出 SSH 后仍保留用户服务
如果需要用户退出登录后继续保留 user systemd 服务,可由管理员执行一次:
sudo loginctl enable-linger user
这不是运行 Podman 容器时使用 sudo,而是管理员对该用户的 systemd linger 设置。
7. 指定 Compose provider 和 Podman socket
当前终端临时配置:
export PODMAN_COMPOSE_PROVIDER="$HOME/.local/bin/docker-compose"
export DOCKER_HOST="unix://${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/podman/podman.sock"
说明:
PODMAN_COMPOSE_PROVIDER指定podman compose调用哪个外部程序;DOCKER_HOST让 Docker Compose 连接当前用户的 Podman socket,而不是/var/run/docker.sock。
为了每次登录自动生效,将以下内容加入 ~/.profile:
export PATH="$HOME/.local/bin:$PATH"
export PODMAN_COMPOSE_PROVIDER="$HOME/.local/bin/docker-compose"
export DOCKER_HOST="unix://${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/podman/podman.sock"
重新加载:
. ~/.profile
检查变量:
printf '%s\n' "$PODMAN_COMPOSE_PROVIDER"
printf '%s\n' "$DOCKER_HOST"
8. 验证 Podman 调用 Docker Compose
先分别验证 Podman 和 Compose:
podman version
docker-compose version
再验证包装调用:
podman compose version
正常情况下会看到类似输出:
>>>> Executing external compose provider "/home/your-user/.local/bin/docker-compose". Please refer to the documentation for details. <<<<
Docker Compose version v5.3.0
这段提示不是错误,它表示 Podman 已经按配置调用外部 Docker Compose provider。
还可以验证 Compose 是否能够访问 Podman API:
docker-compose ls
podman ps -a
9. 离线部署镜像
在联网环境下载并导出镜像:
podman pull docker.io/library/alpine:latest
podman save -o alpine-latest.tar docker.io/library/alpine:latest
将镜像包复制到离线服务器后,以运行服务的同一个普通用户导入:
podman load -i alpine-latest.tar
检查镜像:
podman images
注意:如果使用 sudo podman load 导入,镜像会进入 root 用户的存储,普通用户执行 podman images 时看不到。
10. 使用 Compose 启动服务
进入 Compose 项目目录:
cd ~/compose-app
先检查最终解析结果:
podman compose config
后台启动:
podman compose up -d
如果应用提供了自己的管理脚本,应按照该应用的部署说明执行,而不是绕过脚本直接操作 Compose。
./service.sh up
查看状态:
podman ps -a
podman compose ps
查看日志:
podman compose logs --tail 100
podman logs --tail 100 容器名称
停止服务但保留数据卷:
podman compose down
不要随意使用
down -v,它会删除 Compose 管理的数据卷,可能造成业务数据丢失。
11. .env 文件注意事项
.env 通常包含密码和密钥,应限制权限:
chmod 600 .env
修改 .env 后,通常需要重新创建容器才能让新环境变量生效:
podman compose up -d --force-recreate
对于生产项目,应优先使用项目自带的管理脚本执行重建,避免漏掉覆盖文件、项目名或初始化步骤。
检查 Compose 展开结果时要注意:
podman compose config
该命令可能输出已经展开的敏感变量,不要将完整输出粘贴到公开渠道。
12. 常见问题排查
12.1 podman compose 找不到 provider
检查:
ls -l ~/.local/bin/docker-compose
command -v docker-compose
printf '%s\n' "$PODMAN_COMPOSE_PROVIDER"
重新设置:
export PODMAN_COMPOSE_PROVIDER="$HOME/.local/bin/docker-compose"
12.2 permission denied
增加执行权限:
chmod 755 ~/.local/bin/docker-compose
同时确认所在目录允许当前用户访问:
namei -l ~/.local/bin/docker-compose
12.3 Exec format error
检查服务器与文件架构:
uname -m
file ~/.local/bin/docker-compose
必须重新准备与服务器 CPU 架构一致的 Linux 二进制文件。
12.4 无法连接 Docker daemon 或 Podman socket
典型报错包含:
Cannot connect to the Docker daemon
检查并重启用户 socket:
systemctl --user restart podman.socket
systemctl --user status podman.socket
重新设置地址:
export DOCKER_HOST="unix://${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/podman/podman.sock"
验证 socket 是否存在:
test -S "${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/podman/podman.sock" && echo OK
12.5 使用 SSH 非交互执行时找不到 docker-compose
非交互 shell 不一定读取 ~/.profile。可以使用绝对路径:
PODMAN_COMPOSE_PROVIDER="$HOME/.local/bin/docker-compose" podman compose version
项目脚本中也应优先使用明确路径或主动加载所需环境变量。
12.6 bind mount 源文件被创建成目录
如果 Compose 中声明:
volumes:
- ./script.sh:/usr/local/bin/script.sh:ro
但服务器上 ./script.sh 不存在,容器工具可能把它创建成目录,导致容器内脚本无法执行。
检查:
file ./script.sh
ls -ld ./script.sh
正确结果应为普通文件,而不是 directory。应删除误建的空目录,再复制真实脚本文件:
rmdir ./script.sh
scp script.sh your-user@server.example.com:~/compose-app/script.sh
chmod 755 ./script.sh
12.7 容器一直显示 Waiting 或 starting
先检查状态和健康检查历史:
podman ps -a
podman inspect 容器名称 --format '{{json .State.Health}}'
查看日志:
podman logs --tail 200 容器名称
再检查同一网络内的 DNS 和端口连通性。Node.js 容器示例:
podman exec 容器名称 node -e "require('dns').lookup('服务名',(e,a)=>console.log(e||a))"
12.8 已存在的数据卷警告
可能看到:
volume "example_data" already exists but was not created by Docker Compose
如果该卷本来就是手动创建并需要复用,可在 Compose 文件中明确声明为外部卷:
volumes:
example_data:
external: true
name: example_data
修改前应确认该数据卷确实属于当前项目,禁止为了消除警告而删除生产数据卷。
13. 推荐检查清单
部署前:
uname -m
podman version
file ~/.local/bin/docker-compose
docker-compose version
systemctl --user is-active podman.socket
podman compose version
podman images
启动后:
podman ps -a
podman compose ps
podman compose logs --tail 100
确认以下事项:
- 所有命令都由同一个普通用户执行;
- 没有混用
sudo podman; - Compose 二进制文件架构正确并具有执行权限;
PODMAN_COMPOSE_PROVIDER指向离线二进制文件;DOCKER_HOST指向当前用户的 Podman socket;- 所需镜像已导入普通用户的 rootless Podman 存储;
- bind mount 的源文件和目录均真实存在;
- 数据卷未被误删;
- 关键容器健康检查均已通过。