CTyunOS离线安装Docker-CE实战:依赖闭包与排错全攻略

发布时间:2026/9/15 13:51:05
CTyunOS离线安装Docker-CE实战:依赖闭包与排错全攻略 接到现场电话的时候我就知道又是那个经典戏码生产内网物理隔离服务器清一色天翼云OSCTyunOS对方扔过来一句“把Docker装一下”。出差的兄弟在客户机房现场试了 yum install docker-ce理所当然地失败然后掏出U盘拷了几个rpm进去 rpm -ivh结果连环报缺依赖最后连 yum 都被自己搞出依赖破损。我隔着电话都能感受到那股焦灼。这种活儿我前前后后处理过十几次了CTyunOS 上离线装 Docker-CE 的核心从来不是“Docker 怎么配置”而是“离线依赖闭包怎么搞干净、安装顺序怎么排合理、现场可能踩的坑怎么提前避开”。这篇文章我就把完整依赖包清单、拉包方法、安装步骤和几个高频报错排查链路全部摊开讲给后面进场的人一份能直接照做的作业。1. 动手之前先读懂CTyunOS再谈离线装Docker很多人上来就搜“CentOS离线安装Docker”然后拿一篇 CentOS 7 的教程硬套套不上就抱怨系统有问题。其实问题往往出在没搞清楚 CTyunOS 到底是什么版本、什么生态导致拿错了 el7/el8 的 rpm 包。1.1 CTyunOS的身世与版本判别CTyunOS 是中国电信面向云场景发布的服务器操作系统基于 OpenAnolis龙蜥社区版本构建软件生态上跟 RHEL/CentOS 高度兼容。这意味着Docker-CE 官方提供的 el7/el8 系列 rpm 包在绝大多数情况下可以直接装但前提是版本要对得上。到现场之后的第一件事不是找安装包而是先确认系统版本和数据盘格式。我通常按顺序敲这几条命令cat /etc/os-release cat /etc/redhat-release uname -r df -hT / /data看什么/etc/os-release里的 VERSION_ID 能判断它是偏 CentOS 7 生态还是 CentOS 8/Anolis 8 生态uname -r看内核版本内核 3.10 左右基本是 el7 体系内核 4.18 及以上多半是 el8 体系df -hT看根分区和数据盘的文件系统类型这关系到后文 overlay2 存储驱动能不能用。很多同事跳过了这步直接按网上的 CentOS 8 教程下载了 el8 的包结果装的时候 glibc 依赖对不上反而浪费时间。CTyunOS 的版本命名不像 CentOS 那么直观但通过cat /etc/os-release基本能看出来它基于哪个大版本。拿不准的情况下宁可在联网打包时用同版本的虚拟机拉取也别凭感觉选。1.2 离线安装到底卡在哪里依赖闭包问题先解释一个概念依赖闭包。一个软件能正常运行依赖一组库这组库又各自依赖别的库把所有直接依赖和间接依赖全部收集齐形成一个“闭合的集合”这个集合就叫依赖闭包。Docker-CE 一直不是一个孤零零的 rpm。docker-ce 主包依赖 docker-ce-cli、containerd.iocontainerd.io 依赖 libseccomp、libcgroupdocker-ce 还要求 container-selinux 达到一定版本而 container-selinux 又牵扯到 policycoreutils-python、audit-libs-python、setools-libs 等一堆 SELinux 工具链。在内网环境yum 无法联网自动解析这些依赖如果你只拷主包进去rpm 会当场报缺这个缺那个缺一个补一个补完又缺下一个能把你绕晕。所以离线安装的第一原则是在一台能联网的机器上把依赖闭包完整拉下来而不是到内网现场逐个补包。这个思路看起来简单但实际操作中很多人抱侥幸心理觉得“我就装个 docker 而已哪来那么多依赖”。真到现场 rpm 开始连环报错的时候才后悔没把依赖拉全。1.3 我采用的总体方案与适用边界这套方案分四步走在联网机器上配置 docker-ce 官方仓库或国内镜像仓库用 dnf download / yumdownloader 把 docker-ce 及其全部依赖拉到一个目录打好包把包传到内网先做 sha256 校验再解压在内网机器上按依赖顺序安装优先使用 yum/dnf localinstall 做本地解析写 daemon.json启动 systemd 服务做三层验证。这套方案的适用场景是单机或少量服务器离线部署 Docker-CE没有外网也不能临时开放出口。如果团队后续要接 Kubernetes方案里 cgroup driver 的配置会提前统一成 systemd避免二次改动。如果是大规模集群几十上百台建议在这个基础上搭建本地 yum 仓库而不是逐台 rpm 安装后面我会简单提一句升级思路。2. 完整依赖包清单与拉包方法这一节是很多人最关心的部分。先说结论不要凭着网上的清单手工凑包一定要用工具自动解析依赖闭包清单只是让你心里有数以及用于核对。2.1 核心安装包与版本选型先把几个核心 rpm 搞清楚包名作用选型建议docker-ceDocker 守护进程主包优先 20.10.x 或 23.0.x 稳定版docker-ce-clidocker 命令行工具必须与 docker-ce 同版本containerd.io容器运行时建议 1.6.x兼容性最稳docker-compose-plugindocker compose 子命令插件可选但强烈建议装后面你就知道多省事docker-buildx-plugin多架构构建插件可选内网一般用不上版本选型上我个人的经验是CTyunOS 这种偏“保守”的系统Docker 版本别追新。20.10.24 或 23.0.x 在 el7/el8 上都比较稳尤其是 CentOS 7 内核 3.10 的老环境20.10 系列兼容性最好。Docker 25 之后的版本对 iptables、内核特性要求更高在老内核上偶尔会遇到奇怪问题。内网环境一旦定版升级很麻烦所以一开始就锁一个稳定版本后面所有机器的版本保持一致运维省心很多。2.2 依赖包清单el7 / el8 对照根据我多次实操的经验el7 体系经常缺的依赖包大致如下包名版本要求缺少时会怎样container-selinux 2.107docker-ce 主包拒绝安装libcgroup0.41containerd.io 可能报依赖缺失libseccomp 2.3推荐 2.5容器启动时 seccomp 报错libtool-ltdl2.4.xdocker-ce 运行缺失device-mapper-libs1.02.x非必需但建议带audit-libs-python2.8.x随 container-selinux 引入checkpolicy2.5.x随 container-selinux 引入policycoreutils-python2.5.x随 container-selinux 引入python-IPy0.81随 container-selinux 引入setools-libs3.3.x随 container-selinux 引入el8 体系情况相对干净最常见的依赖主要是包名版本要求说明container-selinux 2.124同样可能成为拦路虎libseccomp 2.5el8 大多自带个别老版本需升iptables-libs与系统版本匹配涉及 nftables 兼容再次强调这些表格只是让你心里有数。真正要装哪些以拉包工具的解析结果为准。系统环境千差万别手工按清单凑包很容易漏也容易带上一堆用不上的旧版本反而制造冲突。2.3 在联网机器上把依赖一次性拉全拉包这一步我建议找一台和 CTyunOS 大版本一致的 CentOS/Anolis 虚拟机来做避免跨大版本打包导致依赖解析偏差。CentOS 7 系的联网机器先装 yum-utils 再配置仓库yum install -y yum-utils yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repoCentOS 8/9 系的联网机器dnf install -y dnf-plugins-core dnf config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo然后创建目录拉取安装包和全部依赖mkdir -p /opt/docker-offline cd /opt/docker-offline # CentOS 7 使用 yumdownloader yumdownloader --resolve --alldeps docker-ce docker-ce-cli containerd.io docker-compose-plugin docker-buildx-plugin # CentOS 8/9 使用 dnf download dnf download --resolve --alldeps docker-ce docker-ce-cli containerd.io docker-compose-plugin docker-buildx-plugin--resolve表示解析依赖--alldeps表示把依赖全部拉下来。如果提示某些依赖找不到通常是 epel 仓库没开先yum install -y epel-release再重试。在联网机器上多花十分钟把源配全比到内网现场面对一堆缺失报错要值得多。拉完后目录里大概是这样docker-ce-20.10.24-3.el7.x86_64.rpm docker-ce-cli-20.10.24-3.el7.x86_64.rpm containerd.io-1.6.24-3.1.el7.x86_64.rpm container-selinux-2.199.0-1.el7.noarch.rpm libseccomp-2.5.2-2.el7.x86_64.rpm policycoreutils-python-2.5-34.el7.x86_64.rpm ...打包并计算校验值cd /opt tar czf docker-offline.tar.gz docker-offline sha256sum docker-offline.tar.gz把校验值记下来传到内网后核对防止传输损坏。2.4 拉包后的依赖自检脚本光拉完还不够我习惯在打包前跑一遍自检确认依赖闭包是完整的。下面这个脚本逐个检查 rpm 的依赖项是否被目录里的包或系统已有包满足#!/bin/bash # 依赖自检脚本在拉完包的联网机器上运行 DIR/opt/docker-offline cd $DIR || exit 1 for rpm in ./*.rpm; do echo 检查 $rpm rpm -qpR $rpm 2/dev/null | while read -r req; do dep$(echo $req | awk {print $1}) # 系统已装包是否满足 if rpm -q --whatprovides $dep /dev/null 21; then continue fi # 目录内安装包是否满足 if rpm -qp --provides ./*.rpm 2/dev/null | grep -q ^${dep} ; then continue fi # 排除基础库等已经由系统内核/glibc提供的项 echo MISSING: $req $rpm done done脚本输出的MISSING项逐个确认是哪些依赖。有时候rpm -q --whatprovides查不到但实际系统里 glibc 等基础库是满足的所以脚本供参考最终以yum localinstall的解析结果为准。脚本的价值在于能在打包前就把明显缺失的依赖发现出来免得现场抓瞎。3. 内网安装完整流程从介质到验证包拉到U盘或者传到内网文件服务器之后才是真正考验耐心的时候。下面按我现场操作的完整顺序来写。3.1 传输与校验别跳过这一步内网机器上建个目录把压缩包传进去解压mkdir -p /data/installer/docker cd /data/installer/docker # 假设 tar 包已经传到当前目录 sha256sum docker-offline.tar.gz tar xzf docker-offline.tar.gz校验值这一步我每次都会做。U盘拷包、内网共享传输都可能出现文件损坏rpm 包损坏装到一半报错排查起来比缺失依赖还麻烦。核对 sha256 只需要几秒钟但能帮你排除一大类低级问题。3.2 安装顺序先依赖后主程序内网机器上我推荐直接使用本地解析cd /data/installer/docker/docker-offline # CentOS 7 yum localinstall -y ./*.rpm # CentOS 8/9 dnf localinstall -y ./*.rpmlocalinstall会把当前目录下的 rpm 当作一个本地仓库自动解析包与包之间的依赖比手动rpm -ivh省心得多。系统里的基础库glibc、systemd 等如果缺失它仍然会尝试去 yum/dnf 仓库找找不到才报错。也就是说这种方式能把“依赖闭包内”的问题自动处理掉只暴露“系统基础库缺失”的问题。如果 localinstall 因为某个依赖版本冲突失败就需要手动分步安装。我通常按这个顺序rpm -Uvh container-selinux-*.rpm rpm -Uvh libseccomp-*.rpm rpm -Uvh containerd.io-*.rpm rpm -Uvh docker-ce-cli-*.rpm rpm -Uvh docker-ce-*.rpm把 container-selinux 放到最前面因为它最容易引发版本冲突libseccomp 紧跟其后它决定容器运行时的安全过滤能力。注意真的不建议用rpm -ivh --nodeps强行装。依赖缺失的情况下rpm 虽然装上了但 docker 守护进程很可能起不来到时候排查问题的成本比老老实实解决依赖高得多。3.3 初始化配置daemon.json 里的坑安装完成后在启动 Docker 之前先把配置文件写好。这一步放在前面能避免服务起来之后反复重启折腾。mkdir -p /etc/docker cat /etc/docker/daemon.json EOF { data-root: /data/docker, log-driver: json-file, log-opts: { max-size: 100m, max-file: 3 }, exec-opts: [native.cgroupdriversystemd], storage-driver: overlay2, iptables: true } EOF逐项说明这些字段都不是随便写的>systemctl daemon-reload systemctl enable --now docker systemctl status docker验证我习惯分三层第一层docker version。看 Client 和 Server 两段都正常返回说明守护进程起来了CLI 能连上。第二层docker info。重点看 Storage Driver 是否为 overlay2、Cgroup Driver 是否为 systemd、Logging Driver 是否为 json-file以及 Docker Root Dir 是否指向了你设置的数据目录。第三层docker load一个离线镜像再跑起来。内网拉不了 Docker Hub所以提前在联网机器上准备一个 hello-world.tar 或者 busybox.tar 带进来docker load -i hello-world.tar docker run --rm hello-world如果能看到经典的 Hello from Docker!整个部署就算真正打通了。4. 高频报错与完整排查链路离线装 Docker 的过程中报错几乎必然会出现只是多和少的区别。我把现场遇到概率最高的几类问题和排查路径逐个拆开写。4.1 container-selinux 冲突先处理还是后处理这类报错最常见的表现是Error: Package: 3:docker-ce-20.10.24-3.el7.x86_64 (docker-ce-stable) Requires: container-selinux 2.103或者package container-selinux-2.28-1.el7.centos.noarch is already installed排查链路是逐步缩小范围的第一步看系统现状。执行rpm -qa | grep container-selinux和rpm -qa | grep selinux-policy确认系统里已经装的是什么版本、和包目录里的新版本差多少。第二步确认 SELinux 当前状态。执行getenforce如果是 Disabled说明系统本身就不强制 SELinux那 container-selinux 的版本冲突其实是可以放宽处理的如果是 Enforcing就必须谨慎操作。第三步选择处理方式。如果系统里没有 container-selinux直接用包里的版本安装rpm -Uvh container-selinux-*.rpm如果系统里已有旧版本优先尝试升级rpm -Uvh container-selinux-2.199.0-1.el7.noarch.rpm升级时如果报文件冲突不要立刻rpm -e --nodeps强卸。注意看冲突的文件是哪个包提供的有时候是 selinux-policy 的版本太旧。更稳妥的做法是先升级 selinux-policy 相关包rpm -Uvh selinux-policy-*.rpm selinux-policy-targeted-*.rpm再升级 container-selinux。我在现场遇到的大部分冲突沿着这条路径都能解决。最后一步如果以上全部无效再考虑强制替换。这是下策因为强卸 container-selinux 可能破坏 selinux-policy 的依赖关系。操作如下但务必评估后果rpm -e --nodeps container-selinux rpm -ivh container-selinux-*.rpm有个经验值得分享我在一次部署中遇到“system 里 container-selinux 版本比包目录里的还新”的奇怪情况原因是 CTyunOS 自带了一个更高版本号但不同构建号的包。这种情况下不要强行覆盖直接保留系统自带版本然后跳过 docker-ce 对 container-selinux 的版本检查因为系统自带版本完全满足运行需求。怎么跳过把 docker-ce 主包单独用rpm -ivh --nodeps装上吗不更好的做法是先装 containerd.io 和 docker-ce-cli再装 docker-ce如果 docker-ce 仍要检查 container-selinux则判断实际版本是否确实满足满足了就不用管 yum 解析器的报错用rpm -Uvh --replacefiles逐个升级剩余 rpm。细节不多说原则就是不盲从 rpm 依赖解算器以实际运行验证为准。4.2 libseccomp 版本过低与 seccomp 告警这是 CentOS 7 系的经典坑。装完 Docker 后看着一切正常结果 run 一个容器就报Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: process_linux.go:545: container init caused: operation not permitted: unknown排查链路第一步确认 libseccomp 版本rpm -qa | grep libseccomp如果版本低于 2.4基本就是它的问题。Docker 20.10 对 seccomp 的默认 profile 要求 libseccomp 2.4CentOS 7 自带 2.3两者不匹配。第二步升级 libseccomp。用包目录里的新版本直接升级rpm -Uvh libseccomp-2.5.2-2.el7.x86_64.rpm libseccomp-devel-2.5.2-2.el7.x86_64.rpm如果没带新版本内网又无法下载临时调试可以用docker run --security-opt seccompunconfined xxx绕过但生产环境不建议长期禁用 seccomp一定要把 libseccomp 升上去。第三步升级后重启 docker 再测试同一个容器。如果报错消失说明根因就是 libseccomp没有别的隐藏问题。4.3 overlay2 存储驱动不支持根因在格式化参数这个问题极具迷惑性因为它不是报“Docker 装不上”而是报“存储驱动不可用”。典型表现docker info ERROR: overlay2 is not supported by your kernel: failed to get d_type support排查链路第一步看内核是否支持 overlay。一般 el7 内核 3.10.0-1127 都支持 overlay2所以问题往往不在内核。第二步看数据盘文件系统。df -hT /data如果文件系统是 xfs多半就是它的问题。xfs 在格式化时如果没有加-n ftype1就无法提供 d_type 支持overlay2 驱动无法使用。第三步确认后再决定解决方案如果数据盘是空白盘或可以重新格式化用以下命令重新格式化mkfs.xfs -n ftype1 /dev/sdb1如果数据盘已有数据不方便重格且分区文件系统是 xfs就换成 ext4 挂载到 /datamkfs.ext4 /dev/sdb1实在不能动数据盘还有一个退路是用 vfs 存储驱动。这不是个理想方案性能和镜像层管理都很弱只建议在测试环境临时用。生产环境宁可重新规划分区。这个坑我栽过一次。当时客户的数据盘是 xfs但创建时格式化参数没带 ftype1Docker 装上后docker info直接报错我一开始还以为是内核问题折腾半天才发现是文件系统的事。后来我在项目标准交付清单里加了一步所有新部署机器必须核对df -hT数据盘统一用 ext4 或mkfs.xfs -n ftype1格式化。这一步做好后面省掉大量运维事故。4.4 服务起不来时的通用排查路径如果systemctl start docker直接失败我的排查路径固定是这三板斧journalctl -xeu docker.service这条命令看 systemd 视角的详细错误大多数启动失败的原因都能在这里看到。常见的有unable to configure the Docker daemon with file /etc/docker/daemon.json: invalid character——daemon.json 语法错误多半是逗号或大括号写错了。用python3 -m json.tool /etc/docker/daemon.json校验一下。fork/exec /usr/lib/iptables/iptables: no such file or directory——说明 iptables 包缺失或路径不对。CenOS 8 上可能是 iptables-nft 与 iptables-legacy 的问题检查相关包。Error creating default bridge network: iptables failed——多半是 firewalld 占用链或者 nftables 冲突。现场最简单的处理是先停掉 firewalld验证后再放行规则systemctl stop firewalld systemctl disable firewalld不少生产环境不允许关闭防火墙那就需要手动放行容器网络所需规则。但这个话题有点复杂不在本文展开。第二板斧看 docker 守护进程自己的日志dockerd --debug前台运行能输出极详细的日志定位问题比 journalctl 更快。第三板斧检查 socket 和权限ls -l /var/run/docker.sock如果Cannot connect to the Docker daemon at unix:///var/run/docker.sock但进程明明在多半是 daemon 启动后立刻退出了回到前两步找根因。这里有一条通用经验遇到问题先别急着卸载重装。Docker 的报错信息绝大多数都有明确指向journalctl -xeu docker.servicedockerd --debug基本能解决九成问题。重装意味着可能要重新处理依赖反而容易引入新问题。5. 内网 Docker 上线后的运维补充装好只是开始内网环境后续的镜像同步、日志维护、交付标准化才是真正拉开运维水平差距的地方。5.1 离线镜像同步save/load 与直连私有仓库内网没有外网镜像怎么进去两种常见方式。第一种小规模场景用 docker save/load。在联网机器上拉取镜像并导出 tar 包内网再用 load 导入。我习惯写一个批量脚本#!/bin/bash # images.txt 每行一个镜像名:标签例如 # busybox:1.36 # nginx:1.25-alpine while read -r img; do name$(echo $img | tr /: __) docker pull $img docker save -o ${name}.tar $img done images.txtload 侧同样批量处理for tar in ./*.tar; do docker load -i $tar done这种方式适合镜像数量少、节点少的场景。镜像多了之后管理成本很高而且各个节点各自 load 一份磁盘空间浪费严重。第二种有规模之后搭一个内网 Harbor 私有仓库。节点的 daemon.json 配insecure-registries如果 Harbor 走 HTTP{ insecure-registries: [harbor.internal.local] }节点不需要手动 load直接docker pull harbor.internal.local/nginx:1.25。离线环境进入“正规化”阶段后这是唯一靠谱的镜像分发方式。在拉包阶段顺便把 docker-compose-plugin 装好配合 Harbor 使用内网发布流程会顺滑很多。5.2 容器日志控制与数据目录规划很多内网环境问题不是 Docker 装不上而是跑着跑着磁盘满了。daemon.json 里的日志轮转配置是基础版生产上还应该加一重保险定期巡检/data/docker目录占用du -sh /data/docker/*清理无用镜像docker image prune -af小心这会把没有容器使用的悬空镜像全部清掉执行前确认没有需要保留的。容器日志手动轮转时不要直接删json.log文件正确方式是执行cat /dev/null xxx-json.log否则磁盘空间不会释放。如果当初没配日志轮转、日志已经涨到几个 GB先修改 daemon.json再清掉旧日志最后重启 docker 让配置生效。数据目录迁移也值得提一句。如果一开始没把>systemctl stop docker mv /var/lib/docker /data/docker ln -s /data/docker /var/lib/docker或者直接改 daemon.json 的>#!/bin/bash set -e INSTALL_DIR$(dirname $0) echo 1. 安装依赖与 Docker-CE if command -v dnf /dev/null 21; then dnf localinstall -y $INSTALL_DIR/*.rpm else yum localinstall -y $INSTALL_DIR/*.rpm fi echo 2. 写入 daemon.json mkdir -p /etc/docker cat /etc/docker/daemon.json EOF { data-root: /data/docker, log-driver: json-file, log-opts: { max-size: 100m, max-file: 3 }, exec-opts: [native.cgroupdriversystemd], storage-driver: overlay2 } EOF echo 3. 启动服务 systemctl daemon-reload systemctl enable --now docker echo 4. 验证 docker version docker info | grep -E Storage Driver|Cgroup Driver|Docker Root Dir脚本虽然精简但把关键动作都覆盖了。现场需要改的只是 daemon.json 里的路径或私有仓库地址其余直接跑即可。最后再分享一个我从多次现场里总结出来的经验CTyunOS 离线装 Docker 这个任务技术难度其实不高真正耗时间的都是环境差异导致的版本冲突和文件系统问题。所以进场后第一件事永远不是“装包”而是“摸底”——看系统版本、看数据盘格式、看 SELinux 状态、看 firewalld 状态。这四项确认完后面基本是一路顺畅。如果你马上要处理类似环境我也建议你把这个顺序固化下来先摸底再动手比什么技巧都管用。