干过几次内网交付项目之后,我对“docker离线安装”这几个字真的又爱又恨。爱的是,一旦把离线环境打通,后面部署中间件简直行云流水;恨的是,第一次操作时,光是把docker装起来,就可能卡在依赖、架构、内核版本这些不起眼的细节上。
这篇文章就把我在x86系统架构服务器上做docker离线安装和中间件部署的完整过程写出来。适用场景很明确:服务器没有外网,只能通过U盘、内网yum源、或者局域网文件共享来传递安装包,但你又需要快速跑起MySQL、Redis、Nginx这类基础设施中间件。整个流程我在CentOS 7.9和Rocky Linux 8.x上都验证过,理论上兼容大多数x86_64的Linux发行版。
如果你接下来要做内网交付、机房设备初始化和离线环境项目,这篇文章的步骤可以直接抄作业,包括我踩过的坑也会原封不动分享出来。
1. 离线安装前的方案选型与前置检查
1.1 先用一条命令确认架构和内核对不对
开始装docker之前,我强烈建议先花两分钟确认三件事:系统架构、发行版版本、内核版本。别小看这一步,很多离线安装失败,根本原因就是架构不对或者内核太老。
uname -m cat /etc/os-release uname -runame -m输出x86_64就说明是x86系统架构的64位机器,这也是docker官方rpm包和二进制包的默认目标。如果是i386、i686这种32位架构,那docker基本不用想了,官方早就停止支持了。/etc/os-release会告诉你具体是CentOS 7还是Rocky Linux 8,这决定了应该去下载哪个rpm包集合。uname -r则是内核版本,CentOS 7默认的3.10内核虽然比较老,但docker 20.10及以前版本还能跑,新版本对内核要求会更高一些。
1.2 三种离线安装方案该怎么选
离线安装docker主要有三条路,我根据使用频率排个序:
第一种:Yum/Apt离线仓库方式。在有网环境下把docker和依赖包全部下载成rpm或者deb文件,然后拷贝到内网机器上,用本地repo源安装。这种方法最符合运维人员习惯,后续如果有其他机器,搭一个内网yum源就能批量安装。缺点是需要提前把依赖关系摸清楚,否则会陷入“缺一个包装一个包”的循环。
第二种:官方静态二进制方式。从docker官网下载docker-版本号.tgz压缩包,里面包含dockerd、docker、containerd这些二进制文件,解压后放到/usr/bin目录,再用systemd管理。这种方式最干净,不依赖系统包管理器,适合批量服务器部署,也适合那种包管理器损坏的极端情况。缺点是需要自己写systemd service文件,对不熟悉systemd的人稍微有点门槛。
第三种:直接搬运行已经做好的操作系统镜像。比如用打包好的虚拟机模板或者轻量系统镜像,docker提前预制在镜像里。这种方式对单机部署很快,但如果是几十台机器,传输镜像的时间成本和定制化成本反而更高。
我自己的习惯是:单台机器试运行用第一种(yum/apt),批量生产环境用第二种(静态二进制),第三种基本不推荐。
2. 离线安装Docker与Docker Compose全流程
2.1 在有网环境准备docker的rpm安装包
这一步需要在能连外网的机器上提前下载。以CentOS/RHEL系列为例,我推荐用yumdownloader和createrepo来做一个本地离线包目录。
先说第一种方式,直接下载指定rpm包:
# 安装yum-utils工具 yum install -y yum-utils # 创建目录,存放docker相关rpm包 mkdir -p /root/docker-offline-packages cd /root/docker-offline-packages # 下载docker和依赖组件 yumdownloader --resolve docker-ce docker-ce-cli containerd.io docker-compose-pluginyumdownloader --resolve会把docker-ce所需的全部依赖包一次性下载到当前目录,包括container-selinux、libseccomp等。下载完后,检查一下目录里的rpm文件数量,正常应该在10个左右。
如果你需要在内网批量安装,更推荐把这些rpm包做成一个本地yum源。方法也很简单,把rpm文件放到一个目录,用createrepo生成元数据,然后在离线机器的/etc/yum.repos.d/下新建一个repo文件,指向这个目录:
createrepo /root/docker-offline-packagesrepo文件内容大致如下:
[docker-offline] name=Docker Offline Repository baseurl=file:///root/docker-offline-packages enabled=1 gpgcheck=0这样在离线机器上执行yum install -y docker-ce就能自动解析依赖并安装。这个方法最适合内网有多台服务器的情况,因为只要把这些rpm包同步到每台机器,或者放在一台内网文件服务器上用HTTP暴露出来,所有机器都能用同一个源。
对于Ubuntu/Debian系统,逻辑类似,用apt-get download或者apt-get install --download-only -y docker.io来缓存deb包,然后在内网执行dpkg -i *.deb即可。
2.2 内网机器安装docker并配置开机启动
拿到rpm包之后,内网安装就很快了。如果只是临时安装一两台,直接:
rpm -ivh *.rpm如果遇到报错提示某个包已存在,就用rpm -Uvh *.rpm升级安装。rpm安装能自动处理依赖,但前提是当前目录下的rpm包必须齐全。我记得有一次在CentOS 7.9上装docker时卡在了container-selinux这个包上,因为系统自带的selinux策略版本过低,docker引用的container-selinux版本装不上。这种情况不要硬扛,最快的方法是查一下docker-ce这个rpm包到底依赖哪些版本,然后从阿里云开源镜像站下载对应版本的container-selinux。
装完之后,立即设置开机启动并启动服务:
systemctl enable --now docker docker versiondocker version如果能看到Client和Server两段信息,说明docker已经正常运行了。如果只看到Client没有Server,多半是dockerd启动失败,这时候要用journalctl -u docker看日志,绝大多数是和iptables、cgroup、存储驱动相关的问题,后面我会单独写一节排查。
2.3 离线安装docker-compose的正确姿势
docker-compose在大规模中间件编排时非常好用,离线环境下安装它也需要提前准备。compose有两种形式:老一代的docker-compose独立二进制,和新版本的docker compose插件。新版本插件会作为docker的一个子命令出现,如果你已经通过yum方式安装了docker-compose-plugin,那docker compose version直接就能用,不需要额外操作。
如果用的是独立二进制,下载时注意选择docker-compose-linux-x86_64这个文件,我通常会放到/usr/local/bin/docker-compose:
cp docker-compose-linux-x86_64 /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose docker-compose --version这里有个容易踩的坑:docker-compose二进制文件依赖于glibc版本,如果内网系统太老,比如CentOS 7自带的glibc 2.17,部分高版本docker-compose会直接抛 "version `GLIBC_2.28' not found"。遇到这种情况就别想升级系统了,老老实实下载一个旧一点的docker-compose版本,比如1.29.x。
3. 中间件镜像的离线获取、传输与导入
3.1 在有网机器上拉取镜像并打标签
docker本体搞定后,真正的重头戏是中间件镜像的离线搬运。这里有一个核心原则:在有网机器上拉取镜像,导出成tar文件,传到内网,再导入。
我平时会在有网环境准备一台“镜像中转机”,把需要的镜像一次性拉齐全:
docker pull nginx:1.24.0 docker pull mysql:8.0.36 docker pull redis:7.2.4如果需要部署harbor,也可以把goharbor/harbor-*相关镜像一并拉下来。如果内网机器比较多,建议先对所有镜像打上将来要用的tag,比如内网registry地址前缀,这样在内网导入后就能直接被docker-compose或私有仓库使用。
3.2 镜像导出、压缩、分卷一整套操作
镜像拉好后,用docker save导出为tar包。很多人会把docker save和docker export搞混,这里说明一下:save保存的是镜像层,完整保留历史元数据、环境变量、入口点,适合镜像迁移;export导出的是容器文件系统,会把历史层合并成一个扁平层,会导致镜像丢失堆栈历史,不适合镜像分发。我们要的是前者。
mkdir -p /root/images docker save -o /root/images/nginx-1.24.0.tar nginx:1.24.0 docker save -o /root/images/mysql-8.0.36.tar mysql:8.0.36 docker save -o /root/images/redis-7.2.4.tar redis:7.2.4镜像文件通常不小,MySQL 8.0 的镜像大概600MB左右,传输和存储都挺占空间的。为了更安全地搬运,我会进一步压缩并分卷:
gzip /root/images/mysql-8.0.36.tar split -b 100M -d /root/images/mysql-8.0.36.tar.gz /root/images/mysql-8.0.36.tar.gz.partsplit -b 100M -d会把大文件按100MB切分成多个part文件,适合用U盘或者FTP传输时规避单文件大小限制。到了内网机器后,先合并再解压:
cat /root/images/mysql-8.0.36.tar.gz.part* > /root/images/mysql-8.0.36.tar.gz gzip -d /root/images/mysql-8.0.36.tar.gz压缩传输这个细节看似不起眼,但在离线环境中非常实用。我第一次做离线传输时没压缩,一张16GB的U盘装两个镜像就满了,后来改成压缩之后,同样容量能装下的镜像数量翻了好几倍。
3.3 内网导入镜像并验证可用性
镜像包到了内网机器上之后,导入动作很简单:
docker load -i /root/images/nginx-1.24.0.tar docker load -i /root/images/mysql-8.0.36.tar docker load -i /root/images/redis-7.2.4.tar导入完成后查看镜像列表:
docker images这里我特别想提醒一个细节:docker load导入后显示的镜像名和tag,来自你docker save之前打的tag。所以如果当时拉下来的是原始名称,导入后也就是原始名称;如果内网要用特定名称,建议在有网机器上先docker tag好再save。
还有一个容易被忽视的问题:镜像导入后是否能在当前机器上启动,取决于镜像的架构。如果你在一台ARM Mac或者ARM服务器上执行了docker pull,导出的镜像也是ARM架构的,传到x86系统机器上根本跑不起来。这也是为什么标题里特别强调x86系统架构——在x86服务器上准备镜像时,先在Windows或者Linux x86机器上执行,或者用docker manifest inspect确认镜像支持linux/amd64。
4. 中间件容器化部署实操
4.1 用docker快速部署Nginx并挂载配置
Nginx是很多应用的前置入口,也是我在内网第一个部署的中间件。部署方式很简单,重点在目录设计上。
先创建Nginx的数据目录:
mkdir -p /data/nginx/{conf,html,logs}然后准备一份基础nginx.conf,建议最少包含worker_processes、events、http三个核心块。我们用容器跑Nginx时,最重要的是把宿主机目录挂载到容器里的/etc/nginx/conf.d、/usr/share/nginx/html和/var/log/nginx,这样以后改配置、放页面、看日志都不用进入容器。
启动命令:
docker run -d \ --name nginx \ --restart=always \ -p 80:80 \ -v /data/nginx/conf:/etc/nginx/conf.d:ro \ -v /data/nginx/html:/usr/share/nginx/html \ -v /data/nginx/logs:/var/log/nginx \ -e TZ=Asia/Shanghai \ nginx:1.24.0--restart=always这个参数我在内网环境特别强调,因为生产环境机器重启是常事,如果docker容器没有设置自动重启,宕机恢复后服务就全断了。-e TZ=Asia/Shanghai是为了时区,Nginx镜像默认UTC时间,日志和实际北京时间差8小时,排查问题时很容易被误导。
启动后验证:
docker ps curl -I http://127.0.0.1如果curl返回200,说明Nginx已经正常提供服务。以后修改配置,只需编辑宿主机/data/nginx/conf/下的文件,然后执行docker exec nginx nginx -s reload让配置生效即可。
4.2 MySQL 8.0数据持久化与初始化配置
MySQL在内网是绝对的刚需。用docker部署MySQL时,最核心的就是数据持久化和字符集配置。数据持久化没啥可讨价还价的,必须挂载volume,否则容器一删数据就全没了。
mkdir -p /data/mysql/{data,conf,logs} docker run -d \ --name mysql \ --restart=always \ -e MYSQL_ROOT_PASSWORD='Strong@Passw0rd' \ -e TZ=Asia/Shanghai \ -p 3306:3306 \ -v /data/mysql/data:/var/lib/mysql \ -v /data/mysql/conf:/etc/mysql/conf.d \ -v /data/mysql/logs:/var/log/mysql \ mysql:8.0.36关于初始化密码,有两点经验。第一,MYSQL_ROOT_PASSWORD这个环境变量只在首次初始化数据目录时生效,如果目录里已经有了旧数据,改这个变量不会覆盖密码。第二,如果忘记初始化密码,最稳妥的方法是挂一个临时配置文件来跳过权限认证,进入容器后修改密码,再恢复配置文件,新版本MySQL 8支持在配置里加skip-grant-tables然后重启来实现。
初始化等待时间通常需要30秒到1分钟,因为MySQL第一次启动时要建系统表。内网机器性能一般,稍安勿躁。验证方式:
docker exec -it mysql mysql -uroot -p进入MySQL后,建议顺手把默认认证插件调整为mysql_native_password或者使用caching_sha2_password,这取决于你的客户端工具是否支持新认证方式。老旧的业务系统如果连不上MySQL 8,大概率是认证插件问题:
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'Strong@Passw0rd'; FLUSH PRIVILEGES;另外,我还会在/data/mysql/conf/下创建一个my.cnf文件,设置默认字符集为utf8mb4,确保中文数据不出乱码。这在很多业务系统上是刚性需求。
4.3 Redis主从模式部署与持久化配置
Redis部署也不难,难点通常在主从和持久化上。内网环境里,我一般会部署一主一从,既能满足高可用要求,又不会给机器太大负担。
先创建数据目录:
mkdir -p /data/redis/{master,slave,conf}主节点部署:
docker run -d \ --name redis-master \ --restart=always \ -p 6379:6379 \ -v /data/redis/master:/data \ -v /data/redis/conf/redis-master.conf:/etc/redis/redis.conf \ redis:7.2.4 \ redis-server /etc/redis/redis.conf从节点通过replicaof命令挂到主节点,在redis-slave.conf里配置:
replicaof 10.0.0.10 6379然后启动从节点:
docker run -d \ --name redis-slave \ --restart=always \ -p 6380:6379 \ -v /data/redis/slave:/data \ -v /data/redis/conf/redis-slave.conf:/etc/redis/redis.conf \ redis:7.2.4 \ redis-server /etc/redis/redis.conf这里有个关键点:Redis持久化推荐同时开启RDB和AOF。RDB做快照恢复快,AOF记录每个写操作丢数据少。对应配置:
appendonly yes appendfsync everysec save 900 1 save 300 10 save 60 10000我遇到过的坑是,Redis容器默认以redis用户运行,挂载到宿主机目录后,目录属主如果不匹配,Redis写入时会报权限错误。解决办法非常直接:
chown -R 999:999 /data/redis如果排查半天还是启动失败,直接看容器日志:
docker logs redis-master5. 内网多机器分发:部署私有镜像仓库
5.1 用registry镜像搭建局域网私有仓库
当你需要在多台内网机器上部署中间件时,每台机器都手动docker load镜像包传文件,效率实在太低了。更好的做法是,在局域网里搭一个私有镜像仓库,把镜像推送上去,其他机器直接docker pull即可。
最简单的方式就是跑一个官方registry容器:
mkdir -p /data/registry docker run -d \ --name registry \ --restart=always \ -p 5000:5000 \ -v /data/registry:/var/lib/registry \ registry:2registry启动后,在有镜像的内网机器上,先给镜像打上私有仓库地址的tag:
docker tag nginx:1.24.0 192.168.1.10:5000/nginx:1.24.0 docker push 192.168.1.10:5000/nginx:1.24.0其他机器需要用到这个镜像时,只需配置/etc/docker/daemon.json添加"insecure-registries": ["192.168.1.10:5000"],然后重启docker,就可以直接docker pull了。
insecure-registries这个配置是必须的,因为私有仓库默认走HTTP,而docker默认要求HTTPS,不把这个地址加入白名单,pull时一定会报证书错误。
5.2 批量推送镜像到私有仓库的脚本思路
如果你要推送的镜像列表很长,一条一条docker tag加docker push确实浪费时间。我用一个非常简单的shell循环处理:
for image in nginx:1.24.0 mysql:8.0.36 redis:7.2.4; do docker tag $image 192.168.1.10:5000/$image docker push 192.168.1.10:5000/$image done推送完成后,可以在registry容器里查看仓库列表:
curl http://192.168.1.10:5000/v2/_catalog这个方法虽然简单,但能省下大量人工操作。其实想过用harbor,它带有Web管理界面和RBAC权限控制,适合团队规模较大的场景。如果只是三五台机器,registry完全够用。若选择harbor,离线安装也不复杂,核心是把harbor的离线安装包(harbor-offline-installer-xxx.tgz)传到内网,把里面的docker镜像load到本机,然后执行install.sh脚本即可。
6. 离线环境常见问题与排查技巧实录
6.1 rpm依赖缺失和版本冲突怎么破
离线环境最常见的报错就是类似libcgroup is needed by docker-ce-...、container-selinux is needed by docker-ce-...这种。别慌,思路就两个:一是把依赖包找到并下载下来一起装,二是修改安装方式。
找依赖包最简单的方法是在有网环境的机器上执行yum deplist docker-ce,它会列出所有依赖包和具体版本。然后逐个yumdownloader下载。如果下载的数量太多,我更推荐在一个干净的有网机器上用yum install --downloadonly --downloaddir=/tmp/dockerdeps docker-ce一把梭下载全部依赖。
另一个很实用的思路是放弃rpm包方式,改用静态二进制。静态包里自带dockerd、containerd、runc等关键组件,不依赖系统包管理器,对离线环境特别友好。我遇到过一台内核较新的Rocky Linux 9,rpm包始终装不上,换成二进制包反而一路绿灯。
6.2 Docker启动失败:iptables、cgroup和存储驱动
启动docker时报Failed to start Docker Application Container Engine是比较头疼的问题。排查时第一看定位的日志:
journalctl -u docker --no-pager -n 50常见的三个原因按出现频率排序:
iptables相关报错。docker默认要操作iptables,如果系统禁用了iptables或者使用nftables,可能会起不来。可以尝试临时用iptables -L -n看是否正常,如果iptables命令本身报错,需要先把iptables服务装好或启用。
cgroup版本不匹配。Docker默认使用cgroupfs,如果系统开启了cgroup v2,而docker版本较老,也可能启动失败。新版本docker通常能自动适配,但是如果一直失败,可以在/etc/docker/daemon.json中显式指定"cgroupdriver": "systemd"或"cgroupdriver": "cgroupfs",实测下来CentOS 7上systemd更稳,Ubuntu 20.04以上用systemd也一致。
存储驱动 overlay2 不可用。如果文件系统不支持overlay2,docker会退回到vfs,虽然能启动但是磁盘占用巨大,性能也差。可以用docker info查看Storage Driver那行,如果显示vfs,建议格式化相关分区为xfs或者ext4,重新加载后重启docker。
解决方案基本都可以用调整/etc/docker/daemon.json加上重启docker来解决。这个文件在内网环境下尤其重要,它的作用不只是给registry配白名单,还包括配置>
AMR磁角度传感器KMZ60与R7KA8D2KFLCAC高精度电机定位实战
1. 这不是“又一个角度传感器”——KMZ60与R7KA8D2KFLCAC组合的真实定位价值你手头那块标着“高精度磁角度测量”的开发板,很可能正在用12位ADC读取霍尔电压,再靠查表法拟合角度,误差动辄1.5——这在伺服电机闭环控制里,意味着转子…
操作系统第2章进程与线程:核心考点、高频误区与得分点详解
期末复习也好,考研冲刺也好,操作系统第2章“进程与线程”基本是整门课的命脉。这一章如果吃透了,后续的内存管理、文件系统,甚至设备管理学起来都会顺很多;反之,如果连进程和线程的关系、PCB里装了什么、信…
基于CSPNet的轻量级火灾检测模型设计与部署
简介:本资源是一套基于卷积神经网络的火灾实时检测系统实现方案,面向深度学习初学者、计算机视觉实践者及安防类项目开发者,解决图像与视频流中火灾目标识别与声光报警联动的实际问题。压缩包共10个文件,含2个核心Python脚本&…
磁力搜索原理与实战:从哈希值到高效资源筛选
先说实话,这篇不是资源导航,也不是什么“神秘网站合集”。我折腾下载这件事十多年,从最早的论坛种子里转出来,再到现在几乎只用磁力链接,中间踩过的坑、摸清的门道,确实可以拿出来聊聊。磁力搜索这个技术&a…
Colibri:面向MoE模型的C语言轻量级边缘推理引擎
1. 项目概述:Colibri 不是蜂鸟,而是一把为 MoE 模型量身打造的 C 语言推理匕首“Colibri”这个名字在搜索引擎里一搜,满屏都是蜂鸟图片和生态学论文——但如果你正盯着 LLM 推理性能瓶颈发愁,或者刚被 MoE 模型的显存爆炸问题按在…
多变量时序预测必看:用PyTorch实现TFT的完整指南
/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …