容器构建进阶:多阶段构建与容器内运行Buildah
立即解锁
发布时间: 2025-08-14 00:33:12 阅读量: 12 订阅数: 14 


Podman for DevOps: 使用容器化技术革新
### 容器构建:从多阶段构建到容器内运行 Buildah
在容器管理领域,容器的创建是一项基础且关键的任务。Podman 常与 Buildah 工具搭配使用,帮助我们完成容器镜像的构建工作。Buildah 拥有众多选项,与 Podman 有很多共通之处,包括存储方面。下面我们将深入探讨多阶段容器构建以及如何在容器内运行 Buildah。
#### 多阶段容器构建
在使用 Podman 和 Buildah 创建自定义容器镜像时,我们不能忽视镜像大小这一重要因素。镜像的最终大小取决于其层数以及每层中更改文件的数量。小尺寸的镜像具有从镜像仓库快速拉取的优势,而大镜像则会占用主机本地存储的大量宝贵磁盘空间。
以往,为了保持镜像紧凑,我们采用了一些最佳实践,如从头开始构建、清理包管理器缓存以及减少 RUN、COPY 和 ADD 指令的使用。然而,当我们需要从源代码构建应用程序并创建包含最终工件的镜像时,问题就变得复杂了。
以构建一个容器化的 Go 应用程序为例,我们通常会从包含 Go 运行时的基础镜像开始,复制源代码,经过一系列中间步骤(如下载所有必要的 Go 包)进行编译,最终生成二进制文件。但在构建结束后,最终镜像仍会包含基础镜像中的 Go 运行时,而这些在编译完成后已不再必要。
##### 解决镜像大小问题的早期尝试
在 Docker 出现以及 Dockerfile 流行起来后,DevOps 团队采用了不同的方法来解决镜像大小问题。例如,二进制构建是将外部编译的最终工件注入到已构建的镜像中。这种方法虽然解决了镜像大小问题,但却失去了运行时/编译器镜像提供的标准化构建环境的优势。
另一种更好的方法是在容器之间共享卷,让最终的容器镜像从第一个构建镜像中获取编译好的工件。
##### 多阶段构建的引入
为了提供一种标准化的方法,Docker 和 OCI 规范引入了多阶段构建的概念。多阶段构建允许用户使用不同的 FROM 指令创建多个阶段的构建,并让后续镜像从之前的镜像中获取内容。
下面我们将分别介绍如何使用 Dockerfiles/Containerfiles 和 Buildah 的原生命令来实现多阶段构建。
##### 使用 Dockerfiles 进行多阶段构建
多阶段构建的第一种方法是在单个 Dockerfile/Containerfile 中创建多个阶段,每个阶段以 FROM 指令开头。构建阶段可以使用 --from 选项从之前的阶段复制文件和文件夹。
以下是一个为 Go 应用程序创建最小多阶段构建的示例:
```Dockerfile
# Builder image
FROM docker.io/library/golang
# Copy files for build
COPY go.mod /go/src/hello-world/
COPY main.go /go/src/hello-world/
# Set the working directory
WORKDIR /go/src/hello-world
# Download dependencies
RUN go get -d -v ./...
# Install the package
RUN go build -v
# Runtime image
FROM registry.access.redhat.com/ubi8/ubi-micro:latest
COPY --from=0 /go/src/hello-world/hello-world /
EXPOSE 8080
CMD ["/hello-world"]
```
在这个示例中,第一个阶段作为纯构建上下文,复制源文件、下载依赖项并构建应用程序。第二个阶段将最终工件复制到一个最小的镜像中。
我们还可以通过使用 AS 关键字为基础镜像添加自定义名称来改进 Dockerfile:
```Dockerfile
# Builder image
FROM docker.io/library/golang AS builder
# Copy files for build
COPY go.mod /go/src/hello-world/
COPY main.go /go/src/hello-world/
# Set the working directory
WORKDIR /go/src/hello-world
# Download dependencies
RUN go get -d -v ./...
# Install the package
RUN go build -v ./...
# Runtime image
FROM registry.access.redhat.com/ubi8/ubi-micro:latest AS srv
COPY --from=builder /go/src/hello-world/hello-world /
EXPOSE 8080
CMD ["/hello-world"]
```
这样,COPY 指令可以使用自定义名称来指定源阶段。
##### 使用 Buildah 原生命令进行多阶段构建
Buildah 的原生命令为多阶段构建提供了更大的灵活性。我们可以在构建过程中添加额外的步骤,例如挂载构建容器的覆盖文件系统并提取构建的工件。
以下是将之前的 Dockerfile 指令转换为原生 Buildah 命令的示例:
```bash
#!/bin/bash
# Define builder and runtime images
BUILDER=docker.io/library/golang
RUNTIME=registry.access.redhat.com/ubi8/ubi-micro:latest
# Create builder container
container1=$(buildah from $BUILDER)
# Copy files from host
if [ -f go.mod ]; then
buildah copy $container1 'go.mod' '/go/src/hello-world/'
else
exit 1
fi
if [ -f main.go ]; then
buildah copy $container1 'main.go' '/go/src/hello-world/'
else
exit 1
fi
# Configure and start build
buildah config --workingdir /go/src/hello-world $container1
buildah run $container1 go get -d -v ./...
buildah run $container1 go build -v ./...
# Create runtime container
container2=$(buildah from $RUNTIME)
# Copy files from the builder container
buildah copy --chown=1001:1001 \
--from=$container1 $container2 \
'/go/src/hello-world/hello-world' '/'
# Configure exposed ports
buildah config --port 8080 $container2
# Configure default CMD
buildah config --cmd /hello-world $containe
```
0
0
复制全文
相关推荐










