
简介面向arm64架构服务器上的Docker离线部署场景该安装包内置Docker与Docker Compose组件并附带一键安装脚本已在openEuler操作系统下完成验证适合内网或无外网环境中快速搭建容器环境的运维人员、测试工程师及开发者使用。压缩包共4个文件包括Docker安装tgz包、systemd服务文件、安装脚本和docker-compose可执行文件整体大小为54.41MB结构简洁部署时只需解压上传并执行安装脚本即可完成Docker安装再将docker-compose拷贝至系统目录并赋权即可直接调用。相比在线安装方式该方案规避了网络限制也减少了版本不一致带来的兼容问题。目前已有645人学习/下载可用于生产或测试环境初始化、批量部署及离线项目交付也可配合作者发布的在线安装与卸载教程对比学习是一套经过实际验证、可直接落地的arm64离线部署工具包。1. arm64 离线装 Docker 与 Docker-ComposeopenEuler 上别照搬 x86 教程拿到一台 arm64 架构、纯内网环境的 openEuler 服务器你的第一反应大概率是上网搜 docker 安装教程照着 yum 一把梭。但真这么干你会发现要么仓库连不上要么装完 docker 服务起不来日志里报一堆 overlay 和依赖错误——因为网上绝大多数教程都是 x86 架构、Ubuntu 或 CentOS 的照搬到 arm64 openEuler 上每一步都可能踩坑。这份在 openEuler 上验证过的 arm64 平台 docker 和 docker-compose 离线安装包就是把整个安装链路的依赖包、配置文件和验证步骤全部固化下来自带一键安装脚本专门解决内网、信创、断网环境下的交付问题。适合离线交付实施、需要快速复现 docker 环境的运维以及不想在依赖解析上浪费时间的一线工程师。2. 拆解离线安装包目录、依赖与一键脚本的工作逻辑2.1 包内有什么目录结构与文件清单拿到手先别急着跑脚本花两分钟把目录结构看清。这类离线包的组织方式大同小异常见布局如下docker-offline-arm64/ ├── README.md ├── install.sh ├── uninstall.sh ├── docker/ │ ├── docker-ce.rpm │ ├── docker-ce-cli.rpm │ ├── containerd.io.rpm │ ├── docker-compose-plugin.rpm │ └── ... 其他依赖 rpm ├── compose/ │ └── docker-compose └── images/ ├── mysql-8.0-arm64.tar ├── redis-7-arm64.tar └── nginx-1.24-arm64.tar这里最容易被忽略的是根目录的README.md它会写明这次打包用的 openEuler 版本、docker engine 和 compose 的版本号以及验证环境的架构信息。我习惯拿到包第一件事是cat README.md确认发布环境跟目标机一致再看install.sh的内容。docker/目录里放的是 rpm 包用 rpm 方式而不是 tar 二进制是因为 openEuler 的 systemd 集成、启动脚本、目录规范都是围绕 rpm 来的用 rpm 装完systemctl start docker就能直接拉起服务不用手工铺可执行文件和配置 unit 文件。compose/下只有一个docker-compose独立二进制这是新版 docker-compose 的常见做法跟 docker engine 解耦方便单独升级。images/放的是预导出的镜像 tar属于可选内容我后面单独讲。2.2 为什么 arm64 的 docker 必须单独准备包docker engine 本身就是编译产物不同 CPU 架构对应的二进制完全不同。arm64 和 amd64 的 rpm 包虽然都叫docker-ce但安装时依赖的containerd.io、runc、libseccomp都有对应架构的版本混用会出现两类问题一是yum install时依赖解析失败提示找不到兼容的依赖包二是包能装上但 docker daemon 启动后拉取的镜像架构不对跑容器报exec format error。openEuler 跟 CentOS 虽然都是 rpm 系但基础库和内核配置有差异。比如 openEuler 在 overlay 存储驱动上遇到过内核模块未加载的问题导致 docker 数据目录初始化失败。这套离线包在 openEuler 上验证过意味着它已经把这个链路里所有可能的阻碍都处理掉了——包括哪些 rpm 是必需依赖、哪些包之间的版本要互相匹配、启动服务前要不要加载特定内核模块。从网络热搜里能看到大量装机到一半翻车的案例比如docker desktop failed to start because virtualisation support wasnt detected或者permission denied while trying to connect to the docker api。这些在服务器环境下都不适用但反映出一个共性问题docker 安装的失败点往往不在 docker 本身而在系统环境。arm64 openEuler 的组合照着官方文档装经常会碰到仓库源里没有 openEuler 专属的 rpm 包只能去 CentOS 源里找兼容包摸不清依赖关系的话很容易陷入装一个报一个的循环。2.3 一键脚本的设计目标install.sh的存在价值是把人工操作从十几条命令收敛成一次执行。脚本内部通常要完成这几件事检查当前机器架构是否为 aarch64、检查是否为 openEuler 系统、安装目录下的所有 rpm 包、把docker-compose放到/usr/local/bin并加执行权限、启动 docker 服务、设置开机自启、最后输出版本信息作为安装完成的标志。脚本里会写死一个架构判断防止有人误把 x86 的包拷到 arm64 机器上执行。这个判断很关键因为离线交付经常是运维同事带着 U 盘拷包出错的概率很大。如果脚本开头没有uname -m检查后面的 rpm 安装会在中途依赖解析失败报错但那时你已经不知道是包不对还是环境不对了。脚本的返回码设计也有讲究。正常执行完会输出Docker installed successfully和两个版本号如果中途失败脚本会直接退出并带上非 0 的退出码方便你在交付单上记录失败节点。我不会把脚本设计成遇到错误继续往下跑的类型那会让问题更难排查。3. 手工复现完整离线安装从系统检查到 docker compose 跑通3.1 先做系统检查架构、系统版本、磁盘即便有一键脚本我还是建议亲手跑一遍完整流程这样出问题时知道是哪个环节挂的。以下是一个标准的复现流程你也可以把它当作交付前的验证清单。# 确认 CPU 架构aarch64 表示 arm64 架构 uname -m # 确认系统版本 cat /etc/openEuler-release # 确认内核版本docker 对内核版本有最低要求 uname -r # 查看 /var/lib/docker 所在分区的剩余空间 df -h /var/lib第一行输出必须是aarch64如果是x86_64直接停包拿错了。第二行看 openEuler 的具体版本号20.03 LTS 和 22.03 LTS 的包管理差异不小确认它跟离线包 README 里写的一致。第三行看内核版本docker 在 openEuler 上一般要求内核 4.19 以上openEuler LTS 默认内核都满足但如果你跑的是裁剪过的内核就要注意 overlay 模块是否存在。最后一行的磁盘检查最容易被忽略/var/lib/docker是 docker 的默认数据目录镜像和容器层都往这里写空间不够后面导入镜像时会报no space left on device。以上四条命令都不需要联网也不会改变系统状态适合在正式操作前先跑一遍。检查通过后再开始执行安装。3.2 安装 docker enginerpm 包安装顺序与依赖openEuler 默认用 dnf 做包管理离线环境下没有可用仓库所以这里不能裸跑dnf install一般有两条路直接用rpm命令逐个安装或者用dnf指定本地目录作为临时 repo。# 进入 rpm 包所在目录 cd docker/ # 一次性安装所有 rpm自动处理包间依赖 rpm -ivh *.rpm --nodeps --force这里我要特别说明--nodeps --force这种写法在离线场景下是可行的因为离线包内的 rpm 已经经过完整的依赖验证所谓跳过依赖检查只是为了让 rpm 不额外去解析系统中不存在的依赖。但如果你不确定包内依赖是否完整就不要加--nodeps让它老老实实报错反而能提前发现问题。包与包之间有依赖顺序runc和containerd.io要先于docker-ce就位。rpm -ivh *.rpm会把文件名排序后依次安装如果顺序不对先后关系并不影响最终结果因为 rpm 会在当前已经安装的包中解析依赖。唯一需要注意的是如果之前装过旧版 docker先卸载干净再装否则rpm -Uvh会报文件冲突。3.3 安装 docker-compose二进制复制与验证新版 docker-compose 已经不依赖 python直接给一个独立二进制就行。相比用 pip 或 dnf 装二进制方式的好处是干净、无依赖、版本可控。# 把 docker-compose 复制到系统 PATH 目录 cp compose/docker-compose /usr/local/bin/docker-compose # 给执行权限 chmod x /usr/local/bin/docker-compose # 验证版本 docker-compose version复制之后先chmod x这个不用提醒你也会做。真正要验证的是执行权限之外的东西——架构。docker-compose version能正常输出版本号说明这个二进制在当前架构下是可执行的。如果提示Exec format error说明你拿到的是 x86 版跟第 4 章讲的镜像架构问题同源。新版 docker 还支持直接用docker compose无连字符作为子命令前提是安装了docker-compose-plugin这个 rpm。离线包里如果带了这个插件两条命令都能用如果只有独立的docker-compose二进制那docker-compose命令可用而docker compose子命令会提示找不到。我一般建议两个都支持因为现在很多 compose 配置文件的说明文档里两种写法混着来。3.4 启动 docker 并验证安装结果rpm 安装完成后加启动服务这一段的顺序不要倒过来。先把开机自启配置好再启动服务避免在启动后还没写 systemd 配置时就被其他操作中断。# 设置开机自启 systemctl enable docker # 启动 docker 服务 systemctl start docker # 查看服务状态 systemctl status docker # 输出 docker 版本、架构、存储驱动等关键信息 docker infodocker info输出里重点看两个字段Architecture如果是aarch64说明 daemon 跑在 arm64 上Server Version是 docker engine 的版本号跟 README 里声明的一致。另外看Storage Driver正常情况下是overlay2如果显示vfs说明内核 overlay 模块没加载能用但性能差很多建议回到第 4 章排查。最后做一次最小验证跑一个本地容器确认整个链路通# 如果有本地镜像直接 run 一个 docker run --rm 本地镜像名:tag /bin/echo docker ok这个容器跑完自动退出并删除不会留下残留。如果你的images/目录里带了镜像 tar先导入再验证如果没带可以暂时跳过这一步等后续有镜像源再验证。上面这套流程我给它起名叫install check每次离线交付前路径不同但流程不变。4. 避坑arm64 openEuler 离线安装的五个典型故障与排查4.1 docker 服务启动即退出日志报 overlay 存储驱动错误现象systemctl start docker执行后systemctl status docker显示 active (failed)用journalctl -u docker看日志报error initializing graphdriver: operation not permitted或failed to mount overlay。原因openEuler 默认可能没有加载 overlay 内核模块常见于最小化安装或裁剪内核的环境。docker 默认首选 overlay2 存储驱动找不到模块就初始化失败。解决先手动加载模块再重启 docker。modprobe overlay lsmod | grep overlay systemctl restart docker如果modprobe报模块不存在说明内核没编这个模块只能降级把存储驱动改成 vfs。在/etc/docker/daemon.json里写{storage-driver: vfs}重启 docker。vfs 慢但能跑起来。4.2 docker-compose 执行报 Exec format error现象docker-compose version输出Exec format error或者直接Permission denied确认 chmod 之后问题不在权限。原因拿到的docker-compose是 amd64 编译的二进制放到 arm64 机器上执行内核不认识这个指令集。这类问题在从网盘下载文件时经常发生因为网页默认给的下载链接可能是 x86 版的。解决用file命令确认二进制架构这是我在 arm64 机器上执行带编译型二进制之前必做的一步。file /usr/local/bin/docker-compose # 期待输出 aarch64, 而不是 x86-64如果确实是架构不符只能重新获取 arm64 版的 docker-compose注意检查下载时选中的 platform 标记。另外docker compose插件方式的架构错误会通过 rpm 安装时由系统直接拦截很少出现这个报错。4.3 有镜像却跑不起来容器启动报 exec format error现象离线导入镜像后docker images能看到镜像docker run却报exec format error或者直接说failed to create shim task。原因镜像 tar 是从 x86 机器上docker save出来的里面是 amd64 的镜像层。arm64 的 docker 只能加载镜像元数据真正运行时就暴露了架构不兼容。解决导入前先检查镜像架构。# 导入镜像 tar docker load -i mysql-8.0-arm64.tar # 查看镜像架构 docker image inspect mysql:8.0 --format{{.Architecture}}输出必须是arm64或aarch64如果是amd64这个镜像在这个环境里跑不了需要去 arm64 的仓库重新拉取镜像再 save。这里的血泪经验是离线交付的完整闭环不只是docker 能启动还包括目标镜像能在目标架构上跑起来。我每次交付前都会把镜像架构检查写进交付清单。4.4 容器端口访问不了firewalld 与 SELinux 双重拦截现象docker ps正常容器也在运行但在宿主机上curl http://localhost:8080不通业务方反馈端口开了却没反应。原因openEuler 默认启用了 firewalld容器映射到宿主机的端口没有放行另外 SELinux 开启的情况下容器进程的网络访问也可能被拦。解决先放行端口再处理 SELinux。# 放行容器映射的端口 firewall-cmd --add-port8080/tcp --permanent firewall-cmd --reload # 临时把 SELinux 设为宽松模式测试是否为拦截原因 setenforce 0如果setenforce 0后容器立即通了说明是 SELinux 策略问题。可以长期开启容器的 SELinux nezha 配置或者在/etc/selinux/config里按业务要求处理。但注意服务器安全策略不该由运维单方面决定涉及生产环境要跟安全确认后再动。4.5 离线 yum 安装时依赖解析失败或卡在等待现象dnf install docker/*.rpm报Could not resolve host或者长时间卡在Downloading再怎么等都没结果。原因离线环境没有配置本地 dnf 仓库系统默认的 repo 指向公网网络不通导致 dnf 直接挂起。有人会改用rpm -ivh绕开仓库解析但如果 rpm 包本身就依赖系统中未装的库比如libseccomp版本过低还是会失败。解决优先用rpm -ivh并配合--nodb场景检查依赖或者临时禁用所有仓库。# 安装时禁用所有仓库避免网络等待 rpm -ivh docker/*.rpm --nodeps离线包内的 rpm 如果是从 openEuler 同版本环境里收集出来的完整依赖集--nodeps是安全操作。如果是从网上下载的散装 rpm就不要加--nodeps让 rpm 告诉你缺哪个库然后去docker/目录里翻有没有对应包。盲猜依赖是离线安装里最浪费时间的事情不如让 rpm 一次性列全。5. 把一键脚本拆开看参数、修改点与卸载清理5.1 脚本执行流程与关键判断逻辑install.sh核心逻辑可以概括成几个阶段每个阶段对应一个函数或一段独立的命令块。下面这个片段可以作为理解脚本结构的最小参考实际包里的脚本可能加了更多日志但骨架一致#!/bin/bash set -e # 阶段 1前置检查不满足条件就直接退出 ARCH$(uname -m) if [ $ARCH ! aarch64 ]; then echo 当前架构不是 aarch64请确认离线包与目标机匹配 exit 1 fi # 阶段 2安装 rpm 包忽略 repo 网络等待 rpm -ivh docker/*.rpm --nodeps # 阶段 3安装 docker-compose 二进制 install -m 755 compose/docker-compose /usr/local/bin/docker-compose # 阶段 4启动并设置开机自启 systemctl daemon-reload systemctl enable docker systemctl start docker # 阶段 5验证 docker info docker-compose version echo Docker offline install completed.脚本开头set -e的语义是任何一条命令失败就立即退出整个脚本不继续往下执行。这个设计在安装场景下是对的因为你不需要保留装了一半的状态。如果你要改脚本注意保持这个行为不要为了让脚本看起来很顺利而用|| true把错误吞掉。我用install -m 755而不是cp加chmod是因为它是原子操作目标文件直接带上可执行权限少一步人工操作也避免了中间状态。5.2 常用参数与自定义修改点实际使用时最常见的需求是改 docker 数据目录。默认/var/lib/docker如果空间不够你需要把数据目录挪到大分区。脚本本身不会帮你做这一步但你可以在执行安装前先修改 docker 的启动配置# 创建自定义数据目录 mkdir -p /data/docker # 编辑 daemon.json cat /etc/docker/daemon.json EOF { data-root: /data/docker, storage-driver: overlay2 } EOF这段配置要写在脚本执行前因为它会导致一个新问题如果 docker 服务已经启动过、数据目录里已经有了内容再改># 1. 停止 docker 服务并禁止开机自启 systemctl stop docker systemctl disable docker # 2. 卸载相关 rpm 包 rpm -qa | grep docker rpm -e docker-ce docker-ce-cli containerd.io runc # 3. 删除数据目录和配置 rm -rf /var/lib/docker rm -f /etc/docker/daemon.json rm -f /usr/local/bin/docker-composerpm -qa | grep docker先列出已安装的 docker 相关包再逐个卸载。卸载顺序跟安装顺序相反先卸 docker-ce 再卸 containerd.io 和 runc是因为 docker-ce 依赖后两者如果先卸底层包rpm 会提示依赖关系报错。/etc/docker/daemon.json是配置文件不删的话下次重装 docker 会自动读这个旧配置可能带上已不存在的># 在能联网的 arm64 机器上拉取镜像并导出 docker pull mysql:8.0 docker save mysql:8.0 -o mysql-8.0-arm64.tar # 在目标离线机器上导入 docker load -i mysql-8.0-arm64.tar这里的关键点是docker pull必须发生在相同架构的机器上。如果拿 x86 开发机拉 mysql 镜像再 save拷到 arm64 上 load 能成功但一docker run就会报第 4 章里的 exec format error。 我一般会在下载镜像的机器上先跑一次docker image inspect确认架构再开始打 tar 包这一步能省掉后面整个交付现场的排查时间。6.2 常见镜像的离线交付验证镜像 load 进去之后不要急着说装好了。用容器实际启动一次确认业务可用才算闭环。下面是一个针对 mysql 的快速验证# 启动一个测试容器验证镜像可运行 docker run -d --name test-mysql \ -e MYSQL_ROOT_PASSWORDtest123456 \ -p 3306:3306 mysql:8.0 # 等待初始化完成后检查日志 docker logs test-mysql | tail -20 # 确认端口监听 ss -tlnp | grep 3306容器能启动、日志没有报错、宿主机端口在监听这三个条件同时满足这个镜像才算真正可用。对于复杂应用比如码云那个开源网盘 kodbox、gitlab 或青龙面板这类依赖多个环境的容器编排项目建议直接使用docker-compose up -d来验证它会把网络、卷、依赖关系全部串起来。在 arm64 openEuler 这个组合下很多镜像在 x86 上跑得好好的换了架构会出现各种奇怪问题比如 qemu 模拟、arm64 镜像 magic 字串不对之类的报错。所以每次离线交付我都把在目标架构上真实启动一次容器列为必做项绝不只到docker load成功为止。从那以后我每次做离线交付哪怕赶时间也强制走一遍架构确认 → save/load → run 验证三步流程确认无误才在交付单上签字。这套方法希望帮到你让你在 arm64 的 openEuler 上少花几个晚上。本文还有配套的精品资源点击获取