ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

内网离线安装Docker与Docker Compose实战指南

内网离线安装Docker与Docker Compose实战指南

简介:这份资源面向需要在无外网环境下部署容器服务的内网运维与开发人员,提供一套完整的离线安装方案,解决服务器无法访问公网、依赖缺失导致 Docker 与 Docker Compose 装不上的问题。压缩包共 22 个文件,约 120.91MB,以 20 个 rpm 依赖包为主,涵盖 docker-ce、docker-ce-cli、containerd.io、container-selinux、fuse-overlayfs、slirp4netns 等核心组件及 python、policycoreutils 等系统依赖,另含 1 个 docker-compose-linux-x86_64 二进制文件和 1 个 install.sh 安装脚本,覆盖从依赖补齐到服务启动的完整链路。已有 4706 人学习下载,适合 CentOS 7 环境下快速落地容器化部署。读者可直接借助脚本一键完成安装,省去逐条排查依赖的繁琐过程,同时通过 rpm 清单理解各组件作用,为后续离线升级与故障排查提供参考。

1. 内网离线安装 Docker 与 Docker Compose:从一台能联网的机器开始

很多做交付的工程师都遇到过这种场景:客户机房完全隔离,没有外网,连 yum 源都是内网自建的,但项目要求必须上容器化部署。这时候你手里只有一台能联网的笔记本和一块 U 盘,要在两小时内把 Docker 和 Docker Compose 装进那台 CentOS 7 或 Ubuntu 20.04 的裸机里。听起来简单,但真动手就会发现,离线安装 Docker 远不止下载一个 rpm 包那么直接——依赖链、systemd 单元、存储驱动、iptables 规则,任何一个环节缺东西都会让你卡在docker info报错上。这篇笔记就是把我自己在多个隔离环境里反复踩过的流程拆开,从联网机准备离线包开始,到目标机验证容器网络结束,每一步都给出可复制的命令和参数说明。适合需要在内网做容器化交付的运维和后端工程师,也适合第一次接触离线部署、不想在机房里对着黑屏干瞪眼的新手。

2. 离线包怎么备:联网机上的依赖收集与版本锁定

2.1 先确定目标机的发行版和内核版本

离线安装最怕的就是“包下错了”。同样是 CentOS 7,内核 3.10.0-1127 和 3.10.0-1160 对container-selinux的版本要求可能不同;Ubuntu 20.04 和 22.04 的libseccomp2版本也不一样。所以第一步不是下载,而是登到目标机上把底细摸清楚。

# 在目标机(内网机)上执行,记录以下信息 cat /etc/os-release # 确认发行版和版本号 uname -r # 确认内核版本 getenforce # 确认 SELinux 状态(Enforcing/Permissive/Disabled) systemctl status firewalld # 确认防火墙是否运行

这几条命令的输出直接决定后面要下哪些包。比如 SELinux 是 Enforcing 的话,container-selinux必须装,而且版本要匹配;如果目标机是 Ubuntu,那就不需要container-selinux,但要额外注意iptables和libseccomp2的版本。我一般会把输出截图或复制到文本里,后面选包时对着看。

2.2 用 yumdownloader 或 apt-get download 拉全依赖

CentOS 系用yumdownloader最省事,它能自动解析依赖并下载到本地目录。但默认只下主包,要加--resolve才会把依赖一起拉下来。

# 在联网的 CentOS 机器上执行 mkdir -p /tmp/docker-offline/packages yum install -y yum-utils # 下载 docker-ce、docker-ce-cli、containerd.io 及其全部依赖 yumdownloader --resolve --destdir=/tmp/docker-offline/packages \ docker-ce docker-ce-cli containerd.io # 如果目标机需要 docker-compose 插件(v2),也一并下载 yumdownloader --resolve --destdir=/tmp/docker-offline/packages \ docker-compose-plugin

--resolve是关键参数,它会递归解析所有依赖包并下载到同一目录。--destdir指定输出路径,避免污染当前目录。执行完后用ls看一眼包的数量,通常 CentOS 7 上 docker-ce 全套下来有 20 到 30 个 rpm,包括container-selinux、libcgroup、fuse-overlayfs等。如果只有三四个包,那说明依赖没拉全,后面装到一半肯定报错。

Ubuntu 系用apt-get download配合apt-rdepends来递归下载:

# 在联网的 Ubuntu 机器上执行 mkdir -p /tmp/docker-offline/debs cd /tmp/docker-offline/debs # 先添加 Docker 官方源(如果还没加) # 然后下载 docker-ce 及其依赖 apt-get download docker-ce docker-ce-cli containerd.io docker-compose-plugin # 用 apt-rdepends 列出所有依赖并逐个下载 apt-rdepends docker-ce | grep -v "^ " | sort -u | \ xargs -I {} apt-get download {} 2>/dev/null

apt-rdepends会输出依赖树,grep -v "^ "过滤掉缩进行,sort -u去重,最后用xargs批量下载。这个过程可能会因为某些包已经安装而报“已是最新版本”,忽略即可,只要.deb文件出现在目录里就行。

2.3 把二进制文件和 systemd 单元也带上

有些内网机不允许装 rpm 或 deb,只能走二进制。这时候需要额外下载 Docker 的静态二进制包和 Compose 的独立二进制。

# 下载 Docker 静态二进制(以 x86_64 为例) cd /tmp/docker-offline curl -LO https://download.docker.com/linux/static/stable/x86_64/docker-24.0.7.tgz # 下载 Docker Compose 独立二进制 curl -L https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-linux-x86_64 \ -o docker-compose chmod +x docker-compose

静态二进制包解压后是docker、dockerd、containerd等可执行文件,直接放到/usr/bin就能用,但需要手动写 systemd 单元文件。Compose 的独立二进制更简单,扔到/usr/local/bin/docker-compose并加执行权限即可。我一般会把这两样都带上,万一 rpm 装不上还有退路。

最后把整个/tmp/docker-offline目录打包,用 U 盘或内网文件服务器传到目标机。

tar czvf docker-offline.tar.gz -C /tmp docker-offline

3. 目标机落地:rpm/deb 安装与 systemd 接管

3.1 CentOS 系用 rpm 本地安装并处理依赖顺序

把离线包传到目标机后,先解压,然后进到 packages 目录。不要用rpm -ivh *.rpm一把梭,因为 rpm 不解析依赖顺序,容易报“依赖失败”。正确做法是用yum localinstall或dnf localinstall,它会自动按依赖关系排序。

# 在目标机 CentOS 上执行 tar xzvf docker-offline.tar.gz cd docker-offline/packages # 用 yum 本地安装,自动处理依赖 yum localinstall -y *.rpm

如果目标机没有yum(比如最小化安装),可以用rpm -ivh配合--nodeps逐个装,但顺序很重要:先装container-selinux、libcgroup、fuse-overlayfs这些底层依赖,再装containerd.io,最后装docker-ce和docker-ce-cli。装完后验证:

rpm -qa | grep -E "docker|containerd" systemctl enable --now docker docker version

systemctl enable --now docker会启动 Docker 并设置开机自启。如果docker version只显示 Client 不显示 Server,说明dockerd没起来,用journalctl -u docker -n 50看日志。

3.2 Ubuntu 系用 dpkg 安装并补依赖

Ubuntu 上用dpkg -i安装 deb 包,但同样有依赖顺序问题。更稳妥的方式是用apt install ./xxx.deb,它会自动处理依赖。

# 在目标机 Ubuntu 上执行 tar xzvf docker-offline.tar.gz cd docker-offline/debs # 用 apt 本地安装,自动解析依赖 apt install -y ./*.deb

如果apt install ./*.deb报依赖缺失,可以先用dpkg -i强制装,再用apt-get install -f修复。但离线环境下apt-get install -f可能因为找不到源而失败,所以最好在联网机上就把依赖拉全。

装完后同样启用并验证:

systemctl enable --now docker docker version docker compose version # 如果装了 compose 插件

3.3 二进制方式:手动写 systemd 单元

如果目标机不允许装包,只能用二进制。解压静态包后,把可执行文件复制到/usr/bin,然后创建 systemd 单元。

# 解压静态二进制 tar xzvf docker-24.0.7.tgz cp docker/* /usr/bin/ # 创建 dockerd 的 systemd 单元 cat > /etc/systemd/system/docker.service <<'EOF' [Unit] Description=Docker Application Container Engine Documentation=https://docs.docker.com After=network-online.target firewalld.service Wants=network-online.target [Service] Type=notify ExecStart=/usr/bin/dockerd ExecReload=/bin/kill -s HUP $MAINPID LimitNOFILE=infinity LimitNPROC=infinity TimeoutStartSec=0 Delegate=yes KillMode=process Restart=on-failure StartLimitBurst=3 StartLimitInterval=60s [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable --now docker

这个单元文件里Delegate=yes是让 Docker 能管理自己的 cgroup,KillMode=process保证只杀 dockerd 不杀容器。LimitNOFILE和LimitNPROC设成 infinity 是为了避免高并发下文件描述符不够。写完daemon-reload再启动,否则 systemd 不认识新单元。

Compose 独立二进制更简单:

cp docker-compose /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose docker-compose version

4. 避坑排查:离线安装 Docker 最常见的五个翻车点

4.1 坑一:container-selinux 版本不匹配导致 dockerd 起不来

现象:systemctl start docker后docker info报Cannot connect to the Docker daemon,journalctl -u docker里看到selinux相关错误。

原因:CentOS 7 的container-selinux版本必须和docker-ce的版本对应。比如 docker-ce 24.0.7 要求container-selinux >= 2.9,但如果你下的是 2.119 而目标机 SELinux 策略是旧版,就会冲突。

解决:在联网机上用yumdownloader --resolve时,确保container-selinux是从同一源拉下来的。如果已经装错,先rpm -e --nodeps container-selinux卸载,再装正确的版本。实在不行就把 SELinux 临时设为 Permissive:setenforce 0,然后改/etc/selinux/config永久生效。但生产环境不建议直接关 SELinux,最好还是把版本对齐。

4.2 坑二:iptables 规则被清空导致容器无法上网

现象:容器能启动,但ping外网不通,docker run --rm alpine ping 8.8.8.8超时。

原因:目标机上可能装了 firewalld 或 iptables-services,重启后规则被清空,Docker 自动添加的 NAT 规则丢失。

解决:先确认iptables -t nat -L -n里有没有DOCKER链。如果没有,重启 Docker:systemctl restart docker。如果重启后仍然没有,检查firewalld是否在 Docker 启动后重新加载了规则。常见做法是把 Docker 加到 firewalld 的信任区:firewall-cmd --permanent --zone=trusted --add-interface=docker0,然后firewall-cmd --reload。或者干脆禁用 firewalld,用 iptables 手动管理。

4.3 坑三:存储驱动 overlay2 不可用

现象:docker info里 Storage Driver 显示vfs而不是overlay2,容器启动极慢,磁盘占用巨大。

原因:CentOS 7 默认内核可能不支持 overlay2,或者/var/lib/docker所在文件系统不是xfs且没有ftype=1。

解决:先lsmod | grep overlay确认模块加载。如果没有,modprobe overlay。然后检查/etc/docker/daemon.json,显式指定存储驱动:

{ "storage-driver": "overlay2", "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" } }

写完重启 Docker。如果 overlay2 仍然不可用,用xfs_info /var/lib/docker看ftype是否为 1。如果是 0,只能重新格式化文件系统或改用vfs(性能差,不推荐)。

4.4 坑四:docker-compose 插件和独立二进制冲突

现象:docker compose version和docker-compose version都能用,但版本不一致,写 compose 文件时语法报错。

原因:同时装了docker-compose-plugin(v2 插件)和独立二进制docker-compose(可能是 v1),两者命令格式有差异。

解决:统一用 v2。卸载独立二进制:rm /usr/local/bin/docker-compose,只保留插件。或者反过来,如果目标机不支持插件,就删掉插件包,只用独立二进制。检查docker compose version输出,确保是 v2.x。compose 文件里version字段在 v2 里已经废弃,直接写services就行。

4.5 坑五:离线包传输过程中损坏

现象:rpm -ivh报Header V3 RSA/SHA256 Signature, key ID ... NOKEY或tar解压报unexpected EOF。

原因:U 盘文件系统不支持大文件,或者传输过程中断导致包不完整。

解决:在联网机上生成校验和:sha256sum docker-offline.tar.gz > docker-offline.sha256,传到目标机后先sha256sum -c docker-offline.sha256验证。如果校验失败,重新传。U 盘尽量用exFAT或NTFS,避免FAT32的 4GB 限制。如果包太大,可以分卷压缩:tar czvf - docker-offline | split -b 2G - docker-offline.tar.gz.,到目标机再cat docker-offline.tar.gz.* | tar xzv。

5. 验证与进阶:从 hello-world 到私有仓库离线拉取

装完只是第一步,真正要确认的是容器网络、存储和 Compose 编排都能在内网跑通。我一般会按下面这个顺序做验证,每一步都对应一个具体的失败模式。

先跑一个最简容器,确认基本功能:

docker run --rm hello-world

如果这步就失败,多半是镜像拉不下来——离线环境没有外网,hello-world镜像需要提前docker save成 tar 包带进来。在联网机上:

docker pull hello-world docker save hello-world -o hello-world.tar

到目标机:

docker load -i hello-world.tar docker run --rm hello-world

docker save把镜像导出成 tar,docker load导入。注意save的是镜像,不是容器。如果要带多个镜像,可以docker save -o all-images.tar img1 img2 img3,一次导出。

接下来验证网络和端口映射:

docker run -d --name test-nginx -p 8080:80 nginx:alpine curl http://localhost:8080

如果curl不通,先docker logs test-nginx看容器日志,再iptables -t nat -L -n看 NAT 规则。常见问题是目标机防火墙没放行 8080 端口,或者net.ipv4.ip_forward没开。检查:

sysctl net.ipv4.ip_forward # 如果为 0,临时开启 sysctl -w net.ipv4.ip_forward=1 # 永久生效 echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf

Compose 的验证稍微复杂一点,因为涉及多容器编排。写一个最简单的docker-compose.yml:

services: web: image: nginx:alpine ports: - "8081:80" redis: image: redis:alpine

然后docker compose up -d,再docker compose ps看两个容器是否都 running。如果 redis 起不来,多半是镜像没导入。用docker compose config检查 compose 文件语法,v2 里不需要version字段,写了会警告但不报错。

最后一步是私有仓库的离线拉取。如果内网有 Harbor 或 registry,需要把镜像推上去再拉下来。没有仓库的话,就用docker save/docker load在机器间倒腾。我习惯在联网机上把所有需要的镜像打成一个包:

docker pull nginx:alpine redis:alpine mysql:8.0 docker save -o offline-images.tar nginx:alpine redis:alpine mysql:8.0

到目标机docker load -i offline-images.tar,然后docker images确认三个镜像都在。这样即使没有外网,也能把整套服务跑起来。

从那以后我每次做离线交付,都会在联网机上先跑一遍完整的yumdownloader --resolve和docker save,把包和镜像校验和一起写进一个manifest.txt,到现场先sha256sum -c再动手。这个习惯帮我省了至少三次在机房里满头大汗找依赖的尴尬。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进