ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

arm64+openEuler离线安装Docker与Compose一键脚本及避坑指南

arm64+openEuler离线安装Docker与Compose一键脚本及避坑指南 简介面向在 arm64 架构服务器中需要离线部署容器环境的运维人员与实施工程师这份资源提供了 docker 及 docker-compose 的离线安装包并已在 openEuler 操作系统下验证通过可直接用于内网、专网或不具备外网连接的服务器环境规避在线安装时镜像拉取超时、依赖缺失等常见问题。压缩包内共 4 个文件分别涵盖 docker 服务镜像、docker-compose 可执行组件、systemd 服务管理文件以及自动安装脚本整体约 54.41MB文件组织简洁清晰用户只需将压缩包上传至目标机器解压并对脚本赋予执行权限后运行即可完成 docker 核心组件的部署。目前已有 645 人学习/下载附带作者整理的安装命令示例与在线教程说明有助于在安装过程中排错与确认环境状态。通过该资源使用者可以免去自行交叉编译、四处搜集依赖包的繁琐过程直接获得适配 arm64 的完整安装组合与自动化安装能力并可顺手启用 docker-compose 对多容器业务进行统一编排显著提升离线环境下的交付效率。1. 离线装 Docker 不是玄学但 arm64 openEuler 有自己的脾气机柜刚通电的 aarch64 服务器openEuler 装好了网络策略却不允许直连软件源——这是 arm64 平台下 docker 和 docker-compose 离线安装最典型的现场。标题里这个「含一键安装脚本」的离线包解决的就是这类场景在一台联网的同架构机器上把 rpm 依赖、compose 二进制、镜像 tar 全部拉齐拷到目标机解压、执行脚本、起服务把容器运行时立起来。适合做国产化交付、内网边缘机房、以及所有不允许直连软件源的环境。这不是玄学难点集中在三处arm64 的包和 x86 不通用、openEuler 不同 LTS 的依赖基线有差异、以及一台没有外网的机器上你能依靠什么。这篇笔记把三条线串起来讲顺带把验证方法也给你。2. 为什么 x86 的离线包经验不能照搬先分清 aarch64 的 rpm 和二进制2.1 x86_64 的包搬到 aarch64 上只有一种结果Exec format error先立一个判断arm64 平台下 docker 和 docker-compose 离线安装包不是把 x86 的安装包换个机器解压就行。docker 官方发布的静态二进制按架构分目录x86_64 的 ELF 文件头在 aarch64 内核上会被直接拒绝执行典型报错就是Exec format error。openEuler 的 yum 源同样分 x86_64 和 aarch64 两套仓库rpm 包头的 Architecture 字段不同强制安装会报架构不匹配。移动生态里常说的 arm64-v8a和服务器上这个 aarch64 指令集基本同源都是 ARMv8 及之后的 64 位指令集。这会让不少人误以为手机上的二进制也能拿来用实际不可能服务器 docker 依赖 glibc 版本、内核模块和 systemd 集成不是一个小工具能比的。即使同样是 aarch64Ubuntu arm64 上编译出来的 docker-compose拿到 openEuler 上也可能因为 glibc 版本不符而起不来。所以原则只有一个架构要对发行版来源也要对。这是整个离线包方案的第一条边界。顺带说一句现场临时编译 docker 是最后的选择。docker 组件多编译依赖工具链和网络离线环境基本不具备条件。老老实实把包带全比现场折腾 gcc 节省时间。2.2 动手前先跑四条命令架构、系统版本、残留 docker、可用软件源拿到机器先别急着装四条命令确认了再动# 1. CPU 架构aarch64 就是 arm64别看到 64 位文件就默认 x86_64 uname -m # 2. 系统版本openEuler 20.03 和 22.03 的依赖基线不一样 cat /etc/os-release # 3. 看系统里有没有残留 docker / containerd避免重复安装覆盖 rpm -qa | grep -E docker|containerd || true # 4. 看可用软件源离线环境这里会失败但失败结果决定安装路径 yum repolistuname -m输出aarch64是唯一正确的目标如果输出armv7l说明是 32 位 ARM这个包不适用。第二条重点看VERSION_ID我一般让离线包和现场机器的大版本保持一致比如打包机是 openEuler 22.03 LTS就尽量不去 20.03 的现场硬装。第三条的|| true是防止 grep 没有匹配时返回非零中断脚本有残留时先看版本再决定覆盖还是先卸载。第四条在纯离线环境大概率超时或报错这不影响后面的安装但它决定了你接下来走 rpm 路径还是二进制路径。这四条命令花不到一分钟能拦住后面至少三个隐蔽坑架构混淆、跨版本依赖、重复安装导致的 systemd 单元被覆盖。很多人跳过这一步直接跑安装脚本装到一半才意识到系统里有个旧版 docker这种现场很尴尬。2.3 选型优先从 openEuler 官方源拉 rpm二进制路线只作兜底离线安装在两条路径之间选先给结论路径依赖管理systemd 集成适用场景openEuler 官方 rpm yumdownloader 拉依赖rpm 自动记录依赖完整可控自带 docker.service开自启直接 enable现场机器与打包机同版本对稳定性要求高docker 官方 static binary docker-compose 独立二进制依赖盲区需要手动验证 glibc 和库文件unit 文件要自己写容易漏参数官方源里版本太旧、或需要特定 docker 版本我一般优先走 rpm 路径因为依赖这个「黑匣子」最小。rpm 装完rpm 数据库里记录得清清楚楚以后rpm -V docker-engine可以校验文件完整性卸载也干净。二进制路径更灵活尤其当你需要新版 docker-compose 时官方源里的版本可能老得让人想换方案而独立二进制的版本选择自由度高得多。但 rpm 路径有一个前提打包机和目标机的 openEuler 大版本一致。20.03 的源拉出来的包依赖的是 20.03 的库版本硬装到 22.03 上通常问题不大反向就容易报依赖缺失。为了处理这种跨版本差异离线包里我会同时放入依赖 rpm 和一份 dnf 本地仓库配置让目标机在离线状态下也能做依赖解析。选 rpm 路径时先在打包机上跑一下yum list available docker-engine确认源里有这个包、版本符合预期。openEuler 各 LTS 的软件仓库里 docker 相关包名不完全一致有的把 compose 独立成 docker-compose 包有的并入 docker-compose-plugin以打包机上的实际包名为准把确认好的名字填进 yumdownloader 参数比照搬命令可靠。3. 制作 arm64 离线安装包的两条路径用 yumdownloader 拉依赖还是直接用官方二进制3.1 路径一在联网的 aarch64 openEuler 上用 yumdownloader 拉全依赖制作离线包的前提是有一台能联网且与目标机同架构、同大版本的 openEuler 机器。没有现成的 aarch64 联网机退而求其次用 qemu 模拟一台 arm64 虚拟机来拉依赖但最终验证别在模拟机上做原因见 5.5 节。联网机准备好后第一件事是装 yum-utils# yumdownloader 属于 yum-utils先装上 yum install -y yum-utils # 把 docker 相关主包和依赖全部拉到一个目录 yumdownloader --resolve --alldeps --destdir/root/rpm_packages \ docker-engine docker-cli containerd docker-compose--resolve是关键参数表示把主包的全部依赖一起下载没有它你只会拿到孤零零的几个 rpm到现场一装就报缺库。--alldeps连建议安装的依赖也一并拉下来离线环境宁可多带几个用不上的 rpm也不能少一个运行时需要的库。--destdir指定输出目录建议用绝对路径脚本里引用不容易出歧义。拉完检查目录里 rpm 的数量。docker 全家桶连同依赖一般在 20 个上下如果只有三四个说明--resolve没生效或者软件源配置有问题。再用 rpm 的查询命令做一轮依赖排雷# 对下载目录里所有 rpm 做依赖汇总看有没有仍指向系统库的硬缺失 rpm -qpR /root/rpm_packages/*.rpm | sort -u | grep -E ^/ || true-qpR是查询未安装 rpm 包需要的依赖grep ^/只留下绝对路径形式的文件依赖。正常结果里应该只剩 glibc、libcgroup、libseccomp 这类基础库如果你的 openEuler 版本自带了就行如果提示缺少某个确定版本的库就把对应依赖包也补拉进目录。这一步是给现场安装提前排雷的关键别跳过。3.2 路径二用官方 static binary 和独立的 docker-compose 二进制兜底官方源里的 docker 版本可能滞后或者你需要特定版本做兼容测试这时走二进制路径。做法是在联网机器上从 docker 官方 release 下载对应 aarch64 的静态包包名通常是 docker-20.10.x.tgz 这种同时下载 docker-compose 的独立二进制命名一般带 linux-aarch64。下载时注意别下成 amd64 版本这是离线包最常见的翻车点。# 解压 docker 静态包目录里是 docker/dockerd/containerd 等一组可执行文件 tar -xzf docker-20.10.x.tgz # 放进 /usr/local/bin 并赋予执行权限 cp docker/docker docker/dockerd docker/containerd* /usr/local/bin/ chmod x /usr/local/bin/docker /usr/local/bin/dockerd /usr/local/bin/containerd* # docker-compose 是单个二进制处理更简单 cp docker-compose-linux-aarch64 /usr/local/bin/docker-compose chmod x /usr/local/bin/docker-compose注意 docker 的静态包里不只有 docker 一个文件docker/dockerd、containerd、containerd-shim-runc-v2 要一起拷贝只拿其中一个会导致启动时找不到组件。containerd*用通配符把整套 containerd 组件都覆盖到。docker-compose 单独放不要塞进 rpm 流程里因为不同 openEuler 版本里 compose 的 rpm 包名和版本差异很大独立二进制反而稳定。如果你担心二进制方式缺少系统库有一个保底检查ldd /usr/local/bin/dockerd看动态链接结果。输出中出现not found说明这台机器的 glibc 或某些库版本不够二进制方式不适用切回 rpm 路径。这里也顺带回答了为什么 openEuler 上解压 tar 包不能直接照搬别的发行版命令——tar 本身没区别区别在于解压出来的二进制能不能被系统加载。3.3 离线包目录把 rpm、二进制、镜像和脚本整理成团队可复用的格式最后把素材组织成目录。我见过太多离线包就是一个塞满 rpm 的文件夹到了现场全靠人肉记忆那不叫安装包叫仓库搬家。可复用的目录组织如下# 创建标准目录骨架 mkdir -p offline-docker/{packages/{rpm,images},bin,conf,scripts} # 最终结构示意 # offline-docker/ # ├── packages/rpm/ # yumdownloader 拉下来的 rpm 依赖 # ├── packages/images/ # docker save/load 用的镜像 tar # ├── bin/ # docker-compose 这类独立二进制 # ├── conf/ # daemon.json、systemd 单元模板 # └── scripts/ # install.sh、uninstall.shrpm 放依赖、images 放镜像 tar、bin 放独立二进制、conf 放配置模板、scripts 放一键脚本职责分明。后续往包里加东西时加镜像只动 images 目录换 compose 新版本只替换 bin 目录互不干扰。打包完成后在包根目录生成一份 SHA256SUMS# 在 offline-docker 根目录执行生成校验文件 find . -type f ! -name SHA256SUMS -exec sha256sum {} \; SHA256SUMS离线包在 U 盘、内网共享、scp 之间转手经常损坏损坏的 rpm 安装时会报奇怪的 checksum 错误看起来像玄学实际是文件坏了。交付时把 SHA256SUMS 和安装说明放一起现场先sha256sum -c SHA256SUMS校验一遍能省掉一大批莫名其妙的安装失败。4. 一键安装脚本怎么设计前置检查、分层安装、失败恢复一次讲清4.1 前置检查root、架构、已有环境三个条件不满足就直接退出一键脚本的第一段不是安装而是「拒绝安装」。脚本要在错误环境里自我拦截否则装一半报错现场更难看。下面这段是我写在 install.sh 开头的前置检查#!/bin/bash set -euo pipefail # 脚本所在目录这样从任意路径调用都能找到 packages SCRIPT_DIR$(cd $(dirname ${BASH_SOURCE[0]}) pwd) LOG_FILE${SCRIPT_DIR}/install.log exec (tee -a ${LOG_FILE}) 21 # root 权限检查rpm 和 systemctl 都需要 root if [[ $EUID -ne 0 ]]; then echo 必须用 root 运行sudo 执行可能丢失环境变量 exit 1 fi # 架构检查只接受 aarch64 if [[ $(uname -m) ! aarch64 ]]; then echo 当前架构 $(uname -m)本包仅支持 arm64/aarch64 exit 1 fi # 已有 docker 检查停旧服务避免 rpm 覆盖时端口和 socket 冲突 if command -v dockerd /dev/null 21; then systemctl stop docker 2/dev/null || true systemctl disable docker 2/dev/null || true fiset -euo pipefail里-u拦截未定义变量-o pipefail保证管道中任何一段出错都让脚本退出避免「最后一步成功了但中间其实失败了」的假象。BASH_SOURCE[0]拿到脚本自身路径这样无论在包根目录还是别的路径执行scripts/install.sh都能正确定位 packages 和 bin。exec (tee ...)是 bash 的进程替换把脚本输出同时打到终端和 install.log现场能看到进度事后能翻日志。command -v dockerd检测旧安装先停旧服务再覆盖避免二进制文件被占用导致 rpm 覆盖失败的报错。4.2 分层安装rpm 优先、二进制兜底配置和离线镜像一次处理安装主体分成几层rpm 层、二进制层、配置层、镜像层。rpm 包存在就用 rpm 批量安装不存在就检查 bin 目录里的二进制兜底最后统一写 daemon.json 和 systemd 单元再导入离线镜像# 第一层本地 rpm 批量安装 if compgen -G ${SCRIPT_DIR}/packages/rpm/*.rpm /dev/null 21; then rpm -Uvh ${SCRIPT_DIR}/packages/rpm/*.rpm || { echo rpm 直接安装失败改用 dnf 做本地依赖解析 dnf install -y ${SCRIPT_DIR}/packages/rpm/*.rpm } fi # 第二层rpm 不存在时用 bin 目录的二进制兜底 if ! command -v dockerd /dev/null 21 [ -d ${SCRIPT_DIR}/bin ]; then cp ${SCRIPT_DIR}/bin/dockerd ${SCRIPT_DIR}/bin/docker \ ${SCRIPT_DIR}/bin/containerd* /usr/local/bin/ chmod x /usr/local/bin/dockerd /usr/local/bin/docker /usr/local/bin/containerd* cat /etc/systemd/system/docker.service EOF [Unit] DescriptionDocker Application Container Engine Afternetwork-online.target firewalld.service Wantsnetwork-online.target [Service] Typenotify ExecStart/usr/local/bin/dockerd --data-root /var/lib/docker ExecReload/bin/kill -s HUP $MAINPID LimitNOFILEinfinity LimitNPROCinfinity LimitCOREinfinity Delegateyes KillModeprocess Restarton-failure RestartSec5 [Install] WantedBymulti-user.target EOF systemctl daemon-reload fi # docker-compose 独立二进制兜底rpm 没带或版本不满足时才用 if ! command -v docker-compose /dev/null 21 [ -f ${SCRIPT_DIR}/bin/docker-compose ]; then cp ${SCRIPT_DIR}/bin/docker-compose /usr/local/bin/docker-compose chmod x /usr/local/bin/docker-compose fi # 第三层daemon.json 统一配置有旧文件就先备份 if [ -f /etc/docker/daemon.json ]; then cp /etc/docker/daemon.json /etc/docker/daemon.json.bak.$(date %s) fi mkdir -p /etc/docker cat /etc/docker/daemon.json EOF { data-root: /var/lib/docker, exec-opts: [native.cgroupdrivercgroupfs], log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 }, iptables: true, storage-driver: overlay2 } EOF # 启动并设置开机自启 systemctl daemon-reload systemctl enable docker systemctl start docker # 第四层导入离线镜像 tar容器栈开箱即用 if compgen -G ${SCRIPT_DIR}/packages/images/*.tar* /dev/null 21; then for image in ${SCRIPT_DIR}/packages/images/*.tar*; do docker load -i ${image} done firpm 层里compgen -G是判断 glob 是否有匹配的语法有 rpm 文件才进入 rpm 安装。rpm -Uvh后面的||兜底很实用某些离线环境下 rpm 因为依赖顺序装不上转用dnf install -ydnf 会把本地目录里的 rpm 当作本地仓库做依赖解析顺序问题自动解决。二进制层的 systemd 单元里Typenotify是 dockerd 的标准通知类型docker 起来后会主动通知 systemd 服务已就绪Delegateyes把 cgroup 管理权交给 dockerKillModeprocess保证重启 docker 服务时不误杀容器进程。daemon.json 里的># uninstall.sh 关键段先清 systemd 注册再删二进制数据目录按需保留 systemctl stop docker 2/dev/null || true systemctl disable docker 2/dev/null || true rm -f /etc/systemd/system/docker.service /etc/systemd/system/docker.service.socket rm -f /usr/local/bin/dockerd /usr/local/bin/docker /usr/local/bin/containerd* rm -f /usr/local/bin/docker-compose systemctl daemon-reload echo 数据目录 /var/lib/docker 未删除确认数据不需要后再手动清理如果安装中途失败先看 install.log 最后 30 行定位是 rpm 层还是 systemd 层。rpm 层的报错信息里通常会直接写缺少哪个依赖按包名补进 packages/rpm 重新打包systemd 层最常见的问题是 dockerd 起不来用systemctl status docker -l看完整错误。卸载脚本特意保留/var/lib/docker因为里面可能存着镜像和容器数据删了就没有后悔药了。等确认数据确实不需要再手动rm -rf /var/lib/docker。注意rpm -Uvh失败后改用 dnf 兜底是现场常用的手段但如果 dnf 也报依赖缺失就回打包机补依赖重打不要现场--nodeps强装。5. arm64 openEuler 离线装 Docker 避坑清单五个高频翻车现场5.1 docker-compose 报 Exec format error架构下错文件现象docker-compose version返回exec format error或退出码 126但 docker 本身正常。原因下载 docker-compose 时选了 amd64 的二进制或者在 x86 机器上下载后直接拷到 aarch64。docker-compose 官方 release 的产物按平台区分linux-aarch64才是目标文件。解决先用uname -m确认架构为 aarch64再用file docker-compose查看二进制 ELF 格式正常应显示ARM aarch64。把错误文件删掉换成匹配版本并chmod x。还有一个隐蔽点docker-compose带横线和docker compose子命令是两套用法脚本里用哪个就装哪个别装了一处调用另一处。5.2 rpm 安装报依赖缺失版本基线不一致现象rpm -Uvh报需要 libseccomp.so.2()(64bit)或需要特定版本 libcgroup但系统里明明装了这些库。原因rpm 包对依赖有精确版本要求。打包机用的 openEuler 大版本比现场机器新拉下来的 rpm 依赖了较新的库版本或者yumdownloader没加--resolve导致依赖拉漏。解决回到打包阶段用 3.1 节的方式rpm -qpR检查全部 rpm 的依赖把缺口补进 packages/rpm。现场救急时用dnf install -y packages/rpm/*.rpm替代rpm -Uvh让 dnf 在本地目录里做依赖解析。不要--nodeps强装强装后 docker 可能能起来但存储驱动或网络组件会在运行期随机暴露问题这是最隐蔽的坑。5.3 dockerd 启动失败ExecStart 路径与二进制位置不一致现象systemctl start docker报ExecStart/usr/bin/dockerd: No such file or directory但which dockerd又能找到路径。原因rpm 安装的 docker 服务单元默认写死/usr/bin/dockerd二进制方式安装时如果把文件拷到/usr/local/binsystemd 单元还是旧的/usr/bin路径两者错位。解决确认ls -l /usr/bin/dockerd是否存在。存在就不管不存在就在/usr/bin下建符号链接ln -s /usr/local/bin/dockerd /usr/bin/dockerd或者按 4.2 节的方式重写 docker.service 的ExecStart指向实际路径。改完必须systemctl daemon-reload否则 systemd 不会重新读取配置。5.4 容器启动成功但网络不通firewalld 和 ip_forward现象docker run能跑宿主机能访问容器但容器内 ping 不通外部DNS 解析也超时。原因openEuler 默认开启 firewalldFORWARD 链策略拦截了 docker0 网桥的转发流量或者/proc/sys/net/ipv4/ip_forward为 0内核不允许转发。解决先看sysctl net.ipv4.ip_forward为 0 就写配置并生效echo net.ipv4.ip_forward 1 /etc/sysctl.d/99-docker.conf sysctl --system再确认 firewalld 状态。现场允许关防火墙就直接systemctl disable --now firewalld不能关就放行 docker 网段firewall-cmd --permanent --zonetrusted --add-interfacedocker0后 reload。注意 docker 服务重启后 docker0 网桥会重建这条规则要保证在 docker 启动之后仍在生效必要时放进 docker.service 的 ExecStartPost 里。5.5 用 qemu 模拟 arm64 做验证看起来通过真机翻车现象宿主机用 qemu 模拟 arm64 跑 openEuler离线包的安装脚本一次通过拿到真机上却出现网络不通、存储驱动报错、性能异常。原因qemu 的用户态模拟对内核模块、iptables、cgroup 这类内核交互支持有限模拟环境里的「正常」不代表真机内核同样正常。用qemu-system-aarch64跑虚拟机时网络走的是模拟网卡和真机网卡驱动完全不同docker 依赖的 overlay2 存储驱动在模拟环境里也可能表现差异。解决qemu 模拟环境只用来做依赖收集、安装脚本语法级验证、逻辑排错不做最终验收。交付前必须在一台同架构真机或云上同架构实例上完整跑一遍安装脚本和容器启动测试。这个原则写进团队交付流程之后现场翻车率会明显下降。6. 交付前的最后一道工序用三个维度验证离线包真的能用6.1 三个命令确认安装结果先跑版本和基本信息docker version --format {{.Server.Version}} docker info --format Architecture: {{.Architecture}} | OS: {{.OperatingSystem}} docker-compose version第一句确认 dockerd 服务真的在响应第二句确认架构显示 aarch64、操作系统显示 openEuler这是「包没装错机器」的证据第三句确认 compose 可用。任何一个报错都说明安装链路还有断点回到前一章对应条目排查。6.2 用 compose 拉一个小栈做端到端验证光有 docker 还不够离线包里通常带着镜像 tar。验证要用 compose 真实拉起一个栈。先在 packages/images 里docker load -i导入镜像再写一个最小 compose 文件services: nginx: image: local/nginx:latest restart: unless-stopped ports: - 8080:80docker compose up -d后看docker compose ps是否正常再curl 127.0.0.1:8080确认端口通。这里restart: unless-stopped顺带验证了开机自启策略docker 服务随系统启动后容器会自动恢复。离线环境下没有人在现场手动 start这个策略必须到位。6.3 把验证结果沉淀成交付模板我习惯在交付前最后做一次完整 reboot重启后确认docker ps里有容器在跑、systemctl is-enabled docker显示 enabled然后把 install.log、SHA256SUMS 校验结果、docker 版本号一起截图存档。这套离线包方案值得投入的地方就在这里一次打包多次交付后续只更新镜像 tar 和 compose 版本。架构差异踩过的坑记录下来比任何口头交接都管用。希望帮到你。本文还有配套的精品资源点击获取
返回列表