强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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 PodCLI 命令简单多容器组✅ generate kube
Podman Composecompose.yaml中等复杂度编排
Quadlet.container 文件生产 systemd 集成✅ .kube 支持
KubernetesYAML 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-composedocker-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 高度兼容

下一步


扩展阅读