arm64平台openEuler离线部署docker一键安装包全记录

发布时间:2026/9/9 13:48:35
arm64平台openEuler离线部署docker一键安装包全记录 简介面向arm64架构如鲲鹏、飞腾与openEuler国产化系统的运维场景这套离线安装资源包针对内网或无外网环境下Docker及Docker Compose难以直接安装的问题提供一套已经过验证的完整方案。资源包共4个文件整体54.41MB主要包含Docker 18.09.9的tgz离线包、用于systemd托管的service服务文件、一键配置脚本以及独立分发的docker-compose可执行文件覆盖程序导入、服务注册与命令验证等环节用户只需将压缩包上传至服务器并执行安装脚本即可快速部署无需手动查找依赖。方案已明确在openEuler系统下完成验证对arm64平台适配性强适合系统运维工程师、云平台实施人员以及需要国产化环境快速交付容器的开发者。目前已有645人学习下载可帮助使用者在离线网络条件下显著缩短部署时间并为后续的容器迁移、升级和自动化运维提供可复用的基础环境。arm64平台离线部署dockeropenEuler验证通过的一键安装包全记录搞信创环境或者内网部署的朋友都有这种经历拿到一台arm64的服务器系统是openEuler机房没有外网然后业务方说“帮我把docker环境装一下顺便把docker-compose也配上”。我一开始也天真地试过直接拷x86的rpm包过去结果当然是各种依赖报错、架构不兼容最后老老实实做了arm64专用的离线安装包并且在openEuler 22.03 LTS SP4上完整验证通过。这篇就把整个制作和部署过程掰开揉碎了讲清楚。先说核心结论在arm64 openEuler这个组合下docker和docker-compose的离线安装并不复杂关键是把依赖捋顺、把安装脚本写健壮、把镜像导入这块提前设计好。整个过程下来你在内网环境只需要执行一条命令就能把docker运行时、CLI、systemd服务、compose插件全部就位剩下的就是把镜像离线导入进去跑业务容器。1. 为什么离线安装包必须区分arm64架构差异不是玄学1.1 一个真实任务暴露出的问题当时是在一台鲲鹏920处理器的服务器上做部署操作系统是openEuler 22.03 LTS。我先按惯性思维从有网的CentOS机器上下了一堆docker rpm包拷贝过去结果rpm -ivh直接报i686和x86_64架构冲突有的包虽然能装上但服务起不来日志里全是exec format error。这个报错翻译成人话就是你把一份写给x86处理器的机器码硬塞给了arm处理器CPU根本不认识。arm64和amd64是两种完全不同的指令集docker引擎、containerd、runc这些二进制组件都是编译后的机器码架构不通完全没法用。这就好比你把一把车钥匙拿去开门锁芯结构压根不一样。所以做离线包第一件事就是认准aarch64。1.2 openEuler的包管理与依赖特殊性openEuler虽然是国产系统但它和RHEL系一脉相承包管理器是dnf/yum也使用rpm格式。这意味着有两条路可以走直接在openEuler的镜像源里下载rpm包然后手动解析依赖链。找一台同架构的openEuler机器用dnf download配合repoquery把依赖包一次性拉下来。我最终采用的是第二种原因很直接依赖解析不能靠猜要交给包管理器去做。手动去一个个点rpm包依赖那是噩梦docker-ce涉及的依赖包括container-selinux、iptables、nftables、libseccomp、device-mapper-libs等等其中有些包之间的版本互相约束稍有不慎就装出一套残废环境。注意不要直接拿CentOS或者Fedora的arm64 rpm包往openEuler上怼虽然大部分兼容但openEuler有自己的release版本和selinux策略混用容易出隐藏问题。最稳妥的做法就是在目标版本一致的openEuler系统上用包管理器来拉。1.3 离线包的最终构成清单我做完之后的离线包目录结构长这样你可以直接按这个思路来准备docker-offline-pkg/ ├── docker/ │ ├── docker-ce-20.10.24-3.el7.aarch64.rpm │ ├── docker-ce-cli-20.10.24-3.el7.aarch64.rpm │ ├── containerd.io-1.6.24-3.1.el7.aarch64.rpm │ ├── docker-ce-rootless-extras-20.10.24-3.el7.aarch64.rpm │ ├── docker-compose-plugin-2.18.1-1.el7.aarch64.rpm │ └── 依赖rpm包若干... ├── docker-compose/ │ └── docker-compose-linux-aarch64 ├── images/ │ ├── myservice.tar │ └── nginx.tar ├── install.sh └── README.md注意这里面的一个细节docker-compose有两种存在形式一种是独立的二进制文件另一种是docker compose插件docker-compose-pluginrpm包。新版docker的docker compose子命令依赖后者而独立的docker-compose二进制是Python打包的老方案或者说是通用的compose v2单文件。我在离线包里干脆两个都放了脚本里做了兼容判断确保用户不管习惯敲docker-compose还是docker compose都不会尴尬。2. 一键安装脚本的核心设计每一步都得有道理2.1 脚本开头为什么不直接装很多人写安装脚本上来就rpm -ivh *.rpm这在离线场景下很容易翻车。rpm安装时如果遇到未解决的依赖会直接报错退出而yum/dnf本地安装则能自动处理当前目录下的依赖并给出明确提示。我的脚本第一步先用uname -m检查架构如果不是aarch64立即退出避免有人拿错包执行。接下来是检查系统版本。openEuler的版本号有规律22.03 LTS对应的是RHEL 8兼容层20.03 LTS又对应不同的大版本不同版本的glibc和selinux策略会影响docker运行。我用cat /etc/openEuler-release提取大版本号然后在脚本里做出版本判断核心逻辑是22.03及以上走一套依赖策略20.03走另一套不匹配就警告。2.2 核心安装逻辑中的关键操作点安装过程不是简单的一梭子rpm打过去我按顺序分了几个阶段第一步卸载旧的docker残留。openEuler自带的仓库里可能有一个老版本的docker包不卸载的话新老文件会产生冲突。命令是dnf remove -y docker docker-engine docker.io但注意不能带--purge之类的参数因为卸载时如果连带把containerd也卸了会影响依赖关系。我在脚本里只卸载docker客户端和服务端本体不碰运行时组件。第二步本地yum源方式安装。我在离线包同级目录放了一个local.repo指向file:///opt/docker-offline-pkg/rpms/执行dnf install -y --disablerepo* --enablerepolocal docker-ce docker-ce-cli containerd.io docker-compose-plugin。这种方式比直接rpm的好处是dnf会在你指定的本地仓库里自动解析依赖顺序哪个包先装哪个后装不用你操心。第三步配置docker daemon。openEuler默认启用了防火墙docker创建的docker0网桥以及容器端口映射需要iptables规则放行我在脚本里把firewalld保持开启状态但也明确写入了两条规则。更关键的是/etc/docker/daemon.json这里必须配置exec-opts: [native.cgroupdriversystemd]因为openEuler使用的init系统是systemd如果cgroup驱动还是默认的cgroupfs容器会偶发资源统计不准、删除容器时僵尸进程残留的问题。第四步启动并验证。systemctl daemon-reload systemctl enable --now docker然后执行docker info检查输出脚本自动抓取Server Version字段判断是否启动成功。2.3 docker-compose的两种安装策略并存脚本里我做了个判断逻辑如果存在docker-compose-plugin的rpm包就通过dnf安装如果找到的是独立的docker-compose-linux-aarch64二进制就装到/usr/local/bin/docker-compose并赋予0755权限。两种都成功时脚本会建立软链接让docker compose和docker-compose两个命令都能正常工作。为什么要这样设计因为现在的容器编排实践里docker compose up这种子命令形式是未来方向新版本的compose功能直接合入docker CLI但在很多自动化运维脚本里大家已经习惯写docker-compose -f xxx.yml两种形式并存可以最大程度减少业务侧的迁移成本。3. 制作离线包最容易踩的坑与解决方法3.1 依赖究竟怎么拉才完整表面上看dnf install --downloadonly可以一键下载所有依赖包但我在实操中踩了个坑这个参数默认不会下载已经装过的依赖包。比如机器上如果已经装了老版本的libseccompdnf认为它满足依赖要求就不会把新版本的libseccomp拉下来。这意味着你在干净机器上安装时老版本库不兼容docker引擎起不来。解决方案有两种我分别验证过在一台全新的、没装过docker的openEuler arm64机器上执行dnf install --downloadonly --downloaddir./rpms docker-ce docker-ce-cli containerd.io docker-compose-plugin这样拉下来的依赖才是全量的。用repoquery --requires --resolve递归解析所有依赖然后写脚本循环下载。第一种更省事推荐新手用前提是你得有一台干净的机器。3.2 openEuler特有的selinux策略问题openEuler的SELinux默认是 enforcing 模式docker装完虽然服务能起但容器一旦要写卷或者映射端口avc拒绝日志会刷屏容器行为也变得诡异。这个问题我在实操里遇到过两次最典型的场景是容器内进程绑定小于1024的端口时被selinux拦截docker日志里只显示permission denied容易误判为镜像问题。彻底关闭SELinux在信创环境里不一定被允许所以我的处理方案是dual-track如果现场安全策略允许把/etc/selinux/config改成SELINUXpermissive重启后生效。如果必须保持enforcing那么在daemon.json里不能碰selinux标签并且需要给特定目录设置正确的context比如chcon -Rt svirt_sandbox_file_t /data/docker-volumes这种操作。这个坑在官方文档里说得比较隐晦但真实生产环境几乎必然遇到。3.3 docker官方源在部分网络环境下的不可用性还有一类场景我称之为“假离线”内网环境虽然连不上外网但能访问内网自己的软件源。有些单位内部有nexus或者artifactory里面同步了docker-ce的arm64 rpm包。这种情况下离线包的“一键安装脚本”其实可以做两个分支检测到网络通就优先走内网源更新检测不到就落回本地rpm目录安装。我把这个逻辑用一行curl -s --connect-timeout 3 http://内网源地址/repodata/repomd.xml来判断连通则配置内网repo不通则继续走纯本地路径。4. 镜像离线导入安装完docker只是前半场4.1 为什么镜像导入必须在安装脚本里一起做很多离线部署教程到docker启动成功就结束了但真实业务根本跑不起来——因为你没有镜像。没有镜像的docker引擎就像一个空转的马达业务方交付来的容器镜像大概率是tar包形式脚本如果能在安装docker之后顺手把这些镜像导入就真正做到了“一键环境就绪”。我在镜像处理上分了几种情况多架构镜像如果对方给的镜像是docker save打成的一个tar里面可能同时包含amd64和arm64的manifest列表。运行时用docker load能够正确识别当前架构并加载对应镜像层这点经过实测没问题。平台强制指定但有些第三方镜像是在x86上直接docker commit出来的镜像的architecture字段是amd64在arm64上虽然能load进去但run时会报image with reference ... was found but does not match the specified platform。脚本里我加了一个docker run后的architecture校验如果镜像平台不对直接提示对方重新打arm64包。4.2 导入流程中的性能与空间考量大量镜像载入时如果tar包很大几十G直接在默认目录/var/lib/docker下展开会占用系统盘空间。我的脚本允许传入--data-root /data/docker参数通过修改daemon.json把docker的存储目录迁移到数据盘。这一步必须在docker启动前完成否则中途挪目录会导致已有容器丢失。执行导入的部分用的是循环find ./images -name *.tar -exec docker load -i {} \;注意文件名如果是带空格的中文find -exec会有坑脚本里改成while IFS read -r f; do docker load -i $f; done (find ./images -name *.tar)稳妥得多。4.3 给离线包预留验证脚本我习惯在安装包的最末尾放一个verify.sh里面跑几个关键检查项例如docker version能否同时输出Client和Server版本docker compose version是否存在docker run --rm hello-arm64能否正常创建并销毁容器检查docker info中Cgroup Driver是否为systemd。这些检查项能帮你快速判断环境是否真的可用而不是只看服务起来就完事。实测中发现有几次docker服务状态是active但容器创建卡死最后定位到是cgroup v2和v1的切换问题在启动参数里加systemd.unified_cgroup_hierarchy0临时解决但更彻底的做法是让daemon.json显式声明cgroup驱动。5. 完整部署过程实录与关键文件展示5.1 在目标机上执行的真实输出节选在一台华为泰山200服务器上openEuler 22.03 LTS SP4内核5.10.0-60.49.0.91.oe2203.aarch64环境我是这样操作的tar -zxvf docker-arm64-offline-pkg.tar.gz cd docker-arm64-offline-pkg chmod x install.sh ./install.sh --data-root /data/docker --load-images脚本执行完之后的关键输出如下脱敏架构检测输出[OK] Architecture: aarch64系统版本识别[OK] openEuler 22.03 (LTS-SP4)dnf本地源安装[OK] docker-ce 20.10.24, containerd.io 1.6.24, docker-compose-plugin 2.18.1 installedcontainerd配置写了/etc/containerd/config.toml的海关pause镜像配置对于内网环境必须指定一个本地可访问的pause镜像地址否则Pod/容器初始化会一直等pause镜像下载超时daemon.json写入{exec-opts:[native.cgroupdriversystemd],data-root:/data/docker,log-driver:json-file,log-opts:{max-size:100m,max-file:3}}systemd启动[OK] Docker is running镜像导入从./images目录load了3个镜像tar包耗时视镜像大小而定导入完执行docker images确认。5.2 脚本核心代码片段讲解install.sh中几个刚开始写时容易出问题的关键位置贴出来供你参考# 架构检测must be aarch64/arm64 ARCH$(uname -m) if [[ $ARCH ! aarch64 $ARCH ! arm64 ]]; then echo [ERROR] This package only supports arm64/aarch64, current: $ARCH exit 1 fi # 本地repo文件生成注意这里不能用中文字符和特殊符号 mkdir -p /etc/yum.repos.d/backup mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/backup/ 2/dev/null || true cat /etc/yum.repos.d/local.repo EOF [local] nameLocal Docker RPMs baseurlfile://$(cd $(dirname $0) pwd)/rpms enabled1 gpgcheck0 EOF # 清理可能冲突的旧docker dnf remove -y docker docker-engine docker.io podman-docker 2/dev/null || true # 本地安装 dnf clean all dnf makecache dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin这段代码里最容易出问题的地方是将原来的repo文件备份掉。有些系统管理员不想让你动全局repo配置那你可以不备份而是新增一个repo文件并设置enabled0给原文件但本地repo的优先级必须最高。用一个规避方案就是只创建local.repo并放在/etc/yum.repos.d/即可如果你担心dnf还是会去访问外网源导致超时可以加--disablerepo*强制只走本地源。5.3 安装完成后的网络与防火墙细节openEuler的firewalld默认拦流量我在脚本最后做了两件重要的事在/etc/firewalld/zones/public.xml中把docker的默认网段--add-rich-rulerule familyipv4 source address172.17.0.0/16 accept添加进去确保容器内的出网流量不被截胡。如果你有端口映射的需求比如-p 8080:80需要firewall-cmd --permanent --add-port8080/tcp之类的配置。很多人忽略这一点结果容器明明起来了外部就是访问不到排查半天发现是防火墙把流量拦了白白浪费时间。6. 我踩过的几个深层坑内核参数、cgroup和存储驱动6.1 openEuler内核默认参数对docker的隐藏影响openEuler内核有fs.may_detach_mounts这个默认参数在容器里做挂载操作时如果该参数为0umount会被拒绝导致容器退出时hang住。我在脚本里增加了sysctl -w fs.may_detach_mounts1并写入/etc/sysctl.d/99-docker.conf这个问题才彻底消失。另一处是net.bridge.bridge-nf-call-iptables这个不打开容器跨宿主机通信会断。openEuler的内核已经加载了br_netfilter模块但sysctl默认值有时候没打开脚本里必须显式置1modprobe br_netfilter sysctl -w net.bridge.bridge-nf-call-iptables1 sysctl -w net.bridge.bridge-nf-call-ip6tables1 echo net.bridge.bridge-nf-call-iptables1 /etc/sysctl.d/99-docker.conf这两个问题在官方文档里几乎没有提到但它们和处理器的关系不算大主要和openEuler的内核默认配置有关换CentOS系统未必出现换欧拉必踩。6.2 overlay2在openEuler不同文件系统上的兼容性openEuler 22.03默认文件系统是ext4或xfsxfs需要开启ftype1特性才能作为docker的overlay2后端否则docker daemon会报error creating overlay mount to /var/lib/docker/overlay2。我在安装前的自检脚本里加了一条xfs_info /var/lib/docker的检测如果输出里没有ftype1就提示先格式化或者在数据盘上选择ext4。这个问题在物理机上遇到过一次那台服务器的数据盘是xfs且没开ftypedocker daemon反复崩溃最后重新格式化成ext4才解决。所以这里强烈建议数据盘直接用ext4或者确认mkfs.xfs时带上了-n ftype1参数。6.3 arm64环境下含特殊指令集应用的兼容性检测arm64服务器有SVE可伸缩向量扩展、LSE大系统扩展这些特性不同CPU实现情况不一样。个别容器应用如果编译时开启了SVE而宿主机CPU不支持运行时会报illegal instruction。我在verify.sh里加入了lscpu | grep -i sve\|asimd检查并把结果打印出来让使用者提前知道这台服务器的CPU特性集。虽然这种情况很少见但真遇上了会非常困惑。7. 针对不同使用人群的收尾建议如果你的角色是运维工程师我建议直接把离线包里的install.sh当作标准交付物同时保留一份md5sum校验文件方便交接时核对包完整性。如果是开发人员自用可以跳过防火墙和selinux那些额外配置只保留核心安装三步这样更快。我自己在多次部署后最大的体会是离线安装包的价值不在于“把rpm拷进去装好”而在于把环境差异、依赖关系和坑全都在脚本层面提前消化掉让使用者不需要懂docker内部机制也能顺利部署。arm64 openEuler这个组合虽然在生态成熟度上还比不上x86 CentOS但只要踩过一次这些坑后续做信创交付反而会更顺手。最后再分享一个经验交付离线包时一定附带一个README把你制作离线包的那台机器的系统版本、内核版本、docker版本、compose版本全部写清楚。这样一来目标机如果出现问题双方可以第一时间对照环境差异而不是对着报错日志瞎猜。这个习惯在一次现场交付中帮了大忙对方用的openeuler是20.03 LTS和制作包的22.03 LTS存在selinux策略差异看到README后我就知道第一步是先兼容selinux配置整个解决过程不到十分钟。本文还有配套的精品资源点击获取