如果你在龙芯 3B6000 平台上部署 GitLab Runner,并且选择了 Docker 执行器,那么你很可能已经踩过或即将踩入一个“深坑”:Runner 容器无法启动,或者启动后无法执行 CI/CD 流水线,错误日志里充斥着 exec format error 、 no matching manifest 或者 exec user process caused: exec format error 这类令人困惑的提示。

这背后的根本原因,是 CPU 架构的错配 。GitLab Runner 默认拉取的 Docker 镜像(如 alpine:latest , golang:latest )通常是基于 x86_64 架构构建的。而龙芯 3B6000 使用的是 LoongArch64 (LA64) 架构。一个 x86 的二进制程序,自然无法在龙芯的 CPU 上运行,这就是所有问题的根源。

网上零散的解决方案,比如手动构建镜像、修改 Runner 配置,往往步骤繁琐且容易遗漏关键环节,导致问题反复出现。本文将提供一个 完整、系统、一键式 的解决方案,覆盖从环境诊断、镜像适配、Runner 配置到最佳实践的每一个环节。读完本文,你将能:

  1. 彻底理解 龙芯平台上 Docker 执行器问题的本质。
  2. 掌握一套 从零开始,在龙芯 3B6000 上搭建稳定 GitLab Runner Docker 执行器环境的方法。
  3. 获得一个 经过验证的、可复用的自动化配置脚本,真正做到“一键解决”。
  4. 规避未来 因架构问题导致的 CI/CD 流程中断风险。

无论你是龙芯平台的早期探索者,还是正在将项目迁移至国产化环境,这篇文章都将为你扫清最大的工程化障碍。

1. 问题根源:为什么 Docker 执行器在龙芯上“水土不服”?

很多人第一次遇到这个问题时,会以为是 Docker 没装好、权限不对或者网络问题。但核心矛盾在于 “镜像指令集” 与 “宿主机指令集” 的不匹配。

我们可以用一个简单的类比来理解:Docker 镜像就像一个打包好的“软件罐头”,里面包含了运行所需的所有文件和指令。GitLab Runner 的 Docker 执行器,就是打开这个罐头并在一个隔离环境(容器)里运行它。但如果这个罐头里的“说明书”(二进制指令)是用“英语”(x86指令集)写的,而你的“工人”(龙芯CPU)只懂“中文”(LoongArch指令集),那工人自然无法执行任何操作,最终报错。

具体到技术层面:

  • 默认行为 :当你在 gitlab-runner 的 config.toml 中指定 image = “alpine:latest” 时,Runner 会命令 Docker 引擎去拉取这个镜像。Docker 默认会根据你宿主机的架构(在龙芯上是 linux/loong64 )去请求对应架构的镜像。
  • 镜像仓库的响应 :Docker Hub 等公共仓库对于 alpine:latest 这类流行镜像,通常都提供了多架构支持。当龙宿主机请求时,仓库应该返回 linux/loong64 的镜像清单。
  • 问题发生点 :
    1. 仓库未提供 :很多镜像,特别是小众或特定版本的镜像,并没有构建 linux/loong64 的版本。此时 Docker 会拉取默认的 linux/amd64 版本,导致架构错误。
    2. 标签歧义 :像 latest 这样的标签是动态的。可能今天仓库提供了龙芯版本,明天维护者更新了 amd64 版本但忘了同步龙芯版本,导致拉取的镜像再次出错。
    3. 自定义镜像 :如果你在 CI 脚本中使用了 docker build 构建自己的镜像,而基础镜像( FROM )指定的是没有龙芯版本的镜像,那么构建出的镜像也无法运行。

所以,解决方案的核心思路非常明确: 确保在龙芯 3B6000 上拉取或构建的所有 Docker 镜像,都是基于 linux/loong64 架构的。

2. 环境准备:确认你的龙芯平台基础状态

在开始“一键解决”之前,我们需要确保基础环境是正常的。请在你的龙芯 3B6000 服务器上执行以下检查。

2.1 确认系统架构与内核

打开终端,运行以下命令:

uname -m

预期输出应该是 loongarch64 ,这确认了你的 CPU 架构。

cat /etc/os-release

查看操作系统信息。常见的龙芯发行版有 Loongnix(麒麟)、UOS、openEuler 龙芯版等。记录下系统版本,例如 PRETTY_NAME="Loongnix Server 20" 。

2.2 安装并验证 Docker

龙芯平台的 Docker 安装方式可能与 x86 不同,通常需要通过发行版的包管理器或从龙芯社区获取适配版本。

对于 Loongnix/Debian 系:

sudo apt update
sudo apt install docker.io docker-compose

安装后验证:

sudo systemctl start docker
sudo systemctl enable docker
sudo docker version

确保 Docker 客户端和服务端都能正常显示版本,并且没有明显的错误信息。

关键验证:查看 Docker 默认平台

sudo docker info --format '{{.OSType}}/{{.Architecture}}'

输出应为 linux/loong64 。这证明 Docker 服务端正确识别了宿主机的架构。

2.3 安装 GitLab Runner

同样,通过包管理器安装:

# 添加 GitLab Runner 官方仓库(请根据你的系统查找对应龙芯架构的仓库或安装包,有时需要直接下载rpm/deb包)
# 例如,对于 Loongnix,可能需要从源码编译或使用社区提供的包。
# 这里假设你已经有了适合 loongarch64 的安装包。

# 示例:安装下载的 .deb 包
sudo dpkg -i gitlab-runner_<version>_loongarch64.deb

# 或者使用官方的安装脚本(注意:需要确认脚本是否支持 loongarch64)
# curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
# sudo apt install gitlab-runner

安装后,注册 Runner 到你的 GitLab 实例(这一步与架构无关):

sudo gitlab-runner register

按照提示输入 GitLab 实例 URL 和注册令牌(从 GitLab 项目或群组设置中获取)。在注册时,执行器先选择 docker 。

3. 核心方案:构建龙芯可用的 Docker 镜像体系

这是解决问题的关键。我们不能依赖不可靠的 latest 标签,而必须建立一个确定的、支持 linux/loong64 的镜像来源。

3.1 策略一:使用已支持 LoongArch64 的官方镜像

越来越多的开源项目开始支持龙芯架构。我们可以优先选择这些镜像。

  • 基础系统镜像 :
    • debian:12-slim (官方已支持多架构,包含 loong64 )
    • ubuntu:22.04 (官方已支持多架构,包含 loong64 )
    • alpine:3.19 (注意:Alpine 官方对 loong64 的支持可能还在完善中,建议优先使用 Debian/Ubuntu)
  • 语言运行时镜像 :
    • openjdk:17-jdk-slim (基于 Debian,支持 loong64 )
    • node:20-bookworm-slim (基于 Debian,支持 loong64 )
    • python:3.12-slim (基于 Debian,支持 loong64 )
    • golang:1.21-bookworm (基于 Debian,支持 loong64 )

如何验证镜像是否支持 loong64? 可以使用 docker manifest inspect 命令(需要开启 Docker CLI 的实验性功能),或者直接尝试拉取并运行一个简单命令:

sudo docker run --rm -it debian:12-slim uname -m

如果输出 loongarch64 ,则镜像可用。

3.2 策略二:手动构建与推送自定义镜像(终极方案)

对于没有官方支持的镜像,或者你需要高度定制化的环境,必须自己构建。

步骤1:编写支持多架构的 Dockerfile 创建一个 Dockerfile ,尽量使用已支持 loong64 的基础镜像。

# 使用已支持 loong64 的 Debian 作为基础镜像
FROM debian:12-slim AS builder

# 安装你的应用依赖(以构建一个简单的Go应用为例)
RUN apt-get update && apt-get install -y wget && \
    wget -O go.tar.gz https://golang.org/dl/go1.21.6.linux-loong64.tar.gz && \
    tar -C /usr/local -xzf go.tar.gz && \
    rm go.tar.gz

ENV PATH="/usr/local/go/bin:${PATH}"

WORKDIR /app
COPY . .
RUN go build -o myapp .

# 第二阶段,创建更小的运行时镜像
FROM debian:12-slim
COPY --from=builder /app/myapp /usr/local/bin/myapp
CMD ["myapp"]

步骤2:在龙芯宿主机上构建镜像 由于宿主机就是 loong64 ,直接构建即可得到对应架构的镜像。

sudo docker build -t my-registry.example.com/my-loongapp:latest .

步骤3:推送至私有镜像仓库 为了在 CI/CD 中使用,需要将构建好的镜像推送到一个 Runner 能够访问的镜像仓库(如 Harbor, Docker Registry)。

sudo docker push my-registry.example.com/my-loongapp:latest

3.3 策略三:配置 Runner 使用明确的镜像标签

在 GitLab Runner 的配置中,避免使用 latest 。使用带有明确版本号且已知支持 loong64 的镜像标签。

编辑 Runner 配置文件(通常位于 /etc/gitlab-runner/config.toml ):

[[runners]]
  name = "loong64-docker-runner"
  url = "https://gitlab.example.com"
  token = "YOUR_RUNNER_TOKEN"
  executor = "docker"
  [runners.docker]
    # 关键配置:使用已知支持 loong64 的镜像
    image = "debian:12-slim"
    # 非常重要:禁用 TLS 验证(仅当使用私有仓库且为自签名证书时需要)
    # pull_policy = "if-not-present"
    # 如果需要,可以配置私有仓库认证
    # [[runners.docker.services]]
    #   name = "postgres:15-alpine"
    #   # 注意:服务镜像也需要支持 loong64!
    #   alias = "db"

修改配置后,重启 Runner:

sudo gitlab-runner restart

4. 一键解决方案:自动化配置脚本

将上述步骤整合成一个 Shell 脚本,实现“一键”配置。将此脚本保存为 setup-loong64-gitlab-runner.sh 。

#!/bin/bash
# setup-loong64-gitlab-runner.sh
# 龙芯 3B6000 GitLab Runner Docker 执行器一键配置脚本

set -e # 遇到错误即退出

echo "=== 龙芯 GitLab Runner Docker 执行器配置脚本 ==="
echo "1. 检查系统架构..."
ARCH=$(uname -m)
if [ "$ARCH" != "loongarch64" ]; then
    echo "错误:此脚本仅适用于 loongarch64 架构,当前架构为 $ARCH。"
    exit 1
fi
echo "✓ 系统架构: $ARCH"

echo -e "\n2. 检查并安装 Docker..."
if ! command -v docker &> /dev/null; then
    echo "Docker 未安装,尝试安装..."
    # 根据不同的发行版调整安装命令
    if [ -f /etc/debian_version ]; then
        sudo apt update
        sudo apt install -y docker.io docker-compose
    elif [ -f /etc/redhat-release ]; then
        sudo yum install -y docker docker-compose
    else
        echo "无法自动识别系统发行版,请手动安装 Docker。"
        exit 1
    fi
    sudo systemctl start docker
    sudo systemctl enable docker
else
    echo "✓ Docker 已安装。"
fi

echo -e "\n3. 验证 Docker 架构..."
DOCKER_INFO=$(sudo docker info --format '{{.OSType}}/{{.Architecture}}')
if [ "$DOCKER_INFO" != "linux/loong64" ]; then
    echo "警告:Docker 架构报告为 $DOCKER_INFO,可能与宿主机不匹配。"
else
    echo "✓ Docker 架构: $DOCKER_INFO"
fi

echo -e "\n4. 拉取并测试基础镜像..."
TEST_IMAGE="debian:12-slim"
echo "拉取镜像 $TEST_IMAGE ..."
sudo docker pull $TEST_IMAGE
echo "测试镜像架构..."
CONTAINER_ARCH=$(sudo docker run --rm $TEST_IMAGE uname -m)
if [ "$CONTAINER_ARCH" = "loongarch64" ]; then
    echo "✓ 基础镜像 $TEST_IMAGE 支持 loongarch64。"
else
    echo "⚠ 镜像返回架构为 $CONTAINER_ARCH,可能存在兼容性问题。"
fi

echo -e "\n5. 安装 GitLab Runner..."
if ! command -v gitlab-runner &> /dev/null; then
    echo "GitLab Runner 未安装。"
    echo "请从以下方式选择一种:"
    echo "  A) 手动下载 loongarch64 安装包并安装。"
    echo "  B) 使用系统包管理器安装(如果仓库有)。"
    echo "  C) 从源码编译。"
    echo "安装后,请重新运行此脚本。"
    exit 1
else
    echo "✓ GitLab Runner 已安装。"
fi

echo -e "\n6. 生成推荐 Runner 配置片段..."
cat << EOF
===========================================
请将以下配置添加到你的 Runner 配置中
(/etc/gitlab-runner/config.toml 的 [[runners]] 部分)
===========================================

[runners.docker]
  # 使用已验证支持 loong64 的镜像
  image = "debian:12-slim"
  # 镜像拉取策略:'always' 每次都拉取,'if-not-present' 本地有则用本地
  pull_policy = "if-not-present"
  # 禁用特权模式(除非必要)
  privileged = false
  # 设置额外的卷挂载(例如缓存目录)
  volumes = ["/cache", "/home/gitlab-runner/.m2:/root/.m2:rw"]

# 如果你的服务也需要特定镜像,例如数据库
# [[runners.docker.services]]
#   name = "postgres:15"
#   alias = "postgres"

EOF

echo -e "\n7. 创建缓存目录(可选)..."
sudo mkdir -p /cache
sudo chown -R gitlab-runner:gitlab-runner /cache 2>/dev/null || echo "无法更改 /cache 所有者,请手动检查。"

echo -e "\n=== 脚本执行完成 ==="
echo "下一步:"
echo "1. 使用 'sudo gitlab-runner register' 注册 Runner(如果尚未注册)。"
echo "2. 根据上面的提示修改 config.toml 配置文件。"
echo "3. 重启 Runner: sudo gitlab-runner restart"
echo "4. 在你的 .gitlab-ci.yml 中,确保使用的镜像标签是支持 loong64 的。"

给脚本添加执行权限并运行:

chmod +x setup-loong64-gitlab-runner.sh
sudo ./setup-loong64-gitlab-runner.sh

这个脚本会自动检查环境、安装必要组件、测试基础镜像,并输出关键的配置建议。

5. 编写适配龙芯的 .gitlab-ci.yml

Runner 配置好了,CI 脚本本身也需要适配。核心原则是: 所有 image: 和 services: 下指定的镜像,都必须明确使用支持 loong64 的版本。

下面是一个完整的示例,构建一个简单的 Go 项目:

# .gitlab-ci.yml
stages:
  - test
  - build

variables:
  # 使用支持 loong64 的 Go 镜像
  GO_IMAGE: "golang:1.21-bookworm"
  # 构建输出的二进制名称
  BINARY_NAME: "myapp-loong64"

# 所有作业共享的基础配置
.default-before_script:
  before_script:
    - uname -m # 打印架构,确认环境
    - go version

unit-test:
  stage: test
  image: $GO_IMAGE
  script:
    - go test ./... -v

build-binary:
  stage: build
  image: $GO_IMAGE
  script:
    - go build -o $BINARY_NAME .
    - ./$BINARY_NAME --version # 简单验证二进制文件能运行
  artifacts:
    paths:
      - $BINARY_NAME
    expire_in: 1 week
  only:
    - tags # 仅当打标签时构建

# 一个使用多阶段构建 Docker 镜像的作业示例
build-docker-image:
  stage: build
  # 使用 Docker-in-Docker (dind) 服务,注意 dind 镜像也需要支持 loong64!
  image: docker:24.0
  services:
    - name: docker:24.0-dind
      alias: docker
  variables:
    DOCKER_HOST: tcp://docker:2375
    DOCKER_TLS_CERTDIR: ""
    IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
  script:
    - docker --version
    - docker build -t $IMAGE_TAG .
    - docker push $IMAGE_TAG
  # 注意:此作业要求 gitlab-runner 以 privileged 模式运行,且 docker:24.0 镜像有 loong64 版本。
  # 如果找不到,你需要自己构建一个支持 loong64 的 docker 客户端镜像。
  only:
    - main

6. 常见问题与排查清单

即使按照上述步骤操作,仍可能遇到问题。下表列出了常见问题及解决方法:

问题现象 可能原因 排查命令/步骤 解决方案
exec /bin/sh: exec format error 拉取的 Docker 镜像架构错误(非 loong64 )。 1. docker image inspect <image_name> 查看 Architecture 字段。
2. 在宿主机运行 docker run --rm <image_name> uname -m 。
1. 在 config.toml 和 .gitlab-ci.yml 中指定已知支持 loong64 的镜像标签。
2. 使用私有仓库,并确保推送的是在龙芯上构建的镜像。
no matching manifest for linux/loong64 in the manifest list entries Docker Hub 或仓库中不存在该镜像的 loong64 版本。 docker manifest inspect <image_name> (需开启 CLI 实验性功能)。 1. 更换为基础镜像(如 debian:12-slim )。
2. 自行构建所需镜像并推送到私有仓库。
Runner 日志显示 Pulling docker image ... 后卡住或失败 网络问题,或私有仓库需要认证。 查看 Runner 日志 sudo gitlab-runner run 或 journalctl -u gitlab-runner 。 1. 配置 Docker 镜像加速器。
2. 在 config.toml 的 [runners.docker] 部分配置 pull_policy = "if-not-present" 并提前手动拉取镜像。
3. 配置私有仓库认证: docker login 并在 Runner 配置中设置 [[runners.docker.volumes]] 挂载 ~/.docker/config.json 。
作业中 docker build 失败 docker 客户端镜像或 dind 服务镜像不支持 loong64 。 检查作业中 image: 和 services: 指定的镜像。 寻找或构建支持 loong64 的 docker 客户端和 dind 镜像。或者考虑使用 kaniko 等无需 Docker daemon 的构建工具。
容器内无法访问宿主机服务或网络 容器网络模式配置问题。 检查 config.toml 中的 network_mode 。 可以尝试设置为 network_mode = "host" (注意安全风险),或确保容器内使用正确的服务别名(在 services 中定义)。
权限错误(如无法写入 /cache ) 容器内用户与宿主机目录权限不匹配。 检查宿主机目录的权限和所有者。 1. 在 config.toml 的 volumes 中明确设置权限,如 :/cache:rw 。
2. 确保宿主机目录对 Runner 运行用户(通常是 gitlab-runner )可写。

7. 最佳实践与长期维护建议

解决了基本问题后,为了团队协作和长期稳定,建议遵循以下实践:

  1. 建立私有镜像仓库并缓存基础镜像 :在内网搭建 Harbor 或 Docker Registry,将常用的、支持 loong64 的基础镜像(如 debian , golang , node )推送上去。在 Runner 配置中优先从私有仓库拉取,提升速度和稳定性。
  2. 固化镜像版本 :在 CI 配置中,永远不要使用 latest 标签。使用完整的、带版本号的标签,例如 debian:12.20240110-slim 、 golang:1.21.6-bookworm 。这能保证构建环境的确定性。
  3. 创建项目专用的基础镜像 :对于大型项目,可以创建包含项目所有构建依赖的专属 Dockerfile,并在龙芯平台上构建、测试、推送至私有仓库。CI 脚本中直接使用这个专用镜像,可以极大减少作业准备时间。
  4. 将镜像构建纳入 CI 流程 :在仓库中维护用于龙芯环境的 Dockerfile。设置一个独立的 CI 流水线(例如在 main 分支更新时),自动在龙芯 Runner 上构建并推送最新版本的基础镜像。这样,应用 CI 就能始终使用最新的、兼容的依赖。
  5. Runner 标签管理 :给你的龙芯 Runner 打上特定的标签,如 loong64 。在项目的 .gitlab-ci.yml 中,为需要在龙芯上运行的作业添加 tags: - loong64 。这样可以精确控制作业在哪个架构的 Runner 上执行,避免误调度。
  6. 定期检查与更新 :关注上游基础镜像(如 Debian, Go, Node.js)对 loong64 架构的支持状态。定期更新你的基础镜像版本,以获取安全补丁和性能改进。

8. 总结

在龙芯 3B6000 平台上成功运行 GitLab Runner Docker 执行器,不是一个简单的配置问题,而是一个 从镜像供应链到 CI 流程的体系化适配过程 。问题的核心始终围绕 CPU 架构 。

本文提供的“一键解决”方案,其精髓在于脚本自动化背后的系统性思路:

  1. 诊断 :首先确认架构不匹配是万恶之源。
  2. 供给 :解决镜像来源问题,要么选用官方已支持的镜像,要么自己构建。
  3. 配置 :在 Runner 和 CI 脚本中,明确指定兼容的镜像标签。
  4. 验证 :通过简单的命令(如 uname -m )验证容器内环境。
  5. 优化 :建立私有仓库、固化版本、制作专用镜像,实现可持续的维护。

将文中的脚本和配置作为起点,结合你项目的具体技术栈进行调整,你就能在龙芯平台上建立起稳定、高效的 CI/CD 流水线。国产化平台的迁移,往往就卡在这些具体的工程细节上。希望这篇详尽的指南,能帮你顺利跨过这道坎。

更多推荐