Podman 完全指南 / 13 - Docker 迁移
第 13 章 — Docker 迁移
13.1 迁移概述
Podman 的 CLI 与 Docker 高度兼容,大多数情况下可以直接替换。但仍有一些差异需要注意。
迁移难度评估
| 场景 | 迁移难度 | 说明 |
|---|---|---|
| 基本容器操作 | ⭐ 极简 | alias docker=podman 即可 |
| Dockerfile 构建 | ⭐ 极简 | 直接兼容 |
| 端口映射/卷挂载 | ⭐ 极简 | 直接兼容 |
| Docker Compose | ⭐⭐ 简单 | 使用 podman-compose |
| CI/CD 流水线 | ⭐⭐ 简单 | 替换命令即可 |
| Docker Socket API | ⭐⭐⭐ 中等 | 需要 podman.socket 兼容层 |
| Docker Swarm | ⭐⭐⭐⭐ 困难 | 需要迁移到 K8s 或 Quadlet |
| Docker Desktop 特性 | ⭐⭐⭐ 中等 | 使用 Podman Desktop |
13.2 快速迁移(alias)
# 最简单的迁移方式 — 设置别名
alias docker=podman
alias docker-compose=podman-compose
# 永久生效
echo 'alias docker=podman' >> ~/.bashrc
echo 'alias docker-compose=podman-compose' >> ~/.bashrc
source ~/.bashrc
# 验证
docker run --rm alpine echo "Hello from Docker... I mean Podman!"
💡 提示
这种方式适合个人开发环境。生产环境建议直接使用
podman命令,避免混淆。
13.3 命令映射表
13.3.1 容器操作
| Docker 命令 | Podman 命令 | 说明 |
|---|---|---|
docker run | podman run | ✅ 完全兼容 |
docker ps | podman ps | ✅ 完全兼容 |
docker exec | podman exec | ✅ 完全兼容 |
docker stop | podman stop | ✅ 完全兼容 |
docker rm | podman rm | ✅ 完全兼容 |
docker logs | podman logs | ✅ 完全兼容 |
docker inspect | podman inspect | ✅ 完全兼容 |
docker cp | podman cp | ✅ 完全兼容 |
docker top | podman top | ✅ 完全兼容 |
docker stats | podman stats | ✅ 完全兼容 |
docker diff | podman diff | ✅ 完全兼容 |
docker rename | podman rename | ✅ 完全兼容 |
docker wait | podman wait | ✅ 完全兼容 |
docker attach | podman attach | ✅ 完全兼容 |
docker commit | podman commit | ✅ 完全兼容 |
docker kill | podman kill | ✅ 完全兼容 |
docker pause | podman pause | ✅ 完全兼容 |
docker unpause | podman unpause | ✅ 完全兼容 |
13.3.2 镜像操作
| Docker 命令 | Podman 命令 | 说明 |
|---|---|---|
docker pull | podman pull | ✅ 完全兼容 |
docker push | podman push | ✅ 完全兼容 |
docker build | podman build | ✅ 兼容(内部调用 Buildah) |
docker tag | podman tag | ✅ 完全兼容 |
docker images | podman images | ✅ 完全兼容 |
docker rmi | podman rmi | ✅ 完全兼容 |
docker save | podman save | ✅ 完全兼容 |
docker load | podman load | ✅ 完全兼容 |
docker history | podman history | ✅ 完全兼容 |
docker import | podman import | ✅ 完全兼容 |
docker export | podman export | ✅ 完全兼容 |
13.3.3 系统操作
| Docker 命令 | Podman 命令 | 差异 |
|---|---|---|
docker system df | podman system df | ✅ 兼容 |
docker system prune | podman system prune | ✅ 兼容 |
docker info | podman info | 格式不同 |
docker login | podman login | ✅ 兼容 |
docker logout | podman logout | ✅ 兼容 |
docker volume create | podman volume create | ✅ 兼容 |
docker network create | podman network create | ✅ 兼容 |
docker compose | podman-compose | 需单独安装 |
13.3.4 有差异的命令
| Docker 命令 | Podman 对应 | 差异说明 |
|---|---|---|
docker swarm | 无 | Podman 不支持 Swarm |
docker stack | 无 | 使用 Quadlet 或 K8s 替代 |
docker service | 无 | 使用 systemd 或 K8s 替代 |
docker context | 无 | Podman 使用 SSH 远程连接 |
docker buildx | podman build --platform | 多架构构建方式不同 |
docker compose up | podman kube play | 或使用 podman-compose |
13.4 Docker Socket 兼容层
许多工具(如 Portainer、Watchtower)依赖 Docker Socket (/var/run/docker.sock)。Podman 提供兼容的 REST API。
13.4.1 启用 Podman Socket
# 用户级 Socket(Rootless)
systemctl --user enable --now podman.socket
# 验证 Socket
ls /run/user/$(id -u)/podman/podman.sock
# 设置 DOCKER_HOST 环境变量
export DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock
# 或创建软链接
ln -s /run/user/$(id -u)/podman/podman.sock /var/run/docker.sock
# ⚠️ Rootless 用户可能需要 sudo
# 测试 API
curl -s --unix-socket /run/user/$(id -u)/podman/podman.sock http://localhost/v1.41/info | python3 -m json.tool | head
13.4.2 系统级 Socket(Root)
# 启用系统级 podman.socket
sudo systemctl enable --now podman.socket
# 查看 Socket
ls /run/podman/podman.sock
# 创建 docker.sock 兼容
sudo ln -sf /run/podman/podman.sock /var/run/docker.sock
# 现在 Docker SDK 可以正常使用
export DOCKER_HOST=unix:///run/podman/podman.sock
13.4.3 使用 Docker SDK(Python 示例)
# 无需修改代码,直接使用 Docker SDK
import docker
# 连接到 Podman Socket
client = docker.DockerClient(base_url='unix:///run/user/1000/podman/podman.sock')
# 列出容器(与 Docker 完全一致的 API)
for container in client.containers.list():
print(container.name, container.status)
# 运行容器
container = client.containers.run("alpine", "echo hello", detach=True)
print(container.logs().decode())
13.5 迁移 Docker Compose 项目
13.5.1 直接替换
# 大多数 docker-compose.yaml 可以直接使用
# 安装 podman-compose
pip3 install podman-compose
# 直接使用现有 compose 文件
cd /path/to/project
podman-compose up -d
# 检查状态
podman-compose ps
podman-compose logs
13.5.2 需要修改的内容
# docker-compose.yaml 修改清单
services:
app:
image: myapp:v1.0
volumes:
- ./data:/app/data:Z # 1. 添加 :Z SELinux 标签
# 如果不是 SELinux 系统,:Z 无副作用,建议保留
ports:
- "8080:80" # 2. 避免 < 1024 的端口
# 不要使用: # 3. 移除不兼容配置
# container_name: xxx # (扩展时会冲突)
# network_mode: host # (Rootless 下行为不同)
db:
image: postgres:16
tmpfs:
- /tmp:size=100m # 4. 添加 tmpfs 减少磁盘写入
volumes:
- pgdata:/var/lib/postgresql/data:Z
13.6 CI/CD 迁移
13.6.1 GitLab CI
# .gitlab-ci.yml
# Before(Docker):
# image: docker:latest
# services:
# - docker:dind
# script:
# - docker build -t myapp .
# - docker push registry.example.com/myapp
# After(Podman):
build:
image: quay.io/podman/stable
services:
- name: registry:2
alias: registry
variables:
DOCKER_HOST: unix:///run/podman/podman.sock
script:
- podman build -t myapp .
- podman tag myapp registry/myapp:latest
- podman push registry/myapp:latest
13.6.2 GitHub Actions
# .github/workflows/build.yml
name: Build and Push
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Podman
run: |
sudo apt-get update
sudo apt-get install -y podman
- name: Login to Registry
run: |
echo "${{ secrets.REGISTRY_PASSWORD }}" | \
podman login registry.example.com -u ${{ secrets.REGISTRY_USERNAME }} --password-stdin
- name: Build Image
run: podman build -t registry.example.com/myapp:${{ github.sha }} .
- name: Push Image
run: podman push registry.example.com/myapp:${{ github.sha }}
13.6.3 Jenkins Pipeline
// Jenkinsfile
pipeline {
agent {
// 使用包含 Podman 的 Jenkins agent
label 'podman-agent'
}
stages {
stage('Build') {
steps {
sh 'podman build -t myapp:${BUILD_NUMBER} .'
}
}
stage('Push') {
steps {
sh '''
echo "${REGISTRY_PASSWORD}" | podman login registry.example.com -u ${REGISTRY_USERNAME} --password-stdin
podman tag myapp:${BUILD_NUMBER} registry.example.com/myapp:${BUILD_NUMBER}
podman push registry.example.com/myapp:${BUILD_NUMBER}
'''
}
}
}
}
13.7 常见迁移陷阱
陷阱 1:Root 模式 vs Rootless 模式
# Docker 默认以 root 运行
sudo docker run -p 80:80 nginx # ✅ 可以绑定特权端口
# Podman 默认以 Rootless 运行
podman run -p 80:80 nginx # ❌ Rootless 无法绑定 80 端口
podman run -p 8080:80 nginx # ✅ 使用非特权端口
解决:配置 sysctl 或使用反向代理(见第 05 章)。
陷阱 2:SELinux 标签
# Docker 中直接挂载目录
docker run -v /data:/data nginx # ✅ 通常可以工作
# Podman(SELinux 系统)需要标签
podman run -v /data:/data:Z nginx # ✅ 添加 :Z
解决:所有卷挂载添加 :Z(私有)或 :z(共享)。
陷阱 3:容器命名冲突
# Docker 容器停止后仍占用名称
docker run --name web nginx
docker stop web
docker run --name web nginx # ❌ 名称冲突
# Podman 同样,需要先删除
podman rm web
podman run --name web nginx
陷阱 4:镜像仓库前缀
# Docker 默认添加 docker.io/library/ 前缀
docker pull alpine
# 等价于 docker.io/library/alpine:latest
# Podman 同样,但可能需要配置 registries.conf
podman pull alpine
# 等价于 docker.io/library/alpine:latest
陷阱 5:Dockerfile 中的 HEALTHCHECK
# Dockerfile 中的 HEALTHCHECK 指令
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost/ || exit 1
# Podman 对 HEALTHCHECK 的支持需要手动触发
podman healthcheck run <container>
podman inspect --format '{{.State.Health.Status}}' <container>
陷阱 6:容器内 PID 1
# Docker 中 systemd 作为 PID 1(需要 --privileged)
docker run --privileged centos /usr/sbin/init
# Podman 中 systemd 可以直接运行(更友好)
podman run --systemd=always fedora /usr/sbin/init
# 或
podman run --systemd=always centos /usr/lib/systemd/systemd
13.8 迁移验证脚本
#!/bin/bash
# migration-test.sh — 验证从 Docker 到 Podman 的迁移
echo "=== Podman 迁移验证 ==="
# 1. 基本运行
echo -n "1. 基本容器运行: "
podman run --rm alpine echo "OK" > /dev/null 2>&1 && echo "✅" || echo "❌"
# 2. 端口映射
echo -n "2. 端口映射: "
podman run --rm -d --name test-port -p 18080:80 nginx:alpine > /dev/null 2>&1
sleep 2
curl -s http://localhost:18080 > /dev/null 2>&1 && echo "✅" || echo "❌"
podman rm -f test-port > /dev/null 2>&1
# 3. 卷挂载
echo -n "3. 卷挂载: "
mkdir -p /tmp/podman-test
echo "test" > /tmp/podman-test/file.txt
podman run --rm -v /tmp/podman-test:/data:Z alpine cat /data/file.txt | grep -q "test" && echo "✅" || echo "❌"
rm -rf /tmp/podman-test
# 4. 环境变量
echo -n "4. 环境变量: "
podman run --rm -e MY_VAR=hello alpine printenv MY_VAR | grep -q "hello" && echo "✅" || echo "❌"
# 5. 镜像构建
echo -n "5. 镜像构建: "
echo "FROM alpine" > /tmp/Dockerfile.test
echo "RUN echo built" >> /tmp/Dockerfile.test
podman build -t test-build -f /tmp/Dockerfile.test /tmp > /dev/null 2>&1 && echo "✅" || echo "❌"
podman rmi test-build > /dev/null 2>&1
rm /tmp/Dockerfile.test
# 6. Socket 兼容
echo -n "6. Socket 兼容: "
SOCKET=/run/user/$(id -u)/podman/podman.sock
[ -S "$SOCKET" ] && echo "✅" || echo "❌ (需要: systemctl --user enable podman.socket)"
# 7. 网络
echo -n "7. 自定义网络: "
podman network create test-net > /dev/null 2>&1
podman network inspect test-net > /dev/null 2>&1 && echo "✅" || echo "❌"
podman network rm test-net > /dev/null 2>&1
# 8. Secrets
echo -n "8. Secrets: "
echo "secret" | podman secret create test-secret - > /dev/null 2>&1
podman secret inspect test-secret > /dev/null 2>&1 && echo "✅" || echo "❌"
podman secret rm test-secret > /dev/null 2>&1
echo "=== 验证完成 ==="
13.9 迁移步骤总结
迁移检查清单:
□ 1. 安装 Podman
□ 2. 配置 Rootless(/etc/subuid, /etc/subgid)
□ 3. 配置 registries.conf(镜像仓库)
□ 4. 测试基本容器操作
□ 5. 验证 Dockerfile 构建
□ 6. 修改卷挂载(添加 :Z)
□ 7. 调整端口映射(避免 < 1024)
□ 8. 安装 podman-compose(如果使用 Docker Compose)
□ 9. 启用 podman.socket(如果依赖 Docker API)
□ 10. 更新 CI/CD 流水线
□ 11. 测试所有应用功能
□ 12. 切换生产环境
□ 13. 监控运行状态
□ 14. 卸载 Docker(可选)
13.10 本章小结
| 知识点 | 要点 |
|---|---|
| 快速迁移 | alias docker=podman |
| 命令兼容性 | 95%+ 命令完全兼容 |
| Socket 兼容 | podman.socket 提供 Docker API 兼容 |
| Compose 兼容 | podman-compose 直接使用 compose.yaml |
| SELinux | 所有卷加 :Z |
| 端口限制 | Rootless 使用 ≥ 1024 端口 |
| CI/CD | 替换 docker 命令为 podman |
下一步
- 👉 第 14 章:生产最佳实践 — 企业级容器部署规范
扩展阅读
- Podman vs Docker
- Migrating from Docker to Podman
- Podman Socket API
- Podman Desktop — Docker Desktop 的替代方案