Containerfile 与多阶段构建:在 Fedora 上用 Podman 打镜像
Fedora 上构建镜像不需要 Docker:podman build 直接读 Containerfile(Dockerfile 语法完全兼容)。
podman --versiondnf list --available buildah | tail -1podman version 6.1.0buildah.x86_64 2:1.45.1-1.fc45 fedoraContainerfile 还是 Dockerfile
Section titled “Containerfile 还是 Dockerfile”两个名字都行,内容语法一样。Podman 默认找 Containerfile,找不到再找 Dockerfile。
podman build -t myapp:1 .podman build -f Containerfile -t myapp:1 .习惯上:给 Podman/Buildah 用 Containerfile,给 Docker 用 Dockerfile。混用不会出错。
多阶段构建:为什么必须用
Section titled “多阶段构建:为什么必须用”编译型语言(Go/Rust/Java)的构建依赖很大,但运行时不需要它们。多阶段构建把”编译环境”和”运行环境”分开,最终镜像只留二进制。
实测一个 Go 程序:
mkdir -p /tmp/cf && cd /tmp/cfcat > Containerfile <<'EOF'FROM docker.io/library/golang:1.27-alpine AS buildWORKDIR /srcRUN echo 'package main' > main.go && echo 'import "fmt"' >> main.go && echo 'func main(){ fmt.Println("built in stage 1") }' >> main.goRUN go build -o /out/hello main.go
FROM docker.io/library/alpine:latestCOPY --from=build /out/hello /usr/local/bin/helloCMD ["hello"]EOFpodman build -t demo:1 .podman images --format "{{.Repository}}:{{.Tag}} {{.Size}}" | grep demopodman run --rm demo:1localhost/demo:1 11.1 MBbuilt in stage 1最终镜像 11.1 MB——如果用单阶段,会带上整个 Go 工具链(数百 MB)。
| 写法 | 最终体积 | 说明 |
|---|---|---|
| 单阶段(golang 基础镜像) | 数百 MB | 编译器和源码都留在镜像里 |
| 多阶段(实测) | 11.1 MB | 只保留二进制与 alpine 基础层 |
COPY --from=build 是关键字:从前一个阶段取文件,那个阶段本身不进最终镜像。
每一行 RUN/COPY 是一层,层顺序决定缓存命中:
# 差:改一行代码就重装全部依赖COPY . /appRUN npm install
# 好:先装依赖,代码后拷COPY package.json package-lock.json /app/RUN npm installCOPY . /app实测的缓存效果:
podman build -t demo:1 . # 第一次:全量podman build -t demo:2 . # 第二次:每层都命中STEP 1/3: FROM ...--> Using cache--> Using cache 就是命中。改代码后只有 COPY . /app 之后的层重建。
buildah:更底层的选择
Section titled “buildah:更底层的选择”| 工具 | 适合 |
|---|---|
podman build | 日常构建,最常用 |
buildah | 脚本化、无守护进程精细操作、CI 里按步构建 |
podman buildx | 多架构构建 |
sudo dnf install buildahbuildah --versionbuildah 的典型用法是不写 Containerfile 也能构建:
ctr=$(buildah from alpine)buildah run $ctr -- apk add --no-cache curlbuildah commit $ctr mycurl:1buildah rm $ctr推到本地 registry
Section titled “推到本地 registry”想给别人/别的机器用,可以起一个本地 registry:
podman run -d --name registry -p 5000:5000 docker.io/library/registry:2curl -s http://127.0.0.1:5000/v2/_catalog打标签并推送:
podman tag demo:1 127.0.0.1:5000/demo:1podman push 127.0.0.1:5000/demo:1curl -s http://127.0.0.1:5000/v2/_catalog{"repositories":["demo"]}| 现象 | 原因 |
|---|---|
podman build 拉基础镜像超时 | 没配 registry 镜像,见国内加速源 |
| 镜像巨大 | 用了单阶段,编译工具链留在镜像里 |
| 改了代码但构建没变 | 缓存命中了不该命中的层,检查 COPY 顺序或用 --no-cache |
COPY --from=build 报找不到 | 阶段名写错,AS build 的名字要一致 |
| 推送 5000 端口失败 | registry 未起,或 SElinux/防火墙拦了端口 |
| 多架构构建失败 | 需要 qemu-user-static 与 podman build --platform |
- Podman 上手 — 装、跑、镜像加速、Quadlet
- 容器与 rootless — uid 映射与安全边界
- 国内加速源 — registry 镜像配置
- SELinux — 挂载卷的
:Z - 官方:Buildah 文档