09 - Podman Compose
第 09 章 — Podman Compose
9.1 概述
Podman Compose 是一个兼容 Docker Compose 格式的多容器编排工具,它使用 docker-compose.yaml(或 compose.yaml)文件来定义和管理多个相关容器。
为什么需要 Compose?
Podman Pod 可以管理多个容器,但需要手动执行多条命令。Compose 提供声明式配置,一条命令启动整个应用栈。
手动管理(繁琐): Compose 管理(简洁):
podman network create ... podman-compose up -d
podman volume create ...
podman run --name db ... 一条命令搞定一切
podman run --name redis ...
podman run --name app ...
方案对比
| 方案 | 配置格式 | 适用场景 | K8s 兼容 |
|---|---|---|---|
| Podman Pod | CLI 命令 | 简单多容器组 | ✅ generate kube |
| Podman Compose | compose.yaml | 中等复杂度编排 | ❌ |
| Quadlet | .container 文件 | 生产 systemd 集成 | ✅ .kube 支持 |
| Kubernetes | YAML manifests | 大规模编排 | ✅ |
9.2 安装 Podman Compose
# Fedora/RHEL
sudo dnf install podman-compose
# Ubuntu/Debian
sudo apt install podman-compose
# pip 安装(通用)
pip3 install podman-compose
# 验证
podman-compose --version
💡 提示
Podman Compose 有 Python 实现和 Go 实现两个版本。这里主要介绍 Python 版本(更成熟)。Podman 5.x 也在实验性地支持
podman compose子命令。
9.3 compose.yaml 文件语法
9.3.1 基本结构
# compose.yaml(或 docker-compose.yaml)
version: "3.8"
services:
web:
image: nginx:1.27-alpine
ports:
- "8080:80"
volumes:
- web-content:/usr/share/nginx/html:ro
depends_on:
- app
networks:
- frontend
app:
image: myapp:v1.0
build:
context: .
dockerfile: Dockerfile
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
depends_on:
- db
networks:
- frontend
- backend
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- backend
volumes:
web-content:
pgdata:
networks:
frontend:
backend:
9.3.2 services 配置详解
services:
app:
# 镜像
image: myapp:v1.0
# 构建上下文
build:
context: ./app
dockerfile: Dockerfile
args:
- APP_VERSION=2.0
# 端口映射
ports:
- "8080:80" # host:container
- "443:443"
- "127.0.0.1:3000:3000" # 指定绑定地址
- "9000-9010:9000-9010" # 端口范围
# 环境变量(两种写法)
environment:
- NODE_ENV=production # 列表格式
DATABASE_URL: "postgres://..." # 映射格式
# 从文件读取环境变量
env_file:
- .env
- .env.production
# 挂载卷
volumes:
- ./src:/app/src:Z # Bind Mount
- app-data:/app/data:Z # Named Volume
- /tmp/cache:/cache:rw,Z
# 网络
networks:
- frontend
- backend
# 依赖(启动顺序)
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
# 重启策略
restart: unless-stopped
# 健康检查
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# 资源限制
deploy:
resources:
limits:
cpus: "2.0"
memory: 1G
reservations:
cpus: "0.5"
memory: 256M
# 容器名称
container_name: my-app
# 主机名
hostname: app01
# 添加 host 映射
extra_hosts:
- "api.example.com:10.0.0.5"
# 只读根文件系统
read_only: true
# tmpfs 挂载
tmpfs:
- /tmp:size=100m
# 工作目录
working_dir: /app
# 用户
user: "1000:1000"
# 命令覆盖
command: ["python", "app.py", "--port", "8080"]
# 入口点覆盖
entrypoint: /app/entrypoint.sh
# 标签
labels:
- "app=myapp"
- "env=production"
# 日志配置
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# 指定网络驱动选项
dns:
- 8.8.8.8
- 114.114.114.114
# 容器内 PID 命名空间
pid: "host" # 共享宿主机 PID
# 特权模式(慎用)
privileged: false
# 添加 Linux Capability
cap_add:
- NET_BIND_SERVICE
cap_drop:
- ALL
9.4 常用命令
9.4.1 基本操作
# 启动所有服务(后台)
podman-compose up -d
# 启动并构建
podman-compose up -d --build
# 启动指定服务
podman-compose up -d web app
# 查看服务状态
podman-compose ps
# 查看日志
podman-compose logs
podman-compose logs -f web # 持续输出指定服务
podman-compose logs --tail 50 # 最后 50 行
# 停止所有服务
podman-compose down
# 停止并删除卷
podman-compose down -v
# 停止并删除镜像
podman-compose down --rmi all
# 重启服务
podman-compose restart web
# 进入容器
podman-compose exec app bash
# 执行一次性命令
podman-compose run --rm app python manage.py migrate
9.4.2 构建与推送
# 构建所有服务的镜像
podman-compose build
# 强制重建
podman-compose build --no-cache
# 只构建指定服务
podman-compose build app
# 拉取最新镜像
podman-compose pull
# 推送镜像
podman-compose push
9.4.3 扩展服务
# 扩展服务实例数
podman-compose up -d --scale app=3
# 注意:需要在 compose.yaml 中去掉 container_name
# 因为多个实例不能使用相同的名称
9.5 实战示例
9.5.1 LEMP 栈(Linux + Nginx + MySQL + PHP)
# compose.yaml
version: "3.8"
services:
nginx:
image: nginx:1.27-alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro,Z
- ./www:/var/www/html:Z
depends_on:
- php
networks:
- lemp
php:
image: php:8.3-fpm-alpine
volumes:
- ./www:/var/www/html:Z
depends_on:
- mysql
networks:
- lemp
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppass
volumes:
- mysqldata:/var/lib/mysql:Z
networks:
- lemp
volumes:
mysqldata:
networks:
lemp:
# 启动
podman-compose up -d
# 查看状态
podman-compose ps
9.5.2 全栈 Web 应用
# compose.yaml
version: "3.8"
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "3000:3000"
networks:
- app-net
backend:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
DATABASE_URL: "postgres://app:secret@postgres:5432/appdb"
REDIS_URL: "redis://redis:6379/0"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
networks:
- app-net
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data:Z
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
interval: 10s
timeout: 5s
retries: 5
networks:
- app-net
redis:
image: redis:7-alpine
volumes:
- redisdata:/data:Z
networks:
- app-net
volumes:
pgdata:
redisdata:
networks:
app-net:
9.5.3 开发环境
# compose.yaml — 开发环境
version: "3.8"
services:
app:
build:
context: .
target: development # 多阶段构建的开发阶段
ports:
- "3000:3000"
- "9229:9229" # Node.js 调试端口
volumes:
- ./src:/app/src:Z # 热重载
- ./config:/app/config:Z
environment:
NODE_ENV: development
DEBUG: "app:*"
command: ["npm", "run", "dev"]
db:
image: postgres:16-alpine
ports:
- "5432:5432" # 暴露端口方便本地工具连接
environment:
POSTGRES_USER: dev
POSTGRES_PASSWORD: dev
POSTGRES_DB: appdb_dev
volumes:
- pgdata:/var/lib/postgresql/data:Z
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro,Z
adminer:
image: adminer:latest # 数据库管理 GUI
ports:
- "8081:8080"
depends_on:
- db
volumes:
pgdata:
9.6 环境变量管理
9.6.1 .env 文件
# .env(自动加载)
POSTGRES_USER=appuser
POSTGRES_PASSWORD=secure_password_here
POSTGRES_DB=mydb
APP_PORT=8080
# compose.yaml 中引用环境变量
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
app:
ports:
- "${APP_PORT}:8080"
9.6.2 多环境配置
# .env — 默认值
# .env.development — 开发环境
# .env.production — 生产环境
# 使用指定环境文件
podman-compose --env-file .env.production up -d
# compose.yaml
services:
app:
env_file:
- .env
- .env.${ENV:-development}
9.7 常见问题
问题 1:podman-compose 与 docker-compose 兼容性
# 大多数 docker-compose.yaml 直接兼容
# 已知差异:
# 1. 不支持 --compatibility 模式
# 2. build.context 中的 .dockerignore 可能需要调整
# 3. 某些高级网络配置需要适配
# 测试兼容性
podman-compose config # 验证配置文件
问题 2:SELinux 标签
# compose.yaml 中的卷需要加 :Z
services:
app:
volumes:
- ./data:/app/data:Z # ✅ 正确
- ./data:/app/data # ❌ 可能被 SELinux 阻止
问题 3:Rootless 端口映射
# Rootless 模式下 < 1024 端口需要特殊配置
# 解决方案:在 compose.yaml 中使用非特权端口
services:
web:
ports:
- "8080:80" # 使用 8080 而非 80
问题 4:网络问题
# 如果容器间无法通信
podman-compose down
podman system reset # 重置网络配置
podman-compose up -d
# 检查网络
podman network ls
podman network inspect <network_name>
9.8 从 Docker Compose 迁移
# 迁移步骤:
# 1. 安装 podman-compose
pip3 install podman-compose
# 2. 设置别名(可选)
alias docker-compose=podman-compose
# 3. 直接使用现有 compose.yaml
podman-compose up -d
# 4. 修复 SELinux 标签
# 在所有 volumes 中添加 :Z
# 5. 检查网络连接
podman-compose ps
podman-compose logs
迁移检查清单
| 检查项 | 操作 |
|---|---|
| SELinux 标签 | 所有卷添加 :Z |
| 特权端口 | 改用 >= 1024 |
| 容器名冲突 | 去掉 container_name 字段 |
| 网络 | 确认容器间 DNS 解析 |
| 环境变量 | 测试 env_file 加载 |
| 构建上下文 | 确认路径正确 |
9.9 本章小结
| 知识点 | 要点 |
|---|---|
| 核心命令 | up -d / down / ps / logs / exec |
| 配置文件 | compose.yaml(或 docker-compose.yaml) |
| 环境变量 | env_file + .env 文件 |
| SELinux | 所有卷加 :Z |
| 扩展 | --scale app=3 多实例 |
| 兼容性 | 与 Docker Compose 高度兼容 |
下一步
- 👉 第 10 章:密钥管理 — 安全地管理容器敏感信息