DevToolBox免费
博客

Podman 完全指南 2026:无守护进程容器、Rootless 安全与 Kubernetes 集成

27 分钟阅读作者 DevToolBox Team

TL;DR

Podman 是无守护进程、默认 Rootless 的容器引擎,提供 Docker 兼容 CLI 而无需后台守护进程。由 Red Hat 开发,使用用户命名空间将容器作为普通用户进程运行。原生支持 Pod、Kubernetes YAML、Quadlet systemd 集成和 Buildah OCI 镜像构建。2026 年 Podman 5.x 是 Fedora/RHEL/CentOS Stream 默认运行时。

Key Takeaways

  • 无守护进程 — 容器作为用户子进程运行
  • Rootless 使用用户命名空间,无需 root
  • Docker 兼容 CLI — alias docker=podman
  • 原生 Pod 支持,共享网络/IPC 命名空间
  • podman generate/play kube 连接本地与 K8s
  • Quadlet systemd 集成,容器即服务

Podman 是 Red Hat 开发的开源容器引擎,已成为 RHEL、Fedora 和 CentOS Stream 的默认容器工具。与 Docker 不同,无需中央守护进程——每个容器直接作为用户子进程运行。拥有 25,000+ GitHub 星标、Docker 兼容 CLI、原生 Pod 支持和深度 Kubernetes 集成。

什么是 Podman 及为什么无守护进程很重要

Podman 是 OCI 兼容的容器引擎。与 Docker 关键区别:Docker CLI 与 root dockerd 守护进程通信,Podman 完全消除守护进程——容器进程直接 fork 为子进程。

无守护进程设计意味着没有特权进程被攻破的风险。容器隔离在用户进程树中,没有单点故障。

  • 无守护进程
  • 默认 Rootless
  • Docker 兼容 CLI
  • 原生 Pod
  • K8s 集成
  • Buildah 镜像构建
  • systemd Quadlet

Podman vs Docker vs containerd vs nerdctl

了解容器工具生态有助于选择。

特性PodmanDockercontainerdnerdctl
守护进程用containerd
Rootless原生实验性rootlesskitrootlesskit
CLIDocker兼容dockerctrDocker兼容
Pod原生
K8s YAMLgenerate/play
Composepodman-composedocker composenerdctl compose
构建BuildahBuildKit需buildkitBuildKit
SystemdQuadlet手动手动手动
GUIPodman DesktopDocker Desktop
许可证Apache 2.0Apache 2.0Apache 2.0Apache 2.0

安装

Fedora/RHEL/CentOS Stream 已预装,其他平台可安装。

Fedora / RHEL / CentOS

# Pre-installed on Fedora/RHEL 8+/CentOS Stream
podman --version

# If not installed
sudo dnf install -y podman podman-compose buildah skopeo

Ubuntu / Debian

# Ubuntu 22.04+ / Debian 12+
sudo apt update && sudo apt install -y podman

# For older Ubuntu, add the Kubic repository
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.opensuse.org/repositories/\
  devel:kubic:libcontainers:unstable/xUbuntu_22.04/Release.key \
  | gpg --dearmor | sudo tee /etc/apt/keyrings/devel_kubic.gpg > /dev/null
sudo apt update && sudo apt install -y podman

macOS

# Install Podman via Homebrew
brew install podman

# Initialize and start the Linux VM
podman machine init
podman machine start

# Verify installation
podman info
podman run hello-world

# Optional: Install Podman Desktop GUI
brew install --cask podman-desktop

Windows

# Install via winget
winget install RedHat.Podman

# Initialize and start the machine
podman machine init
podman machine start

# Install Podman Desktop
winget install RedHat.Podman-Desktop

Podman 基础

会 Docker 就会 Podman,可 alias docker=podman。

运行容器

# Run a container (identical to Docker syntax)
podman run -d --name webserver -p 8080:80 nginx:alpine

# Run interactively
podman run -it --rm ubuntu:24.04 bash

# With environment variables and volume
podman run -d --name postgres \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_DB=myapp \
  -v pgdata:/var/lib/postgresql/data \
  -p 5432:5432 \
  postgres:16-alpine

# With resource limits
podman run -d --name app \
  --memory=512m --cpus=1.5 \
  --restart=always \
  myapp:latest

管理容器和镜像

# Container management
podman ps                      # list running
podman ps -a                   # list all
podman stop/start/restart webserver
podman logs -f webserver       # follow logs
podman exec -it webserver sh   # shell into container
podman inspect webserver
podman rm -f webserver
podman container prune

# Image management
podman pull docker.io/library/nginx:alpine
podman images
podman search nginx
podman tag nginx:alpine myregistry.io/nginx:v1
podman push myregistry.io/nginx:v1
podman rmi nginx:alpine
podman image prune -a

Rootless 容器

Rootless 是核心特性。使用用户命名空间,容器内是 root 但主机上是非特权用户。

即使逃逸也只是非特权用户,结合 SELinux/seccomp 提供纵深防御。

# Verify rootless setup
podman unshare cat /proc/self/uid_map

# Check subuid/subgid allocation
cat /etc/subuid   # username:100000:65536
cat /etc/subgid   # username:100000:65536

# If missing, add entries
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
podman system migrate

# User namespace mapping:
# Host UID 1000    -> Container UID 0 (root)
# Host UID 100000  -> Container UID 1
# Host UID 165535  -> Container UID 65536

# Verify: container sees root, host sees your UID
podman run --rm alpine id          # uid=0(root)
podman top <ctr> user huser        # root -> 1000

Podman Compose vs Docker Compose

支持 podman-compose 或通过兼容套接字使用 docker-compose。

# Option 1: Install and use podman-compose
pip install podman-compose
podman-compose up -d
podman-compose logs -f
podman-compose down

# Option 2: Use docker-compose with Podman socket
systemctl --user enable --now podman.socket
export DOCKER_HOST=unix:///run/user/\$(id -u)/podman/podman.sock

# Now docker-compose works with Podman backend
docker-compose up -d
docker-compose ps
docker-compose down
# docker-compose.yml (works with both)
version: "3.9"
services:
  app:
    image: node:20-alpine
    ports: ["3000:3000"]
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
    depends_on: [db, redis]
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
    volumes: [pgdata:/var/lib/postgresql/data]
  redis:
    image: redis:7-alpine
volumes:
  pgdata:

Pod 管理

Pod 将容器分组共享命名空间,与 Kubernetes Pod 一致。

# Create a pod with port mapping
podman pod create --name webapp -p 8080:80 -p 5432:5432

# Add containers to the pod
podman run -d --pod webapp --name webapp-nginx nginx:alpine
podman run -d --pod webapp --name webapp-db \
  -e POSTGRES_PASSWORD=secret postgres:16-alpine

# Containers in a pod share localhost
# nginx reaches postgres at localhost:5432
podman run -d --pod webapp --name webapp-app \
  -e DATABASE_URL=postgresql://user:pass@localhost:5432/mydb myapp:latest

# Manage pods
podman pod ls
podman pod inspect webapp
podman ps --pod --filter pod=webapp
podman pod stop webapp && podman pod rm webapp

Kubernetes YAML

可生成和部署 Kubernetes YAML,连接本地开发与生产。

# Generate Kubernetes YAML from a pod
podman generate kube webapp > webapp.yaml
podman generate kube webapp -s > webapp-with-service.yaml

# Deploy Kubernetes YAML locally
podman play kube webapp.yaml
podman play kube --down webapp.yaml    # tear down
podman play kube --replace webapp.yaml  # update

# Deploy with configmap
podman play kube webapp.yaml --configmap=config.yaml

# Workflow: develop locally -> export -> test -> deploy
# 1. podman pod create / podman run --pod ...
# 2. podman generate kube webapp > webapp.yaml
# 3. podman play kube webapp.yaml  (local test)
# 4. kubectl apply -f webapp.yaml   (deploy to K8s cluster)

构建镜像:Buildah

底层用 Buildah,支持 Containerfile 和脚本化接口。

# Containerfile (identical to Dockerfile syntax)
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
RUN addgroup -S app && adduser -S app -G app
USER app
EXPOSE 3000
CMD ["node", "dist/index.js"]
# Build commands
podman build -t myapp:latest .
podman build -t myapp:prod -f Containerfile.prod .
podman build --build-arg NODE_ENV=production -t myapp:prod .
podman build --no-cache -t myapp:latest .
# Go application — multi-stage with scratch
FROM golang:1.23-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server ./cmd/server

FROM scratch
COPY --from=build /app/server /server
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/server"]

Podman Desktop 和 Machine

跨平台 GUI 管理容器。macOS/Windows 通过 Podman Machine 运行轻量 VM。

# Install Podman Desktop
brew install --cask podman-desktop        # macOS
winget install RedHat.Podman-Desktop      # Windows
flatpak install flathub io.podman_desktop.PodmanDesktop  # Linux

# Features: container/pod management, image building,
# Kubernetes cluster connection, extension ecosystem
# (Kind, Minikube, OpenShift), Docker Desktop migration
# Podman Machine — VM management for macOS/Windows
podman machine init --cpus 4 --memory 4096 --disk-size 60
podman machine start
podman machine ls
podman machine ssh               # SSH into the VM
podman machine set --rootful     # enable rootful mode
podman machine stop
podman machine rm                # remove and recreate if issues
podman machine init && podman machine start

网络:Netavark

Podman 4+ 用 Netavark + Aardvark-dns,自动 DNS 解析。

# Create custom network
podman network create mynet
podman network create --subnet 10.89.0.0/24 --gateway 10.89.0.1 mynet

# Run containers on custom network
podman run -d --name db --network mynet postgres:16-alpine
podman run -d --name app --network mynet myapp:latest

# Aardvark-dns provides automatic DNS resolution by container name
# Inside "app" container:
podman exec app ping db
# PING db (10.89.0.2): 56 data bytes
# 64 bytes from 10.89.0.2: seq=0 ttl=64 time=0.089 ms

# Port mapping for external access
podman run -d --name web --network mynet -p 8080:80 nginx:alpine

# Network management
podman network ls
podman network inspect mynet
podman network connect mynet existing-container
podman network disconnect mynet existing-container
podman network rm mynet

卷管理

支持命名卷、绑定挂载和 tmpfs。

# Create and use named volume
podman volume create mydata
podman run -d --name db \
  -v mydata:/var/lib/postgresql/data \
  postgres:16-alpine

# Bind mount (host directory) with SELinux labels
podman run -d --name web \
  -v ./html:/usr/share/nginx/html:Z \
  nginx:alpine
# :Z = SELinux private label (single container access)
# :z = SELinux shared label (multiple containers)
# :ro = read-only mount

# tmpfs mount (in-memory, no persistence)
podman run -d --tmpfs /tmp:size=100m myapp:latest

# Volume management
podman volume ls
podman volume inspect mydata
podman volume rm mydata
podman volume prune  # remove all unused volumes

注册表管理

通过 registries.conf 支持多注册表优先级搜索。

# /etc/containers/registries.conf
# Podman searches these registries in order for unqualified images
[registries.search]
registries = ["docker.io", "quay.io", "ghcr.io"]

# Login to multiple registries
podman login docker.io
podman login quay.io
podman login ghcr.io
# Credentials stored in: \${XDG_RUNTIME_DIR}/containers/auth.json

# Pull from specific registry
podman pull docker.io/library/nginx:alpine
podman pull quay.io/podman/stable
podman pull ghcr.io/owner/image:tag

# Use skopeo for advanced registry operations
skopeo inspect docker://docker.io/library/nginx:alpine
skopeo copy docker://src-registry/img docker://dst-registry/img

Quadlet

.container 文件放入 systemd 目录即可管理容器生命周期。

# ~/.config/containers/systemd/webapp.container
[Unit]
Description=Web Application
After=network-online.target

[Container]
Image=docker.io/library/nginx:alpine
PublishPort=8080:80
Volume=./html:/usr/share/nginx/html:Z
AutoUpdate=registry
HealthCmd=curl -f http://localhost/ || exit 1
HealthInterval=30s

[Service]
Restart=always

[Install]
WantedBy=default.target
# Enable and manage
systemctl --user daemon-reload
systemctl --user enable --now webapp.service
journalctl --user -u webapp.service -f

# Auto-update (checks registry for new digests)
systemctl --user enable --now podman-auto-update.timer
podman auto-update --dry-run
podman auto-update

密钥管理

安全传递敏感数据,不包含在镜像或环境变量中。

# Create a secret from stdin
echo "my-database-password" | podman secret create db-password -

# Create from file
podman secret create tls-cert ./server.crt
podman secret create tls-key ./server.key

# List secrets
podman secret ls

# Use as file (mounted at /run/secrets/)
podman run -d --name db \
  --secret db-password \
  -e POSTGRES_PASSWORD_FILE=/run/secrets/db-password \
  postgres:16-alpine

# Use as environment variable
podman run -d --name app \
  --secret db-password,type=env,target=DB_PASS \
  myapp:latest

# Inspect and remove
podman secret inspect db-password
podman secret rm db-password

多架构构建

通过 podman manifest 和 QEMU 支持跨平台构建。

# Create a manifest list for multi-arch image
podman manifest create myapp:latest

# Build for each architecture
podman build --platform linux/amd64 -t myapp:amd64 .
podman build --platform linux/arm64 -t myapp:arm64 .

# Add platform images to manifest
podman manifest add myapp:latest myapp:amd64
podman manifest add myapp:latest myapp:arm64

# Inspect and push
podman manifest inspect myapp:latest
podman manifest push myapp:latest docker://registry.io/myapp:latest

# Or build all platforms in one command
podman build --platform linux/amd64,linux/arm64 \
  --manifest myapp:latest .

CI/CD

无守护进程架构适合共享 CI 运行器。

GitHub Actions

# .github/workflows/build.yml
name: Build with Podman
on: { push: { branches: [main] } }
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: podman build -t myapp:latest .
      - run: podman run --rm myapp:latest npm test
      - run: |
          echo \${{ secrets.GITHUB_TOKEN }} | \
            podman login ghcr.io -u \${{ github.actor }} --password-stdin
          podman tag myapp:latest ghcr.io/\${{ github.repository }}:latest
          podman push ghcr.io/\${{ github.repository }}:latest

GitLab CI

# .gitlab-ci.yml
build:
  image: quay.io/podman/stable
  script:
    - podman build -t \$CI_REGISTRY_IMAGE:\$CI_COMMIT_SHA .
    - podman login -u \$CI_REGISTRY_USER -p \$CI_REGISTRY_PASSWORD \$CI_REGISTRY
    - podman push \$CI_REGISTRY_IMAGE:\$CI_COMMIT_SHA

从 Docker 迁移

大多数工作流迁移简单,CLI 兼容。

  • 安装并别名 docker=podman
  • 替换 socket 路径
  • 用 podman-compose 或配置套接字
  • 用 Podman Desktop
  • 转 Quadlet
  • 更新 CI/CD
  • 测试 Compose 兼容性
# Step 1: Install Podman and compatibility layer
sudo dnf install podman podman-docker  # provides /usr/bin/docker

# Step 2: Create Docker alias
echo "alias docker=podman" >> ~/.bashrc
source ~/.bashrc

# Step 3: Enable Podman socket for Docker Compose
systemctl --user enable --now podman.socket
export DOCKER_HOST=unix:///run/user/\$(id -u)/podman/podman.sock

# Step 4: Test with existing Docker workflows
docker run hello-world          # uses Podman
docker-compose up -d            # uses Podman backend
docker build -t myapp .         # uses Buildah

性能对比

性能相当,无守护进程略快,Rootless 约 2-5% 开销。

  • 启动略快
  • 拉取相同
  • 运行时相同
  • 内存更少
  • Rootless ~2-5%
  • 构建相当

安全最佳实践

强安全默认值,额外加固确保纵深防御。

  • 始终 Rootless
  • SELinux :Z/:z
  • seccomp 限制
  • cap-drop=all
  • 只读根文件系统
  • cosign 签名
  • Trivy 扫描
  • no-new-privileges
# Security-hardened container
podman run -d --name secure-app \
  --cap-drop=all --cap-add=NET_BIND_SERVICE \
  --read-only --tmpfs /tmp:size=50m \
  --security-opt=no-new-privileges \
  --security-opt label=type:container_t \
  --user 1000:1000 --memory=256m --cpus=0.5 \
  -v ./data:/app/data:Z,ro myapp:latest

# Verify image and scan
cosign verify --key cosign.pub myregistry.io/myapp:latest
podman run --rm aquasec/trivy image myapp:latest

生产部署

Quadlet + 自动更新 + 健康检查,无需编排器。

# /etc/containers/systemd/myapp.container
[Unit]
Description=Production App
After=network-online.target myapp-db.service
Requires=myapp-db.service

[Container]
Image=ghcr.io/myorg/myapp:latest
PublishPort=443:8443
Network=myapp.network
Secret=db-password,type=env,target=DATABASE_PASSWORD
Secret=tls-cert,target=/etc/ssl/server.crt
Volume=app-data:/app/data:Z
Environment=NODE_ENV=production
AutoUpdate=registry
HealthCmd=curl -fk https://localhost:8443/health || exit 1
HealthInterval=15s
HealthRetries=5

[Service]
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
# /etc/containers/systemd/myapp-db.container
[Unit]
Description=Production Database
After=network-online.target

[Container]
Image=docker.io/library/postgres:16-alpine
Network=myapp.network
Secret=db-password,type=env,target=POSTGRES_PASSWORD
Volume=pgdata:/var/lib/postgresql/data:Z
Environment=POSTGRES_DB=myapp
HealthCmd=pg_isready -U postgres
HealthInterval=10s

[Service]
Restart=always

[Install]
WantedBy=multi-user.target
# /etc/containers/systemd/myapp.network
[Network]
Subnet=10.89.1.0/24
Gateway=10.89.1.1

# Deploy the full production stack
sudo systemctl daemon-reload
sudo systemctl enable --now myapp-db.service
sudo systemctl enable --now myapp.service
sudo systemctl enable --now podman-auto-update.timer

# Monitor
systemctl status myapp.service
journalctl -u myapp.service -f

故障排查

  • 权限拒绝:检查 subuid/subgid
  • 网络不通:安装 pasta
  • 低端口:设 sysctl
  • 拉取失败:检查 registries.conf
  • 不启动:查 podman logs
  • SELinux:加 :Z
  • Machine 故障:重建 VM
  • Compose:用 1.1+
# Debugging commands
podman info                    # system info
podman system check            # storage consistency
podman events                  # real-time events
podman logs -f <container>     # container logs
podman healthcheck run <ctr>   # manual health check
podman system df               # disk usage
podman system prune -a         # clean everything
podman system reset            # nuclear reset
podman info --format "{{.Host.NetworkBackend}}"

常见问题

能替代 Docker 吗?

绝大多数场景可以,CLI 兼容,Compose 兼容,主要缺少 Swarm。

比 Docker 安全吗?

是,无 root 守护进程、默认 Rootless、SELinux 集成。

Rootless 原理?

用户命名空间映射容器 root 到主机非特权用户。

Pod vs K8s Pod?

直接对应,共享命名空间,可 generate kube 导出。

podman-compose 还是 docker-compose?

简单栈用前者,复杂文件用后者通过套接字。

Quadlet 是什么?

systemd 原生集成,.container 文件替代 docker-compose 用于生产。

能运行 Docker 镜像吗?

能,任何 OCI/Docker 格式镜像。

macOS Machine 原理?

创建 Fedora CoreOS VM,CLI 透明通信,virtiofs 共享文件。

𝕏 Twitterin LinkedIn
这篇文章有帮助吗?

保持更新

获取每周开发技巧和新工具通知。

无垃圾邮件,随时退订。

试试这些相关工具

{ }JSON Formatter

相关文章

Docker 最佳实践:20 个生产容器技巧

掌握 Docker 的 20 个关键最佳实践:多阶段构建、安全加固、镜像优化、缓存策略、健康检查和 CI/CD 自动化。

Kubernetes开发者完整指南:Pod、Helm、RBAC和CI/CD

掌握Kubernetes的开发者指南。含Pod、Deployment、Service、Ingress、Helm、PVC、健康检查、HPA、RBAC和GitHub Actions CI/CD集成。

Docker Compose 教程:从基础到生产就绪的技术栈

完整的 Docker Compose 教程:docker-compose.yml 语法、服务、网络、卷、环境变量、健康检查,以及 Node.js、Python、WordPress 的实际案例。