ARTICLE DETAIL

资讯详情

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

openEuler arm64 离线安装 Docker 与 docker-compose 实践指南

openEuler arm64 离线安装 Docker 与 docker-compose 实践指南 简介面向arm64架构服务器在无外网或内网环境下快速部署容器运行环境的离线安装包已通过openEuler操作系统验证。内含一键安装脚本可自动完成Docker引擎的安装与服务配置适合运维工程师、系统管理员及需要在国产化平台搭建容器环境的技术人员使用。资源整体仅4个文件压缩包54.41MB包含docker-18.09.9.tgz二进制包、docker.service启动服务文件、install-docker.sh安装脚本及docker-compose可执行文件各组件职责清晰无需额外依赖即可在目标机器上独立部署。脚本已赋予可执行权限解压上传后执行一条命令即可完成安装同时支持docker-compose的快速部署免去编译或在线拉取步骤。目前已累计645人学习下载可作为arm64环境下离线安装Docker及Compose的可靠参照。资源将安装过程封装为标准化脚本并附带systemd服务文件便于后续做开机自启与进程管理对在openEuler、麒麟等国产系统上搭建容器服务具有直接参考价值。1. 离线安装包内网 openEuler 环境里最划算的“后悔药”一台只有内网出口的 arm64 服务器刚装好 openEuler第一件事往往是装 Docker 和 docker-compose。但当你把从网上拷贝的安装脚本执行下去大概率会看到一连串依赖错误或者 rpm 报出“No match for argument”——这口锅不该由 openEuler 背而是 arm64 平台的 RPM 依赖链和 x86_64 完全不同。这套离线安装包的核心价值就是把 docker-engine、containerd、docker-compose 插件连同所有依赖预先收齐再配一个验证过的一键安装脚本让没有公网的内网环境也能在几分钟内拥有一套能跑容器、能用 compose 编排的运行时。它适合政企内网、数据隔离区、信创替代项目里的实施工程师。你不需要把每个 RPM 的深层依赖背下来但跟着下面的章节走一遍能彻底搞清楚离线包怎么组织、脚本怎么写、装完怎么验收。2. 动手之前先看清 arm64 与 openEuler 的兼容关系2.1 openEuler 的“类 CentOS”外壳下藏着三处差异openEuler 从操作习惯上非常接近 CentOS但把它当成 CentOS 去处理 arm64 离线安装翻车只是时间问题。第一处差异是架构命名uname -m在 arm64 平台上返回的是aarch64不是arm64而 RPM 包的架构后缀也是aarch64.rpm。很多脚本里直接写死了x86_64的判断逻辑拷到 ARM 机器上要么拒绝执行要么装错包。第二处差异是仓库结构openEuler 的 baseos、EPOL 仓库与 CentOS 的仓库体系并不一一对应依赖的 glibc、systemd 版本也和大版本深度绑定把 CentOS 8 的 aarch64 RPM 强行拿来装会陷入“补了 A 缺 B补了 B 缺 C”的依赖泥潭。第三处差异是预装软件openEuler 默认带了 podman 或相关容器策略包docker-ce 安装后可能与它在 socket 和策略上产生冲突。这三处差异决定了离线包不能简单地从某个镜像站“把 docker 相关包抓下来”就完事必须先确认目标机器属于哪个 openEuler LTS 大版本再按版本去收集依赖。20.03 LTS 和 22.03 LTS 的仓库内容、依赖版本差异不小混用版本是离线安装最常见的隐性风险。2.2 用三条命令确认架构、版本和残留容器栈拿到一台新机器别急着把离线包拷上去先花一分钟跑下面三条命令uname -m # 确认是否为 aarch64 cat /etc/openEuler-release # 确认大版本例如 20.03 LTS / 22.03 LTS rpm -qa | grep -E docker|podman|containerd # 查看是否有容器栈残留第一条命令uname -m是硬性校验结果必须是aarch64注意不要拿lscpu里显示的ARM64字样去判断那可能是厂商的定制输出。第二条命令读/etc/openEuler-release而不是/etc/os-release因为很多基于 openEuler 的衍生发行版会改动 os-release 里的名称字段release 文件相对更可信。第三条命令的返回值决定安装脚本是不是要先做覆盖或卸载动作——如果 yum 源里存在 docker 和 podman 并存的情况后面 daemon 启动时大概率会撞车。这三条命令的输出建议直接作为离线包归档时的一个核对记录。我一般会把它们写进随包附带的 README 里作为“这台机器是在什么状态下装上 docker 的”的存档信息后续排障能省下大量时间。2.3 选型官方 docker-ce 源与 openEuler 自带 docker 包怎么选openEuler 的软件仓库里实际也存在 docker 相关包这就给离线安装提供了两条路线选错路线会让后面的维护成本翻倍。下表是我在一线项目里常用的判断维度对比项openEuler 自带 docker 包docker-ce 官方源包来源openEuler EPOL 仓库Docker 官方为 openEuler 构建的仓库版本迭代跟随 openEuler 发布节奏通常偏保守跟随 docker-ce 主线特性更新及时compose 形态多为独立二进制 docker-compose提供 docker-compose-plugin 插件离线依赖规模依赖集中在系统仓库内依赖相对完整但需要一次性拉全适合场景对版本不敏感只需要基础容器能力需要新特性、compose v2、与官方文档对齐我一般会选择 docker-ce 官方源这条路线。原因是 openEuler 自带 docker 包的版本往往偏旧且 compose 还是 Python 时代的独立二进制方案在内网环境里一旦需要和外部交付文档对齐命令格式和参数行为会有差异。而 docker-ce 源提供的docker-compose-plugin是 Go 语言实现的插件形态装好后直接集成进docker compose子命令离线包更干净升级路径也清晰。不过选 docker-ce 路线意味着必须严格按 openEuler 的版本去收集 RPM不能跨版本拿包。仓库配置文件下载后要先确认它指向的版本路径再执行依赖收集。3. 制作离线安装包依赖收集与目录结构一次说清3.1 在联网的 arm64 机器上把依赖一次性拉全制作离线包有一个硬前提必须在同架构、同大版本的联网机器上操作。也就是说目标机是 openEuler 22.03 LTS aarch64制作机也必须是 openEuler 22.03 LTS aarch64。用 CentOS 或 Ubuntu 的 ARM 机器拉包依赖集会对不上。先启用 docker-ce 仓库然后安装核心组件再用dnf download把依赖全部拉到一个目录# 启用 dnf 插件支持docker-ce 仓库的配置文件需按实际版本放置到 /etc/yum.repos.d/ dnf install -y dnf-plugins-core # 安装核心组件这一步会在当前机器上完成真实的依赖解析 dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin # 创建离线包目录并下载所有 rpm 本体与依赖 mkdir -p /data/offline-docker/packages cd /data/offline-docker/packages dnf download --resolve --alldeps docker-ce docker-ce-cli containerd.io docker-compose-plugin这段命令里最关键的是最后一条。--resolve会让 dnf 把依赖包一并下载--alldeps连“推荐依赖”也算进去避免拷贝到内网后出现“缺一个不起眼的辅助包”的问题。dnf download默认只下载到当前目录所以要先cd到 packages 目录。下载完成后检查目录里是否出现libcgroup、container-selinux这类系统库包它们是 docker 启动时绕不开的底层依赖如果没有说明当前机器的仓库源不完整需要先把 EPOL 仓库启用再重试。注意dnf download不会把 dnf 自身和基础 glibc 一起下载这意味着目标机器的 baseos 源至少要可用或者系统本身已经具备基础运行库。绝大多数内网环境的系统是完整安装的这一点通常不是问题但归档时最好在 README 里写明“本离线包依赖系统 baseos 基础库”。3.2 按“包、脚本、配置、档案”四层整理目录结构拉下来的几十个 rpm 如果散落在一个目录里拷贝到内网后没人知道哪个是本体、哪个是依赖、哪个是从哪个仓库来的。我一般会按下面这个结构归档离线包/data/offline-docker ├── packages/ # 所有 rpm 存放目录包含 docker-ce 本体及全部依赖 │ └── *.rpm ├── scripts/ │ └── install.sh # 一键安装脚本第 4 章给出核心实现 ├── config/ │ └── daemon.json # docker 运行配置内网环境提前配置好 └── README.md # 版本清单、收集时间、制作机器架构与校验结果这个结构看起来简单但每层都有讲究。packages 目录只放 rpm不放任何说明文件因为 rpm 安装时会扫描目录下的所有 rpm多出无关文件不影响安装但影响包体积。scripts 目录放一键安装脚本脚本里不要写死相对路径而是通过cd $(dirname $0)/..计算出离线包的根目录这样整个目录被拷贝到任意路径都能运行。config 目录放 daemon.json内网环境通常没有可用的公网镜像仓库提前把镜像源留空、日志参数配好比装完再改少一步。README 里记录 rpm 来源仓库、收集时间、制作机器的 openEuler 版本和架构这些信息在内网排障时就是“后悔药”。3.3 为什么我不建议在模拟环境中制作离线包有时候手里只有一台 x86 机器有人会想用 qemu 之类的方案模拟一个 arm64 环境来做依赖收集。这条路我踩过结论是能跑通但结果不可控。qemu 的用户态模拟环境缺失真实 arm64 内核特性docker-ce 里的 containerd 运行时依赖 cgroup v2、内核模块等硬件特性模拟环境下 dnf 解析出来的依赖版本可能与真实机器存在偏差甚至 rpm 安装时的脚本检查也会因为模拟环境暴露的 CPU 指令集不同而给出不同结果。更稳妥的做法是申请一台云上的 arm64 实例来做制作机哪怕只有 1 核 2G 也能胜任依赖收集工作。离线包的制作是一次性成本机器配置要求很低但架构一致性不能妥协。制作完成后把 packages 目录的 rpm 数量、总大小记录到 README再把整个目录打包传进内网这样内网机器安装时才能达到“拷过去就能装”的效果。4. 一键安装脚本核心环境判断、依赖安装与配置落位4.1 先从“能不能装”开始root、架构、残留检查一键安装脚本的第一个职责不是安装而是拒绝在不合适的环境里乱装。入口部分我会用下面的逻辑#!/usr/bin/env bash set -euo pipefail BASE_DIR$(cd $(dirname $0)/.. pwd) PKG_DIR${BASE_DIR}/packages # 1. 必须用 root 执行rpm 安装和 systemctl 操作都需要权限 if [[ ${EUID} -ne 0 ]]; then echo 请用 root 执行sudo $0 exit 1 fi # 2. 架构校验arm64 平台的 uname -m 返回值是 aarch64 ARCH$(uname -m) if [[ ${ARCH} ! aarch64 ]]; then echo 当前架构 ${ARCH}不是 aarch64离线包不适用 exit 1 fi # 3. 预检容器栈残留知会用户但不直接卸载 rpm -qa | grep -E podman|docker|containerd || trueset -euo pipefail是三保险-e让脚本在第一个命令失败时立即退出-u防止未定义变量造成静默错误pipefail保证管道中间环节出错也会被捕获。架构判断用${ARCH} ! aarch64而不是! arm64这是最容易踩的坑因为几乎所有 arm64 服务器的uname -m返回都是aarch64。残留检查只输出到屏幕不做卸载具体策略由下一步决定。预检通过后复制 daemon.json 到/etc/docker/目录mkdir -p /etc/docker cp ${BASE_DIR}/config/daemon.json /etc/docker/daemon.json4.2 安装 RPM先试完整依赖解析再决定要不要降级离线安装最容易翻车的就是 rpm 安装顺序。一次性把所有 rpm 交给rpm -Uvh让 rpm 自己解析依赖比手工分批安装可靠得多# 收集 packages 目录下所有 rpm RPM_FILES($(ls ${PKG_DIR}/*.rpm 2/dev/null || true)) if [[ ${#RPM_FILES[]} -eq 0 ]]; then echo packages 目录为空请检查离线包是否完整 exit 1 fi # 优先完整依赖安装失败时降级为 --nodeps rpm -Uvh --nosignature ${RPM_FILES[]} \ || rpm -Uvh --nodeps --nosignature ${RPM_FILES[]}第一行RPM_FILES($(ls ...))把目录下所有 rpm 文件名读进数组用 2/dev/null 屏蔽空目录时的报错|| true防止set -e在目录为空时直接退出脚本后面有专门判断。安装时先不带--nodeps让 rpm 严格解析依赖关系只有当第一次因为某个非关键依赖失败时才回退到--nodeps。注意--nodeps是最后手段它跳过了所有依赖检查docker 本体可能因为缺少系统库而在启动阶段才暴露问题所以我在使用回退后总会执行一次docker version做二次验证。这里没加--force是有意为之。--force会强制覆盖已存在的文件openEuler 系统里有些库文件被 systemd 或其他服务占用强制覆盖后可能导致系统组件异常。--nosignature则是跳过硬件的依赖这是临时办法后续应更新内网 rpm 密钥库。4.3 daemon.json 配置cgroup 驱动、日志与内网镜像源配置文件建议在脚本里直接写入而不是依赖随包拷贝这样即便用户忘记粘贴 config 目录也能装出可用环境{ storage-driver: overlay2, log-driver: json-file, log-opts: { max-size: 100m, max-file: 3 }, iptables: true, exec-opts: [native.cgroupdriversystemd], registry-mirrors: [] }openEuler 的内核默认启用 cgroup v2容器运行时使用systemd作为 cgroup driver 可以避免 docker 与 systemd 在资源管理上的冲突。日志参数max-size: 100m和max-file: 3限制单个容器日志文件大小与轮转数量内网环境里容器日志常常没人清理这个配置能把“磁盘被日志塞满”的故障推后几天。registry-mirrors留空数组是因为离线环境不依赖公网镜像站真正使用时应该把内网私有镜像仓库地址填在这里。注意iptables不要关掉docker 的网络隔离依赖它。4.4 启动、开机自启与 docker-compose 兼容层配置落位后就可以启动服务了systemctl daemon-reload systemctl enable --now docker docker version docker compose versionenable --now等价于先enable再start保证重启后 docker 自动拉起。docker version会分别打印 Client 和 Server 信息如果只显示 Client 没有 Server 段说明 daemon 没起来要立刻去journalctl -u docker看日志。docker compose version用于确认 compose 插件已经挂载到 docker 命令下。这里有一个兼容性细节docker-ce 官方源安装的是 compose 插件命令是docker compose中间有空格但很多旧交付文档和用户肌肉记忆里写的是docker-compose带横线。离线包内如果用户明确要求兼容旧命令我会在脚本末尾生成一个轻量包装脚本cat /usr/local/bin/docker-compose EOF #!/bin/sh exec docker compose $ EOF chmod x /usr/local/bin/docker-compose这个包装层只做参数透传不引入额外的 Python 依赖也不会和 docker 插件机制冲突。需要注意的是它只能覆盖docker-compose的常用命令形态如果业务脚本里用了docker-compose --compatibility等特定参数还是要回归到插件版本来验证。5. 避坑指南离线安装中最常见的四个问题5.1 podman 与 docker 的容器栈冲突现象systemctl start docker执行后一直失败查看状态无明确报错journalctl -u docker日志末尾出现conflict with podman或 socket 文件被占用。原因openEuler 默认预装了 podman 和相关的容器策略包podman 会创建/run/podman和 Docker 兼容的 socket 路径docker 启动时发现这些文件存在认为容器运行时已被占用拒绝拉起自己的 daemon。解决新装机的业务环境里如果确定不需要 podman直接卸载它再启动 dockerdnf remove -y podman podman-docker subuid systemctl start docker docker version注意卸载动作要放在安装 docker 之前执行否则 rpm 依赖检查阶段就可能因冲突失败。如果系统里已经有业务依赖 podman就不能卸载需要保留两套容器栈时要修改 docker 的--containerd参数和 socket 路径但这会让维护成本变高我一般不建议在一台机器上同时跑两套容器栈。5.2 依赖缺失libcgroup 与 container-selinux现象rpm -Uvh阶段报error: Failed dependencies: libcgroup is needed by containerd.io或者container-selinux 2.x is needed by docker-ce离线包目录里却没有这些包。原因集依赖时用了dnf install但机器上没有启用 EPOL 或相关辅助仓库导致部分依赖没有进入到下载目录。尤其是container-selinux它通常在 EPOL 仓库里docker-ce 的仓库不提供它。解决回到制作机上补包。先用 repoquery 查清楚到底缺什么dnf repoquery --requires containerd.io | grep -E libcgroup|container-selinux dnf download --resolve container-selinux libcgroup把补下载的 rpm 丢进 packages 目录重新执行安装脚本。这里要特别注意 openEuler 大版本的一致性——20.03 LTS 的 libcgroup 不能直接用到 22.03 LTS 上即使 rpm 名字看起来一样依赖库版本也可能触发新的冲突。所以补包动作最好在制作机上完成不要在内网机器上“缺哪个包去别的机器拷一个”这往往是依赖雪崩的起点。5.3 docker compose 命令不存在或插件未生效现象装完后执行docker compose version报command not found而docker-compose也不存在或者docker compose提示docker-compose is not a docker plugin。原因可能是 rpm 安装时docker-compose-plugin没有装上也可能是插件文件权限不对。docker compose v2 插件的正确位置是/usr/libexec/docker/cli-plugins/docker-composedocker CLI 启动时会去这个目录扫描插件。解决先确认插件文件是否存在ls -l /usr/libexec/docker/cli-plugins/docker-compose chmod x /usr/libexec/docker/cli-plugins/docker-compose docker compose version如果文件不存在说明安装阶段跳过了 docker-compose-plugin回到 packages 目录找到docker-compose-plugin-*.rpm单独安装即可。如果安装脚本用了--nodeps回退插件 rpm 可能因为依赖缺失没装上这一点在第四章安装命令里要留意。还有个隐藏坑插件文件放在/usr/local/libexec/docker/cli-plugins而不是/usr/libexecdocker CLI 在部分发行版上只会扫/usr/libexec和/usr/lib两个路径路径不对插件就静默失效。5.4 启动失败iptables、firewalld、selinux 三座大山现象systemctl start docker后服务状态是 failedjournalctl -u docker -e --no-pager日志里出现iptables failed或Failed to start Docker Application Container Engine。原因openEuler 默认开启 firewalld 和 SELinuxdocker 需要写 iptables 规则建立容器网络firewalld 的规则变更会把 docker 写入的规则清理掉SELinux 会对容器文件系统标签做限制docker 默认的身份标签可能与 SELinux 策略不匹配。解决按顺序排查不要一上来就关 SELinux。先看 firewalld 状态systemctl status firewalld systemctl disable --now firewalld getenforcefirewalld 在纯内网环境里可以关掉因为容器自身的网络流量都由 docker 的 iptables 规则管理firewalld 的存在反而会周期性刷新规则导致运行中的容器网络瞬时中断。SELinux 可以用getenforce查看当前状态如果显示Enforcing在测试环境里可以临时切到Permissive验证生产环境建议保留 SELinux 但为 docker 添加对应策略模块不过这属于另一个层面的工作量。我的经验是内网实施阶段先把 SELinux 放到 Permissive 保证交付进度同时在 README 里标注“后续需补充 SELinux 策略”这样技术债清晰可见不会留下黑匣子。6. 装完才是开始验收清单与镜像内网迁移技巧6.1 用最小命令确认 docker 真正可用安装脚本执行完不等于环境可用还要做一轮运行态验收。docker version只能说明 daemon 起来了容器能不能跑需要实测systemctl is-active docker docker run --rm arm64v8/hello-worldarm64v8/hello-world是专门为 ARM 平台构建的测试镜像x86 的hello-world镜像在 arm64 机器上拉下来会报exec format error这是个很容易误导人的现象——错误信息看起来像二进制损坏实际是架构不匹配。如果你在内网环境里没有这个镜像可以先跳过去用下面这个更通用的方式验证docker run --rm --entrypoint /bin/echo arm64v8/busybox docker ok--entrypoint直接指定可执行文件绕开镜像默认入口能更干净地验证容器运行时是否可以创建并执行命令。6.2 把镜像从联网机器搬到内网容器运行时装好了内网机器还是没有镜像可跑。常见做法是docker pull到一台能联网的 arm64 机器上再用 save/load 方式迁移# 在联网的 arm64 机器上执行 docker save arm64v8/busybox -o busybox-arm64.tar # 拷贝到内网机器后执行 docker load -i busybox-arm64.tar docker images | grep busyboxdocker save导出的 tar 会保留镜像的所有 tag 和元数据docker load导入后直接用原有 tag 名即可。比docker pull后docker export再docker import更可靠因为 export/import 会丢失镜像的历史分层导致docker history不可用。整套离线包交付时我通常会在 README 里追加一段“镜像迁移标准流程”把 save 命令、tar 包命名规范、load 后校验命令列清楚。这几年交付过的内网环境里真正让人头疼的其实不是 docker 本身安装而是“不知道机器处在什么状态”。我现在拿到一台新的 arm64 openEuler第一件事永远是跑第二章那三条检查命令确认架构、版本、残留容器栈再决定要不要动包。离线包的制作是一次性成本但排障步骤和归档习惯是持续收益把它们沉淀进 README 和脚本注释里比任何“装机神器”都管用。希望帮到你。本文还有配套的精品资源点击获取
返回列表