首页 / 资讯中心 / 文章详情

Docker离线安装完整指南:断网环境下的镜像部署与运维实战

Docker离线安装完整指南:断网环境下的镜像部署与运维实战 ★ FEATURED ARTICLE
做运维和交付实施的朋友大概率都遇到过这种场面服务器就位、操作系统装好网络策略只开了内网互访公网出口全部封死然后业务方说帮我把 Docker 环境弄起来。Docker 离线安装这件事听起来就是拷几个包过去解压装上但真在断网环境里从头到尾做一遍你会发现坑全藏在细节里——依赖缺一个、selinux 策略没弄好、镜像传过去变成 None:None随便哪一个都能让你在现场耗掉半天。这篇文章就把我多次执行离线部署的完整思路、操作命令和踩坑记录整理出来。不管你是刚接触 Docker 的运维新人还是经常给客户做系统交付的实施工程师照着这个流程走一遍基本能把断网装 Docker这件事从玄学变成流程化作业。1. 为什么需要 Docker 离线安装1.1 哪些场景必须走离线安装很多人觉得 Docker 装上很容易一条官方安装脚本就搞定了。但那是在能访问公网的机器上。现实里的隔离环境远比想象中多银行、医院、政务内网的核心业务区防火墙策略严格只允许特定端口进出甚至完全禁止外网访问。工业企业的生产控制网络和办公网物理隔离现场没有任何公网出口。给客户做项目交付客户机房出于安全要求不接受设备直连公网下载任何软件。临时搭建的演示环境、灾备演练环境外网不稳定不能把交付过程赌在网速上。这些环境的共同点是你不能依赖curl ... | sh这种在线安装方式必须提前准备好一套完整的、自包含的安装介质到现场搬过去。1.2 离线安装的整体思路与方案选型离线安装 Docker 的核心思路就一句话在一台能上网的机器上把所有需要的软件包和镜像提前准备好再整体搬到目标机器上安装配置。具体有三种主流方案方案原理优点缺点发行版包安装RPM/deb用 yum/apt 的离线下载功能把 Docker 及依赖包拉下来目标机本地安装和系统集成度好systemd 单元、用户组、selinux 规则基本不用手工管依赖列表复杂牵一发动全身静态二进制包安装下载官方提供的 tgz 包解压后复制到 /usr/bin和环境解耦一套包在大多数 Linux 发行版上都能用需要自己写 systemd 服务文件、自己建用户组、自己处理安全策略源码编译在有网机器编译后搬运可控程度最高编译依赖链长没人愿意为装个 Docker 编译一遍我的建议是如果是 CentOS/RHEL 系的服务器优先走 RPM 包方案如果是 Ubuntu/Debian 系走 deb 包方案如果目标机器系统很杂或者你压根不确定对方系统版本直接用静态二进制包方案。静态二进制包方案最省心也是我实际交付时用得最多的。无论哪种方案完整流程都逃不开这五步准备安装介质 → 传输到目标机 → 安装并配置服务 → 导入业务镜像 → 验证可用性。下面分步拆开讲。2. 第一步在联网机器上准备安装包2.1 先确认目标机的系统与架构准备工作不是上来就下载而是先搞清楚目标机器到底是个什么环境。这一步做错了后面全白搭。重点确认三个信息操作系统发行版和版本cat /etc/os-release看 ID 和 VERSION_ID。CPU 架构uname -m常见的是x86_64和aarch64ARM 服务器。内核版本uname -r老内核可能影响存储驱动选择。举个例子CentOS 7.9 和 CentOS 8.5 的 RPM 包不能混装Ubuntu 18.04 和 Ubuntu 20.04 的 deb 包依赖也不一样。ARM 架构和 x86 架构的包更是完全不同。你必须在联网下载之前就锁定目标机的版本组合否则拷过去的包装不上现场又上不了网只能干瞪眼。2.2 下载 RPM 包时如何把依赖一次拉齐CentOS/RHEL 系离线装 Docker最常用的工具是yumdownloader它能把指定的包和依赖一起下载到本地目录。前提是联网机器上先配置好 Docker 官方软件源。配置源这一步在联网机器上执行sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo然后创建一个目录把 Docker 相关的 RPM 全部拉下来mkdir -p /opt/docker-offline/rpm cd /opt/docker-offline/rpm yumdownloader --resolve docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin--resolve是这条命令的关键它会把依赖一并拉取下来。下载完成后一定要检查一下目录内容确认依赖是否齐全。常见的依赖包包括container-selinux、containerd.io、docker-ce-cli、libcgroup等。这里有个容易踩的坑yumdownloader拉下来的依赖基于联网机器自身的软件源版本。如果联网机器和目标机器的系统大版本一致但小版本差很多个别依赖版本可能对不上。保险做法是在联网机器上把系统更新到和目标机相近的补丁水平或者下载时手动指定与目标机一致的基础软件源。没有把握的话多用几分钟把 rpm 包列表发给现场同事核对一遍。2.3 deb 系系统的离线包收集方式Ubuntu/Debian 系相对简单用apt-get的--download-only参数就行sudo apt-get update mkdir -p /opt/docker-offline/deb cd /opt/docker-offline/deb apt-get download docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugindocker-ce安装时需要把对应系统的 apt 源配置好否则apt-get download找不到软件包。Debian 系也一样。不过apt-get download只下载你指定的包不会自动带依赖。更完整的做法是安装apt-rdepends工具递归列出依赖树再逐个apt-get download。嫌麻烦的话可以先用apt-get install --download-only -y docker-ce安装一遍不真正安装只是下载所有依赖会自动落到/var/cache/apt/archives目录里直接拷贝这个目录下的 deb 文件即可。2.4 另一种更清爽的选择静态二进制包静态二进制包方案是我个人最推荐的尤其是当你面对的机器系统五花八门时。Docker 官方为每个版本提供了解压即用的 tgz 包里面包含dockerd、docker、containerd、runc、ctr等核心组件。你只需要到 Docker 官网的下载页面选择对应的版本和架构下载即可。比如 x86_64 架构的 24.0.9 版本文件名类似docker-24.0.9.tgz。ARM 机器要选 aarch64 对应的包。这个方案的优点很明显不依赖目标机的软件源RHEL、Debian、openEuler 都能用。不涉及复杂的 rpm/deb 依赖关系拷过去解压就能跑。版本固定不会因为源更新被动升级。缺点就是需要自己补一部分系统集成工作主要是 systemd 服务文件、docker 用户组、daemon 配置。这些手工活其实不复杂后面第三节我给出可以直接抄的模板。2.5 传输文件时的压缩与校验安装介质准备好之后传输前建议做两件事压缩和校验。cd /opt/docker-offline tar czf docker-offline-24.0.9.tar.gz rpm/ # deb 或二进制包同理 sha256sum docker-offline-24.0.9.tar.gz checksum.txt到目标机上先校验完整性再解压sha256sum -c checksum.txt别嫌麻烦。离线环境里最怕的就是介质拷贝过程中文件损坏尤其是用 U 盘从一台机器倒到另一台机器U 盘质量参差不齐文件损坏了装到一半报错排查起来非常痛苦。提前做一次校验能省掉大量现场排障时间。3. 第二步目标机器上的安装与配置实战3.1 RPM 包安装与依赖处理把打包好的 rpm 目录传到目标机上例如放到/tmp/docker-rpm然后进入目录执行本地安装cd /tmp/docker-rpm yum localinstall -y ./*.rpmyum localinstall会读取当前目录下所有 rpm 包并自动解决依赖关系。如果手头机器上连 yum 都不可用用 rpm 直接装也行但要控制好顺序rpm -ivh containerd.io-*.rpm rpm -ivh docker-ce-cli-*.rpm rpm -ivh docker-ce-*.rpm一般先装 containerd再装 cli最后装 docker-ce 本体因为 docker-ce 依赖前两者。我见过有人图省事执行rpm -ivh *.rpm --nodeps确实装上了但以后运行中各种莫名其妙的小毛病会冒出来等保有操作别碰。装完以后先别急着高兴验证一下systemctl status docker docker version正常情况下docker version会分别显示 Client 和 Server 的信息。如果 Server 部分报错说明 daemon 没起来继续看后面第五节的排查。3.2 静态二进制包的部署与 systemd 配置二进制包部署流程如下tar xzf docker-24.0.9.tgz cp docker/* /usr/bin/然后把 Docker 的 systemd 服务文件写到/etc/systemd/system/docker.service。这是我最常用的模板[Unit] DescriptionDocker Application Container Engine Documentationhttps://docs.docker.com Afternetwork-online.target firewalld.service Wantsnetwork-online.target [Service] Typenotify ExecStart/usr/bin/dockerd ExecReload/bin/kill -s HUP $MAINPID TimeoutSec0 RestartSec2 Restartalways LimitNOFILEinfinity LimitNPROCinfinity LimitCOREinfinity TasksMaxinfinity [Install] WantedBymulti-user.target注意Typenotify不能改dockerd 启动完成会通过 sd_notify 通知 systemd如果改成Typesimplesystemd 不会正确等待 Docker 就绪。接着创建 docker 用户组避免每次操作都要 sudogroupadd -f docker systemctl daemon-reload systemctl enable --now docker到这里 Docker 本体已经能跑了。二进制方案里还有一个坑默认没有/etc/docker目录和 daemon.jsonDocker 会用默认配置启动。如果你后续要给 k8s 用或者要接私有仓库最好先建好目录、写好配置再启动服务不然改动配置后还要重启一次。3.3 基础设施配置网络、存储与开机自启无论用哪种方案安装装完都要检查三件事服务开机自启、防火墙策略、存储位置。开机自启用一条命令搞定systemctl enable docker如果系统装了 firewalld建议提前放行 Docker 要用的端口。比如私有仓库要监听 5000 端口业务容器要映射 8080 端口都要在防火墙里放行否则容器起来了外部也访问不到firewall-cmd --permanent --add-port5000/tcp firewall-cmd --permanent --add-port8080/tcp firewall-cmd --reload存储位置也值得提前规划。Docker 默认把所有数据放在/var/lib/docker如果根分区不够大后面镜像和容器一多直接把磁盘撑爆。建议单独划一个数据盘挂载到/data然后把>{ data-root: /data/docker, exec-opts: [native.cgroupdriversystemd], log-driver: json-file, log-opts: { max-size: 20m, max-file: 5 }, insecure-registries: [10.20.1.8:5000], storage-driver: overlay2, iptables: true, ip-masq: true, max-concurrent-downloads: 5 }逐个说下关键参数>docker pull myapp:v1.2.3 docker save -o myapp-v1.2.3.tar myapp:v1.2.3也可以压缩一下减小体积docker save myapp:v1.2.3 | gzip myapp-v1.2.3.tar.gz到目标机上docker load -i myapp-v1.2.3.tar.gz # 或者 docker load myapp-v1.2.3.tar.gz这里必须强调一个原则只用 save/load不要用 export/import。docker export会把容器导出成扁平的文件系统丢失镜像的层信息、历史记录和 CMD/ENTRYPOINT 等元数据import 之后得到的镜像根本不能保证和原来行为一致。save/load 才是完整搬运镜像的正确姿势。如果镜像很多可以写个一行命令批量导出docker save $(docker images --format {{.Repository}}:{{.Tag}}) -o all-images.tar但要注意文件名别带斜杠和空格否则 save 命令解析会有问题。批量导出后目标机上 load 同样一条命令搞定。4.2 离线环境下搭建私有镜像仓库如果只是三五台机器save/load 足够了。但如果离线环境里有几十台机器都要拉同一个镜像每台都 load 一遍不仅效率低还容易漏版本。这时候应该在内网搭一个私有镜像仓库所有机器从仓库拉取镜像。离线搭私有仓库的思路很朴素先在联网机器上把 registry 镜像拉下来save 成 tar传到内网一台指定机器上 load然后启动 registry 容器。# 联网机器 docker pull 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然后所有客户端机器在 daemon.json 里声明这个地址为 insecure registryHTTP 内网仓库重启 Docker就能正常 push/pull 了docker pull 10.20.1.8:5000/myapp:v1.2.3如果团队规模大、权限要求细可以用 Harbor 替代 registry。Harbor 提供 Web UI、项目隔离、镜像签名校验等功能也支持离线安装包但组件多、占资源多小环境用不上。4.3 镜像导入后的常见坑镜像 load 完docker images显示一堆none是正常现象吗分情况。如果直接docker load一个 tar镜像名和 tag 都会正常显示。但如果有人给你的是一个 export 产生的 tar或者 save 的时候没有带 tagload 进来就会出现none:none。处理方法是重新打 tagdocker tag 镜像ID myapp:v1.2.3还有一种情况save 时镜像名带了完整仓库地址比如10.20.1.8:5000/myapp:v1.2.3load 出来后名字也会带完整地址使用时要保持一致否则本地运行会试图去仓库拉取。另外离线机的时间一定要校准证书校验依赖系统时间。如果目标机时间差了几年load 镜像或者启动容器时经常报 x509 证书相关的错误很多人第一反应是证书坏了其实是时间不对。5. 常见问题排查实录5.1 缺依赖和包冲突RPM 安装最典型的报错是Error: Package: docker-ce-24.0.9-1.el7.x86_64 (docker-ce) Requires: container-selinux 2.107意思是缺少container-selinux包或者版本太低。解决办法是把依赖包下载齐全后一起安装。container-selinux在 CentOS 7 的 base 源里没有需要从 EPEL 源拉这也是 yumdownloader 时最容易被漏掉的依赖建议手动确认目录里有没有这个包。如果目标机上已经装过老版本 Docker卸载时会遇到file conflictsfile /usr/bin/docker from install of docker-ce-24.0.9 conflicts with file from package docker-1.13.1先把系统自带的旧 docker 卸掉再装新的。卸的时候用yum remove docker docker-client docker-common不要直接删文件否则残留的 systemd 配置会干扰新版本。5.2 服务起不来selinux、iptables 与 network 的纠缠启动 Docker 后systemctl status docker显示 failed先看日志journalctl -u docker --no-pager | tail -50几个高频原因第一静态二进制包方案下最常见的直接复制二进制文件没有安装 systemd 服务文件执行systemctl start docker报 Unit docker.service not found。这种情况要么手动补 unit 文件要么回到 RPM 方案重新装。第二selinux 导致的权限问题。日志里出现operation not permitted或failed to load SELinux policy时说明 Docker 和 selinux 策略冲突。离线环境图省事的话将 selinux 调到 permissive 模式setenforce 0 sed -i s/^SELINUXenforcing/SELINUXpermissive/ /etc/selinux/config注意setenforce 0只是临时生效重启就回去了一定要同时改配置文件。当然更规范的做法是安装对应的 container-selinux 策略包但内网环境下载策略包同样是件麻烦事偶尔 permissive 是务实的折中。第三iptables 规则和网桥冲突。日志里出现Failed to setup IP masquerading或者docker0: iptables: No chain/target/match by that name多半是 docker0 网桥的 iptables 规则被 network 服务重启清掉了。检查一下/sbin/sysctl net.bridge.bridge-nf-call-iptables如果输出是 0改成 1echo net.bridge.bridge-nf-call-iptables 1 /etc/sysctl.conf sysctl -p5.3 cgroup 驱动不一致给 Kubernetes 用的 Docker启动时 kubelet 经常报failed to run Kubelet: failed to validate kubelet flags: cgroup driver cgroupfs is different from systemd翻译过来就是kubelet 用 systemd 驱动Docker 还在用 cgroupfs两者不一致Kubernetes 拒绝启动。解决办法就是前面 daemon.json 里的exec-opts配置项把 Docker 的 cgroup 驱动改成 systemd改完必须重启 Docker。如果你不确定当前 Docker 用的什么驱动执行docker info | grep cgroup查看。5.4 迁移到不同系统版本时的兼容性问题最后说一个很多人容易忽略的兼容性问题。同一个 Docker 版本在 CentOS 7 和 CentOS 8 上表现可能完全不同因为系统自带的 iptables、nftables、systemd 版本都不一样。静态二进制包方案虽然可以跨发行版用但不代表能跨内核版本用。老内核机器比如内核 3.10 的 CentOS 7上overlay2 存储驱动可能因为文件系统不支持 d_type 而启动失败。日志里会有failed to mount overlay: operation not permitted这时要么把数据目录所在分区格式化为 ext4 或 xfsxfs 需要开启 ftype1要么在 daemon.json 里把storage-driver临时改为vfs。vfs 性能差、空间占用巨大只能作为临时救急方案。还有一个经验新机器内核 5.x 上跑老版本 Docker比如 18.09可能正常但跑在带 cgroup v2 的系统上就会出问题。RHEL 8 / Ubuntu 20.04 及以后的系统默认启用 cgroup v2建议至少用 Docker 20.10 以上版本或者干脆跟随官方支持矩阵选版本。6. 离线安装的工程化与维护建议6.1 版本矩阵管理离线安装最怕哪个版本都行的心态。我强烈建议每个交付项目建立一份版本记录表至少包含这些字段环境操作系统内核版本架构Docker 版本containerd 版本镜像列表客户A生产CentOS 7.93.10.0-1160x86_6424.0.91.7.xmyapp:v1.2.3, nginx:1.24客户B生产Ubuntu 22.045.15.xx86_6426.1.41.7.xmyapp:v1.3.0为什么要记录这个因为离线环境改版本成本极高。在线环境一条yum update就能升级 Docker离线环境得重新准备一套完整介质重新走一遍传输流程。有了版本矩阵至少能明确告诉客户你当前这套环境是哪个版本组合对应离线包在哪后续维护不会抓瞎。6.2 一键安装脚本的编写思路既然离线安装是重复劳动就值得把它脚本化。推荐把安装流程封装成一个 install.sh丢到目标机器上执行一遍就能完成装包、写配置、启动服务、验证四步。二进制包方案的一键脚本骨架如下#!/bin/bash set -e DOCKER_TGZdocker-24.0.9.tgz DATA_ROOT/data/docker # 1. 解压并安装二进制 tar xzf $DOCKER_TGZ -C /tmp cp /tmp/docker/* /usr/bin/ rm -rf /tmp/docker # 2. 创建用户组和配置目录 groupadd -f docker mkdir -p /etc/docker $DATA_ROOT # 3. 写 daemon.json cat /etc/docker/daemon.json EOF { data-root: $DATA_ROOT, log-driver: json-file, log-opts: { max-size: 20m, max-file: 5 } } EOF # 4. 写 systemd 服务文件 cat /etc/systemd/system/docker.service EOF [Unit] DescriptionDocker Application Container Engine Afternetwork-online.target Wantsnetwork-online.target [Service] Typenotify ExecStart/usr/bin/dockerd Restartalways RestartSec2 LimitNOFILEinfinity LimitNPROCinfinity LimitCOREinfinity TasksMaxinfinity [Install] WantedBymulti-user.target EOF # 5. 启动并验证 systemctl daemon-reload systemctl enable --now docker docker version --format {{.Server.Version}} || exit 1 echo Docker installed successfully把脚本和 tgz 包放在同一个目录压缩后整个分发。到了现场就两条命令tar xzf docker-offline-bundle.tar.gz cd docker-offline-bundle bash install.sh脚本化之后离线部署从原来的半天工作量压缩到十分钟而且不容易漏步骤。6.3 离线环境下的日常维护离线环境的 Docker 装完只是开始后续维护同样要提前规划。镜像库的更新建议走增量同步的方式。每次业务发新版镜像在有网机器上拉新镜像、save、压缩传到离线环境load 后重新 push 到内网 registry。这个过程最好固定成一条指令链而不是靠手记。日志和监控方面刚才反复强调的 log 轮转别省。离线环境出了问题不能像在线环境那样随手拉一个诊断容器所以日常要确保系统监控和日志收集组件提前部署好不要等磁盘满了才想起来。另外提一个很多人不知道的细节如果离线内网有一台机器能通过企业代理访问外网注意这属于正常的企业网络代理不是任何其他工具你也可以让 Docker daemon 走代理拉镜像但这属于联网方案的范畴离线环境的规矩还是介质齐备、按流程走。6.4 最后再分享一个小技巧我踩过几次坑之后学乖了离线交付安装包时永远保留一份安装包清单里面写清楚每个文件的用途、版本、对应的目标机系统。这个清单用纯文本写在压缩包外层就行。有一次我在现场装到一半发现 rpm 包缺依赖翻遍 U 盘才想起来 EPEL 的包没拉上如果有清单提前检查就不会到现场才发现了。装完 Docker 顺手验证反射docker run --rm hello-world不一定能跑因为 hello-world 镜像也得提前拉所以验证环境时用业务镜像或者提前准备好的busybox镜像代替。这个细节看着小但能避免在客户现场出现装好了却验证不了的尴尬。离线安装的本质是把在线环境下那些被自动化工具藏起来的依赖关系、配置细节、版本匹配问题提前在准备阶段一次性解决掉。流程捋顺了、介质备齐了、脚本写好了断网环境反而比在线环境更可控——因为你安装的每一个环节都是确定性的。
阅读完成 · 觉得有帮助?
咨询建站