news 2026/10/3 9:23:43

离线环境Docker与中间件部署实操指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
离线环境Docker与中间件部署实操指南

1. 为什么说离线安装docker都是被逼出来的

干了几年运维和开发,我越来越觉得"离线安装"这四个字背后全是故事。但凡网络畅通、镜像源可用,谁愿意对着U盘和安装包折腾半天?但现实就是:很多生产环境、内网隔离区、涉密机房、客户现场,别说访问Docker Hub了,连一个普通的yum源都得走审批流程。这时候,把docker以及mysql、redis、nacos、nginx这类中间件一次性离线带进去,就成了最刚需的能力。

这篇文章想讲的不是官网那份离线安装文档的复读,而是我在x86架构服务器上实际操作下来的完整链路——从联网机器上准备安装包、处理依赖、传输镜像,到目标机器上装好docker、启动registry、拉取中间件镜像并跑起来,再到中间遇到的各类坑的排查思路。适合谁看?一线运维、要交付项目的实施工程师、还有自己买了服务器但网络受限的个人开发者。

进正题之前先交代一个总原则:离线安装的复杂程度永远比在线安装高一档,因为在线安装有包管理器帮你解决依赖,离线安装则要求你自己把依赖链看得明明白白。x86架构相对arm要省心不少,不用交叉编译、不用考虑架构转换,但依旧有不少细节容易翻车。下面我会把整套流程按可复现的顺序摊开来讲。

2. 联网环境准备:离线安装包的获取策略与版本锁定

既然是离线安装,第一步当然是在一台能联网的、和目标机器相同操作系统的x86机器上,把需要的rpm包、二进制文件、镜像文件全部捞下来。这一步看似简单,其实暗藏三个关键决策。

2.1 系统版本与内核版本必须先对齐

docker对内核和操作系统版本有硬性要求。我踩过的一个比较典型的坑是:在CentOS 7.9上装了docker 23.0的rpm包,启动一切正常,但运行容器时一直报"operation not permitted",排查到最后才发现是内核3.10太老,对cgroup v2 namespace的支持不完整。所以第一步不是下载docker,而是确认目标机器系统版本和内核版本:

cat /etc/redhat-release uname -r

如果是CentOS 7系列,内核3.10.x,建议使用docker 20.10.x版本,这个版本对旧内核兼容性最好;如果是CentOS 8或Rocky 8/9,内核4.18以上,可以用docker 23.0或24.0;如果是Ubuntu 20.04/22.04,直接下载对应的deb包或者用官方静态二进制包,会少很多麻烦。

2.2 用yumdownloader或repotrack完整拉取依赖

在联网机器上,如果系统是CentOS/RHEL系,我习惯的做法是先配置好docker官方源,然后使用yumdownloader或repotrack把docker及其所有依赖一次性拉下来:

# 配置docker官方源(联网机器上执行) sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 方式一:yumdownloader 只下载指定rpm,不解决依赖 mkdir -p /opt/docker-offline-rpm cd /opt/docker-offline-rpm yumdownloader --resolve docker-ce docker-ce-cli containerd.io docker-compose-plugin # 方式二:repotrack 连依赖一并跟踪下载(推荐) repotrack docker-ce docker-ce-cli containerd.io docker-compose-plugin

这里我特意推荐repotrack,因为它会把所有被依赖的rpm(比如container-selinux、libseccomp、fuse-overlayfs等)全部下载到当前目录。用yumdownloader加--resolve也能解决一部分依赖,但偶尔有遗漏。如果你们公司有内网yum源,用yum install --downloadonly --downloaddir=/opt/docker-offline-rpm docker-ce也是一个方案。

下载完之后,重要的是检查rpm包是否齐全。怎么检查?用一个笨但有效的办法:把所有rpm包拷贝到一个临时目录,用rpm -Uvh *.rpm做一次试安装,如果不报依赖缺失,就说明包齐了。试安装可以用--test参数只做检查不实际安装。

2.3 静态二进制包方案是另一个硬核选择

如果你的目标系统比较冷门,或者不想被rpm依赖折磨,docker官方还提供静态二进制压缩包。下载地址是:

https://download.docker.com/linux/static/stable/x86_64/

选择对应版本的docker-xx.xx.xx.tgz,解压后里面的docker、dockerd、containerd、runc等二进制文件都是编译好的,直接拷贝到/usr/bin目录下就能用。这种方式的优点是:不依赖任何系统库和包管理器,适合各种精简系统、容器化操作系统。但缺点是:需要自行编写systemd服务文件来管理docker守护进程,工作量多了一步。

我个人建议:能走rpm/deb包就优先走包管理器方案,只有包管理器方案走不通时再用静态二进制包。原因很简单:systemd服务写得不规范会导致开机自启失败、日志权限错乱等连锁问题。

2.4 别忘了docker compose的离线安装

现在的应用部署基本离不开docker compose,特别是做中间件多组件编排的时候。docker compose在CentOS上对应的是docker-compose-plugin这个包,联网环境下通过yum安装。但注意:docker compose v2是作为docker的一个插件运行的,它的二进制文件必须放在/usr/libexec/docker/cli-plugins/目录下,并命名为docker-compose。

如果repotrack已经把docker-compose-plugin拉下来了,直接rpm安装即可。如果没有,去GitHub releases下载docker-compose-linux-x86_64二进制文件,改名为docker-compose放到指定目录,记得加执行权限:

chmod +x /usr/libexec/docker/cli-plugins/docker-compose docker compose version

这个坑很多新手会踩:下载了docker-compose但docker死活不认,最后发现是放错了目录或者权限不够。

3. 离线安装docker引擎:从rpm包到可用镜像的全过程

包准备好了,接下来的活就是把这些东西搬到目标机器上然后装好。这部分看起来就是敲几条命令,但实际操作中有不少值得展开的地方。

3.1 rpm包安装顺序与依赖处理

进入目标机器后,把整个目录传到目标机器上——U盘、scp、内网FTP都行。然后执行安装:

cd /opt/docker-offline-rpm rpm -Uvh *.rpm

这里有个细节:如果目录里rpm包很多,直接rpm -Uvh *.rpm可能会因为依赖包顺序问题报错。我遇到过一次非常典型的:container-selinux必须先于docker-ce安装,但按字母排序后它反而排在后面,导致安装失败。解决办法很简单,优先安装依赖包,再安装主包:

rpm -ivh container-selinux-*.rpm libseccomp-*.rpm rpm -ivh containerd.io-*.rpm docker-ce-cli-*.rpm docker-ce-*.rpm

或者干脆用yum localinstall *.rpm,让yum自己算依赖顺序。但前提是rpm包真的是完整的,没有缺漏。

安装完成后,先别急着启动,做一个重要动作:配置镜像加速器或私有仓库地址,因为离线环境下默认的Docker Hub肯定访问不了,不提前配置的话,虽然不影响docker引擎启动,但后续所有镜像拉取都得手动load,反而更麻烦。编辑/etc/docker/daemon.json:

{ "registry-mirrors": ["http://192.168.1.100:8080"], "insecure-registries": ["192.168.1.100:8080"], "data-root": "/data/docker", "log-opts": { "max-size": "50m", "max-file": "5" } }

这里解释一下几个配置的作用:insecure-registries是让docker允许通过HTTP方式访问私有仓库,这对内网环境至关重要,因为大部分企业的私有仓库并没有配HTTPS证书;>systemctl daemon-reload systemctl enable docker systemctl start docker docker info

docker info输出里重点看两行:Storage Driver是不是overlay2,Cgroup Version是不是2。如果Storage Driver显示的是vfs,说明系统内核或文件系统不支持overlay2,这时候容器性能会明显下降,建议检查内核模块是否加载了overlay:

modprobe overlay lsmod | grep overlay

如果加载不了overlay模块,大概率是内核太老或者没有安装对应内核模块包,解决办法是找同版本内核的kernel-modules-extra包,或者考虑升级内核。这是离线环境下比较尴尬但必须面对的问题。

3.3 systemd不受管控时的应急启动方案

有一种场景我很想单独提一下:目标机器不是标准systemd系统,或者出于某些原因无法使用systemctl管理服务。这时候可以直接用dockerd命令后台拉起:

nohup dockerd --iptables=false --ip-masq=false --data-root=/data/docker >/var/log/dockerd.log 2>&1 &

--iptables=false是关闭docker对iptables的自动管理,这在网络环境比较特殊的内网很有用,因为docker默认会修改宿主机的iptables规则,有时候会导致防火墙策略冲突。但注意:关闭iptables管理也意味着容器网络隔离和端口映射的NAT规则需要你手动配置,适合对网络原理比较熟悉的人。普通场景建议还是让systemd正常管理,让docker自动处理网络。

4. 中间件镜像的获取与离线导入:save/load与registry的完整套路

docker引擎装好之后,真正的大头来了——中间件的镜像怎么搞进去。这里的难点不是"能不能搞",而是"怎么搞才高效、怎么搞才不容易出错"。

4.1 单机场景:docker save和docker load够用了

单个镜像的直接搬运是最简单的方式。在联网机器上,先从Docker Hub拉取镜像,然后打包:

docker pull mysql:8.0 docker save -o mysql-8.0.tar mysql:8.0

文件传到目标机器后,load进去:

docker load -i mysql-8.0.tar

这条链路看起来简单,但有三个容易忽略的深层问题:

第一,save和export的区别。save是针对镜像的,保留完整的镜像层级和元数据;export是针对容器的,把容器的文件系统导出成tar包。如果误用了docker export,导入后变成的是不带历史commit记录的扁平文件系统,无法作为镜像直接run,必须再通过docker import处理,而且启动命令、环境变量全部丢失。我见过有人因为在脚本里把export和save搞混,折腾了一整天。

第二,多架构镜像问题。在x86机器上docker pull,默认拉取的是linux/amd64的镜像——这也是我们需要的。但如果目标机器是arm或者ppc64le,而打包机器是x86,拉取到的镜像平台就不匹配。标题里明确了x86系统架构,但如果你手头有一台macOS ARM笔记本做生产环境打包,那就要小心了:Mac M系列上docker pull默认拉取的是arm64镜像,打包出来的tar传到x86服务器上load虽然会成功,但运行时会报"exec format error"。如果确实需要在Mac上为x86打包,要使用docker pull --platform linux/amd64参数。这一点看我们标题是x86架构,反而省了很多事。

第三,镜像里有没有内置的启动脚本对环境有硬依赖。有些中间件镜像内置了健康检查或初始化逻辑,会在启动时尝试访问外网或特定域名,离线环境下会卡住或报错。这个后面在具体中间件实操部分再说。

4.2 批量场景:本地registry仓库才是正解

如果你需要维护的机器很多,或者中间件数量达到十几个镜像,一个一个save/load就太低效了。这时候应该搭建一个离线镜像仓库。思路是这样的:在联网机器上把所有镜像pull下来,推送到一个私有registry,然后把这个registry的存储目录整体打包搬运到内网。

先在联网机器上启动registry容器:

docker run -d --name registry-offline \ -p 8080:5000 \ -v /data/registry-data:/var/lib/registry \ --restart=always \ registry:2

然后给所有需要的镜像重新打tag,推送到这个本地registry:

docker tag mysql:8.0 192.168.1.100:8080/mysql:8.0 docker push 192.168.1.100:8080/mysql:8.0

所有镜像推送完后,把/data/registry-data整个目录打包压缩,传到内网机器上,同样启动一个registry容器并挂载这个目录。这样,内网所有机器就都能通过docker pull 内网registry地址/镜像名:tag来拉取镜像了,和在线环境体验一致。

这里提一个实际经验:registry镜像仓库的存储目录里包含的是镜像的层级和索引数据,跨机器拷贝时不要直接在宿主机上复制文件,更安全的做法是在registry容器运行时用docker cp或者直接在数据目录做一个tar。我犯过一次错误:直接scp数据目录到另一台机器,结果容器启动后registry一直报blob丢失,因为拷贝过程中有几个blob文件没拷全,最后只能重新push。所以批量搬运时务必打包成一整个tar文件再传,不要用rsync逐文件同步的方式,除非你能保证网络传输零丢包。

4.3 镜像仓库的垃圾回收与磁盘占用

registry跑久了之后,你会发现在内网机器上反复push和删除镜像,数据目录会越来越大,因为registry默认不会自动回收被删掉镜像的blob层。离线仓库空间有限的话,需要手动执行垃圾回收:

docker exec registry-offline sh -c 'registry garbage-collect /etc/docker/registry/config.yml'

这个命令会在容器的标准输出里打印回收日志。注意执行垃圾回收前最好把registry停掉,否则并发写操作可能导致数据损坏。另外,垃圾回收之后,之前被删除镜像的tar包再load进来可能会报错,但这个报错不影响正常push新镜像。总体建议是:批量导入前先规划好镜像清单,尽量不要反复清理和导入,减少不必要的风险。

5. 以mysql、redis、nacos为例:离线中间件部署的完整标记

镜像到了目标机器上,接下来的核心就是怎么把这些中间件在离线环境里跑好。这里我挑三个最有代表性的:mysql(有状态、需要持久化)、redis(有密码和持久化配置)、nacos(依赖外部存储配置较复杂的中间件)。把这三个搞明白,其他中间件基本是同理可证的。

5.1 mysql 8.0离线部署:挂载目录与参数调优

mysql是中间件里最常用也最容易出问题的。离线部署和在线部署在docker层面其实没什么区别,主要风险在于:初始化时会不会尝试连接外网、时区配置、字符集、数据持久化位置。

先看部署命令:

mkdir -p /data/mysql/conf /data/mysql/data docker run -d \ --name mysql8 \ --restart=always \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=MyStr0ngPassw0rd \ -e TZ=Asia/Shanghai \ -v /data/mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf \ -v /data/mysql/data:/var/lib/mysql \ -v /etc/localtime:/etc/localtime:ro \ mysql:8.0

我把my.cnf的内容放到了宿主机上,这样改配置不需要进容器。一个比较稳妥的MySQL配置模板:

[mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_general_ci lower_case_table_names=1 max_connections=500 innodb_buffer_pool_size=2G innodb_flush_log_at_trx_commit=1 default-time-zone=+08:00 socket=/var/run/mysqld/mysqld.sock

lower_case_table_names=1是让表名不区分大小写,这在从Windows环境迁移数据到Linux时特别重要,因为Windows上表名默认不区分大小写,而Linux上默认区分。如果初始化后再改这个参数,mysql会直接拒绝启动,必须在初始化前设置好。离线部署最容易忽视的一个参数就是默认时区问题,不加default-time-zone=+08:00的话,所有时间字段会比北京时间早8个小时。

还有一个很关键的初始化参数:-e MYSQL_ROOT_PASSWORD只是设置root密码,如果你还需要初始化其他数据库,可以加MYSQL_DATABASE=appdb和MYSQL_USER=appuser等环境变量——官方镜像的entrypoint脚本会自动处理。

容器启动后验证连接:

docker logs mysql8 docker exec -it mysql8 mysql -uroot -p

日志里看到ready for connections字样说明启动成功。如果发现启动失败,最常见的原因是数据目录里已经有过一份备用的数据或者配置文件的权限不正确。解决方法:清空/data/mysql/data目录,或者用chown -R mysql:mysql /data/mysql修正权限再重来。

5.2 redis 6/7离线部署:AOF与RDB双保险

redis部署起来比mysql简单,但坑少不等于不用注意。redis的离线部署主要注意三点:密码、持久化策略、内存优化参数。

docker run -d \ --name redis \ --restart=always \ -p 6379:6379 \ -v /data/redis/data:/data \ -v /data/redis/conf/redis.conf:/etc/redis/redis.conf \ redis:7 redis-server /etc/redis/redis.conf

redis.conf模板细项:

requirepass YourRedisPassword appendonly yes appendfsync everysec save 900 1 save 300 10 maxmemory 4gb maxmemory-policy allkeys-lru

appendonly yes开启AOF持久化,配合RDB快照可以最大程度降低数据丢失风险。appendfsync everysec是性价比最高的配置,表示每秒同步一次,性能损失很小但最多丢1秒数据。maxmemory-policy allkeys-lru是内存淘汰策略,当redis内存达到4GB上限时会自动淘汰最久未使用的key,这个在业务量不可控的环境里非常有用,不然内存打满redis会直接拒绝写入。

这里说一个我在离线环境下的心得:redis容器跑起来之后,从宿主机访问要记得检查iptables和云安全组规则,特别是如果docker的网络模式用了--network host,端口映射规则和bridge模式完全不同。我自己遇到过Redis能连但所有操作都超时的情况,最后发现是防火墙对6379端口的半开连接状态做了限制。

5.3 nacos 2.x离线部署:从单机到集群的规划

nacos在微服务架构里几乎成了标配,它的部署比前两个复杂不少——因为nacos可以内嵌derby存储,也可以外接mysql存储。离线环境下我强烈建议直接用外接mysql模式,这样数据持久化和后续集群扩展都靠谱得多。

先确定nacos镜像的版本和配置方式。用nacos/nacos-server:v2.3.2举例:

docker run -d \ --name nacos \ --restart=always \ -p 8848:8848 \ -p 9848:9848 \ -p 9849:9849 \ -e MODE=standalone \ -e SPRING_DATASOURCE_PLATFORM=mysql \ -e MYSQL_SERVICE_HOST=192.168.1.10 \ -e MYSQL_SERVICE_PORT=3306 \ -e MYSQL_SERVICE_DB_NAME=nacos_config \ -e MYSQL_SERVICE_USER=nacos \ -e MYSQL_SERVICE_PASSWORD=YourMysqlPass \ -e JVM_XMS=512m \ -e JVM_XMX=512m \ nacos/nacos-server:v2.3.2

在启动nacos之前,先要到nacos的GitHub仓库下载对应版本的nacos-mysql.sql初始化脚本,在你自己部署的mysql上执行建表。很多人在这一步翻车:直接用官方docker镜像自带的初始化脚本,但脚本版本和nacos版本不匹配,导致nacos启动后配置写入失败。

另外一个容易被忽略的点是端口。nacos 2.x版本和1.x不同,除了8848主端口,还开放了9848和9849两个gRPC端口。在离线环境的防火墙和负载均衡配置里,这三个端口都得放通,否则服务注册和各微服务之间的通信会时好时坏,表现很诡异——刚开始能连上,过几秒就断,特别像是网络问题,但其实是端口没开放。

nacos的集群部署就复杂一些,需要保证每个节点都能访问同一个mysql,并且节点之前能够互相通信。离线环境里集群部署的建议是:先把单机模式调通、配上mysql存储,再扩展成集群,不要一上来就搞三节点,排查问题的难度会成倍上升。

5.4 其他常见中间件的部署速查表

中间件的类型实在太多,我不可能在一个文章里全部展开。下面这个表格是我在多个离线项目中沉淀下来的速查清单,照着做基本不会有大问题:

中间件端口持久化目录关键环境变量容易忽略的点
nginx80/443/etc/nginx /usr/share/nginx/html无需要挂载log目录防日志膨胀
rabbitmq5672/15672/var/lib/rabbitmqRABBITMQ_DEFAULT_USER/PASS插件启用需rabbitmq-plugins命令,集群需erlang cookie一致
elasticsearch9200/9300/usr/share/elasticsearch/datadiscovery.type=single-node需要设置vm.max_map_count=262144
minio9000/9001/dataMINIO_ROOT_USER/PASSWORD控制台和API端口不要搞混
zookeeper2181/2888/3888/dataZOO_MY_ID, ZOO_SERVERS数据目录权限必须是zookeeper用户
kafka9092/var/lib/kafka/dataKAFKA_ZOOKEEPER_CONNECTadvertised.listeners配置不对会报leader不存在的错

拿elasticsearch说一个真实的坑:在x86的CentOS上离线部署elasticsearch,容器刚启动就会被kill掉,docker logs里看不到任何报错,看systemd日志才发现是vm.max_map_count内核参数太小导致的。解决办法是执行:

sysctl -w vm.max_map_count=262144 echo "vm.max_map_count=262144" >> /etc/sysctl.conf

这个参数是elasticsearch的运行基石,不调大概率会出问题,但很少有人第一时间想到。

6. 离线环境特有的坑与排查链路:从启动失败到数据损坏

每一个做离线环境的人,都应该为"意外状态"做好准备。在线环境遇到的问题在离线环境也会遇到,而离线环境还会额外叠加"包不齐全""架构不匹配""依赖冲突"这些层次的问题。这一节我想把几个真正让我印象深刻的坑完整讲清楚——不是列几条注意事项那么简单,而是把排查的链路一起写出来,方便你以后照着复现排查思路。

6.1 依赖缺失导致的dockerd启动失败,怎么一步步定位

现象:执行systemctl start docker后,docker没有起来,systemctl status docker显示进程启动失败。

排查第一步:看journal日志:

journalctl -u docker --no-pager | tail -n 50

如果看到类似error creating overlay mount to /var/lib/docker/overlay2/...或者operation not permitted的行,多半是内核不兼容或者缺少overlay模块;如果看到failed to load listeners: no listeners found,则是网络配置问题。

一个非常典型的离线安装问题:iptables v1.8.4 (nf_tables): unknown option "--wait"。这通常是iptables版本不兼容导致。docker 20.10以上的版本默认需要iptables支持--wait参数,但CentOS 7自带的iptables 1.4.21不支持。解决办法是升级iptables:

yum install -y iptables

或者把docker切换到--iptables=false模式凑合运行。但注意,关闭iptables之后docker端口映射不会生效,所以如果你的应用恰好依赖-p映射端口,这个方法就失灵了。

排查链路的核心逻辑是:先看进程是否活着,再看网络和存储子系统是否正常,最后看i节点和磁盘空间。磁盘空间在离线环境尤其容易被忽视——因为镜像被打成tar包在宿主机上占了一份,load进docker又占了一份,如果忘了删除tar包,磁盘很快就会满。

6.2 镜像load成功但运行时"exec format error"的根因

这个问题的排查过程我印象极深。那是一个内网项目,我从一台办公用的Mac上打好镜像tar包,传到x86服务器上load,docker images能看到镜像,但docker run直接报:

standard_init_linux.go:228: exec user process caused: exec format error

第一反应是问自己:镜像是不是arm64架构的?用docker inspect查看镜像的平台信息:

docker inspect <镜像ID> | grep Architecture

果然显示的是arm64。原因很简单:Mac M1/M2上docker pull默认拉取arm64镜像。解决办法是重新在Mac上执行带平台参数的pull:

docker pull --platform linux/amd64 mysql:8.0 docker build --platform linux/amd64 -t my-image:tag .

但这里面有个更隐蔽的问题:如果你是直接用Dockerfile build的自定义镜像,即使pull基础镜像时指定了--platform linux/amd64,build过程中如果某些RUN命令执行了不兼容x86的二进制,一样会报错。所以稳妥做法是:所有打包操作尽量在一台x86的Linux机器上完成,不要用ARM Mac捣腾——除非你加CI,用docker buildx做多平台构建。

6.3 容器数据目录权限问题:一种"看起来像没权限其实不是"的报错

中间件容器跑不起来,最常见的一类报错是chown: changing ownership of '/var/lib/mysql': Operation not permitted或者mkdir: cannot create directory '/var/lib/postgresql/data': Permission denied。

很多人的第一反应是给宿主机目录疯狂chmod 777,但这样往往无效。真正的原因是:容器内的进程是以某个非root用户运行的(比如mysql用户、postgres用户),而宿主机挂载进去的目录宿主机属主是root,容器内那个用户没有写权限。

正确做法是找到镜像内用户对应的UID/GID,然后把宿主机目录属主改掉:

# 先查一般是mysql的uid docker inspect mysql:8.0 | grep -A 5 "User" # 或者直接看镜像的Dockerfile里声明的用户 chown -R 999:999 /data/mysql

mysql官方镜像的mysql用户通常是999,postgres是999,redis是999。但不同版本可能有差异,别写死,用docker inspect查最稳妥。这一步看着简单,但在离线交付的场景里,因为常驻U盘上的包都是提前做好的,现场往往没有外网去查文档,所以一定要把排查链路记熟。

6.4 镜像源连接失败的"假网络故障"

还有一个我反复见到的场景:离线环境内网里明明配置了私有registry,docker pull却一直报connection refused。

排查链路是这样的:

  1. 先在目标机器上直接测试registry端口通不通:telnet 192.168.1.100 8080,如果不通,查防火墙和网络;
  2. 端口通了但还是连不上,检查daemon.json里的insecure-registries是否包含了这个地址和端口;
  3. 确认配置没写错后,systemctl restart docker——注意,daemon.json的修改不执行restart是不会生效的,SIGHUP信号有时候不够;
  4. 如果还是不行,看看registry容器的日志:docker logs registry-offline,确认它是否真的在监听。

这个排查链路本身没什么高深的,但有几个步骤容易跳过:第二步很多人改了daemon.json但忘了重启docker或者是以为只需reload就行;第五步也是我自己的经验——registry容器经常因为没用--restart=always,系统重启后根本没起来,而宿主机的端口却是监听的(被其他进程占用了),排查起来非常迷惑。

6.5 离线安装后常见的网络与存储混淆点

在离线环境里,还有一个概念性认知要建立:docker的网络隔离并不代表容器可以访问宿主机所在内网的所有服务。比如容器里运行的应用需要连接宿主机上的某个数据库,直接用localhost是连不通的,因为在bridge网络模式下,容器里的localhost指向的是容器自身。

正确做法:

  • 使用--network host模式运行容器,让容器直接共享宿主机网络栈,在容器里用localhost就能访问宿主机服务;
  • 或者使用--add-host host.docker.internal:host-gateway参数,在容器内通过host.docker.internal访问宿主机;
  • 或者在docker compose里使用extra_hosts配置。

这个坑在离线中间件场景里特别常见,因为很多人会把mysql跑在宿主机上、应用跑在容器里,然后在配置里写了localhost:3306,结果应用一直报数据库连接失败。光这一条,我在实际交付过程中就看同事排查了小半天。

7. 一次交付场景的完整实操复盘

说了这么多理论、命令和坑,我觉得有必要把整个流程串起来做一个真实场景的推演——这样你才好在自己的项目里直接照猫画虎。假设目标环境是一台Rocky 8.4的x86服务器,内网隔离,不通外网。需要离线交付的内容是:docker引擎 + mysql 8.0 + redis 7 + nginx + nacos。

整个操作步骤可以归纳为"五步走":

第一步:在联网的CentOS/Rocky机器上准备rpm包和镜像tar包。

mkdir -p /opt/delivery/{rpm,images} cd /opt/delivery/rpm repotrack docker-ce docker-ce-cli containerd.io docker-compose-plugin cd /opt/delivery/images docker pull mysql:8.0 docker pull redis:7 docker pull nginx:1.24 docker pull nacos/nacos-server:v2.3.2 docker pull registry:2 docker save -o mysql-8.0.tar mysql:8.0 docker save -o redis-7.tar redis:7 docker save -o nginx-1.24.tar nginx:1.24 docker save -o nacos-2.3.2.tar nacos/nacos-server:v2.3.2 docker save -o registry-2.tar registry:2

第二步:把rpm包和镜像tar包传到内网目标机器上。

这里我一般先传rpm包,把docker引擎装好、确定能正常拉镜像之后,再传镜像tar包。没必要在全新环境里一股脑全传过去,出了问题还要一个个排除。

第三步:内网机器装docker并配置私有仓库。

cd /opt/delivery/rpm yum localinstall -y *.rpm cat > /etc/docker/daemon.json <<EOF { "insecure-registries": ["192.168.1.100:5000"], "data-root": "/data/docker", "log-opts": { "max-size": "50m", "max-file": "5" } } EOF systemctl enable docker && systemctl start docker docker load -i registry-2.tar docker run -d --name registry --restart=always -p 5000:5000 -v /data/registry:/var/lib/registry registry:2

第四步:把剩余镜像load进来,推送到私有仓库。

这一步有两个操作路径:如果机器数量少,直接docker load -i然后docker run即可;如果机器数量多,就load registry后把其余镜像load进这台机器,再tag、push到registry,让其他机器从registry拉取。

docker load -i mysql-8.0.tar docker tag mysql:8.0 192.168.1.100:5000/mysql:8.0 docker push 192.168.1.100:5000/mysql:8.0 # redis、nginx、nacos依此类推

第五步:所有机器配置daemon.json指向私有仓库,通过拉取镜像启动中间件。

docker pull 192.168.1.100:5000/mysql:8.0 docker pull 192.168.1.100:5000/redis:7 ...

最后验证一次完整链路:在另一台机器上执行docker pull 192.168.1.100:5000/mysql:8.0,然后docker run,确认能正常访问。这个"从零到全部可用"的信号明确之后,交付才算真正完成。

这套流程我一共用过很多次,每次的核心原则都一样:先小范围验证再批量复制,先保证链路再追求速度。离线环境的试错代价比在线环境高得多,一次容器启动失败牵涉的可能是现场的整套交付流程延期。

8. 一些离线部署的补充建议与个人经验

文章写到这里,核心内容基本都覆盖了。最后我再挑几条零零散散但非常影响实际操作体验的经验来讲,这些不在官方文档里,但都来自真实项目中的一手体会。

关于传输工具:如果目标机器只能通过跳板机访问,scp传大文件容易断,建议用rsync配合断点续传,或者把大tar包分卷再传。我遇到过传了半个小时的mysql镜像包突然断掉,重新scp又要半小时,最后用split按1GB分块传输,配合rsync --partial才解决。

关于清单管理:在离线交付时,我强烈建议在U盘或打包目录里维护一个manifest.txt,把所有文件的版本、大小、校验值记录清楚。进到内网环境没外网可查,如果哪个文件损坏了,你只能靠这份清单判断。每次都用sha256sum校验重要文件,不要嫌麻烦。

关于版本固定:离线系统的软件版本一旦装上,基本不会频繁升级。所以决定版本时一定要慎之又慎,把需求方要的中间件特性、已知安全漏洞、数据库兼容性都摸清楚再定版本。在离线环境里做版本回退的代价,比在在线环境高得多——因为旧版本的rpm包和镜像你可能并没有存下来。

关于演练:离线部署不是看文档就能掌握的技能。强烈建议在家里或测试环境里,完整走一遍"从联网机器准备包到离线机器启动中间件"的流程。第一次可能会花掉半天时间,但一旦走通,这个能力体系就建立了,以后的交付效率会有质的提升。我最早也是在一台闲置的x86服务器上反复练习,才慢慢积累了处理各类意外状况的经验。

这套东西说下来其实没有太多花哨的技巧,核心就是细心、耐心、有条理。离线和在线的差别,无非是"可用的资源"从无限变成了有限,而你的应对策略也从"缺什么装什么"变成了"装什么带什么"。理解了这一点,整个离线部署的思维模式就有了。

希望这篇内容对你的实际操作能有实实在在的帮助。如果后面在离线部署中间件的过程中遇到具体问题,欢迎在评论区把报错信息发出来,我们一起研究。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 9:22:49

AWSIM多相机传感器仿真全流程:从相机标定到Autoware集成实战

1. 为什么要在AWSIM里加多相机 搞过自动驾驶仿真的人应该深有体会&#xff0c;单目相机在感知开发面前永远是不够用的。AWSIM作为开源自动驾驶模拟器&#xff0c;基于Unity引擎构建&#xff0c;能和Autoware这套开源自动驾驶软件栈无缝衔接&#xff0c;是不少团队做感知算法验证…

作者头像 李华
网站建设 2026/10/3 9:20:15

EasyTier实战:去中心化异地组网与虚拟局域网搭建指南

我最近把家里 NAS、办公室台式机和一台常年开着的云主机用 EasyTier 拉进了同一个虚拟局域网。以前想从外面拿家里文件&#xff0c;要么开端口映射&#xff0c;要么依赖某台固定服务器转发&#xff0c;心里总是没底。EasyTier 是一个强调去中心化的异地组网工具&#xff0c;它没…

作者头像 李华
网站建设 2026/10/3 9:20:15

数据压缩在大数据中的核心价值:让Hive与Spark作业加速

作为常年跟Hadoop、Spark、Hive打交道的数仓开发&#xff0c;我越来越觉得“数据压缩”这词被大家理解得太窄了。很多人一听到压缩&#xff0c;第一反应就是“省硬盘钱”。但在真正的大数据生产环境里&#xff0c;压缩最核心的价值根本不是存储&#xff0c;而是让处理速度飞起来…

作者头像 李华
网站建设 2026/10/3 9:19:41

Spring Boot社区医院管理系统实战:从需求分析到部署上线

1. 项目概述与需求拆解 先聊个真实的场景。很多社区卫生服务中心、小型民营诊所&#xff0c;目前的就诊流程还是“患者排队→纸面登记→医生手写病历→收费员人工算账→药房手写发药单”。这种模式的问题&#xff0c;一线从业者都懂&#xff1a;早上高峰期挂号台能挤成一团&…

作者头像 李华
网站建设 2026/10/3 9:19:29

从OSI到TCP/IP:一张分层地图搞定网络故障排查

1. 为什么网络工程师都要啃这两个模型&#xff1a;从一次真实故障说起前阵子值班&#xff0c;接到一个客户报障&#xff0c;说办公室的财务系统突然连不上服务器了&#xff0c;销售部门却一切正常。我远程登录核心交换机&#xff0c;ping网关能通&#xff0c;ping服务器也在线&…

作者头像 李华