- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 1. 引言:容器生态系统的两大阵营
- 2. 架构对比:Docker vs Podman
- 3. 安装与初始配置
- 4. 镜像管理命令
- 5. 镜像构建
- 6. 容器生命周期命令
- 7. 容器监控与调试
- 8. 网络管理
- 9. 卷与存储管理
- 10. Docker Compose vs Podman Compose
- 11. Podman 独有功能
- 12. 安全相关命令与配置
- 13. 系统管理与清理
- 14. 实战速查表:最常用的 30 条命令
- 15. 故障排查指南
- 16. 参考资料与文献
1. 引言:容器生态系统的两大阵营
1.1 Docker 的历史与当下地位
2013 年 Solomon Hykes 在 PyCon 上用一场 5 分钟的闪电演讲「The future of Linux Containers」把 Docker 公之于众。它带动了容器技术的普及,并彻底革新了软件的交付方式。在 Docker 之前,LXC(Linux Containers)、FreeBSD Jail、Solaris Zones 等容器技术就已经存在,但 Docker 把 镜像分层系统、基于 Dockerfile 的声明式构建、名为 Docker Hub 的中央registry 结合起来,把「Build once, run anywhere」的愿景变成了现实。
Docker 后来成为 OCI(Open Container Initiative)标准的基础,也坐稳了 Kubernetes 生态核心运行时的位置。然而 2020 年 Kubernetes 宣布弃用 dockershim,加上 Docker Desktop 商业授权政策变更(250 人以上企业需付费),业界开始寻找替代方案。
目前 Docker 依然是开发环境的事实标准(de facto standard),Docker Hub 也仍是全球最大的容器镜像仓库。但在生产环境中,containerd、CRI-O 以及 Podman 正在快速扩大自己的地盘。
1.2 Podman 登场的背景
Podman(Pod Manager)是由 Red Hat 主导开发的 兼容 OCI(Open Container Initiative)的容器引擎。Red Hat 判断 Docker 的单一守护进程(daemon)架构在安全性、稳定性和系统集成方面存在根本性局限,于是开发了 Podman、Buildah、Skopeo 三款工具来解决这些问题。
- Podman:容器的运行与管理(替代 docker CLI)
- Buildah:专注于镜像构建(替代 docker build)
- Skopeo:镜像复制、检查、签名(跨registry的镜像管理)
这三款工具各自独立工作,同时又互为补充。这是一种忠实遵循 Unix 哲学「一个工具把一件事做好」的设计。
1.3 为什么选择 Podman?
需要考虑从 Docker 迁移到 Podman 的核心理由如下。
Daemonless Architecture:Docker 始终需要在后台运行 dockerd 守护进程。一旦这个守护进程挂掉,所有容器管理都无法进行(Single Point of Failure)。Podman 没有守护进程,直接对每个容器执行 fork-exec,因此不存在这个问题。
Rootless Containers:Docker 虽然也支持 rootless 模式,但 Podman 从一开始就把 rootless 作为基本设计原则。普通用户无需 root 权限即可运行容器,安全性大幅提升。
Fork-Exec Model:由于遵循传统的 Unix 进程模型,与 systemd、audit、cgroup 的集成非常自然。每个容器都作为 Podman 进程的子进程存在。
Pod 原生支持:可以在本地直接使用 Kubernetes 的 Pod 概念,还能用 podman generate kube 命令自动生成 Kubernetes YAML。
CLI 兼容性:只要设置 alias docker=podman,大部分 Docker 命令都可以照原样使用。
2. 架构对比:Docker vs Podman
2.1 Docker 架构:Client-Daemon 模型
Docker 采用典型的 客户端-服务器架构。当用户执行 docker run 时,Docker CLI(客户端)通过 REST API 向 Docker Daemon(dockerd)发送请求。Daemon 把请求委托给 containerd,containerd 再调用 runc 来真正创建容器。
┌─────────────────────────────────────────────────────────────────┐
│ Docker Architecture │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ REST API ┌──────────────────┐ │
│ │ Docker │───────────────▶│ Docker Daemon │ │
│ │ CLI │ /var/run/ │ (dockerd) │ │
│ │ │ docker.sock │ │ │
│ └──────────┘ │ ┌──────────────┐ │ │
│ │ │ containerd │ │ │
│ │ │ │ │ │
│ │ │ ┌────────┐ │ │ │
│ │ │ │ runc │ │ │ │
│ │ │ │ (OCI) │ │ │ │
│ │ │ └────┬───┘ │ │ │
│ │ └───────┼─────┘ │ │
│ └──────────┼───────┘ │
│ │ │
│ ┌─────────────────────┼──────────────────┐ │
│ │ Container 1 │ Container 2 │ │
│ │ (process) │ (process) │ │
│ └─────────────────────┴──────────────────┘ │
│ │
│ ⚠ dockerd 挂掉则所有容器无法管理 (SPOF) │
│ ⚠ 访问 docker.sock = 可获得 root 权限 │
└─────────────────────────────────────────────────────────────────┘
这种结构的核心问题在于,对 docker.sock 的访问权限事实上等同于 root 权限。属于 docker 组的用户可以挂载宿主机文件系统,或用 --privileged 标志运行容器,从而完全控制宿主机。
2.2 Podman 架构:Daemonless Fork-Exec 模型
Podman 没有守护进程。执行 podman run 时,Podman 进程直接 fork 出 conmon(Container Monitor),conmon 再运行 OCI 运行时(crun 或 runc)来创建容器。
┌─────────────────────────────────────────────────────────────────┐
│ Podman Architecture │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ direct fork-exec ┌──────────┐ │
│ │ Podman │────────────────────▶│ conmon │ │
│ │ CLI │ (no daemon!) │(container │ │
│ │ │ │ monitor) │ │
│ └──────────┘ └─────┬────┘ │
│ │ │
│ ┌─────▼────┐ │
│ │ crun │ │
│ │ (OCI │ │
│ │ runtime) │ │
│ └─────┬────┘ │
│ │ │
│ ┌─────────────────────┼──────────────────┐ │
│ │ Container 1 │ Container 2 │ │
│ │ (child proc) │ (child proc) │ │
│ └─────────────────────┴──────────────────┘ │
│ │
│ ✅ 无守护进程 → 无 SPOF │
│ ✅ 每个容器都是独立进程 → 易于与 systemd 集成 │
│ ✅ 默认支持 Rootless │
└─────────────────────────────────────────────────────────────────┘
conmon 是一个轻量级监控进程,它捕获容器的 stdout/stderr,记录容器的 exit code,并在 Podman CLI 退出之后继续监视容器。
2.3 核心对比表
| 对比项 | Docker | Podman |
|---|---|---|
| 架构 | Client-Daemon (dockerd) | Daemonless (fork-exec) |
| 默认运行权限 | root (daemon) | rootless(普通用户) |
| OCI 运行时 | runc | crun(默认),支持 runc |
| 镜像构建 | 内置 (BuildKit) | 联动 Buildah |
| 守护进程 | 必需 (dockerd + containerd) | 无 |
| systemd 集成 | 有限 | 原生 (Quadlet) |
| Pod 支持 | 无(用 Compose 替代) | 原生支持 |
| 生成 K8s YAML | 无 | podman generate kube |
| 运行 K8s YAML | 无 | podman play kube |
| 基于 Socket 的 API | docker.sock(始终启用) | podman.sock(按需启用) |
| 容器监控器 | containerd-shim | conmon |
| 默认registry | 仅 docker.io | 多registry搜索 |
| Compose 支持 | Docker Compose(官方) | podman-compose / podman compose |
| 许可证 | Apache 2.0 + 商业 (Desktop) | Apache 2.0(完全开源) |
| 安全框架 | AppArmor/Seccomp | SELinux/AppArmor/Seccomp |
3. 安装与初始配置
3.1 安装 Docker
Ubuntu / Debian
# 移除已有的 Docker 软件包
sudo apt-get remove docker docker-engine docker.io containerd runc
# 安装必需软件包
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release
# 添加 Docker 官方 GPG 密钥
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# 添加 Docker 软件源
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装 Docker Engine
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
# 把当前用户加入 docker 组(需要重新登录)
sudo usermod -aG docker $USER
# 启动服务并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker
# 确认安装
docker version
docker run hello-world
CentOS / RHEL / Rocky Linux
# 移除已有软件包
sudo yum remove -y docker docker-client docker-client-latest \
docker-common docker-latest docker-latest-logrotate \
docker-logrotate docker-engine
# 添加 Docker 软件源
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
# 安装 Docker Engine
sudo yum install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
# 启动服务
sudo systemctl start docker
sudo systemctl enable docker
# 添加用户组
sudo usermod -aG docker $USER
macOS (Docker Desktop)
# 通过 Homebrew 安装
brew install --cask docker
# 或者从 Docker Desktop 官网下载 .dmg
# https://www.docker.com/products/docker-desktop/
# 安装后启动 Docker Desktop 应用 → Docker Engine 自动启动
docker version
3.2 安装 Podman
Ubuntu / Debian
# Ubuntu 22.04+ 的默认软件源中已包含
sudo apt-get update
sudo apt-get install -y podman
# 需要最新版本时 (Ubuntu)
sudo mkdir -p /etc/apt/keyrings
curl -fsSL "https://download.opensuse.org/repositories/devel:kubic:libcontainers:unstable/xUbuntu_$(lsb_release -rs)/Release.key" \
| gpg --dearmor \
| sudo tee /etc/apt/keyrings/devel_kubic_libcontainers_unstable.gpg > /dev/null
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/devel_kubic_libcontainers_unstable.gpg] \
https://download.opensuse.org/repositories/devel:kubic:libcontainers:unstable/xUbuntu_$(lsb_release -rs)/ /" \
| sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list > /dev/null
sudo apt-get update
sudo apt-get install -y podman
# 确认安装
podman version
podman info
CentOS / RHEL / Rocky Linux
# RHEL 8+ / CentOS Stream 8+ 默认已包含
sudo dnf install -y podman
# 安装附加工具
sudo dnf install -y buildah skopeo podman-compose
# Podman 没有守护进程,因此不需要 systemctl start!
podman version
macOS (Podman Machine)
# 通过 Homebrew 安装
brew install podman
# 初始化 Podman Machine(创建 Linux VM)
podman machine init
# 启动 Machine
podman machine start
# 确认安装
podman version
podman machine list
# 停止 Machine
podman machine stop
# 删除 Machine
podman machine rm
参考:在 macOS 和 Windows 上,Podman 会创建一个轻量级 Linux VM(基于 QEMU 或 Apple Hypervisor Framework),并在其中运行容器。方式与 Docker Desktop 类似,但还额外提供了名为 Podman Desktop 的免费 GUI 工具。
3.3 兼容性配置:alias docker=podman
要让既有的 Docker 脚本和工作流不做任何改动就能继续使用,只需简单设置一个 alias 就够了。
# 添加到 shell 配置文件 (~/.bashrc 或 ~/.zshrc)
alias docker=podman
alias docker-compose=podman-compose
# 立即生效
source ~/.bashrc # 或 source ~/.zshrc
# RHEL/CentOS 提供 podman-docker 软件包
sudo dnf install -y podman-docker
# 该软件包会创建 /usr/bin/docker → /usr/bin/podman 的符号链接
# 同时也包含 docker.sock 的模拟
3.4 Docker Desktop vs Podman Desktop
| 对比项 | Docker Desktop | Podman Desktop |
|---|---|---|
| 许可证 | 250 人以上企业收费 ($5+/user/month) | 完全免费 (Apache 2.0) |
| GUI | 功能丰富的 UI | 基础 UI(正在快速改进) |
| Extension | Docker Extensions 市场 | 支持 Extension |
| Kubernetes | 内置 K8s 集群 | 联动 Kind/Minikube |
| VM 后端 | Linux Kit (macOS), WSL2 (Windows) | QEMU / Apple Hypervisor |
| 资源管理 | CPU、Memory、Disk 设置 | podman machine init --cpus --memory |
4. 镜像管理命令
容器由镜像创建而来。镜像管理是容器运维中基础中的基础。
4.1 搜索镜像
# 在 Docker Hub 上搜索镜像
docker search nginx
podman search nginx
# 限制结果数量
docker search --limit 5 nginx
podman search --limit 5 nginx
# 只搜索官方镜像 (Docker)
docker search --filter is-official=true nginx
# Podman 会同时搜索多个registry(取决于 registries.conf 的配置)
# /etc/containers/registries.conf
# unqualified-search-registries = ["docker.io", "quay.io", "ghcr.io"]
podman search --list-tags docker.io/library/nginx
4.2 拉取镜像 (Pull)
# 下载默认镜像
docker pull nginx
podman pull nginx
# 指定特定标签
docker pull nginx:1.25-alpine
podman pull nginx:1.25-alpine
# 从特定registry拉取
docker pull ghcr.io/myorg/myapp:latest
podman pull quay.io/prometheus/prometheus:latest
# 指定特定平台(架构)
docker pull --platform linux/arm64 nginx:latest
podman pull --platform linux/arm64 nginx:latest
# 下载所有标签
docker pull --all-tags nginx
podman pull --all-tags nginx
# 用 Digest 指定特定构建(不可变引用)
docker pull nginx@sha256:abc123...
podman pull nginx@sha256:abc123...
4.3 查看镜像列表
# 本地镜像列表
docker images
podman images
# 详细形式(等价命令)
docker image ls
podman image ls
# 过滤特定镜像
docker images nginx
podman images nginx
# 只显示 dangling 镜像(无标签的镜像)
docker images -f dangling=true
podman images -f dangling=true
# 只输出镜像 ID
docker images -q
podman images -q
# 自定义格式输出
docker images --format "{{.Repository}}:{{.Tag}} - {{.Size}}"
podman images --format "{{.Repository}}:{{.Tag}} - {{.Size}}"
# JSON 格式输出 (Podman)
podman images --format json
4.4 镜像详细信息 (Inspect)
# 查看镜像详细信息
docker inspect nginx:latest
podman inspect nginx:latest
# 只提取特定字段 (Go template)
docker inspect --format '{{.Config.ExposedPorts}}' nginx
podman inspect --format '{{.Config.ExposedPorts}}' nginx
# 查看镜像大小
docker inspect --format '{{.Size}}' nginx
podman inspect --format '{{.Size}}' nginx
# 查看环境变量
docker inspect --format '{{.Config.Env}}' nginx
podman inspect --format '{{.Config.Env}}' nginx
# 查看 Entrypoint 与 Cmd
docker inspect --format '{{.Config.Entrypoint}} {{.Config.Cmd}}' nginx
podman inspect --format '{{.Config.Entrypoint}} {{.Config.Cmd}}' nginx
4.5 镜像历史 (History)
# 查看镜像分层历史
docker history nginx
podman history nginx
# 显示完整命令(不截断)
docker history --no-trunc nginx
podman history --no-trunc nginx
# JSON 格式 (Podman)
podman history --format json nginx
4.6 镜像标签 (Tag)
# 给镜像添加新标签
docker tag nginx:latest myregistry.com/nginx:v1.0
podman tag nginx:latest myregistry.com/nginx:v1.0
# 添加多个标签
docker tag myapp:latest myapp:v2.1.0
docker tag myapp:latest myapp:stable
podman tag myapp:latest myapp:v2.1.0
podman tag myapp:latest myapp:stable
4.7 删除镜像 (Remove)
# 删除镜像
docker rmi nginx:latest
podman rmi nginx:latest
# 等价命令(推荐)
docker image rm nginx:latest
podman image rm nginx:latest
# 强制删除(即使有正在运行的容器)
docker rmi -f nginx:latest
podman rmi -f nginx:latest
# 删除所有镜像
docker rmi $(docker images -q)
podman rmi -a
# 只删除 Dangling 镜像
docker image prune -f
podman image prune -f
# 删除所有未被使用的镜像
docker image prune -a -f
podman image prune -a -f
# 按特定条件过滤后删除(超过 24 小时的镜像)
docker image prune -a --filter "until=24h"
podman image prune -a --filter "until=24h"
4.8 镜像保存/加载 (Save/Load)
在气隙(air-gapped)环境或离线传输时非常有用。
# 把镜像保存为 tar 文件
docker save -o nginx.tar nginx:latest
podman save -o nginx.tar nginx:latest
# 把多个镜像保存到一个 tar 中
docker save -o images.tar nginx:latest redis:latest postgres:15
podman save -o images.tar nginx:latest redis:latest postgres:15
# 以 gzip 压缩保存
docker save nginx:latest | gzip > nginx.tar.gz
podman save nginx:latest | gzip > nginx.tar.gz
# 从 tar 文件加载镜像
docker load -i nginx.tar
podman load -i nginx.tar
# 从 gzip 压缩文件加载
docker load -i nginx.tar.gz
podman load -i nginx.tar.gz
4.9 镜像 Import/Export
save/load 会包含镜像元数据(分层、标签、历史),而 export/import 只处理容器的文件系统。
# 把容器的文件系统导出为 tar
docker export my-container -o container-fs.tar
podman export my-container -o container-fs.tar
# 从 tar 文件创建镜像
docker import container-fs.tar myimage:imported
podman import container-fs.tar myimage:imported
# 直接从 URL 导入
docker import https://example.com/rootfs.tar.gz myimage:latest
podman import https://example.com/rootfs.tar.gz myimage:latest
4.10 registry登录/推送
# 登录 Docker Hub
docker login
podman login docker.io
# 登录特定registry
docker login ghcr.io
podman login quay.io
# 直接指定用户名/密码
docker login -u username -p password registry.example.com
podman login -u username -p password registry.example.com
# 登出
docker logout
podman logout docker.io
# 推送镜像
docker push myregistry.com/myapp:v1.0
podman push myregistry.com/myapp:v1.0
# 推送所有标签
docker push --all-tags myregistry.com/myapp
podman push --all-tags myregistry.com/myapp
5. 镜像构建
5.1 Dockerfile vs Containerfile
Docker 使用 Dockerfile 这个名字,而 Podman/Buildah 默认识别 Containerfile。不过 Podman 也会自动识别 Dockerfile,所以不用改名就能直接使用。内容和语法完全相同。
# Dockerfile 或 Containerfile —— 语法相同
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY /app/dist ./dist
COPY /app/node_modules ./node_modules
EXPOSE 3000
USER node
CMD ["node", "dist/main.js"]
| 项目 | Dockerfile | Containerfile |
|---|---|---|
| 使用工具 | Docker | Podman, Buildah |
| 语法 | 相同 | 相同 |
| 默认文件名 | Dockerfile | Containerfile |
| 互相兼容 | Podman 能识别 Dockerfile | Docker 不识别 Containerfile |
| 指定构建 | docker build -f Containerfile . | podman build -f Dockerfile . |
5.2 docker build vs podman build
# 基本构建
docker build -t myapp:latest .
podman build -t myapp:latest .
# 指定特定 Dockerfile
docker build -f Dockerfile.prod -t myapp:prod .
podman build -f Containerfile.prod -t myapp:prod .
# 传递 Build argument
docker build --build-arg NODE_ENV=production -t myapp:prod .
podman build --build-arg NODE_ENV=production -t myapp:prod .
# 不使用缓存构建
docker build --no-cache -t myapp:latest .
podman build --no-cache -t myapp:latest .
# 指定目标阶段(多阶段)
docker build --target builder -t myapp:builder .
podman build --target builder -t myapp:builder .
# 多平台构建 (Docker BuildKit)
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .
# Podman 多平台构建
podman build --platform linux/amd64,linux/arm64 --manifest myapp:latest .
# 构建时限制内存
docker build --memory 2g -t myapp:latest .
# 最小化构建上下文大小 —— 必须善用 .dockerignore
# .dockerignore(或 .containerignore)
# node_modules
# .git
# *.md
# dist
# .env
5.3 多阶段构建 (Multi-stage Build) 实战示例
多阶段构建通过分离构建环境与运行环境,把最终镜像体积大幅缩小,是一项核心技巧。
Go 应用示例
# ============================================
# Stage 1: 构建环境
# ============================================
FROM golang:1.22-alpine AS builder
# 安装构建所需的工具
RUN apk add --no-cache git ca-certificates
WORKDIR /app
# 先复制依赖定义(利用缓存)
COPY go.mod go.sum ./
RUN go mod download
# 复制源码并构建
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags="-w -s" -o /app/server ./cmd/server
# ============================================
# Stage 2: 运行环境 (scratch = 空镜像)
# ============================================
FROM scratch
# 复制 CA 证书(用于 HTTPS 通信)
COPY /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# 只复制二进制文件
COPY /app/server /server
# 以非 root 用户运行
USER 65534:65534
EXPOSE 8080
ENTRYPOINT ["/server"]
Java (Spring Boot) 示例
# ============================================
# Stage 1: 构建
# ============================================
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /app
COPY gradle/ gradle/
COPY gradlew build.gradle.kts settings.gradle.kts ./
RUN ./gradlew dependencies --no-daemon
COPY src/ src/
RUN ./gradlew bootJar --no-daemon -x test
# 生成 JRE 自定义运行时 (jlink)
RUN jlink \
--add-modules java.base,java.logging,java.sql,java.naming,java.management,java.instrument,java.security.jgss,java.desktop \
--strip-debug \
--no-man-pages \
--no-header-files \
--compress=zip-6 \
--output /custom-jre
# ============================================
# Stage 2: 运行(自定义 JRE)
# ============================================
FROM alpine:3.19
COPY /custom-jre /opt/java
COPY /app/build/libs/*.jar /app/app.jar
ENV JAVA_HOME=/opt/java
ENV PATH="${JAVA_HOME}/bin:${PATH}"
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
Python 示例
# ============================================
# Stage 1: 构建依赖
# ============================================
FROM python:3.12-slim AS builder
RUN pip install --no-cache-dir poetry
WORKDIR /app
COPY pyproject.toml poetry.lock ./
RUN poetry export -f requirements.txt --output requirements.txt --without-hashes
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# ============================================
# Stage 2: 运行环境
# ============================================
FROM python:3.12-slim
# 只安装必要的系统库
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq5 \
&& rm -rf /var/lib/apt/lists/*
COPY /install /usr/local
WORKDIR /app
COPY . .
RUN useradd --create-home appuser
USER appuser
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
5.4 Build Cache 策略
# ❌ 低效:源码一改动就要重新执行 npm install
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
# ✅ 高效:只有 package.json 变化时才重新执行 npm install
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
缓存层顺序原则:把变更频率低的层放在上面,把变更频率高的层放在下面。
变更频率低 ─── FROM (基础镜像)
│ RUN apt-get install (系统软件包)
│ COPY package.json (依赖定义)
│ RUN npm ci (安装依赖)
│ COPY . . (源代码)
变更频率高 ─── RUN npm run build (构建)
5.5 用 Buildah 做脚本化构建
Buildah 是一款强大的工具,不需要 Dockerfile 也能用 shell 脚本构建镜像。
#!/bin/bash
# buildah-build.sh —— 不用 Dockerfile 构建镜像
# 创建新容器(从空镜像开始)
container=$(buildah from scratch)
# 或者从基础镜像开始
container=$(buildah from alpine:3.19)
# 安装软件包
buildah run $container -- apk add --no-cache nginx
# 复制文件
buildah copy $container ./nginx.conf /etc/nginx/nginx.conf
buildah copy $container ./html /var/www/html
# 配置
buildah config --port 80 $container
buildah config --entrypoint '["/usr/sbin/nginx", "-g", "daemon off;"]' $container
buildah config --author "DevOps Team" $container
buildah config --label maintainer="devops@example.com" $container
# 提交为镜像
buildah commit $container myapp:latest
# 清理
buildah rm $container
# 确认构建结果
buildah images
5.6 用 Skopeo 复制/检查镜像
Skopeo 可以在 不下载镜像的情况下 直接在registry之间复制或检查镜像。
# 在registry之间直接复制镜像(无需本地下载!)
skopeo copy docker://docker.io/nginx:latest docker://quay.io/myorg/nginx:latest
# 检查镜像元数据(无需下载)
skopeo inspect docker://docker.io/library/nginx:latest
# 查看镜像标签列表
skopeo list-tags docker://docker.io/library/nginx
# 把镜像保存到本地目录 (OCI 格式)
skopeo copy docker://nginx:latest oci:./nginx-oci:latest
# 把镜像保存为 tar 文件
skopeo copy docker://nginx:latest docker-archive:./nginx.tar:nginx:latest
# 带认证复制到私有registry
skopeo copy --src-creds user:pass --dest-creds user:pass \
docker://source-registry.com/app:v1 \
docker://dest-registry.com/app:v1
# 删除镜像(从registry中)
skopeo delete docker://myregistry.com/myapp:old-tag
6. 容器生命周期命令
6.1 创建容器 (Create)
create 只创建容器而不启动它。之后可以用 start 来启动。
# 创建容器(不启动)
docker create --name my-nginx nginx:latest
podman create --name my-nginx nginx:latest
# 确认已创建的容器
docker ps -a
podman ps -a
# 启动已创建的容器
docker start my-nginx
podman start my-nginx
6.2 运行容器 (Run) —— 全部主要选项整理
run 一次性完成 create + start。它是容器运维中使用频率最高的命令。
# ============================================
# 基本运行
# ============================================
docker run nginx
podman run nginx
# ============================================
# 后台运行 (-d: detach)
# ============================================
docker run -d --name web nginx
podman run -d --name web nginx
# ============================================
# 交互模式 (-it: interactive + tty)
# ============================================
docker run -it ubuntu:22.04 bash
podman run -it ubuntu:22.04 bash
# ============================================
# 退出时自动删除 (--rm)
# ============================================
docker run --rm -it alpine sh
podman run --rm -it alpine sh
# ============================================
# 端口映射 (-p host:container)
# ============================================
docker run -d -p 8080:80 nginx # 指定端口
docker run -d -p 80:80 -p 443:443 nginx # 多端口
docker run -d -p 127.0.0.1:8080:80 nginx # 指定网络接口
docker run -d -P nginx # 随机端口自动映射
podman run -d -p 8080:80 nginx
# ============================================
# 挂载卷 (-v host:container[:options])
# ============================================
docker run -d -v /host/data:/container/data nginx # Bind mount
docker run -d -v myvolume:/container/data nginx # Named volume
docker run -d -v /host/data:/container/data:ro nginx # 只读
docker run -d --mount type=tmpfs,destination=/tmp nginx # tmpfs
podman run -d -v /host/data:/container/data:Z nginx # SELinux 标签 (:Z)
# ============================================
# 环境变量 (-e, --env-file)
# ============================================
docker run -d -e MYSQL_ROOT_PASSWORD=secret mysql:8
docker run -d -e DB_HOST=db -e DB_PORT=5432 myapp
docker run -d --env-file .env myapp
podman run -d -e MYSQL_ROOT_PASSWORD=secret mysql:8
# ============================================
# 资源限制 (--memory, --cpus)
# ============================================
docker run -d --memory 512m --memory-swap 1g nginx
docker run -d --cpus 1.5 nginx
docker run -d --cpus 2 --memory 1g --memory-reservation 512m myapp
podman run -d --memory 512m --cpus 1.5 nginx
# ============================================
# 重启策略 (--restart)
# ============================================
docker run -d --restart always nginx # 总是重启
docker run -d --restart unless-stopped nginx # 手动停止除外都重启
docker run -d --restart on-failure:5 nginx # 失败时最多重启 5 次
docker run -d --restart no nginx # 不重启(默认值)
podman run -d --restart always nginx
# ============================================
# 网络设置 (--network)
# ============================================
docker run -d --network my-network nginx
docker run -d --network host nginx # 直接使用宿主机网络
docker run -d --network none nginx # 无网络
podman run -d --network my-network nginx
# ============================================
# 共享 PID / IPC 命名空间
# ============================================
docker run -d --pid host nginx # 宿主机 PID 命名空间
docker run -d --pid container:other nginx # 共享其他容器的 PID
docker run -d --ipc host nginx # 共享宿主机 IPC
# ============================================
# 其他实用选项
# ============================================
docker run -d --hostname myhost nginx # 设置主机名
docker run -d --dns 8.8.8.8 nginx # 指定 DNS 服务器
docker run -d --add-host mydb:10.0.0.5 nginx # 添加 /etc/hosts 条目
docker run -d --workdir /app myapp # 工作目录
docker run -d --user 1000:1000 myapp # 指定运行用户
docker run -d --read-only myapp # 只读文件系统
docker run -d --log-driver json-file \
--log-opt max-size=10m --log-opt max-file=3 nginx # 日志驱动设置
6.3 容器列表 (ps)
# 正在运行的容器列表
docker ps
podman ps
# 所有容器(包含已停止的)
docker ps -a
podman ps -a
# 最近创建的 n 个容器
docker ps -n 5
podman ps -n 5
# 只输出容器 ID
docker ps -q
podman ps -q
# 自定义格式
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
podman ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# 按特定状态过滤
docker ps -f status=exited
docker ps -f status=running
docker ps -f name=web
podman ps -f status=exited
6.4 启动/停止/重启容器
# 启动容器
docker start my-nginx
podman start my-nginx
# 同时启动多个容器
docker start web db redis
podman start web db redis
# 停止容器 (SIGTERM → 10 秒后 SIGKILL)
docker stop my-nginx
podman stop my-nginx
# 指定超时时间(30 秒后强制终止)
docker stop -t 30 my-nginx
podman stop -t 30 my-nginx
# 停止所有正在运行的容器
docker stop $(docker ps -q)
podman stop -a
# 强制终止容器 (SIGKILL)
docker kill my-nginx
podman kill my-nginx
# 发送特定信号
docker kill -s SIGHUP my-nginx
podman kill -s SIGHUP my-nginx
# 重启容器
docker restart my-nginx
podman restart my-nginx
# 重启所有容器
docker restart $(docker ps -q)
podman restart -a
6.5 暂停/恢复容器
# 暂停容器 (SIGSTOP —— 冻结进程)
docker pause my-nginx
podman pause my-nginx
# 解除暂停 (SIGCONT)
docker unpause my-nginx
podman unpause my-nginx
6.6 删除容器
# 删除已停止的容器
docker rm my-nginx
podman rm my-nginx
# 强制删除正在运行的容器
docker rm -f my-nginx
podman rm -f my-nginx
# 连同卷一起删除
docker rm -v my-nginx
podman rm -v my-nginx
# 删除所有已停止的容器
docker container prune -f
podman container prune -f
# 删除所有容器(含运行中的)
docker rm -f $(docker ps -aq)
podman rm -f -a
6.7 容器重命名 / 提交 / 等待
# 重命名容器
docker rename old-name new-name
podman rename old-name new-name
# 把容器保存为镜像(对当前状态做快照)
docker commit my-container myimage:snapshot
podman commit my-container myimage:snapshot
# 提交时添加元数据
docker commit -m "Added config files" -a "Author" my-container myimage:v2
podman commit -m "Added config files" -a "Author" my-container myimage:v2
# 等待容器退出(返回 exit code)
docker wait my-container
podman wait my-container
7. 容器监控与调试
7.1 查看日志 (Logs)
# 输出全部日志
docker logs my-container
podman logs my-container
# 实时日志流 (follow)
docker logs -f my-container
podman logs -f my-container
# 只显示最后 N 行
docker logs --tail 100 my-container
podman logs --tail 100 my-container
# 包含时间戳
docker logs -t my-container
podman logs -t my-container
# 特定时间之后的日志
docker logs --since 2024-01-01T00:00:00 my-container
docker logs --since 30m my-container # 最近 30 分钟
docker logs --since 2h my-container # 最近 2 小时
podman logs --since 30m my-container
# 特定时间之前的日志
docker logs --until 2024-01-01T12:00:00 my-container
podman logs --until 2024-01-01T12:00:00 my-container
# 组合:实时 + 最后 50 行 + 时间戳
docker logs -f --tail 50 -t my-container
podman logs -f --tail 50 -t my-container
7.2 实时资源监控 (Stats)
# 实时显示所有运行中容器的资源使用量
docker stats
podman stats
# 只监控特定容器
docker stats my-container
podman stats my-container
# 只输出一次(适合脚本)
docker stats --no-stream
podman stats --no-stream
# 自定义格式
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}"
podman stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}"
输出示例:
NAME CPU % MEM USAGE / LIMIT NET I/O BLOCK I/O
web 0.50% 45.2MiB / 512MiB 12.5kB / 8.3kB 4.1MB / 0B
db 2.30% 256MiB / 1GiB 45.2kB / 12.1kB 50MB / 120MB
redis 0.10% 12.5MiB / 256MiB 3.2kB / 1.1kB 0B / 0B
7.3 查看进程 (Top)
# 容器内部进程列表
docker top my-container
podman top my-container
# 传递 ps 选项
docker top my-container -aux
podman top my-container -aux
# Podman 专属:支持额外字段
podman top my-container user pid ppid args %cpu %mem
podman top my-container huser hpid
7.4 查看详细信息 (Inspect)
# 容器全部信息 (JSON)
docker inspect my-container
podman inspect my-container
# 查看 IP 地址
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-container
podman inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-container
# 查看挂载信息
docker inspect -f '{{json .Mounts}}' my-container | jq .
podman inspect -f '{{json .Mounts}}' my-container | jq .
# 查看状态
docker inspect -f '{{.State.Status}}' my-container
podman inspect -f '{{.State.Status}}' my-container
# 查看重启次数
docker inspect -f '{{.RestartCount}}' my-container
# 查看环境变量
docker inspect -f '{{range .Config.Env}}{{println .}}{{end}}' my-container
podman inspect -f '{{range .Config.Env}}{{println .}}{{end}}' my-container
# 查看日志路径 (Docker)
docker inspect -f '{{.LogPath}}' my-container
7.5 复制文件 (cp)
# 宿主机 → 容器
docker cp ./config.yml my-container:/app/config.yml
podman cp ./config.yml my-container:/app/config.yml
# 容器 → 宿主机
docker cp my-container:/app/logs/error.log ./error.log
podman cp my-container:/app/logs/error.log ./error.log
# 复制目录
docker cp ./configs/ my-container:/app/configs/
podman cp ./configs/ my-container:/app/configs/
# 归档模式(保留符号链接)
docker cp -a my-container:/app/data ./backup/
podman cp -a my-container:/app/data ./backup/
7.6 进入容器 (Exec)
这是在运行中的容器内部执行命令的核心调试工具。
# 进入交互式 shell
docker exec -it my-container bash
docker exec -it my-container sh # 没有 bash 时(Alpine 等)
podman exec -it my-container bash
# 执行单条命令
docker exec my-container ls -la /app
podman exec my-container ls -la /app
# 设置环境变量后执行
docker exec -e DEBUG=true my-container node script.js
podman exec -e DEBUG=true my-container node script.js
# 以特定用户执行
docker exec -u root my-container apt-get update
podman exec -u root my-container dnf update
# 指定工作目录
docker exec -w /app my-container npm test
podman exec -w /app my-container npm test
# 在后台执行命令 (detach)
docker exec -d my-container touch /tmp/healthcheck
podman exec -d my-container touch /tmp/healthcheck
7.7 容器变更内容 (Diff)
# 查看容器文件系统的变更内容
docker diff my-container
podman diff my-container
# 输出示例:
# A /tmp/new-file (Added)
# C /etc/nginx/nginx.conf (Changed)
# D /var/log/old.log (Deleted)
7.8 查看端口映射 (Port)
# 查看容器的端口映射
docker port my-container
podman port my-container
# 查看特定端口
docker port my-container 80
podman port my-container 80
# 输出示例:
# 80/tcp -> 0.0.0.0:8080
# 443/tcp -> 0.0.0.0:8443
7.9 事件监控 (Events)
# 实时事件流
docker events
podman events
# 按特定事件类型过滤
docker events --filter event=start
docker events --filter event=stop
docker events --filter event=die
podman events --filter event=start
# 只过滤特定容器的事件
docker events --filter container=my-container
podman events --filter container=my-container
# 指定时间范围
docker events --since 1h --until 30m
podman events --since 1h
# JSON 格式输出
docker events --format '{{json .}}'
podman events --format json
8. 网络管理
8.1 网络类型
| 网络驱动 | 说明 | Docker | Podman |
|---|---|---|---|
| bridge | 默认网络,通过虚拟网桥实现容器间通信 | docker0(默认网桥) | Netavark (v4+) / CNI |
| host | 直接使用宿主机网络,无需端口映射 | 支持 | 支持 |
| none | 无网络,完全隔离 | 支持 | 支持 |
| macvlan | 给容器分配 MAC 地址,直连物理网络 | 支持 | 支持 |
| overlay | 多宿主机网络 (Swarm) | 支持 | 不支持 |
| ipvlan | 与 macvlan 类似但共享同一 MAC | 支持 | 支持 |
8.2 网络 CRUD 命令
# ============================================
# 创建网络 (Create)
# ============================================
# 创建默认 bridge 网络
docker network create my-network
podman network create my-network
# 指定子网与网关
docker network create \
--subnet 172.20.0.0/16 \
--gateway 172.20.0.1 \
my-network
podman network create \
--subnet 172.20.0.0/16 \
--gateway 172.20.0.1 \
my-network
# 指定 IP 范围
docker network create \
--subnet 172.20.0.0/16 \
--ip-range 172.20.240.0/20 \
--gateway 172.20.0.1 \
my-network
# 内部网络(无法从外部访问)
docker network create --internal internal-net
podman network create --internal internal-net
# 指定特定驱动
docker network create --driver macvlan \
--subnet 192.168.1.0/24 \
--gateway 192.168.1.1 \
-o parent=eth0 \
macvlan-net
# ============================================
# 网络列表 (List)
# ============================================
docker network ls
podman network ls
# ============================================
# 网络详细信息 (Inspect)
# ============================================
docker network inspect my-network
podman network inspect my-network
# 查看已连接的容器
docker network inspect -f '{{range .Containers}}{{.Name}} {{end}}' my-network
# ============================================
# 删除网络 (Remove)
# ============================================
docker network rm my-network
podman network rm my-network
# 清理未使用的网络
docker network prune -f
podman network prune -f
8.3 网络连接/断开
# 把运行中的容器连接到网络
docker network connect my-network my-container
podman network connect my-network my-container
# 以固定 IP 连接
docker network connect --ip 172.20.0.10 my-network my-container
podman network connect --ip 172.20.0.10 my-network my-container
# 从网络中断开
docker network disconnect my-network my-container
podman network disconnect my-network my-container
8.4 容器间通信示例
在用户自定义网络中,按容器名进行 DNS 解析 是自动完成的。
# 创建用户自定义网络
docker network create app-net
podman network create app-net
# 数据库容器
docker run -d --name db --network app-net \
-e POSTGRES_PASSWORD=secret \
postgres:16-alpine
# 应用容器(可以用名字访问 DB)
docker run -d --name app --network app-net \
-e DATABASE_URL="postgresql://postgres:secret@db:5432/mydb" \
-p 3000:3000 \
myapp:latest
# 测试:从 app 容器 ping db
docker exec app ping -c 3 db
# PING db (172.20.0.2): 56 data bytes
# 64 bytes from 172.20.0.2: icmp_seq=0 ttl=64 time=0.123 ms
注意:在 Docker/Podman 的默认
bridge网络中,无法按容器名进行 DNS 解析。必须创建 用户自定义网络(user-defined network),才能用容器名互相通信。
8.5 Docker: docker0 bridge vs Podman: Netavark/CNI
Docker 默认会创建一个名为 docker0 的 Linux bridge 接口,并用 iptables 规则来管理网络。
Podman 从 v4.0 起把默认网络栈从 CNI(Container Network Interface) 切换到了 Netavark。Netavark 是用 Rust 编写的容器网络栈,与 Aardvark-dns 一起提供 DNS 解析。
# 确认 Podman 网络后端
podman info --format '{{.Host.NetworkBackend}}'
# 输出: netavark
# Netavark + Aardvark-dns 结构:
# ┌──────────────┐ ┌──────────────┐
# │ Container A │ │ Container B │
# │ 172.20.0.2 │ │ 172.20.0.3 │
# └──────┬───────┘ └──────┬───────┘
# │ │
# ┌──────▼─────────────────────▼──────┐
# │ Netavark Bridge │
# │ (基于 nftables 的网络管理) │
# ├───────────────────────────────────┤
# │ Aardvark-dns │
# │ (容器名 → IP 解析) │
# └───────────────────────────────────┘
9. 卷与存储管理
9.1 Named Volume 管理
# ============================================
# 创建卷
# ============================================
docker volume create my-data
podman volume create my-data
# 指定驱动与选项
docker volume create --driver local \
--opt type=nfs \
--opt o=addr=192.168.1.100,rw \
--opt device=:/path/to/share \
nfs-volume
# 添加标签
docker volume create --label project=myapp --label env=prod my-data
podman volume create --label project=myapp --label env=prod my-data
# ============================================
# 卷列表
# ============================================
docker volume ls
podman volume ls
# 过滤
docker volume ls -f label=project=myapp
podman volume ls -f label=project=myapp
# Dangling 卷(未连接到任何容器)
docker volume ls -f dangling=true
podman volume ls -f dangling=true
# ============================================
# 卷详细信息
# ============================================
docker volume inspect my-data
podman volume inspect my-data
# 查看挂载点
docker volume inspect -f '{{.Mountpoint}}' my-data
podman volume inspect -f '{{.Mountpoint}}' my-data
# ============================================
# 删除卷
# ============================================
docker volume rm my-data
podman volume rm my-data
# 清理未使用的卷
docker volume prune -f
podman volume prune -f
9.2 Bind Mount vs Named Volume vs tmpfs
| 对比项 | Bind Mount | Named Volume | tmpfs |
|---|---|---|---|
| 宿主机路径 | 直接指定 | 由 Docker/Podman 管理 | 无(内存) |
| 数据持久性 | 永久保存在宿主机 | 永久保存在卷中 | 容器退出时删除 |
| 性能 | 依赖宿主机 FS | 可优化 | 最高(基于内存) |
| 可移植性 | 低(依赖宿主机路径) | 高 | 高 |
| 备份 | 自行管理 | docker volume 命令 | 不可 |
| 使用场景 | 挂载源码、配置文件 | DB 数据、上传文件 | 临时文件、密钥 |
# Bind Mount
docker run -d -v /home/user/data:/app/data nginx
docker run -d --mount type=bind,source=/home/user/data,target=/app/data nginx
# Named Volume
docker run -d -v app-data:/app/data nginx
docker run -d --mount type=volume,source=app-data,target=/app/data nginx
# tmpfs(基于内存,容器退出时删除)
docker run -d --tmpfs /tmp:rw,size=100m nginx
docker run -d --mount type=tmpfs,destination=/tmp,tmpfs-size=100m nginx
# 只读挂载
docker run -d -v /host/config:/app/config:ro nginx
docker run -d --mount type=bind,source=/host/config,target=/app/config,readonly nginx
# 在 Podman 中设置 SELinux 标签
podman run -d -v /host/data:/app/data:Z nginx # Z: 私有标签
podman run -d -v /host/data:/app/data:z nginx # z: 共享标签
9.3 数据备份/恢复模式
# ============================================
# 备份卷数据
# ============================================
# 方法 1: 利用临时容器备份
docker run --rm \
-v my-data:/source:ro \
-v $(pwd):/backup \
alpine tar czf /backup/my-data-backup.tar.gz -C /source .
podman run --rm \
-v my-data:/source:ro \
-v $(pwd):/backup \
alpine tar czf /backup/my-data-backup.tar.gz -C /source .
# 方法 2: 按日期备份
docker run --rm \
-v postgres-data:/source:ro \
-v $(pwd)/backups:/backup \
alpine tar czf /backup/postgres-$(date +%Y%m%d).tar.gz -C /source .
# ============================================
# 恢复卷数据
# ============================================
# 创建新卷后恢复
docker volume create restored-data
docker run --rm \
-v restored-data:/target \
-v $(pwd):/backup:ro \
alpine tar xzf /backup/my-data-backup.tar.gz -C /target
podman run --rm \
-v restored-data:/target \
-v $(pwd):/backup:ro \
alpine tar xzf /backup/my-data-backup.tar.gz -C /target
# ============================================
# 卷之间的数据迁移
# ============================================
docker run --rm \
-v old-volume:/from:ro \
-v new-volume:/to \
alpine sh -c "cp -a /from/. /to/"
10. Docker Compose vs Podman Compose
10.1 docker-compose.yml 基本结构
# docker-compose.yml(或 compose.yml)
version: '3.9' # Compose 文件格式版本(v2 中为可选)
services:
web:
build: ./app # Dockerfile 路径
image: myapp:latest # 构建出的镜像名
container_name: myapp-web # 容器名
ports:
- '3000:3000' # 端口映射
environment: # 环境变量
- NODE_ENV=production
- DB_HOST=db
env_file: # 环境变量文件
- .env
volumes: # 挂载卷
- ./app:/app
- node_modules:/app/node_modules
depends_on: # 依赖关系
db:
condition: service_healthy
redis:
condition: service_started
networks: # 网络
- app-net
restart: unless-stopped # 重启策略
deploy: # 资源限制
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
healthcheck: # 健康检查
test: ['CMD', 'curl', '-f', 'http://localhost:3000/health']
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
db:
image: postgres:16-alpine
container_name: myapp-db
environment:
POSTGRES_DB: myapp
POSTGRES_USER: admin
POSTGRES_PASSWORD: secret
volumes:
- postgres-data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- app-net
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U admin -d myapp']
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: myapp-redis
command: redis-server --appendonly yes --maxmemory 256mb
volumes:
- redis-data:/data
networks:
- app-net
volumes:
postgres-data:
driver: local
redis-data:
driver: local
node_modules:
networks:
app-net:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/16
10.2 主要 Compose 命令
# ============================================
# Docker Compose (v2: docker compose / v1: docker-compose)
# ============================================
# 启动服务(后台)
docker compose up -d
docker compose -f docker-compose.prod.yml up -d
# 构建后启动服务
docker compose up -d --build
# 只启动特定服务
docker compose up -d web db
# 扩缩容(调整服务实例数)
docker compose up -d --scale web=3
# 停止服务并清理资源
docker compose down
# 连同卷一起删除
docker compose down -v
# 连同镜像一起删除
docker compose down --rmi all
# 服务列表与状态
docker compose ps
# 服务日志
docker compose logs
docker compose logs -f web
docker compose logs --tail 100 web db
# 在服务内部执行命令
docker compose exec web bash
docker compose exec db psql -U admin -d myapp
# 执行一次性命令(run 会创建新容器)
docker compose run --rm web npm test
docker compose run --rm web python manage.py migrate
# 构建服务
docker compose build
docker compose build --no-cache web
# 重启服务
docker compose restart
docker compose restart web
# 校验配置有效性
docker compose config
# 拉取镜像
docker compose pull
# ============================================
# Podman Compose
# ============================================
# 安装 podman-compose
pip3 install podman-compose
# 或者在 Podman 4.7+ 中使用 podman compose(插件方式)
# 用法与 docker compose 相同
podman compose up -d
podman compose down
podman compose ps
podman compose logs -f web
podman compose exec web bash
10.3 实战示例:Web 应用 + DB + Redis 三层架构
# compose.yml —— 生产环境三层架构
services:
# ============================================
# Tier 1: Reverse Proxy (Nginx)
# ============================================
nginx:
image: nginx:1.25-alpine
container_name: proxy
ports:
- '80:80'
- '443:443'
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
app:
condition: service_healthy
networks:
- frontend
restart: unless-stopped
# ============================================
# Tier 2: Application (Node.js)
# ============================================
app:
build:
context: ./app
dockerfile: Dockerfile
args:
NODE_ENV: production
container_name: app
expose:
- '3000'
environment:
- NODE_ENV=production
- DB_HOST=postgres
- DB_PORT=5432
- DB_NAME=appdb
- DB_USER=appuser
- DB_PASS=${DB_PASSWORD}
- REDIS_URL=redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- frontend
- backend
healthcheck:
test:
[
'CMD',
'node',
'-e',
"require('http').get('http://localhost:3000/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) })",
]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
cpus: '2.0'
memory: 1G
restart: unless-stopped
# ============================================
# Tier 3: Database (PostgreSQL)
# ============================================
postgres:
image: postgres:16-alpine
container_name: postgres
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
POSTGRES_PASSWORD: ${DB_PASSWORD}
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- postgres-data:/var/lib/postgresql/data
- ./db/init:/docker-entrypoint-initdb.d:ro
networks:
- backend
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U appuser -d appdb']
interval: 10s
timeout: 5s
retries: 5
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
restart: unless-stopped
# ============================================
# Cache: Redis
# ============================================
redis:
image: redis:7-alpine
container_name: redis
command: >
redis-server
--appendonly yes
--maxmemory 256mb
--maxmemory-policy allkeys-lru
--requirepass ${REDIS_PASSWORD}
volumes:
- redis-data:/data
networks:
- backend
healthcheck:
test: ['CMD', 'redis-cli', '-a', '${REDIS_PASSWORD}', 'ping']
interval: 10s
timeout: 5s
retries: 5
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
restart: unless-stopped
volumes:
postgres-data:
driver: local
redis-data:
driver: local
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # 阻断外部访问
10.4 按环境划分配置 (Override)
# compose.override.yml —— 开发环境(自动加载)
services:
app:
build:
args:
NODE_ENV: development
volumes:
- ./app/src:/app/src # 源码热重载
environment:
- NODE_ENV=development
- DEBUG=app:*
ports:
- '3000:3000' # 开发时直接访问
- '9229:9229' # Node.js 调试器端口
postgres:
ports:
- '5432:5432' # 开发时直接访问
# compose.prod.yml —— 生产环境
services:
app:
build:
args:
NODE_ENV: production
deploy:
replicas: 3
resources:
limits:
cpus: '4.0'
memory: 2G
nginx:
deploy:
resources:
limits:
cpus: '1.0'
memory: 256M
# 开发环境(自动合并 compose.yml + compose.override.yml)
docker compose up -d
# 生产环境(排除 override,使用 prod 配置)
docker compose -f compose.yml -f compose.prod.yml up -d
# 使用按环境划分的 .env 文件
docker compose --env-file .env.production up -d
11. Podman 独有功能
11.1 Pod 管理
Podman 的 Pod 与 Kubernetes 的 Pod 是同一个概念。同一个 Pod 内的容器会共享 Network namespace、IPC namespace、PID namespace。
# ============================================
# 创建 Pod
# ============================================
# 创建基本 Pod
podman pod create --name my-pod
# 创建时带端口映射(在 Pod 层面定义端口)
podman pod create --name web-pod -p 8080:80 -p 8443:443
# 指定网络
podman pod create --name web-pod --network my-network
# ============================================
# 向 Pod 添加容器
# ============================================
# 在 Pod 中运行容器(--pod 选项)
podman run -d --pod my-pod --name nginx nginx:latest
podman run -d --pod my-pod --name php php:8.2-fpm
# Pod 内的容器之间可以通过 localhost 通信
# nginx → localhost:9000 → php-fpm
# ============================================
# Pod 管理
# ============================================
# Pod 列表
podman pod list
podman pod ps
# Pod 详细信息
podman pod inspect my-pod
# 启动/停止/重启 Pod
podman pod start my-pod
podman pod stop my-pod
podman pod restart my-pod
# 暂停/恢复 Pod
podman pod pause my-pod
podman pod unpause my-pod
# 删除 Pod(含容器)
podman pod rm my-pod
podman pod rm -f my-pod # 强制删除
# 删除所有 Pod
podman pod rm -a -f
# 查看 Pod 的进程
podman pod top my-pod
# Pod 的资源使用量
podman pod stats my-pod
11.2 生成 Kubernetes YAML (podman generate kube)
可以把本地测试过的容器/Pod 配置自动转换成 Kubernetes YAML。
# 从容器生成 K8s YAML
podman generate kube my-container > deployment.yaml
# 从 Pod 生成 K8s YAML
podman generate kube my-pod > pod.yaml
# 包含 Service 信息
podman generate kube --service my-pod > pod-with-service.yaml
# 生成的 YAML 示例:
# apiVersion: v1
# kind: Pod
# metadata:
# name: my-pod
# spec:
# containers:
# - name: nginx
# image: nginx:latest
# ports:
# - containerPort: 80
# hostPort: 8080
# - name: php
# image: php:8.2-fpm
11.3 运行 Kubernetes YAML (podman play kube)
反过来,也可以在 Podman 中直接运行 Kubernetes YAML 文件。
# 用 K8s YAML 文件创建 Pod
podman play kube deployment.yaml
# 支持 ConfigMap
podman play kube pod.yaml --configmap configmap.yaml
# 支持密钥
podman play kube pod.yaml --seccomp-profile-root ./profiles
# 替换已有资源(更新)
podman play kube --replace pod.yaml
# 删除资源
podman play kube --down pod.yaml
# 包含构建
podman play kube --build pod.yaml
11.4 生成 Systemd 服务
# 把容器生成为 systemd 服务
podman generate systemd --name my-container > ~/.config/systemd/user/my-container.service
# 应用新配置(用户级别)
systemctl --user daemon-reload
systemctl --user enable my-container.service
systemctl --user start my-container.service
# 把 Pod 生成为 systemd 服务
podman generate systemd --name my-pod --files
# → 生成 pod-my-pod.service, container-nginx.service, container-php.service
# 选项
podman generate systemd --name my-container \
--restart-policy always \
--time 30 \
--new # 启动时创建新容器(推荐)
11.5 Quadlet:systemd 原生容器管理
Quadlet 是 Podman 4.4+ 引入的机制,用 systemd unit 文件格式以声明式方式管理容器。
# ~/.config/containers/systemd/webapp.container
[Unit]
Description=My Web Application
After=network-online.target
[Container]
Image=docker.io/library/nginx:latest
ContainerName=webapp
PublishPort=8080:80
Volume=webapp-data:/usr/share/nginx/html:ro
Environment=NGINX_WORKER_PROCESSES=auto
AutoUpdate=registry
HealthCmd=curl -f http://localhost/ || exit 1
HealthInterval=30s
[Service]
Restart=always
TimeoutStartSec=300
[Install]
WantedBy=default.target
# 放置 Quadlet 文件后应用
# 系统级别: /etc/containers/systemd/
# 用户级别: ~/.config/containers/systemd/
systemctl --user daemon-reload
systemctl --user start webapp.service
systemctl --user enable webapp.service
systemctl --user status webapp.service
# 查看日志
journalctl --user -u webapp.service -f
11.6 Rootless Containers 详解
Podman 的 rootless 容器利用 User Namespace,把容器内部的 root(UID 0)映射到宿主机上的普通用户 UID。
# 查看当前用户的 UID 映射
cat /etc/subuid
# user1:100000:65536
# → user1 把宿主机 UID 100000~165535 用作容器内部 UID
cat /etc/subgid
# user1:100000:65536
# 映射结构:
# 容器 UID 0 (root) → 宿主机 UID 100000(普通用户)
# 容器 UID 1 → 宿主机 UID 100001
# 容器 UID 65535 → 宿主机 UID 165535
# 确认 rootless 状态
podman info --format '{{.Host.Security.Rootless}}'
# true
# 确认 rootless 模式下的限制事项
podman info | grep -A 5 rootless
# 运行 rootless 容器(默认值)
podman run -d --name rootless-nginx -p 8080:80 nginx
# 在宿主机上查看进程 —— 以普通用户而非 root 运行
ps aux | grep nginx
# user1 12345 ... nginx: master process
12. 安全相关命令与配置
12.1 Rootless 模式配置
# ============================================
# Docker Rootless 模式配置
# ============================================
# 安装 rootless Docker
curl -fsSL https://get.docker.com/rootless | sh
# 设置环境变量 (~/.bashrc)
export PATH=$HOME/bin:$PATH
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
# 启动 rootless Docker
systemctl --user start docker
systemctl --user enable docker
# ============================================
# Podman Rootless 模式配置(默认启用)
# ============================================
# 确认 subuid/subgid 配置
grep $USER /etc/subuid /etc/subgid
# 若没有配置则添加
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
# 命名空间迁移(重新映射已有镜像)
podman system migrate
12.2 Security Options
# ============================================
# Capability 管理
# ============================================
# 移除全部 capability 后只添加需要的(最小权限原则)
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE nginx
podman run --cap-drop ALL --cap-add NET_BIND_SERVICE nginx
# 确认默认 capability
docker run --rm alpine cat /proc/1/status | grep Cap
podman run --rm alpine cat /proc/1/status | grep Cap
# 主要 Capability 列表:
# NET_BIND_SERVICE : 绑定 1024 以下端口
# SYS_PTRACE : 进程调试 (strace 等)
# NET_RAW : 使用 RAW 套接字 (ping 等)
# CHOWN : 修改文件所有权
# DAC_OVERRIDE : 忽略文件权限
# SETUID/SETGID : 修改 UID/GID
# ============================================
# Security Options
# ============================================
# no-new-privileges: 防止 execve 时提权
docker run --security-opt no-new-privileges:true myapp
podman run --security-opt no-new-privileges:true myapp
# 应用 Seccomp 配置文件
docker run --security-opt seccomp=./custom-seccomp.json myapp
podman run --security-opt seccomp=./custom-seccomp.json myapp
# AppArmor 配置文件 (Docker/Ubuntu)
docker run --security-opt apparmor=docker-default myapp
# SELinux 标签 (Podman/RHEL)
podman run --security-opt label=type:container_t myapp
podman run --security-opt label=disable myapp # 关闭 SELinux
12.3 只读容器
# 把文件系统设为只读
docker run --read-only nginx
podman run --read-only nginx
# 只把需要写入的目录挂载为 tmpfs
docker run --read-only \
--tmpfs /tmp \
--tmpfs /var/run \
--tmpfs /var/cache/nginx \
nginx
podman run --read-only \
--tmpfs /tmp \
--tmpfs /var/run \
--tmpfs /var/cache/nginx \
nginx
12.4 镜像安全验证
# ============================================
# Docker Content Trust (DCT)
# ============================================
# 只允许拉取/运行已签名的镜像
export DOCKER_CONTENT_TRUST=1
docker pull nginx:latest # 已验证签名
# 给镜像签名
docker trust sign myregistry.com/myapp:v1.0
# 确认签名
docker trust inspect --pretty myregistry.com/myapp:v1.0
# ============================================
# Podman 镜像签名(基于 GPG)
# ============================================
# 查看签名策略文件
cat /etc/containers/policy.json
# 用 GPG 密钥给镜像签名
podman push --sign-by security@example.com myregistry.com/myapp:v1.0
# 签名策略配置示例 (/etc/containers/policy.json)
# {
# "default": [{"type": "reject"}],
# "transports": {
# "docker": {
# "myregistry.com": [
# {
# "type": "signedBy",
# "keyType": "GPGKeys",
# "keyPath": "/etc/pki/rpm-gpg/RPM-GPG-KEY-myorg"
# }
# ],
# "docker.io": [{"type": "insecureAcceptAnything"}]
# }
# }
# }
# 用 Skopeo 验证镜像完整性
skopeo inspect --raw docker://myregistry.com/myapp:v1.0 | jq .
12.5 安全最佳实践检查清单
| 项目 | Docker 命令 / 配置 | Podman 命令 / 配置 |
|---|---|---|
| 禁止以 root 运行 | USER nonroot in Dockerfile | 默认 rootless |
| 最小化 Capability | --cap-drop ALL --cap-add ... | --cap-drop ALL --cap-add ... |
| Read-only FS | --read-only | --read-only |
| 防止提权 | --security-opt no-new-privileges | --security-opt no-new-privileges |
| 资源限制 | --memory --cpus --pids-limit | --memory --cpus --pids-limit |
| 网络隔离 | --network none / internal network | --network none / internal network |
| 镜像签名 | Docker Content Trust | GPG signing / sigstore |
| 密钥管理 | Docker Secrets / 环境变量 | Podman secrets / 环境变量 |
| 基础镜像 | distroless / scratch / alpine | distroless / scratch / alpine |
| 镜像扫描 | docker scout / Trivy | Trivy / Grype |
13. 系统管理与清理
13.1 查看磁盘占用 (system df)
# 磁盘占用摘要
docker system df
podman system df
# 详细信息
docker system df -v
podman system df -v
# 输出示例:
# TYPE TOTAL ACTIVE SIZE RECLAIMABLE
# Images 15 5 4.2GB 2.8GB (66%)
# Containers 8 3 120MB 80MB (66%)
# Local Volumes 10 4 1.5GB 800MB (53%)
# Build Cache 20 0 500MB 500MB (100%)
13.2 整体清理 (system prune)
# 清理已停止的容器 + dangling 镜像 + 未使用的网络 + 构建缓存
docker system prune
podman system prune
# 不显示确认提示直接执行
docker system prune -f
podman system prune -f
# 连未使用的镜像一起清理(注意!)
docker system prune -a -f
podman system prune -a -f
# 连卷一起清理(非常注意!可能丢失数据)
docker system prune -a --volumes -f
podman system prune -a --volumes -f
# 只清理超过特定时间的资源
docker system prune -a --filter "until=720h" -f # 30 天以上
13.3 系统信息与版本
# 系统整体信息
docker info
podman info
# 主要确认项:
# - Storage Driver
# - Cgroup Version (v1/v2)
# - Security Options
# - Kernel Version
# - OS/Architecture
# - Registry 配置
# 版本信息
docker version
podman version
# 分别确认客户端/服务端版本
docker version --format '{{.Client.Version}}'
docker version --format '{{.Server.Version}}'
podman version --format '{{.Client.Version}}'
14. 实战速查表:最常用的 30 条命令
下面这张表把容器运维中使用频率最高的 30 条命令一览式整理出来。Docker 和 Podman 使用完全相同的语法。
| # | 操作 | 命令 |
|---|---|---|
| 1 | 下载镜像 | docker pull nginx:latest |
| 2 | 镜像列表 | docker images |
| 3 | 删除镜像 | docker rmi nginx:latest |
| 4 | 清理 Dangling 镜像 | docker image prune -f |
| 5 | 构建镜像 | docker build -t myapp:latest . |
| 6 | 运行容器(后台) | docker run -d --name web -p 80:80 nginx |
| 7 | 运行容器(交互式) | docker run -it --rm alpine sh |
| 8 | 运行中的容器列表 | docker ps |
| 9 | 所有容器列表 | docker ps -a |
| 10 | 停止容器 | docker stop web |
| 11 | 启动容器 | docker start web |
| 12 | 重启容器 | docker restart web |
| 13 | 删除容器 | docker rm web |
| 14 | 强制删除容器 | docker rm -f web |
| 15 | 删除所有已停止容器 | docker container prune -f |
| 16 | 容器日志 | docker logs -f --tail 100 web |
| 17 | 进入容器 (exec) | docker exec -it web bash |
| 18 | 复制文件(宿主机→容器) | docker cp file.txt web:/app/ |
| 19 | 复制文件(容器→宿主机) | docker cp web:/app/log.txt ./ |
| 20 | 资源监控 | docker stats |
| 21 | 详细信息 (JSON) | docker inspect web |
| 22 | 创建网络 | docker network create my-net |
| 23 | 网络列表 | docker network ls |
| 24 | 创建卷 | docker volume create my-vol |
| 25 | 卷列表 | docker volume ls |
| 26 | 启动 Compose 服务 | docker compose up -d |
| 27 | 停止 Compose 服务 | docker compose down |
| 28 | 登录registry | docker login registry.example.com |
| 29 | 推送镜像 | docker push myregistry.com/app:v1 |
| 30 | 清理整个系统 | docker system prune -a -f |
Tip:上面所有命令把
docker换成podman都能照常运行。
15. 故障排查指南
15.1 权限相关问题 (Permission Issues)
Docker: "permission denied while trying to connect to the Docker daemon socket"
# 原因: 当前用户不属于 docker 组
# 解决:
sudo usermod -aG docker $USER
newgrp docker # 或者重新登录
# 确认
groups $USER
docker ps # 应当无错误运行
Podman Rootless: "Error: could not get runtime: cannot re-exec process"
# 原因: 未配置 subuid/subgid
# 解决:
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
# 命名空间迁移
podman system migrate
# 确认
podman unshare cat /proc/self/uid_map
Podman Rootless: 卷挂载权限问题
# 原因: 宿主机与容器的 UID 映射不一致
# 方法 1: 用 unshare 修改所有权
podman unshare chown 1000:1000 /host/path/data
# 方法 2: 用 :U 选项自动映射 UID (Podman 4.0+)
podman run -v /host/data:/data:U myapp
# 方法 3: 添加 SELinux 标签 (RHEL/CentOS)
podman run -v /host/data:/data:Z myapp
15.2 网络连接问题
容器无法访问外部网络
# 确认 DNS
docker exec my-container cat /etc/resolv.conf
docker exec my-container nslookup google.com
# 解决 1: 直接指定 DNS 服务器
docker run --dns 8.8.8.8 --dns 8.8.4.4 myapp
# 解决 2: 检查 iptables/nftables 规则
sudo iptables -L -n -t nat
sudo nft list ruleset
# 解决 3: 启用 IP 转发
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
# 永久配置: 在 /etc/sysctl.conf 中添加 net.ipv4.ip_forward=1
容器之间无法用名字通信
# 原因: 正在使用默认 bridge 网络(不支持 DNS 解析)
# 解决: 创建用户自定义网络
docker network create my-net
docker run -d --network my-net --name db postgres:16
docker run -d --network my-net --name app \
-e DB_HOST=db myapp # 现在可以用 "db" 访问了
Podman rootless 绑定 1024 以下端口失败
# 原因: rootless 模式下默认无法使用 1024 以下端口
# 解决 1: 使用 1024 以上端口
podman run -d -p 8080:80 nginx
# 解决 2: 用 sysctl 修改 unprivileged 端口范围
sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80
# 永久配置: /etc/sysctl.conf
# 解决 3: 使用 rootful 模式
sudo podman run -d -p 80:80 nginx
15.3 存储/磁盘空间问题
"no space left on device" 错误
# 确认磁盘占用
docker system df -v
podman system df -v
# 分阶段清理:
# 第 1 步: 删除已停止的容器
docker container prune -f
# 第 2 步: 删除 Dangling 镜像
docker image prune -f
# 第 3 步: 删除未使用的卷(注意: 先确认数据!)
docker volume prune -f
# 第 4 步: 删除构建缓存
docker builder prune -f
# 第 5 步: 整体清理(最后手段)
docker system prune -a --volumes -f
# 只清理超过特定时间的镜像
docker image prune -a --filter "until=720h" -f # 30 天以上
修改 Podman 存储路径
# Rootless Podman 的默认存储位置
# ~/.local/share/containers/storage/
# 修改存储路径: ~/.config/containers/storage.conf
# [storage]
# driver = "overlay"
# graphroot = "/mnt/large-disk/containers/storage"
# 修改后迁移
podman system reset # 注意: 会删除所有容器/镜像
15.4 镜像构建失败
Multi-platform 构建错误 (Docker)
# 需要安装 QEMU 模拟器
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
# 创建 buildx 构建器
docker buildx create --name multiarch --use
docker buildx inspect multiarch --bootstrap
# 多平台构建
docker buildx build --platform linux/amd64,linux/arm64 \
-t myapp:latest --push .
构建上下文过大的情况
# 创建 .dockerignore 文件
cat > .dockerignore << 'EOF'
.git
node_modules
dist
*.log
.env
.DS_Store
**/*.test.js
**/*.spec.js
coverage
.nyc_output
EOF
# 确认构建上下文大小
du -sh . --exclude=.git --exclude=node_modules
# 只把特定路径用作构建上下文
docker build -f Dockerfile -t myapp . --build-context src=./src
15.5 通用调试模式
# ============================================
# 调试立即退出的容器
# ============================================
# 查看退出日志
docker logs my-container
docker inspect -f '{{.State.ExitCode}}' my-container
docker inspect -f '{{.State.Error}}' my-container
# 覆盖 entrypoint 进入 shell
docker run -it --entrypoint sh myapp:latest
podman run -it --entrypoint sh myapp:latest
# ============================================
# 网络调试
# ============================================
# 网络调试专用容器
docker run --rm -it --network container:target-container \
nicolaka/netshoot bash
# 在特定网络中调试
docker run --rm -it --network my-net nicolaka/netshoot bash
# → 可以使用 nslookup, dig, curl, tcpdump, iperf3, netstat 等
# ============================================
# 文件系统调试
# ============================================
# 确认容器文件系统的变更内容
docker diff my-container
# 把容器的文件系统提取为 tar 后分析
docker export my-container | tar -tf - | head -50
# ============================================
# 确认资源限制
# ============================================
# 确认容器的 cgroup 配置
docker exec my-container cat /sys/fs/cgroup/memory.max
docker exec my-container cat /sys/fs/cgroup/cpu.max
# 确认是否被 OOM Killed
docker inspect -f '{{.State.OOMKilled}}' my-container
16. 参考资料与文献
官方文档
- Docker 官方文档: https://docs.docker.com/
- Docker CLI Reference: https://docs.docker.com/engine/reference/commandline/cli/
- Docker Compose Reference: https://docs.docker.com/compose/compose-file/
- Dockerfile Reference: https://docs.docker.com/engine/reference/builder/
- Podman 官方文档: https://docs.podman.io/
- Podman CLI Reference: https://docs.podman.io/en/latest/Commands.html
- Buildah 官方文档: https://buildah.io/
- Skopeo 官方文档: https://github.com/containers/skopeo
OCI 标准
- OCI Image Spec: https://github.com/opencontainers/image-spec
- OCI Runtime Spec: https://github.com/opencontainers/runtime-spec
- OCI Distribution Spec: https://github.com/opencontainers/distribution-spec
安全指南
- CIS Docker Benchmark: https://www.cisecurity.org/benchmark/docker
- NIST Container Security Guide (SP 800-190): https://csrc.nist.gov/publications/detail/sp/800-190/final
- Docker Security Best Practices: https://docs.docker.com/engine/security/
- Podman Rootless Tutorial: https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md
相关项目
- containerd: https://containerd.io/
- CRI-O: https://cri-o.io/
- Netavark: https://github.com/containers/netavark
- Quadlet: https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html
- Podman Desktop: https://podman-desktop.io/