本文我们将探讨如何使用预装程序创建并分发 AArch64 容器。
创建容器
首先需要建立一个 Dockerfile。由于创建容器的目的是为小程序提供一个快速且占用资源最小的环境,因此选择 Alpine Linux 作为容器运行时。Alpine Linux 是一个小型的容器基础镜像(8MB),在容器开发中很受欢迎。初始的 Dockerfile 非常小,在获取容器镜像后,将“fizzbuzz”可执行文件从预存的构建环境第一部分复制到容器中,并将可执行文件设置为容器的入口点(当容器运行时将自动运行可执行文件):
FROM alpine:latest
WORKDIR /root/
COPY ./fizzbuzz ./
ENTRYPOINT [“./fizzbuzz”]
本次演示以 Podman 为例,其命令行与 Docker 的原理类似:
[dneary@fedora fizzbuzz-c]$ podman build -f ./Dockerfile -t fizzbuzz:1
STEP 1/4: FROM alpine:latest
STEP 2/4: WORKDIR /root/
--> Using cache
a6bfe2eb0767b7966a2e25e3f1fdd638b5a74aaf8abf2abd12f54a8f20fdf9be
--> a6bfe2eb076
STEP 3/4: COPY ./fizzbuzz ./
--> 772088bc011
STEP 4/4: ENTRYPOINT ["./fizzbuzz"]
COMMIT fizzbuzz:1
--> 4afe789ccb9
Successfully tagged localhost/fizzbuzz:1
4afe789ccb91e3ce11907a81f0b0ebbe559b65b10d9e50ff86da8a610670434d
以上步骤看似可成功运行,但当运行容器时:
[dneary@fedora fizzbuzz-c]$ podman run fizzbuzz:1
{"msg":"exec container process (missing dynamic library?) `/root//./fizzbuzz`:
No such file or directory","level":"error","time":"2022-07-11T2334.000019606Z"}
显然,运行失败了,但失败是容器开发的必经之路。
问题在于:构建在 Fedora 主机上的二进制文件被编译后要动态链接到 glibc,而 Alpine Linux 基于 Busybox 并采用 musl 的小型 libc,旨在用于桌面和服务器之外的嵌入式环境。当我们在 Alpine 中运行二进制文件时,无法找到其所需的库,于是便出现以上错误。
对于以上问题,有几个选项可以进行修正:
1. 在主机上静态链接二进制文件并复制一个嵌入了静态版本 glibc 的全包二进制文件;
2. 给 Alpine 添加一个兼容层,让它运行时动态链接到 glibc 的二进制文件;
3. 最后一项则为最优选:在 Alpine Linux 容器中构建应用程序。新 Docker 文件在 Alpine 上安装“build-base”,并复制 C 文件和 Makefile,在容器内编译 fizzbuzz。
FROM alpine:latest
WORKDIR /build/
# Install C compiler and Make
RUN apk --no-cache add build-base
COPY fizzbuzz.c Makefile ./
RUN make clean && make all
ENTRYPOINT ["./fizzbuzz"]
该方法相当简单且有效。当我们建立一个新的容器并使用 Podman 运行它时,熟悉的 FizzBuzz 输出便呈现出来。然而,当运行“podman images fizzbuzz”时,出现了以下问题:
内存优化
由于我们安装了 GNUMake 以及整个 C 和 C++ 开发堆栈,轻量型小容器从不到 6MB 急剧扩大到了 192MB。那么是否存在一种方法将新构建的可执行文件复制回较小的镜像?
答案是肯定的:使用“多阶段构建”容器来返回到容器的上一层,并从构建环境中复制文件。使用“builder”标记构建的环境,然后将可执行文件复制到原始容器。这也使得 Dockerfile 的复杂性略有增加。
FROM alpine:latest AS builder
WORKDIR /build/
# Install C compiler and Make
RUN apk --no-cache add build-base
COPY fizzbuzz.c Makefile ./
RUN make clean && make all
FROM alpine:latest AS app
WORKDIR /root/
# Add our executable from the builder container
COPY --from=builder /build/fizzbuzz ./
ENTRYPOINT ["./fizzbuzz"]
不过镜像大小问题可以很好地得到解决:迁移到多阶段构建后,容器镜像恢复到可管理的 5.84MB:
接下来准备在 GitHub 中构建自定义运行程序。我们将使用预安装在 GitHub “ubuntu-latest” 虚拟环境上的 Docker 堆栈,并在 OCI 上的自托管 AlmaLinux 实例上使用 Podman。
将 Dockerfile 添加到存储库后,选择“添加新工作流”,GitHub Actions 将推荐“构建 Docker 容器”操作:
我们自己的 Docker 镜像工作流程如下所示。因为在每个运行器上使用的工具链不同,所以新的工作流程文件中存在较多重复。遗憾的是,没有一种简单的方法来创建一个 build matrix,从而实现在共享其余操作的同时,还能指示不同主机使用哪个容器运行时和 Builder。
name: Docker Image CI
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
x86-fizzbuzz-container:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build the Docker image
run: docker build . --file Dockerfile --tag fizzbuzz:latest –-tag fizzbuzz:${{ github.sha }}
- name: Run the Docker image
run: docker run fizzbuzz
aarch64-fizzbuzz-container:
runs-on: [self-hosted, linux, ARM64]
steps:
- uses: actions/checkout@v3
- name: Build the Docker image with Buildah
uses: redhat-actions/buildah-build@v2
with:
image: fizzbuzz
tags: latest ${{ github.sha }}
containerfiles: |
./Dockerfile
- name: Run the Docker image
run: podman run fizzbuzz
在运行器上安装 Podman 和 Buildah 工具后,每一次推送项目到 GitHub 后,我们终于可以在本地构建容器并在两个不同的操作系统上运行。随后还可以采取一些后续措施来进行完善,比如:
在构建前将项目源目录安装到容器中;
运行一组“冒烟测试”验证最后一次提交未产生任何破坏;
构建容器后自动上传到容器注册表;
潜在地构建一个跨硬件架构容器。
不过,本文不会就构建 CI pipeline 进行介绍,我们将在今后的文章中针对基于 Ampere 处理器构建持续集成和持续交付(CI/CD)系统进行介绍。
非常感谢 Podman 社区,GitHub 贡献的大量操作文档,以及来自 Twitter 的优秀容器开发者对初学者问题的解答帮助。Ampere 持续挖掘开发社区的创造力,并将其与我们世界一流的处理器相结合。
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !