资讯详情

Docker 19.03.9离线部署全攻略:从RPM依赖到镜像分发避坑

📅 2026/10/7 21:46:31 | 华诺云谱 👁 阅读
Docker 19.03.9离线部署全攻略:从RPM依赖到镜像分发避坑
简介docker19.03.9离线部署工具包面向内网、无外网环境下的容器运行环境搭建适合运维工程师、系统管理员以及需要在隔离网络中快速交付Docker服务的技术团队。资源基于官方19.03.9稳定版本将Docker引擎、systemd服务管理、环境配置文件与自动化安装脚本整合在一起能有效规避手动逐台下载依赖、配置源和启动服务的重复劳动显著提升批量部署效率。压缩包共含4个文件主要包括.tgz程序包、.service系统服务文件、.sh安装脚本与.tpl配置模板分别承担组件分发、服务托管、执行安装和运行参数生成等职责结构紧凑、职责清晰便于根据业务需要调整。整个资源包约57.69MB可直接拷贝到离线主机中使用。目前已有9041人学习或下载方案成熟度在实际场景中得到较多验证。通过这套离线工具使用者可以拿到现成的Docker二进制、systemd配置、部署脚本和模板文件了解从解压、注册服务、生成环境变量到启动容器的完整链路尤其适合企业内部进行Docker版本固定、标准化交付和无人值守安装。1. docker19.03.9 离线部署把外网依赖提前锁死在安装包里内网机器上装 Docker 19.03.9最磨人的不是命令记不住而是机器根本没有外网。yum 源不可用、依赖包缺一堆、镜像没地方拉这三件事足以让半天排期变成一整周。所谓“docker19.03.9离线部署工具”并不是某个开源软件的名字而是一整套可复制的落地套路一台有网机器把 RPM 依赖抓全、打成 tar把业务镜像导出把 daemon.json 和 systemd 配置写死目标机解包后跑一个脚本就能完成装包、起服、导镜像。适合做内网交付、机房隔离环境、离线建设项目的运维工程师照着重现少交点学费。2. 离线安装包怎么凑rpm 依赖树与版本锁定的取舍2.1 为什么锁 19.03.9生产环境验证过编排模板都按它来选 19.03.9 不是因为它最新而是因为它是 2019-2020 年那波生产环境用得最稳的版本。当时 Kubernetes 1.18 的容器运行时兼容矩阵里Docker 19.03 是主力推荐版本大量业务镜像、docker-compose 模板、CI 流水线都是在这个版本上调试通过的。现在很多内网项目要求“指定版本部署”手里拿到的材料往往就是 19.03.9 的包和配置这也是它至今还在离线交付脚本里频繁出现的原因。版本锁定还有一层现实考虑离线环境升级成本极高。生产上跑着的容器编排和监控脚本可能依赖某个版本的 Docker API 行为。19.03.9 之后 Docker 在 cgroup v2、非 root 运行等方向大步调整内网机器内核和 systemd 版本未必跟得上。与其在生产环境赌新版兼容性不如把验证过的 19.03.9 和配套的 containerd.io 锁死在安装包里让所有目标机器长成一个样。2.2 在有网机器上抓全依赖yumdownloader 与 --resolve离线包的核心工作是在一台有外网、yum 源完整的机器上把 docker-ce、docker-ce-cli、containerd.io 以及它们的所有依赖全部拉下来。常见做法是用 yumdownloader它对 CentOS 7 这类老系统的依赖解析比手动一个个 rpm 靠谱得多# 先装 yum-utils它提供 yumdownloader 命令 yum install -y yum-utils # 指定版本号下载--resolve 会把依赖树一起拉下来 mkdir -p /root/docker19.03.9-pkgs yumdownloader --resolve --destdir/root/docker19.03.9-pkgs \ docker-ce-19.03.9 \ docker-ce-cli-19.03.9 \ containerd.io这里的几个参数值得说清楚。--resolve是关键中的关键它会分析这三个 rpm 的依赖关系把 libseccomp、libltdl、policycoreutils-python 这类底层库一并下载目标机装的时候才不会卡在“缺少依赖”上。--destdir指定输出目录方便后面直接打包。docker-ce-19.03.9这种写法是让 yum 自己匹配对应的小版本不需要去记完整的 release 号。如果目标机需要配合 SELinux 策略使用docker-ce 的依赖里还会出现 container-selinux 这类与安全策略相关的 rpm。抓完包后建议看一眼目录内容确认 containerd.io 和 docker-ce-cli 都在里面。缺少 cli 包的症状很隐蔽docker 命令找不到但 dockerd 进程其实已经起来了排查时会绕一大圈。# 查看抓包结果确认核心组件齐全 ls -lh /root/docker19.03.9-pkgs/ # 在目标机上模拟安装一遍校验依赖是否齐全 rpm -ivh /root/docker19.03.9-pkgs/*.rpm --testrpm --test只是模拟安装不写入系统。如果这一步报缺依赖说明有网机器上抓包时漏了东西回上一步补。这里我建议不要在目标机上用rpm --nodeps强装虽然当时能装上但运行时缺动态库会让容器起不来而且报错信息跟真实原因离得很远。2.3 目标机的安装顺序能用 yum localinstall 就别碰 rpm --nodeps把打包好的 rpm 目录整体拷到目标机后安装顺序是一个值得较真的点。containerd.io 是最底层docker-ce-cli 依赖它docker-ce 依赖 cli顺序反了必然报错。两个可用的方法# 方法一yum localinstall 自动解析本地依赖 cd /root/docker19.03.9-pkgs yum localinstall -y ./*.rpm # 方法二按依赖顺序手动 rpm -Uvh rpm -Uvh containerd.io-*.rpm rpm -Uvh docker-ce-cli-*.rpm rpm -Uvh docker-ce-*.rpmyum localinstall的好处是它会读取当前目录下所有 rpm 的依赖信息自动决定安装顺序并在缺包时给出明确提示。缺点是如果这台机器本身 yum 源配置有问题它可能会尝试访问网络源然后超时离线环境里建议先配置一个空的本地源或者直接断开网络等超时结束。方法二更可控适合批量部署场景。把三个 rpm 拆成三条命令哪一步失败看得清清楚楚。安装完成后不要急着起服务先确认可执行文件存在which dockerd docker containerd还有一种常见做法是直接用官方静态二进制包解压后把 docker、dockerd、containerd 拷到 /usr/bin再手写一个 systemd 服务。这种方式不碰 rpm 依赖适合精简系统但后续升级和卸载全靠自己维护systemd service 文件也要自己写踩坑空间不小。我一般只在对 rpm 生态完全不熟悉的目标机上才用常规 CentOS 7 环境还是走 rpm 路线更省心。3. 一键部署脚本与 daemon.json 的三个必调参数3.1 部署脚本骨架从装包到起服务一条命令跑完离线部署工具里最值钱的部分是一份经过验证的部署脚本。它做的事不多装 rpm、写配置、起服务、导镜像、校验结果。但每一步的失败处理都要提前想好。下面这份脚本是我在离线项目里常用的骨架#!/usr/bin/env bash # docker19.03.9 离线部署脚本 set -Eeuo pipefail PKG_DIR/opt/docker-offline/packages IMG_TAR/opt/docker-offline/images.tar DATA_ROOT/data/docker for cmd in tar rpm systemctl; do command -v $cmd /dev/null || { echo 缺少命令: $cmd; exit 1; } done echo [1/5] 安装 rpm 包 cd $PKG_DIR rpm -Uvh containerd.io-*.rpm rpm -Uvh docker-ce-cli-*.rpm rpm -Uvh docker-ce-*.rpm echo [2/5] 写入 daemon.json mkdir -p /etc/docker cat /etc/docker/daemon.json EOF { data-root: /data/docker, log-driver: json-file, log-opts: {max-size: 50m, max-file: 3}, live-restore: true } EOF echo [3/5] 启动 docker systemctl enable --now docker systemctl is-active docker || { journalctl -u docker -n 50; exit 1; } echo [4/5] 导入镜像 if [ -f $IMG_TAR ]; then docker load -i $IMG_TAR fi echo [5/5] 校验 docker version --format server: {{.Server.Version}} docker info --format root: {{.DockerRootDir}} driver: {{.Driver}}set -Eeuo pipefail这行不是装饰。-e让脚本在 rpm 安装失败时立刻退出-u能在变量名拼错时直接报错pipefail保证管道中任何一环失败都会被捕捉到。离线批量部署最怕的就是脚本“半成功半失败”地往下走最后每台机器状态都不一样。rpm 安装部分没有在一行命令里用通配符装全部包而是拆成三条原因是 containerd.io、cli、docker-ce 三者有严格依赖顺序合并成一条虽然也能装但失败时定位不到具体是哪一层出的问题。daemon.json 写入用的是 heredoc 方式比 echo 拼接字符串靠谱得多JSON 文件里的引号和逗号不用转义。3.2 daemon.json 参数逐项拆解哪些必调、哪些容易翻车daemon.json 是 Docker 守护进程的配置文件离线场景下我建议至少调下面几个参数。先看完整示例{ data-root: /data/docker, exec-opts: [native.cgroupdriversystemd], log-driver: json-file, log-opts: {max-size: 50m, max-file: 3}, storage-driver: overlay2, live-restore: true, insecure-registries: [192.168.10.20:5000] }每个字段的作用和适用场景我整理了一张参数表参数离线环境建议选型理由># 数据目录检查与创建 if [ ! -d $DATA_ROOT ]; then mkdir -p $DATA_ROOT fi df -h $DATA_ROOT | awk NR2{if ($4 ~ /G/) {size$4} print $4}如果部署时这台机器已经有正在运行的旧 Docker改># 在有网机器上把多镜像打成一个 tar docker save -o /data/offline-images.tar \ nginx:1.19.10 \ app/web:v2.0.1 # 在目标机上导入 docker load -i /data/offline-images.tar两个细节值得特别说明。第一docker save 支持在一个命令里导出多个镜像tar 文件会更大但导入效率高目标机只需执行一次 load。第二save 的对象必须写仓库名和 tag即nginx:1.19.10而不是镜像 ID。用镜像 ID 导出的 tar 在 load 之后REPOSITORY 和 TAG 列会显示none看起来像没导入成功。这个坑我在第 5 章详细拆。离线镜像 tar 的命名规范我建议在团队里统一比如images-20240618-app-v2.tar让人一眼看出内容物和制作时间避免拿错包。导入完成后立刻用docker images | grep app/web确认 tag 在列表里这一步应该写进部署脚本作为 load 的强制校验。4.2 内网 registry 自建registry:2 启动、push 与证书如果离线环境不只是单台机器而是几十台机器都要拉同一个镜像docker save/load 逐台拷包就不划算了。常见做法是自建一个内网 registry。最简单的方案是用 registry:2 镜像跑一个容器# 有网机器上先准备好 registry:2 镜像随离线包一起带进去 docker save -o registry2.tar registry:2 # 内网机器上导入并启动 docker load -i registry2.tar docker run -d --name registry --restartalways \ -p 5000:5000 \ -v /data/registry:/var/lib/registry \ registry:2启动之后把要分发的镜像打上内网地址的 tag 再 pushdocker tag app/web:v2.0.1 192.168.10.20:5000/app/web:v2.0.1 docker push 192.168.10.20:5000/app/web:v2.0.1这里有个很容易踩的配置点。如果内网 registry 没有配 HTTPS 证书docker push 会报server gives HTTP response to HTTPS client原因是 Docker 客户端默认用 HTTPS 访问 registry。解决办法两个选一个要么在 daemon.json 的insecure-registries里加上这个地址并重启 docker要么把自签名证书放到/etc/docker/certs.d/192.168.10.20:5000/ca.crt后重启。测试环境我通常用 insecure-registries生产环境建议走证书内网虽然不怕被监听但证书方式能避开很多客户端兼容问题。registry:2 适合镜像不多、不需要权限管理的场景。如果离线环境要支撑几十个项目和团队Harbor 更合适但它自身依赖 PostgreSQL、Redis 等组件离线部署时要额外准备一批镜像和配置复杂度高出不少。先跑通 registry:2 再评估要不要上 Harbor是最稳的推进路径。5. 离线部署避坑依赖错位、镜像掉 tag 与内核存储驱动5.1 libseccomp 过旧hello-world 都跑不起来的典型信号遇到过最典型的一个现象docker 装好了、服务也 active执行docker run hello-world却直接报错错误里能看到standard_init_linux.go和operation not permitted。第一次遇到这种报错时我也懵了以为是权限问题把 SELinux 关了还是不行。真实原因是 runc 需要系统 libseccomp 库提供较新的 seccomp 过滤能力。CentOS 7 早期最小化安装的机器上libseccomp 版本可能停留在 2.3.1而 Docker 19.03.9 绑定的 runc 在某些系统调用上需要更新的 seccomp 规则。检查方法rpm -q libseccomp如果版本低于 2.3.2就在有网机器上抓一个 libseccomp 的升级包带进内网rpm -Uvh升级后重启 docker。解决这个问题的顺序是先查 libseccomp再查内核版本不要一上来就重装 docker。这个坑在 CentOS 7.4 以下版本尤其常见7.9 自带版本一般没问题。5.2 docker load 后全是 save 的时候姿势就错了这个坑是离线交付里最隐蔽的目标机执行docker load -i images.tar没报错但docker images里看不到业务镜像只有一排none:的行。很多人在这一步怀疑镜像包损坏重新打包、重新拷贝折腾一下午。实际上问题不在 load而在 save 那一步。如果导出时用的是docker save 镜像IDtar 里只包含镜像层和 manifest不包含仓库名和 tag 信息load 进来自然全是none。另一个常见误操作是把docker export产生的容器文件系统 tar 当镜像 tar 拿去 load那样直接报no manifest found错误。解决办法写在导出规范里save 必须写仓库名:tag并且 load 后立刻执行docker images | grep 期望名做断言校验。部署脚本里我会加一行# 镜像导入校验期望的 tag 不存在则脚本失败退出 docker images | grep -q app/web:v2.0.1 || { echo 镜像 tag 未导入; exit 1; }5.3 overlay2 起不来XFS 的 ftype 与内核模块另一类高频故障是 dockerd 起不来journalctl 里能看到 overlay 挂载失败或operation not permitted。这类问题多半出在存储驱动与文件系统特性的匹配上。CentOS 7 上如果数据盘用 XFS 格式化时的ftype参数不是 1overlay2 驱动无法正常工作因为 overlay2 依赖目录项的 d_type 能力。检查当前分区是否支持xfs_info /data | grep ftype如果输出ftype0说明这块分区的 overlay2 路被堵死了。两个处理办法一是把数据备份后重新格式化分区mkfs.xfs -f -n ftype1二是在 daemon.json 里把存储驱动降级为overlay或vfs。注意 vfs 没有写时复制能力性能差只能临时验证用。这个坑要在进入交付现场之前确认因为重新格式化属于不可逆操作提前发现还能调整部署方案等机器上线再处理就很被动了。5.4 daemon.json 改完 daemon 起不来语法与数据目录两层坑离线部署脚本最常被改的就是 daemon.json改出问题的概率也最高。第一种情况是 JSON 语法错误。daemon.json 是严格要求合法 JSON 格式的文件多一个逗号、少一个引号dockerd 都会拒绝启动。19.03 版本对配置解析是硬校验只要语法错服务直接起不来。这个问题在交付前就能拦截方法是在有网机器上先校验# 校验 JSON 合法性 python -m json.tool /etc/docker/daemon.json第二种情况是前面提过的>systemctl stop docker rsync -a /var/lib/docker/ /data/docker/ systemctl start docker这三行命令的顺序不能乱。先复制再改配置启动后数据无缝衔接改完配置再复制新目录里没数据容器列表自然为空。6. 部署完别急着走校验三连与排查顺序6.1 校验三连version、info、images离线部署脚本跑完不是终点机器要真正能跑容器才算交付完成。我在确认一台机器时习惯固定在三个命令它们看的信息正好覆盖“服务在不在、配置对不对、镜像全不全”三个层面# 1. 服务端版本 docker version --format server: {{.Server.Version}} # 2. 关键配置 docker info --format root: {{.DockerRootDir}} driver: {{.Driver}} # 3. 镜像清单 docker images --format {{.Repository}}:{{.Tag}} | grep -E nginx|app/web如果这三点都正常再用一个业务镜像做冒烟测试比如docker run --rm app/web:v2.0.1 /bin/true或者带健康检查命令的容器。注意离线环境不要指望 hello-world它必须提前和其他镜像一起打入镜像包否则还是会卡在“镜像不存在”上。6.2 出问题先看哪三处部署遇到问题时我的排查顺序是固定的。第一看journalctl -u docker -n 200 | grep -i error先确认 daemon 层面有没有硬错误第二看docker info确认存储驱动、root 目录和容器运行时是否就位第三才用docker inspect 容器名看具体容器的 State 和 Logs定位是启动失败还是运行崩溃。这个顺序能避免在一个镜像的日志里翻半天结果发现问题其实是存储驱动没起来。这套脚本和校验命令我习惯放在离线包根目录下命名为check-docker.sh和部署脚本放一起。目标机器跑完部署后顺手执行一遍输出在终端里一目了然下次批量交付时也能通过输出快速对比每台机器的状态少记不少笔记。经历过一次镜像 tag 全部变none的现场后我把“load 后校验 tag”写进了所有部署模板从那以后同类问题再没找上门。希望这份从装包到排障的思路帮到你至少让你在点击执行按钮之前心里有底。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑