简介:面向中初级运维工程师及容器平台实施人员,这份资源包针对无外网、内网隔离的部署环境,集中解决了离线安装Docker引擎与Docker Compose时依赖包缺失、下载困难的问题。包体包含22个文件,主要由20个RPM格式的系统依赖与Docker组件包、一个install.sh自动安装脚本,以及适配Linux x86_64架构的docker-compose二进制文件组成,整体打包约120.91MB。其中RPM包完整覆盖了Docker守护进程、命令行工具、容器运行时及运行所需的系统基础库,避免离线环境中因缺失关键依赖导致安装中断。安装时只需将压缩包拷贝至目标机器,通过脚本即可自动完成依赖检查、RPM包安装与Compose工具部署,特别适合政务内网、企业私有化、生产隔离区等批量交付场景。目前已有4696人学习使用,这些组件能帮助技术人员在离线环境下快速搭建可用的容器运行环境,提升交付效率并减少排障成本。 前几天接到一个现场任务,机房里有台服务器要部署一套业务系统,环境要求装docker和docker-compose。网线是插着的,但交换机上联口根本没开通外网,yum源ping不通,apt源更不用说。这种场景在政企、工业、金融内网里太常见了,装个docker本来一条命令的事,离线环境下硬是能折腾半天。这篇文章就聊聊我在内网离线安装docker、docker-compose的完整过程,包括准备工作、二进制安装、systemd注册、镜像导出导入、私有仓库配置这些环节,给同样被困在离线环境里的运维和实施同学一个能直接抄的作业。
1. 内网离线安装的整体思路:为什么离线环境这么麻烦
1.1 在线安装和离线安装的本质区别
在线安装docker时,包管理器会自动处理依赖关系。比如在CentOS上用yum install docker-ce,yum会去配置好的软件源里拉取docker主包、containerd、runc等一系列依赖,装完顺手把开机启动也配好。apt也一样,一条命令自动解决依赖链。整个过程对使用者来说就是“黑盒”,你不需要知道docker由哪些组件组成,也不需要关心文件放到了哪里。
离线环境最大的问题就在这里:依赖链断了。你没有外网,yum源不可用,如果手里只有一份docker的rpm包,安装时会直接卡在依赖解析上——要么报缺libltdl,要么报缺container-selinux,一个个补齐的过程能让人怀疑人生。更麻烦的是,不同操作系统的依赖包还不一样,CentOS 7的rpm放到Ubuntu上完全不兼容,同一套离线包在不同版本系统上表现也可能有差异。
所以离线安装不能走“依赖包补齐”这条路,最稳妥的方式是用官方提供的静态二进制包。docker官方把运行所需的所有组件和依赖都编译进了一个压缩包里,解压后拷贝到对应目录就能直接运行,不依赖系统的软件源,也不依赖其他动态库。这就是整个离线安装方案的基石。
1.2 三种离线安装方案对比
我在实际项目里试过三种思路,各有适用场景,这里直接放对比:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| rpm/deb离线包 | 符合系统包管理习惯,能用yum/apt卸载和升级 | 依赖解析麻烦,换台机器可能缺依赖,需要逐个补齐 | 批量交付且操作系统完全一致 |
| 官方二进制tgz | 无依赖、拷贝即用,一条命令完成安装 | 需要手动注册systemd服务,开机自启要自己配 | 大多数内网场景,推荐优先使用 |
| 源码编译 | 能定制编译参数 | 耗时长,依赖编译工具链,离线环境基本没法用 | 几乎没有,不推荐 |
我推荐二进制方案有三个理由。第一,它对操作系统版本不敏感,CentOS 7、Ubuntu 20.04、麒麟、统信这些系统都能跑,只要内核满足要求就行;第二,升级方便,下次拿到新版本tgz,解压后直接覆盖/usr/bin下的文件,重启docker服务就完事;第三,排查问题简单,没有包管理器介入,出问题能很快定位到具体文件和配置。
1.3 离线安装前必须想清楚的清单
离线安装最怕的就是到了现场发现缺东西,所以出发前一定要列清单。我的建议是至少在清单里确认四件事:一是docker和docker-compose的具体版本,二是需要运行哪些容器、对应哪些镜像,三是目标服务器的CPU架构是x86_64还是arm64,四是要不要搭私有镜像仓库。
版本和架构这两项最容易踩坑。docker的二进制包区分x86_64和aarch64,docker-compose也区分不同架构,拿错包直接exec format error。镜像同样有架构标签,在一台x86服务器上load一个arm版的镜像,容器起不来还说不出原因。宁可出发前多花十分钟确认,也不要到现场才手忙脚乱。
2. 准备工作:在有网机器上下载离线安装包
2.1 下载docker二进制包的正确姿势
docker官方把静态二进制包放在download.docker.com/linux/static/stable/目录下,按架构分子目录。以x86_64为例,直接访问download.docker.com/linux/static/stable/x86_64/,能看到按版本号命名的tgz文件,比如docker-24.0.9.tgz。选择版本时有两条经验:一是不要盲目追新,生产环境选稳定版,等新版本发布两三个月后再考虑升级;二是避开.rc、.beta这类预发布版本。
下载和校验我一般这样操作:
# 下载docker二进制包 wget https://download.docker.com/linux/static/stable/x86_64/docker-24.0.9.tgz # 查看压缩包内容,确认包含docker、dockerd、containerd等组件 tar tzf docker-24.0.9.tgz # 校验SHA256,确保文件完整、未被篡改 sha256sum docker-24.0.9.tgztar tzf这一步很多人会跳过,但我建议每次都看一眼。正常的docker静态包解压后是一个docker/目录,里面有docker、dockerd、containerd、containerd-shim-runc-v2、ctr、runc、docker-init等组件。如果发现文件缺失或者名字对不上,说明包有问题,不要继续往下走。
下载完成后,把tgz包传到U盘或者内网共享目录。压缩包本身只有几十MB,传起来不费劲。
2.2 下载docker-compose并选对版本
docker-compose的官方下载地址在GitHub Releases页面,github.com/docker/compose/releases。需要下载的文件名是docker-compose-linux-x86_64(对应x86_64架构)或者docker-compose-linux-aarch64(对应arm64架构)。这个文件本身就是可执行的二进制文件,不需要解压,下载后改名、加执行权限就能用。
选版本时先要搞清楚一个概念:docker-compose有两个“形态”。一个是独立的docker-compose命令,通常放在/usr/local/bin/docker-compose,适合老项目和脚本里直接调用;另一个是docker的插件docker compose(注意中间没有横杠),放在~/.docker/cli-plugins/或/usr/local/lib/docker/cli-plugins/目录下,通过docker compose命令调用。两种形态的底层实现一样,compose文件格式也完全兼容,只是命令名和存放位置不同。
我个人的建议是下载v2.x版本的独立二进制,命名为docker-compose并放到/usr/local/bin/下。这样既能用docker-compose命令兼容旧习惯,又能支持新版compose文件格式。下载命令如下:
# 下载v2.24.5版本,按实际版本号调整 wget https://github.com/docker/compose/releases/download/v2.24.5/docker-compose-linux-x86_64 # 改名并加执行权限 mv docker-compose-linux-x86_64 docker-compose chmod +x docker-compose2.3 提前准备镜像并导出
装好docker和docker-compose只是第一步,没有镜像是跑不起来任何容器的。在离线环境里,镜像只能靠提前导出、现场导入。这一步要在有网机器上提前完成,而且镜像版本要在出发前确定好,到了内网再改版本就麻烦了。
我通常会准备两类镜像:一类是业务直接依赖的,比如mysql:8.0、redis:7.0、nginx:1.24这类中间件;另一类是基础工具镜像,用来排查问题,比如busybox、alpine,体积小,关键时刻非常有用。导出命令:
# 拉取镜像 docker pull mysql:8.0 docker pull redis:7.0 # 导出为tar包 docker save -o mysql-8.0.tar mysql:8.0 docker save -o redis-7.0.tar redis:7.0导出时注意docker save的参数顺序,-o指定输出文件名,后面跟镜像名。如果一次导出多个镜像,可以写在一行,比如docker save -o all.tar mysql:8.0 redis:7.0,导入时会一起加载,但导出文件会比较大。我习惯每个镜像单独导出,现场按需逐个导入,这样单文件小、传输快,也方便排查哪个镜像有问题。
2.4 把安装包传到目标服务器
从有网机器到内网服务器的传输方式,取决于现场条件。最常见的三种:一是用U盘拷贝,适合单台服务器;二是通过内网FTP或者共享目录,适合多台批量分发;三是从跳板机用scp传到目标机器。无论哪种方式,我建议都建一个专门的目录,比如/root/offline_install/,把docker二进制包、docker-compose、镜像tar包统一放进去,方便后续操作。
3. 内网服务器安装:从解压到systemd注册
3.1 docker核心组件的安装步骤
到了内网服务器上,先把之前准备的docker二进制包解压,然后拷贝到系统路径:
# 解压 tar xzf docker-24.0.9.tgz # 把解压后的二进制文件拷贝到/usr/bin/ cp docker/* /usr/bin/ # 创建docker用户组,非root用户要用docker命令需要它 groupadd docker # 验证版本 docker version执行到docker version时,如果能看到Client和Server两个部分的信息,说明docker已经能运行了。但这里有个细节:手动执行docker version时,系统会自动拉起一个临时的dockerd进程来响应请求,所以你看到Server信息不代表服务已经配置成开机自启了。要让docker在重启后自动启动,必须注册systemd服务。
在/etc/systemd/system/docker.service中写入以下内容:
[Unit] Description=Docker Application Container Engine Documentation=https://docs.docker.com After=network-online.target firewalld.service containerd.service Wants=network-online.target [Service] Type=notify ExecStart=/usr/bin/dockerd ExecReload=/bin/kill -s HUP $MAINPID TimeoutSec=0 RestartSec=2 Restart=always LimitNOFILE=infinity LimitNPROC=infinity LimitCORE=infinity Delegate=yes KillMode=process [Install] WantedBy=multi-user.target然后执行:
systemctl daemon-reload systemctl enable docker systemctl start docker systemctl status docker加粗提醒一点:ExecStart=/usr/bin/dockerd这里的路径必须和实际拷贝路径一致。如果你把二进制放到了/usr/local/bin/,这里就要改成/usr/local/bin/dockerd。路径不一致,服务会启动失败,报Exec format error或者No such file or directory。
3.2 把docker-compose装成独立命令
docker-compose的安装比docker简单得多,本质就是拷贝一个可执行文件:
# 拷贝到/usr/local/bin/,让它对所有用户可用 cp docker-compose /usr/local/bin/ # 加执行权限 chmod +x /usr/local/bin/docker-compose # 验证 docker-compose version这里有一个新手很容易忽略的问题:从网上下载的docker-compose-linux-x86_64文件本身没有可执行权限,如果你忘了chmod +x,运行docker-compose version会直接报Permission denied。拷贝到/usr/local/bin/之后,如果没有执行权限,即使切换root用户也没用,因为Linux执行文件靠的是文件的权限位,不是用户身份。
如果你更喜欢docker compose插件方式的安装,把文件放到/usr/local/lib/docker/cli-plugins/目录下,命名为docker-compose即可。推荐使用独立命令方式,因为老版本的shell脚本和CI工具里基本都是调docker-compose,兼容性更好。
3.3 配置daemon.json与常用参数
安装完成后,别急着跑容器,先配置daemon.json。对于离线环境,最重要的配置项是insecure-registries。后续如果搭建了私有仓库,docker从仓库拉取镜像走的是HTTP协议,默认会被拒绝,必须在这里把仓库地址加白名单。
{ "data-root": "/var/lib/docker", "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" }, "insecure-registries": ["192.168.1.100:5000"] }配置文件路径是/etc/docker/daemon.json,改完后执行systemctl restart docker生效。>mkdir -p /root/offline_install/images
把镜像tar包上传到这个目录,然后逐个导入:
docker load -i mysql-8.0.tar docker load -i redis-7.0.tar导入完成后用docker images查看,如果能看到对应镜像和tag,说明导入成功。这里有个细节要注意:docker save导出的是完整镜像,包括所有层和元数据,所以tar包一般比较大。但tar包不支持跨平台导入,比如你在x86_64的机器上save的镜像,不能load到arm64的机器上运行,架构必须一致。
当服务器数量多、镜像包大的时候,频繁load效率很低。这个时候更优的方案是搭一个内网私有仓库,镜像只导入一次,其他机器从仓库拉取。
4.2 搭建一个内网私有仓库
搭私有仓库需要用到registry镜像。先在有网机器上操作:
docker pull registry:2 docker save -o registry-2.tar registry:2把registry-2.tar带到内网服务器,load之后运行容器:
docker load -i registry-2.tar docker run -d \ --name registry \ --restart=always \ -p 5000:5000 \ -v /data/registry:/var/lib/registry \ registry:2这里把registry的数据目录挂载到了宿主机的/data/registry,容器删了重建数据还在,这个设计在生产环境里非常关键。如果容器不挂数据卷,一旦容器被删除,仓库里所有镜像就全没了。
仓库起来之后,把已有的镜像打上指向内网仓库地址的标签,然后推送上去:
docker tag mysql:8.0 192.168.1.100:5000/mysql:8.0 docker push 192.168.1.100:5000/mysql:8.0当然,前提是daemon.json里已经配置了insecure-registries。推送成功后,在内网其他机器上就可以直接执行docker pull 192.168.1.100:5000/mysql:8.0拉取镜像了。
4.3 私有仓库在团队协作中的优势
如果只有一台内网服务器,私有仓库的价值不明显,docker load直接导入就行。但一旦涉及多台服务器,私有仓库的价值就体现出来了。
首先是节省时间。一台服务器load一份几百MB的镜像包,三台就要load三次;有了仓库,每台机器直接pull,网络传输通常比本地解压快,而且不用每次拿着U盘到处跑。其次是版本统一。团队多个人部署同一套业务时,各自load镜像容易出现版本不一致,仓库里只有一份镜像,所有人从同一个仓库拉取,版本天然统一。最后是回滚方便。旧版本镜像也保留在仓库里,需要回滚时直接用旧tag重新拉取即可。
当然,私有仓库也有额外成本:需要一台机器或一个目录专门跑registry容器,还要定时清理无用镜像,避免磁盘被占满。对于5台以内的内网环境,我通常还是会用docker load,管理成本更低;超过5台,或者部署频率较高的场景,果断上私有仓库。
5. 常见问题与排查技巧实录
5.1 systemd启动docker失败
用systemctl start docker启动时卡住,然后报Job for docker.service failed because the control process exited with error code,这种情况我遇到过好多次。先别慌,第一步是用systemctl status docker -l看完整日志,或者journalctl -u docker查最近的日志。常见的坑有:
一个可能的原因是docker.service文件里的ExecStart路径写错了,导致找不到dockerd。解决方法是排查路径,确保和实际二进制所在位置一致。另一个是SELinux拦截,在CentOS和麒麟系统上比较常见。先执行getenforce看SELinux状态,如果是Enforcing,可以先setenforce 0临时关闭,再执行systemctl start docker。如果问题消失,说明是SELinux策略问题,可以在配置文件里调整策略,或者在内网安全要求允许的情况下将SELinux改为Permissive模式。
还有一个隐蔽的问题:daemon.json配置错误。比如JSON格式写错了一个逗号,或者写入了不可识别的配置字段,dockerd启动时会直接报错。可以先运行dockerd --validate或dockerd --debug查看具体报错信息,也可以用python3 -m json.tool /etc/docker/daemon.json校验JSON格式。
5.2 docker-compose命令找不到或版本不对
在服务器上执行docker-compose version,提示command not found,大概率是文件没有放到PATH路径下,或者没加执行权限。先检查echo $PATH看看路径里有没有/usr/local/bin,再检查ls -l /usr/local/bin/docker-compose的权限位。如果文件存在且有执行权限,但新开的终端还是提示找不到,可能是当前session的PATH缓存问题,退出重新登录即可。
另一种情况是docker-compose能执行,但版本显示是v1.x。v1版本已经停止维护,compose文件里很多新字段(比如version字段废弃后的写法)不支持,建议升级到v2.x。直接替换二进制文件,然后重启相关服务即可。
5.3 镜像加载成功但容器启动失败
docker load导入镜像后,docker images里能看到镜像,但docker run时直接退出,docker logs也看不到有效日志。这种情况优先怀疑镜像架构和宿主机架构不匹配。用docker inspect <镜像名> | grep Architecture查看镜像架构,再用uname -m查看宿主机架构,如果不一致,重新下载对应架构的镜像。
另外一个常见原因是端口冲突。如果docker run时指定了-p 3306:3306,但宿主机3306端口已经被其他进程占用,容器会启动失败。用ss -lntp | grep 3306查端口占用情况,换端口或停掉占用进程即可。还有镜像内部应用本身的启动参数问题,这个就得具体问题具体分析了。
5.4 内网仓库拉取镜像报HTTP错误
从私有仓库拉取镜像时,报http: server gave HTTP response to HTTPS client,这是典型的daemon.json没配好。docker默认用HTTPS访问仓库,私有仓库用的是HTTP,必须把仓库地址配置到insecure-registries里,然后重启docker。
改完daemon.json后,重启docker再拉取一次。如果还是同样报错,检查docker info里Insecure Registries是否已经包含目标地址。注意,配置后一定要systemctl restart docker,只reload是无效的。
5.5 故障速查表
| 现象 | 可能原因 | 排查命令 | 解决思路 |
|---|---|---|---|
| docker.service启动失败 | ExecStart路径错误、SELinux拦截、daemon.json有误 | journalctl -u docker,dockerd --debug | 核对路径,调整SELinux,校验JSON |
| docker-compose command not found | 未加执行权限、文件不在PATH中 | ls -l /usr/local/bin/docker-compose | chmod +x,重新登录session |
| 容器启动立即退出 | 架构不匹配、端口冲突、应用参数错误 | docker inspect,ss -lntp | 拉取正确架构镜像,换端口,检查应用日志 |
| 私有仓库拉取报HTTPS错误 | 未配置insecure-registries | docker info | 修改daemon.json后重启docker |
| dockerd运行时磁盘满 | 日志没有设置上限、镜像过多 | df -h,du -sh /var/lib/docker | 配置log-opts,清理无用镜像 |
离线安装docker和docker-compose这件事,做一次觉得麻烦,但把流程跑顺之后会发现,真正核心的只有三件事:安装包准备全、镜像提前导出、服务配置对。我个人习惯是把下载好的二进制包、镜像tar包、daemon.json模板、systemd service文件全部按版本号归档存一份在U盘和共享存储里,下次再遇到内网环境,直接整套拷过去就能用,不用重新上网找资源。
最后再分享一个实战小技巧:第一次去现场之前,先在本地找一台相同操作系统的测试机,把整套离线安装流程完整走一遍,包括docker、docker-compose、镜像导入、容器启动验证。测试机上跑通了,现场基本就能一次成功。离线环境没有试错的余地,提前演练是最省时间的方式。
本文还有配套的精品资源,点击获取