ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

麒麟V10 arm64离线部署Docker与Compose的完整攻略

2026/9/16 9:02:22 拓冰建站 浏览量
麒麟V10 arm64离线部署Docker与Compose的完整攻略 前几天在一台纯内网的麒麟 V10arm64服务器上我把 Docker 24.0.6 和 Compose 2.30.1 整套离线环境跑通了。原以为就是解压、启动两个动作结果在 systemd、cgroup、iptables 和插件目录上反复折腾了将近一个下午。这篇记录不打算讨论 Docker 是什么而是聚焦你已经决定用它、现在必须离线装好的场景尽量把手边的命令、文件路径和排查链路都写清楚给做国产化替代、内网交付、边缘节点部署的朋友省点时间。先说明一下我这次部署的机器是麒麟 V10 的 arm64 版本也就是uname -m输出为aarch64的服务器。如果你拿到的是 x86_64 的机器思路完全一致只是安装包架构要改成x86_64。下面的步骤里我也会单独标注哪些地方最容易因为架构踩坑。1. 动手之前先用这几条命令把“底子”摸清楚1.1 确认系统发行版、架构和内核版本很多人上来就直接找 RPM 包开始装结果装到一半提示依赖冲突或者发现包管理器里根本没有 Docker。与其赌运气不如先花两分钟把系统信息看明白。cat /etc/kylin-release cat /etc/os-release uname -a uname -m lscpu | grep -i architecture/etc/kylin-release会显示当前麒麟版本比如“银河麒麟高级服务器操作系统 V10 SP3”。/etc/os-release则能看出它到底基于哪个上游发行版这决定了你后续能不能直接用 CentOS 或 openEuler 的仓库包。uname -m是最关键的一步如果输出是aarch64后续所有二进制都必须选aarch64或arm64版本如果出现Exec format error十有八九是你把 amd64 的包拷过来用了。这里也顺手解释下“arm64 和 amd64 有何不同”两者是完全不同的指令集操作系统内核和用户态程序都不能直接通用。arm64 是 64 位 ARM 架构在 Linux 内核里的名称很多软件下载页写aarch64指的是同一个东西。这不是简单的“性能差异”而是二进制格式不同Docker 官方静态包里docker、dockerd、containerd这些可执行文件全是针对特定架构编译的拿错就会直接启动失败。1.2 为什么静态二进制比 RPM/DEB 更适合这种场景在麒麟上安装 Docker 通常有三条路用系统自带的 yum/dnf 配置 Docker 官方源在线安装、下载 RPM 离线安装、使用官方 static binary 离线解压。我个人这么多次离线交付下来最推荐的还是 static binary 包。原因很简单不依赖发行版包管理器的依赖解析只要内核满足要求、glibc 版本够新解压就能跑不会把一堆依赖 RPM 装进系统也不会和麒麟自带的旧版 container 包冲突卸载干净直接把二进制删掉、停掉 systemd 服务就行版本完全可控比如这次明确要求 Docker 24.0.6用 static 包可以精确固定版本不受系统源里 Docker 版本滞后的影响。当然如果你想用.rpm包理论上也能装但离线环境下 RPM 的依赖链非常别扭。尤其像container-selinux、iptables这些基础依赖可能因为你系统里安装的版本不一致而拒绝安装折腾一圈还是回到 static 包。1.3 确认 overlay、cgroup 这些隐藏需求Docker 默认存储驱动是overlay2内核必须支持 overlay 文件系统。检查方法cat /proc/filesystems | grep overlay modprobe overlay如果没有输出先加载内核模块如果加载失败说明内核可能被裁剪过这种机器即使装上 Docker 也容易出现“存储驱动不支持”的问题。再看 cgroup 版本stat -fc %T /sys/fs/cgroup如果输出是cgroup2fs说明系统使用 cgroup v2如果输出是tmpfs则是 cgroup v1。Docker 24 对两者都支持但你在配置daemon.json时native.cgroupdriversystemd和系统实际 cgroup 版本要匹配。麒麟 V10 默认多数是 cgroup v1但也有更新的 SP 版本开始启用 cgroup v2这个信息会在后面docker info里体现。最后看一眼 systemd 是否存在且可用systemd --version ps -p 1 -o comm如果 PID 1 不是 systemd而是 SysV init 或容器内的其他 init后面的systemctl enable docker就不适用了只能考虑service docker start或直接前台启动 dockerd 做验证。2. 在联网机器上准备离线安装包版本、校验、传输2.1 离线包清单少了任何一个都会半路翻车离线部署的第一步是在一台能联网且架构也是aarch64的机器上下载所需文件。我这次准备的清单如下文件说明docker-24.0.6.tgzDocker 官方 static binary 包内含 dockerd、docker、containerd、runc 等docker-compose-linux-aarch64Docker Compose v2.30.1 的独立二进制需要离线运行的镜像 tar 包例如 redis、nginx、MySQL 的 arm64 镜像后续docker load使用static 包不像安装包那样有复杂的依赖但里面的containerd、runc、docker-init、docker-proxy都是 dockerd 运行时的关键组件。很多人只复制了docker和dockerd两个文件启动时就会报containerd: executable file not found。所以解压后务必将整个目录原样保留。2.2 下载命令和架构注意事项在联网的 aarch64 Linux 机器上可以这样下载DOCKER_VERSION24.0.6 ARCHaarch64 curl -fL -o docker-${DOCKER_VERSION}.tgz \ https://download.docker.com/linux/static/stable/${ARCH}/docker-${DOCKER_VERSION}.tgz curl -fL -o docker-compose-linux-${ARCH} \ https://github.com/docker/compose/releases/download/v2.30.1/docker-compose-linux-${ARCH}如果你的网络访问官方源比较慢可以用国内云厂商的镜像源替代但注意确认目录结构和文件名一致。下载完后随手把校验值记录下来这是离线部署最容易忽略的一步sha256sum docker-24.0.6.tgz docker-compose-linux-aarch64把输出的 Hash 值保存到一个SHA256SUMS.txt文件里。等你用 U 盘、scp 或内网共享服务器把文件拷到麒麟机器上再执行一次同样的命令两个 Hash 能对上就说明传输过程中文件没有损坏。这一步看着多余却在很多交付现场救过我的大命尤其是大文件拷贝后偶尔出现莫名缺字节的问题。2.3 传到内网的正确姿势文件拷入内网时我建议优先使用tar.gz或zip压缩后传输避免单个二进制文件在 USB 设备或 FTP 传输中被“自动转换换行符”。尤其是从 Windows 机器中转时二进制内容可能被截断解压后所有文件都能看到但一执行就报段错误。传输完成后先在目标机器上做三件事uname -m sha256sum -c SHA256SUMS.txt tar tzf docker-24.0.6.tgz | head确认架构、校验和、压缩包内容都正常再继续下一步。3. 麒麟 V10(arm64) 上安装 Docker 24.0.6 的具体落地步骤3.1 解压静态包并放置到系统路径我习惯把运行环境放到独立目录而不是一股脑塞到/usr/bin。这样版本升级、卸载都很清晰。mkdir -p /usr/local/docker tar xzf docker-24.0.6.tgz -C /usr/local/docker解压后目录结构是/usr/local/docker/docker/里面包含docker、dockerd、containerd、containerd-shim-runc-v2、ctr、runc、docker-init、docker-proxy这些二进制文件。然后把这些文件链接到/usr/local/bin或/usr/bin。我建议用/usr/local/bin因为系统默认 PATH 一般都包含它mkdir -p /usr/local/bin ln -sf /usr/local/docker/docker/* /usr/local/bin/验证一下which dockerd which containerd which runc docker --version只要which都能找到说明路径没问题。这一步别偷懒直接复制也完全可以但后续如果目录里有旧版本残留复制容易覆盖符号链接则更清晰。3.2 配置 daemon.json 和 systemd 服务Docker 数据目录尽量放到大分区不要在根分区根目录下裸跑。很多国产服务器系统盘不大容器镜像多起来后/var/lib/docker很容易把根分区占满。我自己一般会提前在/data下独立分一个区。mkdir -p /etc/docker /data/docker/etc/docker/daemon.json内容如下{ data-root: /data/docker, exec-opts: [native.cgroupdriversystemd], log-driver: json-file, log-opts: { max-size: 100m, max-file: 3 }, storage-driver: overlay2, iptables: true }>[Unit] DescriptionDocker Application Container Engine Documentationhttps://docs.docker.com Afternetwork-online.target Wantsnetwork-online.target [Service] Typenotify ExecStart/usr/local/bin/dockerd ExecReload/bin/kill -s HUP $MAINPID TimeoutSec0 RestartSec2 Restartalways StartLimitBurst3 StartLimitInterval60s LimitNOFILEinfinity LimitNPROCinfinity LimitCOREinfinity TasksMaxinfinity Delegateyes KillModeprocess [Install] WantedBymulti-user.target如果你的系统里已经通过其他方式装过 containerd 服务这里要注意端口和 socket 路径冲突。但因为我们用的是 static 包dockerd会以子进程方式启动和接管 containerd不需要单独为 containerd 写 service。3.3 启动、开机自启和安装后自检systemctl daemon-reload systemctl enable --now docker systemctl status docker如果一切正常你会看到Active: active (running)。接着做三个自检docker version docker info --format {{.Server.Version}} docker info | grep -E Storage Driver|Cgroup Driver|Cgroup Versiondocker version要确认 Client 和 Server 版本都是 24.0.6。docker info里能看到存储驱动是否为overlay2、Cgroup Driver 是否和你daemon.json里的一致。如果docker info有 WARNING也不要慌后面避坑实录里会针对性说明。这里再补一个非 root 用户使用 Docker 的配置。如果你希望某个普通账号直接敲docker命令而不用 sudogroupadd -f docker usermod -aG docker yourname改完重新登录生效。别小看这一步很多现场交付文档里都没写结果开发账号每次都得 sudo后面写脚本特别别扭。4. Compose 2.30.1 的两种安装姿势与工程配置4.1 插件模式推荐和 docker 命令无缝集成Compose v2 已经不是一个独立的大组件而是 Docker CLI 的插件。只要插件目录和文件名正确docker compose就能被自动识别。全局生效的插件目录是/usr/local/lib/docker/cli-plugins也可以放到用户目录~/.docker/cli-plugins。生产环境我建议放到全局目录避免不同用户重复维护一份mkdir -p /usr/local/lib/docker/cli-plugins cp docker-compose-linux-aarch64 /usr/local/lib/docker/cli-plugins/docker-compose chmod x /usr/local/lib/docker/cli-plugins/docker-compose注意一个细节插件文件名必须是docker-compose不能带版本后缀也不能是docker-compose-linux-aarch64。Docker CLI 找插件时会遍历插件目录下的特定文件名名字不对就提示docker: compose is not a docker command。安装完成验证docker compose version输出类似Docker Compose version v2.30.1。这样后续在任意 compose.yml 目录里直接执行docker compose up -d就行。4.2 独立命令模式兼容老脚本和习惯如果你曾经在旧项目里写过很多docker-compose命令或者还有同事习惯了 v1 风格可以再做一个独立命令模式的软链。它和插件模式并不冲突cp docker-compose-linux-aarch64 /usr/local/bin/docker-compose chmod x /usr/local/bin/docker-compose docker-compose version这里有个坑需要注意旧版 Docker Compose v1 是 Python 实现依赖 docker-py而 v2 是 Go 编译的单一二进制不依赖 Python 环境。所以从脚本兼容性来说直接用 v2 的独立二进制替换docker-compose是没问题的但个别老脚本里如果检测了docker-compose --version的返回格式可能需要微调。如果你两个都装了docker compose会走插件docker-compose会走独立二进制。大部分情况下二者行为一致毕竟底层是同一个 compose 项目只是入口不同。4.3 一份能在离线环境跑通的 compose 样例离线环境最忌讳 compose 文件里写build因为构建需要拉基础镜像和构建上下文内网机器根本没有这个条件。所以我建议所有离线部署尽量使用本地已经加载好的镜像。举一个简单例子单机跑 Redis 7.0.14services: redis: image: redis:7.0.14 container_name: redis-offline restart: unless-stopped ports: - 6379:6379 volumes: - ./redis-data:/data执行前先校验docker compose config --quiet docker compose up -d docker compose psdocker compose config --quiet会在真正拉镜像、启容器之前帮你找出 YAML 语法错误是离线环境里很好用的“预检”手段。restart: unless-stopped能保证机器重启后容器跟着起来省去手工维护。5. 离线镜像的搬运与多机分发5.1 单机迁移docker save 和 docker load镜像在内网里最原始也最灵活的迁移方式就是 save/load。在联网的 aarch64 机器上先拉好镜像再导出为 tar 包docker pull redis:7.0.14 docker save -o redis-7.0.14-arm64.tar redis:7.0.14 gzip -v redis-7.0.14-arm64.tar这里我特意强调-arm64.tar这个后缀是防止以后时间久了分不清这个包到底是哪个架构的。docker save不会自动做跨架构转换如果你在 x86_64 机器上保存redis:7.0.14拿到 arm64 上docker load也能加载但运行时极可能报Exec format error。到了目标麒麟机器上加载gunzip -c redis-7.0.14-arm64.tar.gz | docker load docker images | grep redis如果你有多个镜像要导入可以写成循环但要注意磁盘空间for f in *.tar.gz; do gunzip -c $f | docker load done5.2 多机分发用私有 registry 替代一个个传 tar 包如果一个离线环境里有三台以上机器都要跑同一批容器用 tar 包来回拷效率太低。更靠谱的方式是在内网搭一个私有 registry一次性导入然后各节点从这个 registry 拉取。先用离线包把 registry 镜像导入到某一台机器比如 192.168.10.10docker load -i registry-2.tar docker run -d \ --name local-registry \ --restartalways \ -p 5000:5000 \ -v /data/registry:/var/lib/registry \ registry:2然后给镜像打上 registry 地址的 tag 并推送docker tag redis:7.0.14 192.168.10.10:5000/redis:7.0.14 docker push 192.168.10.10:5000/redis:7.0.14其他机器上如果要拉取这个私服镜像需要在/etc/docker/daemon.json里加入insecure-registries{ insecure-registries: [192.168.10.10:5000] }修改后重启 Dockersystemctl restart docker docker pull 192.168.10.10:5000/redis:7.0.14不使用 TLS 的 HTTP registry 默认会被 Docker 拒绝所以insecure-registries是必须的如果你有内网可信证书也可以配置为正规 HTTPS registry但多数离线交付场景用 ip:port 加 insecure 就够了。5.3 磁盘空间与镜像层数的血泪提醒离线导入镜像时最容易栽在“看起来文件不大但docker load后占用翻倍”这个问题上。原因在于 Docker 镜像保存为 tar 时是压缩后的层数据加载后要在>journalctl -u docker --since today | tail -80日志里隐约能看到类似containerd: executable file not found in $PATH的信息。第一反应是docker和dockerd都装了为什么 containerd 找不到后来才发现static 包解压后一共有 8 个文件而我当时只复制了docker和dockerd到/usr/local/bin以为 containerd 会被 dockerd 自动内嵌启动。实际上 Docker 的静态包并不把 containerd 链接进 dockerd它是作为独立二进制被 dockerd 调用的。排查链路如下which dockerd which containerd which runc ls -l /usr/local/bin | grep -E containerd|docker|runc只要发现 containerd 没有软链补一下就行ln -sf /usr/local/docker/docker/* /usr/local/bin/如果重试后还是失败再看一下是不是 PATH 环境变量里没有/usr/local/bin。普通用户登录通常都有但 systemd 服务的默认 PATH 不包含/usr/local/bin这也是为什么我建议要么用/usr/bin要么在 service 里显式设置 PATH。官方最常见的做法是在ExecStart前加一行EnvironmentPATH/usr/local/bin:/usr/bin:/bin加完再systemctl daemon-reload systemctl restart docker这个问题基本就解决了。6.2 docker run 端口映射报错iptables 与 ip_forward 的坑另一个高频报错是容器能启动但端口映射失败docker run -p 6379:6379时出现类似driver failed programming external connectivity的提示。完整排查链路是先看错误详细信息journalctl -u docker --since today | grep -i error检查 iptables 是否存在which iptables iptables -L -n如果iptables命令根本不存在说明系统里缺 iptables。Docker 的 bridge 网络默认要操作nat表没有 iptables 就会失败。在离线环境里如果系统本身的包管理器里没有 iptables 的离线安装包可以临时把 daemon 的iptables关掉改成 host 网络或 macvlan姿势不同但至少先把容器跑起来。检查 IP 转发sysctl net.ipv4.ip_forward输出如果为 0会导致容器无法访问外网端口映射也可能异常。临时开启sysctl -w net.ipv4.ip_forward1 echo net.ipv4.ip_forward1 /etc/sysctl.d/99-docker.conf sysctl -p /etc/sysctl.d/99-docker.conf如果 iptables 存在但规则被别人改乱了最简单粗暴的方法是重启 Docker让它重新刷本机的 DOCKER 链systemctl restart docker如果重启后仍不行再检查系统防火墙。Kylin 默认可能开启了 firewalld需要把6379等端口放行或者直接关闭 firewalld内网环境可以生产环境谨慎。6.3 docker compose 命令不识别插件路径与权限排查装完 Compose 后docker compose version报docker: compose is not a docker command这个报错基本就是插件没放对位置。排查顺序docker info --format {{json .ClientInfo}} 2/dev/null | grep -i plugin ls -l /usr/local/lib/docker/cli-plugins/docker-compose file /usr/local/lib/docker/cli-plugins/docker-composedocker info --format会打印当前客户端的插件路径信息不过不同版本输出格式不完全一样更多时候直接用ls看最直观。特别注意file命令的输出。如果显示ELF 64-bit LSB executable, ARM aarch64说明架构没选错如果显示x86-64那说明你把 amd64 的 Compose 拷到了 arm64 机器上执行时反而会报Exec format error。二进制文件不是可执行状态也会导致“找不到命令”所以chmod x也要确认。6.4 我把哪些习惯固化进了后续部署流程经过这一轮踩坑我给自己定了一个比较固定的部署验证流程分享出来做个参考每次部署完都用一套命令做验收而不是只看docker psdocker version docker compose version docker info | grep -E Storage Driver|Cgroup Driver|Cgroup Version docker run --rm hello-world docker compose -f /path/to/docker-compose.yml config --quiet如果hello-world镜像没有就从已经 load 进的镜像里挑一个最小镜像跑比如busyboxdocker run --rm busybox:1.36 echo ok另外我会把离线包的版本号和 SHA256 写进一个独立文件随交付文档一起给运维。这样后续哪怕环境被重新初始化也能按照同一套流程快速还原。部署用的docker.service、daemon.json也建议统一备份到一个目录别只留在/etc里免得哪天被误改。最后一个小技巧在>