简介:面向需要在内网或无外网环境完成Docker容器引擎部署的运维人员,这份离线工具包以docker19.03.9稳定版为核心,有效规避在线安装时网络源不可用、依赖缺失等常见问题。压缩包内含四个文件,包括容器引擎镜像压缩包、环境配置模板、系统服务管理单元以及一键执行脚本,覆盖了从解压镜像到注册服务、再到按需调整配置的完整部署流程。整体体积约五十七兆字节,轻量便携,便于在离线网络中拷贝传输和批量推送;整个部署过程无需额外下载依赖,特别适用于离线机房、内网隔离等场景。已有超过九千人学习下载,部署方案经过实际环境验证,能够显著减少人工配置步骤与排错时间。模板中还预留了仓库地址、数据目录等参数,用户可快速修改,适配不同业务场景。
1. docker19.03.9离线部署工具:内网环境最省心的Docker安装方式
一台刚从机房上架的CentOS 7.9服务器,贴着“禁止外联”的标签,要求你三小时之内把 Docker 19.03.9 跑起来,后面还有二十台等着批量铺。这种场景下,docker19.03.9离线部署工具就是解决“没有外网,却要装指定版本Docker”的标准动作:在一台有网的机器上把 Docker 19.03.9 及其全部依赖打成一个自包含目录,拷到内网目标机,用一条命令完成安装启动。它适合运维、实施工程师,也适合在政企内网、物理隔离机房做交付的人。只要内核不低于3.10、架构一致,这套方案基本能做到“打包一次,批量复用”。
2. 离线部署前先想清楚:rpm包、静态二进制还是自建Yum源?
2.1 三种离线部署方案的适用边界和坑
先泼一盆冷水:离线部署 Docker 19.03.9 不是“把安装包拷过去就行”,难在依赖关系的收集。Docker 19.03.9 的安装包依赖一堆系统库和容器运行时,不同方案的依赖处理方式完全不同,选错方案后多半要从头再来。
第一种是抓取 rpm 依赖包方案。在有网机器上配置 Docker 官方仓库,用 yumdownloader 把 docker-ce 主包和依赖包全部拉到本地,然后拷到目标机器用 rpm 或本地 yum 安装。这个方案的优点是安装过程贴近在线体验,系统能自动处理依赖顺序;缺点是必须提前考虑到目标系统的版本和架构,不能只拷主包。
第二种是静态二进制方案。从官方 Release 渠道下载 docker-19.03.9.tgz,解压得到 dockerd、docker、containerd、runc 等可执行文件,直接放到 /usr/local/bin,再手工写一个 systemd 服务。它的优点是包大小小、不依赖 rpm 体系,适合没有 yum 的精简系统;缺点是要自己维护 systemd、启动参数、存储驱动,很多遗漏的坑都是在这里埋下的。
第三种是自建 Yum 源。把 rpm 包上传到内网一台服务器或对象存储,用 createrepo 生成 repodata,然后所有目标机器的 yum 源都指向这个内网地址。这个方案适合几十台、上百台的批量交付,后续再装其他软件也好扩展;但前期要准备一台源服务器,还要考虑 repo 文件分发、GPG 校验、后续版本更新,单机场景没必要搞这么重。
| 方案 | 准备成本 | 依赖处理 | 批量友好度 | 最容易翻车的点 |
|---|---|---|---|---|
| rpm包+依赖目录 | 低 | yum自动 | 中 | 漏依赖、包架构不符 |
| 静态二进制 | 中 | 手工处理 | 低 | systemd/存储配置缺失 |
| 自建Yum源 | 高 | yum自动 | 高 | repo配置错误、元数据过期 |
表格里的三行我都写过。如果你的目标机器是 RHEL/CentOS 系列,第一行通常是性价比最高的;如果系统太精简连 rpm 都缺,才考虑第二行;如果内网机器超过二十台,第三行值得花半天去搭建。
2.2 我为什么建议优先选rpm包+依赖目录
以 CentOS 7.x 为例,docker-ce-19.03.9-3.el7.x86_64.rpm 不是孤立的。它依赖 container-selinux >= 2.107、containerd.io、docker-ce-cli、docker-ce-rootless-extras,往下还依赖 libcgroup、iptables、libtool-ltdl、pigz 等系统包。用在线 yum 安装时,这些依赖会被自动拉取;到了离线环境,它们不会自己冒出来。我见过有人只拷了 docker-ce 过去,结果安装时第一行就报Requires: container-selinux >= 2.107,然后卡在那里。
抓取 rpm 依赖包方案的好处就在这里:yumdownloader 的--resolve选项会帮我们把所有依赖按当前系统解析出来,一次性拉到同一个目录。到了目标机器,只要目录完整,用本地 yum 源就能像在线一样自动安装,几乎不需要人工排序。这也是离线部署工具最常见的实现方式。
还有一层理由:rpm 包安装后,systemd 服务、docker.sock、logrotate 配置、bash 补全都是现成的。相比之下,静态二进制方案要手工补服务文件,还要确认 socket 权限、日志轮转,每个细节都容易漏。所以只要目标系统支持 rpm,我一般优先选这个方向。
2.3 离线部署前的环境采集:内核、系统版本、架构一个不能少
在准备离线包之前,我会先在目标机器上执行几条命令做个环境登记,这些输出决定了你去下载哪些包:
# 在目标机器上执行,记录下来再回有网机器上找包 cat /etc/redhat-release uname -m uname -r getenforce rpm -q iptables libcgroup 2>/dev/null逐条说明:
/etc/redhat-release确认是 CentOS 7 还是 8。7 和 8 的 rpm 包名、依赖关系、甚至仓库地址都不一样,不能通用。uname -m确认架构是 x86_64 还是 aarch64。Docker 官网的 rpm 包按架构分文件,拷错架构的包进去,安装时会出现架构冲突。uname -r看内核版本。Docker 19.03.9 对内核最低要求是 3.10,CentOS 7 的 3.10.0 系列可以跑,但太低会有 overlay2 兼容问题。getenforce查看 SELinux 状态。如果是 Enforcing,那么 container-selinux 这个依赖绝对不能少,否则 Docker 创建容器时会报 Permission denied。rpm -q iptables libcgroup是为了确认目标机器上已有的包版本,避免离线包里重复安装冲突。
环境信息采集完成后,你还需要确认有网机器上安装了 yum-utils、createrepo 等打包工具。这一步常见误用是直接在目标机器执行 yum 安装,结果因为无网失败;正确顺序是先在无网机器上采集,再到有网机器上准备包,最后把包带回。这个过程看起来啰嗦,但能省去后面所有“装到一半缺依赖”的麻烦。
3. 在有网机器上制作docker 19.03.9离线部署包:两条可复现的路径
3.1 路径一:用yumdownloader把依赖rpm包全部拖下来
在有外网权限的机器上,推荐用 docker-ce 官方源。先安装打包工具,再添加 Docker 仓库,最后把 docker19.03.9 和它的依赖全部拉到一个目录。我常用的命令如下:
# 在有网机器上执行,准备 rpm 离线包 sudo yum install -y yum-utils createrepo sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum clean all sudo yum makecache sudo yumdownloader --resolve --destdir=/tmp/docker19039 docker-ce-19.03.9-3.el7.x86_64命令背后的逻辑:yumdownloader 的--resolve会读取当前系统的 yum 依赖关系,把 docker-ce 需要的全部 rpm 下载到--destdir指定目录。这里有一个容易被忽略的细节:下载机器的系统版本和目标机器越接近越好。如果你在一台 CentOS 8 上下载 CentOS 7 的包,依赖解析很可能不对,拉下来的包要么多要么少。
参数说明:docker-ce-19.03.9-3.el7.x86_64是 CentOS 7 x86_64 的完整包名。如果目标系统是 CentOS 8,包名中的el7要换成el8,依赖关系也会变成 containerd 1.2 以上版本,必须重新解析。如果是 aarch64 架构,则用aarch64后缀,且下载机器也必须是 aarch64 系统,否则依赖解析不准确。
下载完成后,不要急着拷贝,先做一个自检:
# 确认所有依赖已经完整,并生成 repodata ls /tmp/docker19039 | grep -E "docker-ce|containerd|container-selinux" sudo createrepo --update /tmp/docker19039ls检查主包是否存在,createrepo生成 repodata 元数据。这一步可以让很多直接rpm -ivh的翻车现场免掉——yum 会按 repodata 正确排序,而 rpm 手动安装顺序一错就会报循环依赖。
另外说一句:如果你下载时发现 yumdownloader 把系统自带的旧版本 docker 依赖也拉进来了,不要慌。只要目标机器的 yum 源只有这个本地目录,它就会在这个目录里解析依赖,不会跑回系统库找。
3.2 路径二:直接下载官方静态二进制并做最小systemd单元
有的内网机器用的是精简 CentOS,连 rpm 包管理都没有完整依赖,甚至 yum 源都不全,这时静二进制方案更直接。从 Docker 官方 Release 渠道下载docker-19.03.9.tgz,解压后结构大致是:
docker/ ├── docker ├── dockerd ├── docker-init ├── docker-proxy ├── containerd ├── containerd-shim ├── ctr └── runc我把这些可执行文件全部放到/usr/local/bin,然后手工创建 systemd 服务。需要指出的是,官方 tgz 包里的 docker 版本和 rpm 包并不完全等同,19.03.9 的静态包需要手动指定 Cgroup 驱动为 systemd,否则容器内存控制会不准。下面是一个能用的最小docker.service:
[Unit] Description=Docker Application Container Engine After=network-online.target firewalld.service containerd.service Wants=network-online.target [Service] Type=notify ExecStart=/usr/local/bin/dockerd ExecReload=/bin/kill -s HUP $MAINPID LimitNOFILE=1048576 LimitNPROC=infinity LimitCORE=infinity TimeoutStartSec=0 Restart=always StartLimitBurst=3 [Install] WantedBy=multi-user.target这个 service 文件有几个关键点:
Type=notify表示 docker daemon 启动完成后会通过 sd_notify 通知 systemd,避免systemctl start卡住不返回。ExecStart明确指向/usr/local/bin/dockerd,因为静态包没有 rpm 安装的/usr/bin/dockerd。LimitNOFILE=1048576要求把文件描述符上限拉高,否则容器一多就会报 “too many open files”。Restart=always保证 Docker 因 OOM 或崩溃被 kill 后能自动拉起,这是生产环境的基本习惯。
创建完 service 文件后,还需要配置/etc/docker/daemon.json,内容会在第四章详细说。静态二进制方案最大的坑在于:很多人解压后只拷贝了 docker 和 dockerd,漏掉了 docker-proxy,导致端口映射时容器起来但外部访问失败。所以建议解压后直接整个目录复制到目标机器,再用ln -sf建立软链接。
3.3 离线包目录结构设计和自检清单
不管用哪条路径,最后离线工具包的结构要清楚。我一般做成这样:
docker19.03.9-offline/ ├── packages/ # 存放所有rpm包或静态二进制的子目录 ├── install.sh # 主安装脚本,自动完成所有步骤 ├── docker.service # systemd单元(静态二进制方案用) ├── daemon.json # 镜像加速、日志、存储配置 ├── images/ # 预导出的镜像tar包 └── README.md # 写明目标系统版本和架构packages目录在 rpm 方案下需要有repodata/子目录,否则无法作为本地 yum 源;在二进制方案下则直接放解压后的 docker 目录。images目录用来存放docker save导出的镜像,内网环境拉不了镜像,这一步必须在有网机器上提前做好。
自检清单就三条:
- 目标机架构和离线包架构一致,用
uname -m对过。 - 所有依赖 rpm 包都在,没有遗漏,用
rpm -Uvh --test提前模拟安装。 - daemon.json 里没有写死内网 DNS 或某台机器的 IP,避免换机器后启动不了。
完整性校验的做法是在有网机器打 tar 包时同时生成 sha256 校验文件,目标机器解压后执行sha256sum -c sha256sum.txt。这一步看起来多余,但能避免因为 scp 中断导致 docker.service 文件半截,systemd 报 parse 错误却找不到原因。
4. 目标机器安装:从拷贝到docker info全绿的完整命令序列
4.1 最小安装命令:一条脚本完成依赖安装与启动
离线包拿到目标机器后,最常见的失败原因是目标机器上没有对应版本的 docker-ce 源。我的做法是先把packages目录拷到/opt/docker19.03.9-offline/packages,然后写入一个本地 repo 文件,让 yum 只从这个离线目录安装:
# 目标机器上执行:配置本地仓库并安装 sudo mkdir -p /opt/docker19039 sudo cp -r packages/ /opt/docker19039/ sudo tee /etc/yum.repos.d/docker-local.repo <<'EOF' [docker-local] name=docker-local baseurl=file:///opt/docker19039/packages enabled=1 gpgcheck=0 EOF sudo yum clean all sudo yum makecache sudo yum install -y docker-ce-19.03.9 sudo systemctl start docker sudo systemctl enable docker这段命令的逻辑是:先用baseurl=file://把离线包目录变成 yum 能识别的本地源,再用yum install docker-ce-19.03.9让 yum 自动解析依赖。yum clean all和yum makecache必须成对出现,否则旧缓存会让 yum 找不到新源里的包。
参数说明:gpgcheck=0是离线环境下的便利解法,前提是离线包来源可控。如果公司安全规范要求开启签名校验,需要在打包时把 GPG 公钥也放入目录,并在 repo 文件中用gpgkey=file:///opt/docker19039/RPM-GPG-KEY-docker指定。我在实际项目里两种都写过,后者在审计时更容易通过。
安装过程中如果看到No more mirrors to try,说明 yum 在本地源里找不到某个依赖。这时不要急着换源,先确认/opt/docker19039/packages目录下是否有repodata子目录。有的同事喜欢用 tar 打包时排除隐藏文件,结果 repodata 没拷全,yum 缓存也重建不了。重新把 repodata 完整拷贝过去,再执行yum clean all && yum makecache就能解决。
4.2 配置镜像加速器、数据目录和日志轮转的常见做法
安装完成后不要立刻投入使用,先配置/etc/docker/daemon.json。Docker 19.03.9 的默认数据目录在/var/lib/docker,日志默认无限增长,这两个在生产环境都是定时炸弹。我的标配:
sudo mkdir -p /etc/docker /data/docker sudo tee /etc/docker/daemon.json <<'EOF' { "data-root": "/data/docker", "exec-root": "/var/run/docker", "log-driver": "json-file", "log-opts": { "max-size": "50m", "max-file": "5" }, "storage-driver": "overlay2", "storage-opts": [ "overlay2.override_kernel_check=true" ] } EOF sudo systemctl restart docker># 第一分钟:确认daemon配置和驱动状态 docker info | grep -E "Cgroup|Storage|Server Version" docker info | grep -A 2 "Overlay" # 第二分钟:启动一个测试容器 docker run --rm busybox:latest sh -c "echo ok && sleep 1" # 第三分钟:验证bridge网络和端口映射 docker network create -d bridge testnet docker run -d --name nginx-test --network testnet -p 8080:80 nginx:1.19-alpine curl -s http://127.0.0.1:8080/ docker network rm testnet
第一分钟的重点是看Cgroup Driver是否显示 systemd。19.03.9 在某些环境下会退化成 cgroupfs,导致容器内存限制不精确,需要通过 daemon.json 的"exec-opts": ["native.cgroupdriver=systemd"]修正。第二分钟用 busybox 跑个小命令,能过说明容器运行时和镜像加载都没问题。第三分钟真正要测的是端口映射链路,因为 curl 通不通取决于 iptables 规则是否正常,这比单纯docker ps有意义得多。
离线环境里没有现成的 nginx 镜像?那就在有网机器上先把镜像 save 下来,放进images目录,再用docker load导入。离线镜像导入命令可以批量做:把images目录下的 tar 包逐一 load,然后 grep 输出中的Loaded image,确认是否带 tag。如果发现有镜像没有 tag,用docker tag补齐后再执行后续编排。这里的验证逻辑是一样的:容器能不能起、端口能不能通、网络能不能用,只看版本号是看不出来的。
5. docker 19.03.9离线部署避坑指南:5个翻车现场和对应解法
5.1 现象:安装时卡在依赖报错libltdl.so.7找不到
我最早做离线包时,在目标机器执行完yum install,再跑docker version直接报error while loading shared libraries: libltdl.so.7。原因是 docker-ce 的 rpm 包依赖 libtool-ltdl 这个系统库,而我的离线包只收集了 docker 主包和它的直接依赖,把这个间接依赖漏了。
原因:yumdownloader 的--resolve在部分 CentOS 源里不会把 libtool-ltdl 这类不在 docker 官方仓库、而在 Base 源里的包拉全,尤其是有网机器已经装过同版本库的时候。
解决:在有网机器上单独把 libtool-ltdl 拉下来加进离线包:
yumdownloader --resolve --destdir=/tmp/docker19039 libtool-ltdl sudo createrepo --update /tmp/docker19039然后把整个 packages 目录重新拷贝到目标机器,再次yum install docker-ce-19.03.9就会自动装上。这个坑提醒我:离线包的依赖检查不能只看 docker-ce 的 Requires 字段,还要用ldd检查 docker 二进制依赖的 .so 文件。
5.2 现象:systemd启动Docker失败,提示iptables chain不存在
目标机器执行systemctl start docker后,报错里出现类似Failed to create docker-up: iptables: No chain/target matching by that name。第一次遇到时我以为是 iptables 服务没启动,反复重启 iptables 也没用。
原因:Docker 19.03.9 启动时会操作DOCKER-USER、FORWARD等链,但它要求的这些链需要由 firewalld 或 iptables 服务先创建。目标机器上 firewalld 虽然 enabled,但内核 nftables 接管了规则集,旧版 iptables 工具读写的是 nft 兼容层,两边链表不同步。
解决:先确认 firewalld 状态,然后按顺序重启:
sudo systemctl restart firewalld sudo systemctl restart docker如果还不行,直接放开 FORWARD 链默认策略:
sudo iptables -P FORWARD ACCEPT不过这个操作在生产环境要先和网络安全负责人确认。更彻底的做法是在 systemd unit 里给 docker 加After=firewalld.service,并让 docker 服务启动前先触发 firewalld 的规则生成。这类问题属于离线部署里的常见玄学,建议看journalctl -u docker -n 50结合排查。
5.3 现象:部署后容器网络不通,ping不通网关
容器能创建,但进入容器后ping 192.168.1.1不通,宿主机的端口映射也无法访问。这条坑在离线环境里特别常见,因为最小化安装的系统默认关闭了 IP 转发。
原因:Docker 的 bridge 网络依赖net.ipv4.ip_forward=1,CentOS 默认值为 0。在线 yum 安装时这个参数通常会被网络服务脚本改掉,离线环境没人管它。
解决:先确认参数,再写入内核参数文件:
sysctl net.ipv4.ip_forward echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf sysctl -p systemctl restart docker另外还要检查br_netfilter模块是否加载,桥接 iptables 规则需要它:
modprobe br_netfilter echo "br_netfilter" >> /etc/modules-load.d/docker.conf这两个检查做完,容器网络基本就通了。如果你的系统开启了 nftables 且 FORWARD 默认 DROP,还要在上游防火墙里放行 docker 网段,这是另一个层面的问题,和 docker 本身无关。
5.4 现象:离线导入的镜像tar包tag丢失
在有网机器上执行docker save -o busybox.tar busybox:latest,到目标机器docker load -i busybox.tar后,docker images里看到的是<none>:<none>。这个问题我在一次交付中碰到,导致后面的 compose 文件全部启动失败。
原因:docker save保留的是镜像元数据里的 tags,但如果你 save 时用的是镜像 ID(如docker save -o busybox.tar 123456789),保存的就是一个无名镜像;或者源镜像本身就没有 tag。另外docker load不会自动把<none>改成名字。
解决:load 完成后人工补 tag:
docker tag $(docker load -i busybox.tar | grep -o "sha256:.*" | cut -d: -f2) busybox:latest但这种写法很绕,不如在 save 时养成习惯:
docker save -o busybox.tar docker.io/library/busybox:latest看到 load 输出提示Loaded image: busybox:latest,就是带了 tag。镜像 tar 包也是离线部署工具的一部分,建议每次打包镜像时用完整的仓库名,不要偷懒用 ID。
5.5 现象:yumdownloader在CentOS 8.x上默认拉取的是podman-docker
CentOS 8 系统自带的软件源里有一个podman-docker,它提供一个基本兼容 docker 命令的工具,让你误以为 docker 装好了。我在做 CentOS 8.5 离线包时,下载命令写得很宽泛,结果目标机器装完跑docker version,CLI 能执行但 daemon 是 podman 套壳,容器运行时路径全都不对。
原因:CentOS 8 的 AppStream 源开启了 module 流,且系统默认启用了container-tools模块,该模块把 docker 指到了 podman-docker。yumdownloader 解析时如果没有指定确切版本,优先命中了模块流里的包。
解决:下载时把版本号写满,并显式指定架构:
yumdownloader --resolve --destdir=/tmp/docker19039 docker-ce-19.03.9-3.el8.x86_64同时在目标机器上禁用无关模块,防止被串包:
sudo dnf module disable -y container-tools离线部署工具在 CentOS 8 上比 CentOS 7 更容易出现“感觉装了但用不了”的问题,根本原因是包管理器的模块流机制太活跃,版本号不写死就会翻车。
6. 落地后的进阶技巧:把离线包做成可复用脚本,顺手验证内核参数
当离线包从“一次性安装”变成“批量交付工具”,install.sh 就不该只是几条命令的拼凑。我会在脚本开头做环境预检,在结尾做运行验证,并让每个关键步骤输出日志。
# install.sh 的关键开头片段 set -e [ -f /etc/redhat-release ] || { echo "仅支持RHEL/CentOS"; exit 1; } [ "$(uname -m)" = "x86_64" ] || { echo "仅支持x86_64"; exit 1; } modprobe overlay || true modprobe br_netfilter || true sysctl -w net.ipv4.ip_forward=1 >/dev/nullmodprobe br_netfilter和sysctl -w net.ipv4.ip_forward=1必须放在 Docker 启动之前,这是 5.3 节网络问题的预防动作。把set -e放在脚本开头后,任何一条安装命令失败都会立即退出,避免装了一半还继续执行后面操作,把现场搞得更乱。
脚本尾部我还习惯加入一段自检:
echo "开始验证 Docker 19.03.9" docker info >/tmp/docker-info.log 2>&1 && echo "daemon ok" || echo "daemon failed"这里的关键不是输出 ok 还是 failed,而是把docker info日志留档。后续如果需要定位问题,/tmp/docker-info.log里已经记录了版本、存储驱动、Cgroup 驱动,不用再重跑命令。
我自己的经验是:版本管理一定要覆盖离线包。把 daemon.json、docker.service 和 install.sh 全部纳入 git,升级到 20.10 或 24.x 时直接 diff 这几个文件,能快速看出新版本有哪些配置项被弃用。有一次我在新项目里直接复制了 19.03.9 的 daemon.json,结果新版 Docker 把exec-root字段改成了containerd命名空间,启动直接失败。从那次以后,我只在目标机器内核和系统版本完全一致时复用旧包,其余情况都重新走一遍打包流程。
希望帮到你。
本文还有配套的精品资源,点击获取