# Docker CE loongarch64 编译部署 [toc] Docker CE 基于 Moby 项目构建,是目前最流行的容器引擎。官方未提供 loongarch64 预编译包,需要从源码编译。本文基于 Loongnix-Server 8(内核 4.19)完成 Docker CE 27.0.3 的编译,涵盖组件架构、编译过程、使用方式和 systemd 服务化部署。 ## Docker CE 组件架构 Docker CE 并非单一程序,而是由多个独立组件协同工作的容器平台。理解各组件之间的关系是解决问题的前提。 ### 组件关系图 ``` ┌─────────────────────── Docker CE 组件关系 ─────────────────────────┐ │ │ │ 用户 │ │ │ │ │ ▼ │ │ ┌──────────┐ Unix Socket ┌──────────────┐ │ │ │ docker │──── /var/run/ ───────▶│ dockerd │ │ │ │ (CLI) │ docker.sock │ (daemon) │ │ │ └──────────┘ └──────┬───────┘ │ │ 客户端工具 │ API Server │ │ docker build/pull/run/ps... │ Image Management │ │ │ Volume/Network │ │ │ │ │ gRPC + ttrpc │ │ │ /run/containerd/ │ │ │ containerd.sock │ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ containerd │ │ │ │ (container │ │ │ │ runtime) │ │ │ └────────┬────────┘ │ │ │ │ │ ┌─────────────────────┼──────────────┐ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌──────────────┐ ┌─────────────────┐ ┌──────────┐ │ │ │containerd- │ │ docker-proxy │ │ 其他shim │ │ │ │shim-runc-v2 │ │ (网络代理) │ │ │ │ │ └──────┬───────┘ └─────────────────┘ └──────────┘ │ │ │ │ │ │ OCI Runtime │ │ ▼ │ │ ┌──────────┐ │ │ │ runc │ │ │ │ (OCI │ │ │ │ runtime)│ │ │ └──────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ ``` ### 各组件职责 | 组件 | 说明 | 用户可见 | |---|---|---| | **docker (CLI)** | 命令行客户端,接收用户输入,通过 REST API 与 dockerd 通信 | 直接使用 | | **dockerd** | Docker 守护进程,管理镜像、容器、网络、存储卷,是 Docker 的核心 | 后台服务 | | **containerd** | 容器运行时管理器,负责镜像传输、容器生命周期管理、存储 | 后台服务 | | **containerd-shim-runc-v2** | shim 进程,作为 containerd 和 runc 之间的桥梁,实现无守护进程容器 | 每个容器一个 | | **runc** | OCI 标准容器运行时,创建和运行容器进程 | 被 shim 调用 | | **docker-proxy** | 容器端口映射的网络代理,处理 `docker run -p` 的网络流量转发 | 透明 | ### 一次 `docker run` 的完整调用链 > **请求流程**: > 1. `docker run nginx` → docker CLI 发送 REST 请求到 dockerd 的 `/var/run/docker.sock` > 2. dockerd 解析请求,查找本地镜像(若无则从仓库拉取) > 3. dockerd 通过 gRPC 调用 containerd 的 API,要求创建容器 > 4. containerd 根据配置启动 containerd-shim-runc-v2 进程 > 5. shim 进程调用 runc 创建容器的 namespace、cgroup、文件系统 > 6. runc 执行完成后退出,shim 持续运行,管理容器生命周期 > 7. 容器运行期间,若配置了端口映射,docker-proxy 负责将宿主机端口流量转发到容器 > 8. 容器退出后,shim 收集退出状态并上报给 containerd,然后清理退出 ``` docker run nginx │ ▼ docker CLI ──REST──▶ dockerd ──gRPC──▶ containerd ──fork──▶ containerd-shim-runc-v2 ──exec──▶ runc │ ┌─────────┘ ▼ nginx 容器 ``` ### 各组件版本对应关系 Docker CE 每个版本限制了各组件的配套版本,不能混用: | Docker CE 版本 | containerd 版本 | runc 版本 | |---|---|---| | 27.0.3 | v1.7.18 | v1.1.13 | | 26.0.x | v1.7.15 | v1.1.12 | | 25.0.x | v1.7.6 | v1.1.10 | | 24.0.x | v1.7.2 | v1.1.7 | > 这些版本对应关系在 moby 源码的 `hack/dockerfile/install/` 目录下定义,编译时会自动下载对应的 containerd 和 runc 源码。 ## 编译环境 | 项目 | 详情 | |---|---| | OS | Loongnix-Server 8 | | 内核 | 4.19.190-6.5.lns8.loongarch64 | | CPU | 8 核 loongarch64 | | GCC | 8.3.0 (Loongson) | | Go | 1.22.12 ABI1.0(龙芯官方) | ### Go 版本选择 Google 官方的 Go 二进制基于较新内核(ABI2.0),在 Loongnix-Server 8(内核 4.19)上会段错误。龙芯官方提供了兼容 4.19 内核的 ABI1.0 版本。 ``` [root@localhost ~]# uname -r 4.19.190-6.5.lns8.loongarch64 [root@localhost ~]# wget https://ftp.loongnix.cn/toolchain/golang/go-1.22/abi1.0/go1.22.12.linux-loong64.tar.gz [root@localhost ~]# tar -C /usr/local -xzf go1.22.12.linux-loong64.tar.gz [root@localhost ~]# /usr/local/go/bin/go version go version go1.22.12 linux/loong64 ``` > **ABI 版本说明**:龙芯有两个 ABI 版本: > - **ABI1.0**:兼容 Linux 4.19 内核 UAPI,适用于 Loongnix 20、Loongnix Server 8.4、UOS、麒麟等 > - **ABI2.0**:需要 Linux 5.10+ 内核,适用于 Loongnix Server 23、Anolis OS 23 等 ### 安装编译依赖 ``` [root@localhost ~]# yum install -y make git device-mapper-devel libseccomp-devel libtool ``` ## 源码编译 Docker CE 包含三套源码仓库,需要分别编译: | 仓库 | 版本 | 产物 | |---|---|---| | moby/moby | v27.0.3 | dockerd, docker-proxy | | docker/cli | v27.0.3 | docker CLI | | containerd/containerd | v1.7.18 | containerd, containerd-shim-runc-v2 | | opencontainers/runc | v1.1.13 | runc | ### 下载源码 ``` [root@localhost ~]# git clone --depth 1 --branch v27.0.3 https://github.com/moby/moby.git /tmp/moby [root@localhost ~]# git clone --depth 1 --branch v27.0.3 https://github.com/docker/cli.git /tmp/cli [root@localhost ~]# git clone --depth 1 --branch v1.7.18 https://github.com/containerd/containerd.git /tmp/containerd [root@localhost ~]# git clone --depth 1 --branch v1.1.13 https://github.com/opencontainers/runc.git /tmp/runc ``` ### loongarch64 架构适配修补 上游源码缺失 loong64 架构的支持标签,需手动修补 3 处: **1. cilium/ebpf —— 缺失 loong64 小端架构定义** 文件:`containerd/vendor/github.com/cilium/ebpf/internal/endian_le.go` loong64 是 little-endian 架构,但不在构建标签中。`NativeEndian` 变量因此未被定义,导致编译失败。 ``` // 修补前 //go:build ... || ppc64le || riscv64 // +build ... ppc64le riscv64 // 修补后 //go:build ... || ppc64le || riscv64 || loong64 // +build ... ppc64le riscv64 loong64 ``` > loong64 使用小端字节序(Little Endian),与 amd64、arm64 相同。 **2. runc —— 缺失 loong64 64位架构定义** 文件:`runc/libcontainer/system/syscall_linux_64.go` `Setuid`/`Setgid` 函数定义在 64 位架构文件中,但 loong64 未被包含,导致 `undefined` 错误。 ``` // 修补后 //go:build linux && (... || riscv64 || s390x || loong64) // +build linux // +build ... riscv64 s390x loong64 ``` **3. Moby/CLI go.mod —— 使用非标准 vendor.mod** Moby 和 CLI 仓库不使用标准的 `go.mod`,而是使用 `vendor.mod`(因为 Docker 使用 CalVer 而非 SemVer)。 ``` [root@localhost moby]# cp vendor.mod go.mod [root@localhost cli]# cp vendor.mod go.mod ``` ### 编译 dockerd 和 docker-proxy ``` [root@localhost moby]# export PATH=/usr/local/go/bin:$PATH GOROOT=/usr/local/go GO111MODULE=on # 编译 dockerd(守护进程) [root@localhost moby]# CGO_ENABLED=0 go build -mod=vendor -ldflags="-X github.com/docker/docker/dockerversion.Version=27.0.3" -o dockerd ./cmd/dockerd # 编译 docker-proxy(网络代理) [root@localhost moby]# CGO_ENABLED=0 go build -mod=vendor -o docker-proxy ./cmd/docker-proxy ``` > dockerd 使用 CGO_ENABLED=0(纯 Go 编译)是因为当前环境下某些 C 依赖库不完整。纯 Go 编译会损失部分特性(如原生 overlay diff、seccomp),但核心功能完整可用。若需完整特性,需先补齐 C 开发库后用 `make dynbinary` 编译。 ### 编译 docker CLI ``` [root@localhost cli]# CGO_ENABLED=0 go build -mod=vendor -ldflags="-X github.com/docker/cli/cli/version.Version=27.0.3" -o docker ./cmd/docker ``` ### 编译 containerd 和 shim ``` [root@localhost containerd]# CGO_ENABLED=0 go build -o containerd ./cmd/containerd [root@localhost containerd]# CGO_ENABLED=0 go build -o containerd-shim-runc-v2 ./cmd/containerd-shim-runc-v2 ``` ### 编译 runc runc 依赖 C 库(libseccomp),不支持纯 Go 编译,需动态链接: ``` [root@localhost runc]# go build -trimpath -tags "seccomp" -ldflags="-X main.gitCommit=v1.1.13 -X main.version=1.1.13" -o runc . [root@localhost runc]# file runc runc: ELF 64-bit LSB executable, dynamically linked ``` ### 编译产物 ``` [root@localhost ~]# ls -lh /usr/bin/{dockerd,docker,docker-proxy,containerd,containerd-shim-runc-v2,runc} -rwxr-xr-x 1 root root 38M dockerd -rwxr-xr-x 1 root root 24M docker -rwxr-xr-x 1 root root 2.0M docker-proxy -rwxr-xr-x 1 root root 36M containerd -rwxr-xr-x 1 root root 13M containerd-shim-runc-v2 -rwxr-xr-x 1 root root 8.3M runc ``` ## systemd 服务化部署 生产环境必须使用 systemd 管理 Docker,而非 nohup 或后台进程。各组件有严格的启动顺序要求:containerd 先于 dockerd。 ### containerd.service ``` [root@localhost ~]# cat > /etc/systemd/system/containerd.service << 'EOF' [Unit] Description=containerd container runtime Documentation=https://containerd.io After=network.target local-fs.target [Service] ExecStartPre=-/sbin/modprobe overlay ExecStart=/usr/bin/containerd Type=notify Delegate=yes KillMode=process Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF ``` ### docker.service ``` [root@localhost ~]# cat > /etc/systemd/system/docker.service << 'EOF' [Unit] Description=Docker Application Container Engine Documentation=https://docs.docker.com After=network-online.target containerd.service Wants=network-online.target Requires=containerd.service [Service] Type=notify ExecStart=/usr/bin/dockerd --iptables=false --bridge=none ExecReload=/bin/kill -s HUP $MAINPID TimeoutStartSec=0 RestartSec=2 Restart=always [Install] WantedBy=multi-user.target EOF ``` > **关键配置说明**: > 1. `Requires=containerd.service`:dockerd 强依赖 containerd,containerd 未启动则 dockerd 无法启动 > 2. `After=containerd.service`:确保 containerd 先于 dockerd 启动 > 3. `Type=notify`:两个服务都使用 sd_notify 协议通知 systemd 启动完成 > 4. `--iptables=false --bridge=none`:简化网络配置,K8s 环境下由 CNI 插件接管网络 ### 配置仓库镜像 ``` [root@localhost ~]# cat > /etc/docker/daemon.json << 'EOF' { "registry-mirrors": ["https://cr.loongnix.cn"], "insecure-registries": ["cr.loongnix.cn", "lcr.loongnix.cn"], "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" }, "storage-driver": "overlay2" } EOF ``` ### 启动服务 ``` [root@localhost ~]# systemctl daemon-reload [root@localhost ~]# systemctl enable containerd docker Created symlink /etc/systemd/system/multi-user.target.wants/containerd.service → /etc/systemd/system/containerd.service. Created symlink /etc/systemd/system/multi-user.target.wants/docker.service → /etc/systemd/system/docker.service. [root@localhost ~]# systemctl start containerd [root@localhost ~]# systemctl start docker [root@localhost ~]# systemctl status docker containerd ``` 服务启动时序: ``` 系统启动 │ ▼ systemd │ ├── (1) containerd.service 启动 │ └── containerd 就绪 (/run/containerd/containerd.sock) │ └── (2) docker.service 启动(依赖 containerd 就绪) └── dockerd 就绪 (/var/run/docker.sock) ``` ## 使用方式 ### 基本命令 ``` [root@localhost ~]# docker version Client: Docker Engine - Community Version: 27.0.3 API version: 1.46 Go version: go1.22.12 OS/Arch: linux/loong64 Server: Docker Engine - Community Engine: Version: 27.0.3 API version: 1.46 (minimum version 1.24) OS/Arch: linux/loong64 [root@localhost ~]# docker info | head -10 Client: Version: 27.0.3 Context: default Debug Mode: false Server: Server Version: 27.0.3 Storage Driver: overlay2 ``` ### 拉取并运行容器 ``` [root@localhost ~]# docker pull lcr.loongnix.cn/debian:latest latest: Pulling from debian 82b20a1336b2: Pull complete Digest: sha256:8b4ef51fce92269ed2747ae9fd... Status: Downloaded newer image for lcr.loongnix.cn/debian:latest [root@localhost ~]# docker run --rm lcr.loongnix.cn/debian:latest echo "Hello loong64" Hello loong64 [root@localhost ~]# docker run -d --name nginx lcr.loongnix.cn/debian:latest sleep infinity ``` ### 常用管理命令 | 命令 | 作用 | |---|---| | `docker ps` | 查看运行中的容器 | | `docker images` | 查看本地镜像 | | `docker logs <容器>` | 查看容器日志 | | `docker exec -it <容器> bash` | 进入容器 | | `docker stop/start/restart <容器>` | 启停容器 | | `docker rm <容器>` | 删除容器 | | `docker rmi <镜像>` | 删除镜像 | | `docker system prune -a` | 清理所有未使用资源 | ### Docker 服务管理 | 命令 | 作用 | |---|---| | `systemctl start docker` | 启动 Docker | | `systemctl stop docker` | 停止 Docker | | `systemctl restart docker` | 重启 Docker | | `systemctl status docker` | 查看状态 | | `journalctl -u docker -f` | 实时查看日志 | | `journalctl -u containerd -f` | 查看 containerd 日志 | ### 离线安装包 将编译产物打包用于其他机器部署: ``` [root@localhost ~]# tar czf docker-ce-27.0.3-loong64.tar.gz \ dockerd docker docker-proxy containerd containerd-shim-runc-v2 runc [root@localhost ~]# ls -lh docker-ce-27.0.3-loong64.tar.gz -rw-r--r-- 1 root root 53M docker-ce-27.0.3-loong64.tar.gz ``` 在其他 loongarch64 机器上部署: ``` [target@node ~]# tar xzf docker-ce-27.0.3-loong64.tar.gz -C /usr/bin/ [target@node ~]# cp containerd.service docker.service /etc/systemd/system/ [target@node ~]# systemctl daemon-reload && systemctl enable --now containerd docker ``` ## Docker 网络模式 Docker 支持多种网络模式,不同场景选不同网络: | 模式 | 命令 | 说明 | |---|---|---| | **bridge** | `docker run` (默认) | Docker 创建的虚拟网桥 (docker0),容器通过 NAT 访问外网 | | **host** | `docker run --network host` | 容器共享宿主机网络栈,性能最好,端口会冲突 | | **none** | `docker run --network none` | 无网络,容器只有 lo 接口 | | **自定义网桥** | `docker network create` | 用户自定义网桥,支持 DNS 解析 | > **loongnix 环境建议**:当前 dockerd 以 `--bridge=none --iptables=false` 启动禁用了默认网桥,容器网络由用户自行管理。若需默认网桥功能,需确保内核有 `br_netfilter` 模块: > ``` > [root@localhost ~]# modprobe br_netfilter > # 从 docker.service 中移除 --bridge=none --iptables=false > [root@localhost ~]# systemctl restart docker > ``` ## 故障排查 ### 容器起不来:containerd-shim-runc-v2 缺失 ``` docker: failed to start shim: runtime "io.containerd.runc.v2" binary not installed "containerd-shim-runc-v2": file does not exist ``` **解决**:containerd-shim-runc-v2 必须和 containerd 同时编译安装,它是 containerd 和 runc 之间的桥梁进程。 ### 拉取镜像超时 ``` docker: Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection ``` **解决**:国内网络无法直连 Docker Hub,须配置龙芯仓库镜像 `/etc/docker/daemon.json`。 ### Go 段错误 ``` [root@localhost ~]# go version Segmentation fault (core dumped) ``` **解决**:Go 官方二进制用的是 ABI2.0,在 4.19 内核上不兼容。改用龙芯官方 ABI1.0 版本的 Go。