资讯动态

Rocky Linux 9 离线环境部署 Docker 完整实战指南

发布时间:2026/9/16 22:23:35 来源:尧图企业网站定制
Rocky Linux 9 离线环境部署 Docker听起来就是个运维的常规操作但真正在内网交付环境里经历过一次你就知道这事远没有“拷个 rpm 包过去装上”这么简单。最近我刚帮一个做等保改造的项目组搞定了一批物理隔离服务器没有外网连内网软件源都没有唯一能和外部交换数据的手段是经过审批的移动介质。这批机器全是 Rocky Linux 9.4要求跑 Docker 承载业务应用应用镜像还得一并准备好。整个过程从依赖打包、离线仓库构建、引擎安装到镜像导入和私有仓库搭建每个环节都有值得记录的细节今天整理出来给后面要处理类似环境的人当个参考。不管你是做内网交付、边缘节点部署还是给机房那批从不联网的服务器装环境这套思路都通用。核心就一句话把“联网机器上能完成的准备”尽量做完把“离线机器上需要的东西”一次性带齐然后按正确的顺序执行安装和验证。1. 项目概述与离线部署思路1.1 什么场景会逼着你走离线部署这条路离线部署 Docker 的需求多半来自网络隔离要求严格的行业。政务内网、医疗系统、金融单位、电力监控、军工科研这些环境出于安全合规考虑服务器要么物理隔离要么只在受控的内网里通信外网连接被明确禁止。还有一种常见情况是项目交付现场根本没有外网条件设备搬进机房才发现连不上 yum 源这时候再临时想办法就非常被动。这类环境有个共同点不仅不能访问外网连内网的软件源也未必齐全。很多单位内部的软件源只有基础系统的 ISO 包像 Docker、Kubernetes 这类第三方组件的源根本没有维护。所以做离线部署之前不能假设“内网有镜像站”要做就做全套携带的准备方案。1.2 为什么选择 Rocky Linux 9 作为承载系统Docker 官方对 RHEL 系的 CentOS、Rocky、AlmaLinux 都有完善的安装源这对离线部署来说非常友好。Rocky Linux 9 属于 RHEL 9 的二进制兼容发行版生命周期覆盖到 2032 年内核版本 5.14cgroup v2、overlay2 存储驱动这些 Docker 运行依赖的关键特性都支持得不错。相比 Ubuntu/Debian 系Rocky 这类 RHEL 派生系在离线管理生态上有个明显优势rpm 依赖管理有成熟的工具链createrepo 可以轻松把一堆 rpm 包转成本地 yum 源dnf 安装时能自动解析依赖树。Ubuntu 离线装 Docker 通常只能用 dpkg 一个个装依赖冲突处理起来很痛。再加上国内很多运维团队的既有技术栈就是 CentOS 系Rocky 9 迁移成本极低命令习惯全都用得上。1.3 离线部署的整体路线图抛开命令细节先建立全局视角。整个离线部署是一个单向链路联网打包机准备依赖包用 dnf download 把 Docker 引擎及其所有依赖下载到本地目录再用 createrepo 生成仓库元数据最后压缩打包内网目标机收到压缩包后解压创建本地 repo 文件通过 dnf 自动解决依赖并安装配置 daemon.json启动 Docker 服务并验证然后通过 docker save/load 或私有 registry 方式把镜像导入内网。这个链路里有三个关键决策点很多人栽跟头就栽在这三处。第一打包机和目标机的系统版本、CPU 架构必须一致甚至补丁版本都建议对齐。第二依赖包版本要明确锁定不要抱着“顺手升个级”的想法离线环境里升级路径完全不可控。第三镜像准备要单独规划docker save 出来的镜像 tar 包和 rpm 依赖包分开打包、分开传递避免混淆。2. 准备工作从联网环境把依赖打包带进去2.1 先准备一台同版本的联网打包机打包机的作用就是替目标机完成所有需要联网才能做的“采购”工作。这台机器建议选择与目标机系统版本一致的 Rocky Linux 9架构也必须一致x86_64 就对应 x86_64aarch64 就对应 aarch64。版本不一致容易踩大坑比如打包机是 9.3目标机是 9.4rpm 包之间的依赖关系可能发生变化内网安装时就会突然冒出来缺依赖的错误。打包机自身也要保持干净的软件源状态不要装一堆杂七杂八的包之后再来打包避免把无关的依赖也拉进目录。可以准备一台全新的虚拟机装完系统、配好网络之后就专门用来做离线打包。2.2 配置 Docker 官方源并拉取依赖Rocky Linux 9 使用 dnf 作为包管理器第一步先安装 dnf 插件然后添加 Docker 官方仓库。官方建议的仓库地址是 CentOS 的 docker-ce.repo因为 Rocky 与 CentOS 的兼容性让它可以直接复用。sudo dnf install -y dnf-plugins-core sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo如果打包机访问官方源比较慢可以换成国内镜像站这里以清华镜像源为例sudo sed -i s|https://download.docker.com|https://mirrors.tuna.tsinghua.edu.cn/docker-ce|g /etc/yum.repos.d/docker-ce.repo sudo dnf makecache源配好之后开始下载依赖。这里我用的是 dnf download 加 --alldeps 参数这个参数会自动把所选包的整棵依赖树全部拉下来。命令如下mkdir -p /data/docker-rpms sudo dnf download docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin --destdir/data/docker-rpms --alldeps这里要重点说明一下为什么要用 --alldeps。如果不加这个参数dnf download 只会下载你指定的几个主包而 Docker 安装依赖的 container-selinux、fuse-overlayfs、slirp4netns 等辅助包默认不会跟下来到内网装的时候就会各种报缺依赖。我实际打包时80 多个 rpm 文件、约 150MB 的内容绝大多数都是辅助依赖。下载完成后看一眼目录确认关键包都在ls /data/docker-rpms | wc -l ls /data/docker-rpms | grep container-selinux2.3 转成本地仓库createrepo 生成元数据rpm 包只是散落一堆文件dnf 安装时需要仓库元数据repodata来解析依赖关系。这一步使用 createrepo 工具Rocky 9 自带的版本是 createrepo_c功能完全够用。sudo dnf install -y createrepo_c sudo mkdir -p /data/docker-rpms sudo createrepo /data/docker-rpms执行完之后目录下会多出一个 repodata 文件夹里面是仓库的元数据索引。有了这个索引内网机器才能通过本地 repo 文件正常安装。createrepo 会生成 repomd.xml、primary.xml.gz 等文件如果后续往目录里追加了新的 rpm 包需要重新执行 createrepo --update让元数据和实际文件保持一致。2.4 打包传输压缩、校验一步不能少依赖目录准备好之后压缩打包。这里建议用 tar gzip文件大小可控传输效率也还不错。cd /data tar -czvf docker-offline-rpms.tar.gz docker-rpms md5sum docker-offline-rpms.tar.gz docker-offline-rpms.tar.gz.md5校验这一步非常关键尤其是走 U 盘或移动硬盘拷贝时大文件经过拷贝之后经常出现损坏。到了内网机器解压之前先跑一遍 md5sum -c 校验确认文件完整再解压避免装到一半发现某个 rpm 包损坏排查起来极费时间。3. 离线环境安装 Docker步骤拆解与参数背后逻辑3.1 环境检查动手之前先摸清家底拿到目标机之后不要急着解压安装。先花几分钟做一轮系统体检把几个关键项记录清楚后面的安装可以少走很多弯路。检查项命令理想状态内核版本uname -r5.14 以上系统版本cat /etc/os-release与打包机一致CPU 架构arch与打包机一致SELinux 状态getenforceEnforcing 或 Permissive 都可防火墙状态systemctl status firewalld确认是否开启按需放行端口数据盘挂载df -h/data 目录有足够的可用空间系统时间timedatectl与真实时间偏差不要太大每一项检查都不是走形式。内核版本决定 overlay2 存储驱动能不能正常工作系统版本和架构一致性直接关系到 rpm 依赖包能否顺利装上SELinux 和防火墙则决定了启动容器后会不会被系统安全策略拦截。这些环境因素在联网环境下可能不敏感在离线环境下任何一个异常都会放大成安装失败。3.2 推荐安装方式本地 repo dnf 自动装依赖解压依赖包到目标机然后创建本地 repo 文件sudo mkdir -p /data/docker-rpms sudo tar -xzvf docker-offline-rpms.tar.gz -C /data/创建文件 /etc/yum.repos.d/docker-local.repo[docker-local] nameDocker Offline Repo baseurlfile:///data/docker-rpms enabled1 gpgcheck0这里 gpgcheck0 值得展开说。离线环境下没有 Docker 官方源的 GPG 公钥如果保留 gpgcheck1dnf 会尝试导入公钥本地仓库里又没有安装过程会直接报错。所以离线场景一般选择关闭 GPG 检查。如果对安全要求很高可以在打包机上提前导出 Docker 源 GPG 公钥拷到内网后导入系统再开启 gpgcheck但多数内网环境中这一步是非必需的。刷新缓存后开始安装sudo dnf clean all sudo dnf makecache sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugindnf 会从本地仓库读取 repodata自动解析依赖树把前面下载好的 rpm 包按依赖顺序依次安装。这个方式最稳因为它和在线安装行为完全一致只是数据源从远程换成了本地。3.3 备选安装方式rpm 本地安装如果目标机上已经有基础依赖并且不想创建 repo 文件也可以直接用 rpm 方式安装cd /data/docker-rpms sudo rpm -ivh *.rpm但 rpm -ivh 有个副作用它不会自动调整安装顺序如果某个包依赖关系依赖的包排在后面就会报错。更稳妥的替代方案是 dnf localinstallsudo dnf localinstall /data/docker-rpms/*.rpm -ydnf localinstall 会先扫描所有 rpm 包的依赖关系再统一排序安装比单独跑 rpm -ivh 省心得多。不过这个方式有个限制它要求本地已经存在大部分基础依赖如果目标机是最小化安装缺的依赖多了还是建议走 repo 方式。3.4 daemon.json 配置一次配到位Docker 引擎装好之后先别急着启动把配置文件写好再启动避免后面反复改配置、重启服务。配置文件路径是 /etc/docker/daemon.json。{ data-root: /data/docker, exec-opts: [native.cgroupdriversystemd], log-driver: json-file, log-opts: { max-size: 100m, max-file: 3 }, storage-driver: overlay2, ipv6: false, registry-mirrors: [], insecure-registries: [] }每个参数背后都有实际考量。data-root 把 Docker 的数据目录放到大数据盘上避免容器、镜像把系统盘写满导致系统不可用。exec-opts 设置 cgroupdriver 为 systemd这和 Rocky 9 默认的 cgroup v2 管理方式一致后续如果想在上面部署 Kubernetes 或者其他编排工具不会出现 cgroup 驱动不一致的问题。日志限制参数 max-size 和 max-file 是防止容器日志无限增长把磁盘占满这个配置在生产环境里几乎是必备的。storage-driver 指定 overlay2这是现代内核下 Docker 的默认存储方案性能和可靠性都有保障。registry-mirrors 和 insecure-registries 在离线环境下要特别小心。很多教程默认让用户填阿里云加速器但在离线内网里填公网加速器地址毫无意义反而会让 docker pull 在尝试访问外网时长时间卡住最后超时报错。离线环境正确做法是先留空等私有仓库搭好之后再填入内部地址。3.5 启动服务并验证配置写好后启动服务并设置开机自启sudo systemctl daemon-reload sudo systemctl enable --now docker sudo systemctl status docker查看 Docker 引擎是否正常工作用 docker info 检查版本、存储驱动、cgroup 驱动等关键信息docker info离线环境下不要直接跑 docker run hello-world这个明明是在线教程的常用验证命令但 hello-world 镜像需要联网拉取离线环境下会一直卡住然后报错。正确的验证方式是先检查镜像列表确认本地有能用的镜像然后运行一个本地已有镜像的容器来验证容器运行时是否正常。4. 离线镜像管理没有网也能让容器跑起来4.1 从 docker save 到 docker load镜像平滑迁移Docker 引擎装好只是第一步真正跑业务还需要镜像。离线环境导入镜像的标准做法是 docker save 导出、docker load 导入。在联网打包机上提前把所有需要的镜像拉取好docker pull nginx:1.27-alpine docker pull redis:7-alpine docker pull registry:2然后用 docker save 把镜像导出成 tar 包docker save -o nginx-1.27-alpine.tar nginx:1.27-alpine docker save -o redis-7-alpine.tar redis:7-alpine有多个镜像时可以合并成一个 tar 包docker save -o app-images.tar nginx:1.27-alpine redis:7-alpine把 tar 包拷进内网目标机后执行 docker load 导入docker load -i app-images.tar docker imagesdocker save/load 适合镜像数量少、目标机数量也少的场景。如果内网有几十台机器都要用同样的镜像逐台 load 太费劲就需要搭私有仓库。4.2 搭建内网私有镜像仓库让镜像分发起飞私有镜像仓库的优势是一次导入全内网共享。由于现在内网还没有 Docker 镜像可用需要先把 registry 镜像本身通过 save/load 的方式传进去# 联网机器上 docker pull registry:2 docker save -o registry2.tar registry:2 # 拷贝到内网目标机 docker load -i registry2.tar然后启动 registry 容器sudo mkdir -p /data/registry docker run -d -p 5000:5000 --restartalways --name registry \ -v /data/registry:/var/lib/registry \ registry:2内网其他机器如果要从这台 registry 拉取镜像需要修改 daemon.json把 registry 地址加入 insecure-registries。因为很多内网环境不会给 registry 配置 HTTPS 证书默认的 HTTPS 访问会失败加入 insecure-registries 后Docker daemon 会允许使用 HTTP 访问该地址{ insecure-registries: [registry-offline.internal:5000] }修改完重启 Dockersudo systemctl restart docker之后给镜像打上内部仓库的 tag推送上去其他机器就能拉取了docker tag nginx:1.27-alpine registry-offline.internal:5000/library/nginx:1.27-alpine docker push registry-offline.internal:5000/library/nginx:1.27-alpine # 其他机器 docker pull registry-offline.internal:5000/library/nginx:1.27-alpineregistry 目录里也可以按项目分 namespace方便后续做镜像版本管理。这里提醒一下insecure-registries 这种方式只建议在受控内网使用如果是开放网络一定要给 registry 配 HTTPS 证书。4.3 离线环境下 docker compose 的正确玩法实际部署业务很少只跑一个容器基本都是用 docker compose 编排。上一节已经安装了 docker-compose-plugin也就是 compose v2 插件直接以 docker compose 子命令形式调用。离线环境用 compose 最容易踩的坑是镜像拉取。compose 文件里声明的镜像如果本地没有它就会尝试去远程拉取离线环境没有远程可拉于是卡在 pull 阶段。解决办法有两种要么先把 compose 里涉及的所有镜像全部 load 到本地要么在服务配置里加 pull_policy: never明确告诉 compose 只用本地镜像不要尝试联网拉取。一个典型的离线 compose 文件如下services: nginx: image: nginx:1.27-alpine pull_policy: never ports: - 8080:80 redis: image: redis:7-alpine pull_policy: never ports: - 6379:6379先把两个镜像 load 到本地然后执行docker compose up -ddocker compose up -d 会检查依赖、创建网络、启动容器全部基于本地镜像完成不会再碰网络。这也侧面验证了离线环境下镜像准备清单很重要业务需要什么镜像提前想清楚全部收进 tar 包里。5. 离线部署常见问题与排查实录5.1 docker0 网桥创建失败NetworkManager 与内核参数的坑Rocky Linux 9 上第一次启动 Docker 后如果发现容器网络不通docker0 网桥没有正常创建多半和两个因素有关。第一内核模块 br_netfilter 没加载导致与网桥相关的 iptables 规则不生效。第二NetworkManager 可能干扰 Docker 创建虚拟网卡。排查方法ip link show docker0 sysctl net.bridge.bridge-nf-call-iptables如果输出显示 bridge-nf-call-iptables 为 0说明模块没加载需要手动加载并写入系统配置sudo modprobe br_netfilter sudo sysctl -w net.bridge.bridge-nf-call-iptables1 echo net.bridge.bridge-nf-call-iptables1 /etc/sysctl.confNetworkManager 的干扰通常表现为 docker0 网桥创建后又被 NetworkManager 回收掉可以创建一个配置文件来排除 Docker 相关的网卡# /etc/NetworkManager/conf.d/docker.conf [keyfile] unmanaged-devicesinterface-name:docker*然后重启 NetworkManager 和 Dockersudo systemctl restart NetworkManager sudo systemctl restart docker这个问题在 RHEL/CentOS 系环境里非常典型建议装完 Docker 之后第一时间检查 docker0 是否正常别等容器启动才暴露。5.2 SELinux 拦截导致容器无法正常挂载Rocky Linux 9 默认开启 SELinux如果保持 Enforcing 模式容器挂载主机目录时经常被 SELinux 策略拦截。典型报错是 docker run 时出现 Permission denied查看 /var/log/audit/audit.log 会看到大量 AVC denial 记录。处理方式取决于环境对安全策略的要求。测试环境可以直接临时调整 SELinux 为 Permissivesudo setenforce 0但生产环境不推荐关闭 SELinux更规范的做法是给挂载目录打上容器文件标签。docker run 挂载数据卷时可以加 :z 或 :Z 后缀让 Docker 自动调整 SELinux 标签docker run -v /data:/data:z nginx:1.27-alpine或者手动给目录设置容器文件上下文sudo chcon -Rt container_file_t /data我自己的项目经验是内网交付环境能保持 Enforcing 就保持 Enforcing能通过标签解决的绝不整体关闭 SELinux不然等保或者安全扫描的时候一个风险项挂在动态调整安全策略上后面处理起来很尴尬。5.3 cgroup 驱动不一致引发连锁问题Rocky Linux 9 默认使用的是 cgroup v2 体系而 Docker 早期版本的默认 cgroupdriver 是 cgroupfs这就会造成整个容器运行时与系统实际 cgroup 管理方式不一致。单独跑 Docker 可能看不出问题一旦后续在同一台机器上部署 Kubernetes、Podman 或者其他需要与 cgroup 打交道的组件就会爆出驱动不匹配的错误。解决方式就是前面 daemon.json 中已经配置过的 exec-optsexec-opts: [native.cgroupdriversystemd]docker info 输出里确认 Cgroup Driver 显示的是 systemd而不是 cgroupfs就正常了。这个配置建议在第一次启动 Docker 前就写好别等服务跑起来再改。5.4 离线环境 docker pull 卡住超时离线环境里如果镜像没准备好就执行 docker pull容器引擎会尝试与远程仓库建立连接网络不通的情况下表现为长时间 hang 住最后报 Error response from daemon 超时错误。这个问题的根因就是 daemon.json 里 registry-mirrors 的配置如果填了公网加速地址离线环境就会反复去连那些外网 IP。正确的预防手段是离线环境 registry-mirrors 保持空数组涉及私有仓库时把地址写进 insecure-registries。实际工作中镜像管理原则很简单宁可多 load 一个不需要的镜像也别让运行中的服务启动时再去远程拉镜像。所有镜像提前就位容器启动就不依赖网络。5.5 本地仓库 repodata 损坏导致安装失败安装阶段有可能会遇到 dnf 报 repomd.xml parser error 或者 repodata 相关错误。这种情况多发生在依赖包目录有变动但 repodata 没有刷新或者压缩包传输过程中部分元数据文件损坏。解决办法是重新生成仓库元数据再清理 dnf 缓存重试cd /data/docker-rpms sudo createrepo --update . sudo dnf clean all sudo dnf makecache如果这一步还不行大概率是解压后的 rpm 文件有问题回到第 2 章的 md5sum 校验环节重新比对传输前后的文件哈希找到损坏的包重新拷贝。6. 一些实操经验收尾这套离线部署方案我前后交付过不少次重复踩坑之后总结下来最核心的认知是离线部署的难度不在“装包”这一个动作而在“链路完整性”。依赖包全不全、版本齐不齐、镜像有没有提前准备好、配置有没有一次写对任何一个环节断层都会让内网环境里的调试成本成倍放大。我个人现在做离线交付习惯在打包机上先把整个安装流程跑一遍包括 docker load、docker run、compose up全部验证通过之后再做 tar 打包。这条验证链路能提前消掉大部分问题而不是把问题带到内网现场才暴露。另外建议所有 rpm 包和镜像 tar 包都固化版本号交付文档里明确记录这样后续内网扩容或者升级才能有据可查。如果你也在做类似的内网部署欢迎交流实际操作中遇到的其他坑。离线环境的经验积累大多都是一个个问题“喂”出来的多分享一次别人就能少踩一个坑。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价