写在最前

1. Buildx

1.1 部署安装

# 创建 CLI 插件目录(如果不存在)
mkdir -p ~/.docker/cli-plugins
# 下载 buildx v0.26.1 for linux-amd64
curl -L https://github.com/docker/buildx/releases/download/v0.26.1/buildx-v0.26.1.linux-amd64 -o ~/.docker/cli-plugins/docker-buildx
# 添加执行权限
chmod +x ~/.docker/cli-plugins/docker-buildx
# 查看版本确认成功
docker buildx version


[root@localhost ~]# # 下载 buildx v0.26.1 for linux-amd64
[root@localhost ~]# curl -L https://github.com/docker/buildx/releases/download/v0.26.1/buildx-v0.26.1.linux-amd64 -o ~/.docker/cli-plugins/docker-buildx
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 64.5M  100 64.5M    0     0  11.2M      0  0:00:05  0:00:05 --:--:-- 14.4M
[root@localhost ~]# # 添加执行权限
[root@localhost ~]# chmod +x ~/.docker/cli-plugins/docker-buildx
[root@localhost ~]# # 查看版本确认成功
[root@localhost ~]# docker buildx version
github.com/docker/buildx v0.26.1 1a8287f22cf5a38339a4c1bf432b803c5f8b2aae
[root@localhost ~]# 

1.2 代理配置

172.31.0.1:7890 是我在 Windows 上通过 Clash for Windows 配置的代理端口,用来让容器或其他服务访问外网资源(如 docker.io)。

# 删除缓存
docker buildx prune --all --force
rm -rf ~/.docker/buildx/

#创建buildx环境,并使用本机代理
docker buildx create --use --name mybuilder --driver-opt env.http_proxy=172.31.0.1:7890 --driver-opt env.https_proxy=172.31.0.1:7890
 
#修改所使用的buildx环境
docker buildx use mybuilder
 
#使用完毕后如果你不想要这个代理网络了,可以删除环境
docker buildx rm mybuilder

1.3 使用方式

# 默认构建出来使用docker images是看不到的,你要么使用--push直接推送上去仓库,要么使用--load才能使用docker images看到
# --progress=plain 输出详细格式化后的内容
docker buildx build --platform linux/amd64,linux/arm64 --push -t tanqidi/builder-base:v3.1.0-podman .

2. 试验流程

为了支持在 ARM 架构上部署 Kubesphere 3.4.1 的 DevOps 流水线,官方没有提供相应的 ARM 架构镜像。因此,我们将自己动手构建这些镜像,以确保能够在 ARM 环境中顺利运行。

2.1 分析镜像层级

在 v3.2.0 的 maven/podman/Dockerfile 中,我们注意到其基础镜像使用的是 kubespheredev/builder-base:v3.1.0-podman。经过确认这个镜像目前仅支持 amd64 架构,因此问题的根源就在这里。我们必须先将 builder-base:v3.1.0-podman 适配为 arm64 这样才能顺利完成后续镜像的构建。

image-TUXm.png

https://github.com/kubesphere/devops-agent/tree/v3.2.0

2.2 构建 builder-base:v3.1.0-podman

构建时和运行时 runtime 不一定完全同步

  • 构建镜像时(podman build),你可以指定使用 runccrun,这会影响镜像构建过程的底层容器运行时。

  • 容器运行时(podman run)默认可能是 crun,除非你也修改了运行时配置。

  • 这就导致构建和运行时用的 runtime 不一定一样,可能会出现兼容差异。


Podman 与 Docker 的兼容性

  • Podman 力求兼容 Docker CLI 和镜像标准,但底层实现不完全一样。

  • crunrunc 都是 OCI runtime,理论上兼容,但实际细节和特性支持有差异,可能导致某些镜像构建或运行异常。

  • Podman 的 crun 通常更轻量快速,但相较 runc 有时在某些边缘场景表现不同。

# 这里仅作为记录,实际上构建Dockerfile可能会出现一点问题例如:
sh-5.1# podman info --format '{{.Host.OCIRuntime.Name}}'
crun


# 可能的异常
。。。。。。。
STEP 3/7: ENV TZ Asia/Shanghai
--> 3c485a8a8503
STEP 4/7: RUN bash -c 'touch /app/test-devops.jar'
error running container: from /usr/bin/crun creating container for [/bin/sh -c bash -c 'touch /app/test-devops.jar']: cannot resolve `null` under rootfs: No such file or directory
: exit status 1
time="2025-08-05T12:28:45Z" level=error msg="did not get container create message from subprocess: EOF"
Error: building at STEP "RUN bash -c 'touch /app/test-devops.jar'": while running runtime: exit status 1
script returned exit code 1

devops-agent-3.2.0.tar.gz

创建独立目录存放不同架构的 runc 可执行文件,并分别命名为 runc-amd64runc-arm64

https://github.com/opencontainers/runc/releases/download/v1.1.9/runc.arm64

https://github.com/opencontainers/runc/releases/download/v1.1.9/runc.amd64

FROM almalinux:9.4
# FROM centos:7

# utils
RUN yum install -y epel-release ca-certificates && \
  yum install -y unzip \
  glibc-locale-source \
  glibc-langpack-zh \
  glibc-langpack-en \
  which \
  make \
  wget \
  zip \
  bzip2 \
  gcc \
  gcc-c++ \
  curl-devel \
  autoconf \
  expat-devel \
  gettext-devel \
  openssl-devel \
  perl-devel \
  zlib-devel \
  python-pip \
  git \
  java-1.8.0-openjdk && \
  yum -y clean all --enablerepo='*'

# Set the locale(en_US.UTF-8)
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8

# USER jenkins
WORKDIR /home/jenkins

ENV SONAR_SCANNER_VERSION 3.3.0.1492

RUN curl -o sonar_scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip && \
    unzip sonar_scanner.zip && rm sonar_scanner.zip \
    && rm -rf sonar-scanner-$SONAR_SCANNER_VERSION-linux/jre && \
    sed -i 's/use_embedded_jre=true/use_embedded_jre=false/g' /home/jenkins/sonar-scanner-$SONAR_SCANNER_VERSION-linux/bin/sonar-scanner && \
    mv /home/jenkins/sonar-scanner-$SONAR_SCANNER_VERSION-linux /usr/bin

ENV PATH $PATH:/usr/bin/sonar-scanner-$SONAR_SCANNER_VERSION-linux/bin

COPY ./ ./

ENV EXCLUDE_DOCKER 1
RUN ./hack/install_utils.sh && rm -rf ./*

# Install podman
RUN curl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable.repo https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/CentOS_7/devel:kubic:libcontainers:stable.repo && \
    yum -y install podman fuse-overlayfs && \
    echo "alias docker=podman" >> /root/.bashrc

COPY storage.conf /etc/containers/storage.conf
COPY containers.conf /etc/containers/containers.conf

# 用 buildx 的内置变量,不用自己传
ARG TARGETARCH
COPY runc-${TARGETARCH}/runc /usr/bin/runc
RUN chmod +x /usr/bin/runc

# 配置 podman 使用 runc 而非 crun
# 创建 containers.conf 文件(如果不存在)并设置 runtime = "runc"
RUN mkdir -p /etc/containers && \
    if [ ! -f /etc/containers/containers.conf ]; then \
        echo -e '[engine]\nruntime = "runc"' > /etc/containers/containers.conf; \
    elif grep -q '^runtime *= *' /etc/containers/containers.conf; then \
        sed -i 's/^runtime *= *.*/runtime = "runc"/' /etc/containers/containers.conf; \
    else \
        echo 'runtime = "runc"' >> /etc/containers/containers.conf; \
    fi

VOLUME /var/lib/containers

CMD ["podman", "info"]
docker buildx build --platform linux/arm64 --load -t tanqidi/builder-base:v3.1.0-podman .

2.3 构建 builder-maven:v3.2.0-podman

FROM tanqidi/builder-base:v3.1.0-podman

# ----------- Java (固定版本,ARM64) ------------
RUN curl -fsSL https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u392-b08/OpenJDK8U-jdk_aarch64_linux_hotspot_8u392b08.tar.gz \
    | tar -xz -C /opt && \
    mv /opt/jdk8u392-b08 /opt/java

ENV JAVA_HOME=/opt/java
ENV PATH="${JAVA_HOME}/bin:${PATH}"

# ----------- Maven(固定版本) ------------
RUN curl -fsSL https://archive.apache.org/dist/maven/maven-3/3.5.3/binaries/apache-maven-3.5.3-bin.tar.gz \
    | tar -xz -C /opt && \
    mv /opt/apache-maven-3.5.3 /opt/maven

ENV M2_HOME=/opt/maven
ENV PATH="${M2_HOME}/bin:${PATH}"

# ----------- Ant(固定版本,可选) ------------
RUN curl -fsSL https://archive.apache.org/dist/ant/binaries/apache-ant-1.10.7-bin.tar.gz \
    | tar -xz -C /opt && \
    mv /opt/apache-ant-1.10.7 /opt/ant

ENV ANT_HOME=/opt/ant
ENV PATH="${ANT_HOME}/bin:${PATH}"

# 
RUN ln -sf /usr/bin/podman /usr/bin/docker

# 默认运行 Maven 版本查看
CMD ["mvn", "-version"]

网络不行?可以先下载再构建吧。

https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u392-b08/OpenJDK8U-jdk_aarch64_linux_hotspot_8u392b08.tar.gz

https://archive.apache.org/dist/maven/maven-3/3.5.3/binaries/apache-maven-3.5.3-bin.tar.gz

https://archive.apache.org/dist/ant/binaries/apache-ant-1.10.7-bin.tar.gz

FROM tanqidi/builder-base:v3.1.0-podman

# ----------- Java (固定版本,ARM64) ------------
COPY OpenJDK8U-jdk_aarch64_linux_hotspot_8u392b08.tar.gz /tmp/
RUN tar -xzf /tmp/OpenJDK8U-jdk_aarch64_linux_hotspot_8u392b08.tar.gz -C /opt && \
    mv /opt/jdk8u392-b08 /opt/java && \
    rm -f /tmp/OpenJDK8U-jdk_aarch64_linux_hotspot_8u392b08.tar.gz

ENV JAVA_HOME=/opt/java
ENV PATH="${JAVA_HOME}/bin:${PATH}"

# ----------- Maven(本地文件) ------------
COPY apache-maven-3.5.3-bin.tar.gz /tmp/
RUN tar -xz -C /opt -f /tmp/apache-maven-3.5.3-bin.tar.gz && \
    rm /tmp/apache-maven-3.5.3-bin.tar.gz

ENV M2_HOME=/opt/apache-maven-3.5.3
ENV PATH="${M2_HOME}/bin:${PATH}"

# ----------- Ant(本地文件) ------------
COPY apache-ant-1.10.7-bin.tar.gz /tmp/
RUN tar -xz -C /opt -f /tmp/apache-ant-1.10.7-bin.tar.gz && \
    mv /opt/apache-ant-1.10.7 /opt/ant && \
    rm /tmp/apache-ant-1.10.7-bin.tar.gz

ENV ANT_HOME=/opt/ant
ENV PATH="${ANT_HOME}/bin:${PATH}"

# 将 podman 映射为 docker
RUN ln -sf /usr/bin/podman /usr/bin/docker

# 默认运行 Maven 版本查看
CMD ["mvn", "-version"]
# --progress=plain 输出详细格式化后的内容
docker buildx build --platform linux/arm64 --load -t tanqidi/builder-maven:v3.2.0-podman .

2.4 构建 builder-maven-jdk21

docker buildx build --platform linux/arm64 --load -t tanqidi/builder-maven:jdk21-maven3.9.9 .

https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.8%2B9/OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.8_9.tar.gz

https://archive.apache.org/dist/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz

https://archive.apache.org/dist/ant/binaries/apache-ant-1.10.7-bin.tar.gz

FROM tanqidi/builder-base:v3.1.0-podman

# ----------- Java (固定版本,ARM64) ------------
COPY OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.8_9.tar.gz /tmp/
RUN tar -xzf /tmp/OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.8_9.tar.gz -C /opt && \
    mv /opt/jdk-21.0.8+9 /opt/java && \
    rm -f /tmp/OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.8_9.tar.gz

ENV JAVA_HOME=/opt/java
ENV PATH="${JAVA_HOME}/bin:${PATH}"

# ----------- Maven(本地文件) ------------
COPY apache-maven-3.9.9-bin.tar.gz /tmp/
RUN tar -xz -C /opt -f /tmp/apache-maven-3.9.9-bin.tar.gz && \
    rm /tmp/apache-maven-3.9.9-bin.tar.gz

ENV M2_HOME=/opt/apache-maven-3.9.9
ENV PATH="${M2_HOME}/bin:${PATH}"

# ----------- Ant(本地文件) ------------
COPY apache-ant-1.10.7-bin.tar.gz /tmp/
RUN tar -xz -C /opt -f /tmp/apache-ant-1.10.7-bin.tar.gz && \
    mv /opt/apache-ant-1.10.7 /opt/ant && \
    rm /tmp/apache-ant-1.10.7-bin.tar.gz

ENV ANT_HOME=/opt/ant
ENV PATH="${ANT_HOME}/bin:${PATH}"

# 将 podman 映射为 docker
RUN ln -sf /usr/bin/podman /usr/bin/docker

# 默认运行 Maven 版本查看
CMD ["mvn", "-version"]

2.5 构建 builder-nodejs-nvm

docker buildx build --platform linux/arm64 --load -t tanqidi/builder-nodejs-nvm:latest .

# 使用 tanqidi 的基础镜像
FROM tanqidi/builder-base:v3.1.0-podman

# 创建 nvm 目录并下载 nvm
RUN mkdir -p /root/.nvm && \
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash

# 设置 nvm 环境变量并加载 nvm
RUN echo 'export NVM_DIR="/root/.nvm"'                                       >> /root/.bashrc && \
    echo '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"'                   >> /root/.bashrc && \
    echo '[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"' >> /root/.bashrc

# 使用 nvm 安装多个 Node.js 版本,并设置默认版本
RUN bash -c 'export NVM_DIR="/root/.nvm" && \
    [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && \
    nvm install node && \
    nvm install 4 && \
    nvm install 6 && \
    nvm install 8 && \
    nvm install 10 && \
    nvm install 12 && \
    nvm install 14 && \
    nvm install 16 && \
    nvm install 18 && \
    nvm install 19 && \
    nvm install 20 && \
    nvm install 21 && \
    nvm install 22 && \
    nvm alias default 14 && \
    nvm ls'

# 确认所有安装的 Node.js 版本(nvm ls)
RUN bash -c 'export NVM_DIR="/root/.nvm" && \
    [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && \
    nvm ls'

# 将 podman 映射为 docker
RUN ln -sf /usr/bin/podman /usr/bin/docker

# 默认启动 shell
CMD ["bash", "-l"]
set +x
. ~/.nvm/nvm.sh
. ~/.bashrc
nvm list --no-colors
# 根据需求变更版本
nvm use 16

npm --version
node -v
npm config set registry https://registry.npmmirror.com/
npm install --ignore-scripts --prefer-offline --no-audit --verbose
npm run build

2.x openjdk8 with skywalking

2.x openjdk21 with skywalking

2.x nginx1.24.0

3. 镜像成品

  • tanqidi/builder-base:v3.1.0-podman

  • tanqidi/builder-maven:v3.2.0-podman

  • tanqidi/builder-maven:jdk21-maven3.9.9

  • tanqidi/builder-nodejs-nvm:latest

写在最后