ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CentOS 7离线安装Docker全攻略:rpm与二进制包实战

CentOS 7离线安装Docker全攻略:rpm与二进制包实战 做部署的兄弟应该都有过这种经历内网环境的机器隔离得严严实实外网基本不通业务又等着要上线。我第一次真正面对 Docker 离线安装是在交付一个内部管理系统的时候。目标机器是 CentOS 7上面干干净净连容器运行时都没有而我们的应用是以 Docker 镜像形式交付的。当时我脑子里第一反应还是yum install -y docker-ce结果敲完命令才发现根本跑不通——没网。后来翻了不少文档踩了一堆坑才把 rpm 包和静态二进制包这两套离线安装方案彻底摸透。这篇文章就是把那次实战经验整理出来。核心针对 CentOS 7 环境覆盖离线安装包的准备、目标机器上的完整安装流程、镜像导入导出、服务配置和常见故障排查。适合正在做内网部署、等保环境交付或者公司内部有离线安装 Docker 需求的朋友直接参考。内容不算难但中间有几个关键细节容易让新人卡很久我用踩坑实录的方式讲清楚你照着做基本能一次跑通。1. 开工前的准备先想清楚三件事再动手1.1 离线安装的典型场景不是只有“没网”这么简单很多教程把离线安装简单定义成“服务器连不上外网”其实真实场景要更细一些。我遇到过的主要有三类第一类是物理隔离网络服务器在独立机房跟外网没有任何链路最常见于政府和国企类项目。这类环境不只是“没网”的问题连把文件拷进去可能都要走光盘、专用摆渡机或者审批流程。第二类是内部私有云环境有网络但只能访问内网源外网 DNS 和路由全部被禁。这种环境一般可以通过内网的软件仓库来装但前提是仓库里提前录入了对应的安装包。第三类是临时离线环境比如在客户现场搭一套演示环境现场只提供了基础网络没法访问外网但又不想把时间耗在等运营商开通上。不同的离线程度决定了你的准备方案。如果是完全隔离的网络就得把所有依赖一次性打包好带进去。如果只是没有外网但有内网源可以优先考虑在内网搭建一个仓库。这篇文章主要讲最硬核的情况——完全离线所有东西自己带。1.2 两种主流离线方案rpm 本地仓库 vs 静态二进制包离线装 Docker 主要有两条路线我建议根据你的使用场景选方案一rpm 包方式。在有外网的机器上通过 yumdownloader 把 docker-ce 相关的所有 rpm 依赖包拉下来拷到目标机器上再用本地 yum 源方式安装。这种方案的好处是跟系统包管理结合紧密卸载、升级都方便后续用yum update也能管理。缺点是依赖关系比较绕特别是 container-selinux 这类包版本匹配不对就会装不上。方案二静态二进制包方式。从 Docker 官方 GitHub Release 页面下载 docker-xx.x.x.tgz里面已经编译好了 dockerd、docker、containerd 等可执行文件。把它解压后拷到 /usr/bin 目录再手动写好 systemd 服务文件即可。这种方案完全绕开了 rpm 依赖问题一套包在几乎所有 x86_64 的 Linux 发行版上都能用。缺点是需要自己维护 systemd 单元文件升级时也要手动替换二进制。我个人更倾向于方案一因为生产环境里 Yum 源管理依赖确实更省心。但如果你要在多台不同发行版的机器上装方案二的通用性会更强。下面两章我会把两种方案的准备工作都讲清楚你自己按场景选。1.3 环境信息确认清单版本、架构、内核一个都不能少无论选哪种方案开工前必须确认三件事。一是操作系统版本。不同版本对应的源和依赖不一样CentOS 7 和 CentOS 8 的 rpm 包不能混用。确认命令是cat /etc/redhat-release。如果是 Ubuntu/Debian就得换用 deb 包方案这篇文章的 rpm 部分就不适配了。二是系统架构。绝大多数服务器是 x86_64但不少国产化项目会用到 ARM 架构或龙芯、飞腾这类平台。架构不匹配装上了也起不来服务用uname -m可以一键确认。x86_64 就下载 amd64 的包aarch64 对应 arm64。三是内核版本。Docker 对内核有明确要求CentOS 7 默认 3.10 内核跑 Docker 20.x 以上版本问题不大但如果内核低于 3.10overlay2 存储驱动可能没法正常用。检查命令是uname -r。如果内核太老建议先升级内核再装 Docker否则后面跑容器会出现各种莫名其妙的问题。一个额外建议在目标机器上执行df -h看一下磁盘和分区挂载点给 Docker 的数据目录预留足够空间。我之前遇到过 / 分区只有 20G镜像还没拉几个磁盘就满了的尴尬后面干脆把>yum install -y yum-utils createrepo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo仓库配置好后创建一个目录用 yumdownloader 把 docker 相关包连同依赖一起拉下来。这里有个坑yumdownloader 默认不会自动解析依赖要加--resolve参数才会把依赖包也拉下来。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 --destdir/opt/docker-offline/rpm执行完之后目录里会有几十个 rpm 文件包括依赖的 audit-libs、iptables、libcgroup、slirp4netns 等等。注意检查一下有没有 container-selinux这个包不在 docker-ce 仓库里而是在 epel 或 CentOS 的 extras 仓库。如果你的机器没配 epel需要单独处理yum install -y epel-release yumdownloader --resolve container-selinux --destdir/opt/docker-offline/rpm为什么要这么在意这一个包因为 docker-ce 在 CentOS 7 上强依赖 container-selinux缺了它 rpm 安装会直接报错。很多网上的教程没提到这个导致新手跟着步骤走总是在最后一步失败。依赖都齐了之后把 rpm 文件再收拢检查一遍ls -l /opt/docker-offline/rpm/ | wc -l如果数量大于十且包含 docker-ce、docker-ce-cli、containerd.io 这几个关键包基本就没问题了。2.2 方案B实操直接下载 Docker 静态二进制包如果你不想折腾 rpm 依赖直接去 Docker 官方 GitHub Release 页面下载静态包。文件名格式通常是docker-26.1.4.tgz这种注意选择对应架构的包。下载完解压看下内容tar -xzf docker-26.1.4.tgz ls -l docker/解压后出现 docker 目录里面有 docker、dockerd、containerd、containerd-shim-runc-v2、ctr、runc、docker-init、docker-proxy 这些二进制文件。把这些文件看清楚后面在目标机器上部署时要用。静态包里不包含 docker-compose 插件。如果你需要在离线环境里用docker compose还要单独下载 docker-compose-plugin 对应的二进制或 rpm 包。如果只用到docker run这类基础命令compose 可以不装。静态包的好处是同一个包里还包含 containerd 的完整二进制意味着你不需要额外安装 containerd 就能跑起来。rpm 方式反而要额外装 containerd.io 这个包。所以从“下载文件数”角度看静态包更省心。2.3 方案包整理目录结构、清单记录与传输建议准备工作不只是把包拉下来就完事建议顺手做三件小事能让后面到客户现场省很多麻烦。第一建立清晰的目录结构。把 rpm 包或二进制包、镜像 tar 包、安装脚本、说明文档分开存放。一个比较顺手的结构是这样docker-offline/ ├── rpm/ # rpm 包方式所需 ├── binary/ # 二进制包方式所需 ├── images/ # docker save 出来的镜像文件 ├── scripts/ │ ├── install_rpm.sh │ ├── install_binary.sh │ └── load_images.sh └── README.md第二用一条命令生成文件清单和校验信息cd /opt/docker-offline md5sum $(find . -type f) checksums.txt cat checksums.txt | wc -l到了现场拷完文件后可以先用 md5sum 核对一遍避免 U 盘拷贝过程中文件损坏。这个习惯帮我排掉过一次镜像损坏的问题值得坚持。第三传输方式根据网络环境选择。如果网线能通用 scp 或者 rsync 最快scp -r /opt/docker-offline root目标机器IP:/opt/如果网络隔离只能用 U 盘或者摆渡机那也要注意分批拷的时候别漏文件。不管用什么方式拷完后第一件事就是检查 md5。传输这块我多说一句文件数量太多的时候建议先打一个 tar 包再传不然几十个文件一个一个传中间断掉很难发现tar -czf docker-offline.tar.gz docker-offline/3. 目标机器安装全流程从传包到启动一条龙3.1 目标机器上的基础工具准备无论用哪种方式安装目标机器上最好先确保 yum 可用同时安装 unzip、tar、vim 这些基础工具yum install -y vim tar unzip如果系统原本是一个精简的最小化安装连 yum 都没有那要先确认有没有本地的安装介质或者系统镜像先把 yum 基础环境恢复出来。这一步不难但很容易被忽略经常有人到了现场才发现系统连 makecache 都跑不了。另外建议提前看一下 selinux 状态getenforce如果输出是 Enforcing可以配置容器相关参数或者临时设成 Permissive。CentOS 7 下 Docker 跟 SELinux 的配合一直有些摩擦很多环境干脆关闭 SELinux。但关闭 SELinux 属于改变系统安全策略的操作在客户的等保环境里要提前确认能不能这么干。3.2 方式A实操制作本地 yum 源并安装 rpm 包把整个 docker-offline 目录传到目标机器后如果你的包结构里已经准备好了 rpm 目录直接在 /opt/docker-offline/rpm 下执行cd /opt/docker-offline/rpm yum localinstall -y *.rpmyum localinstall 会智能解析当前目录下的 rpm 依赖关系逐个安装。如果遇到提示缺依赖但明明目录里已经有那个 rpm可能是顺序问题可以加--skip-broken试试或者干脆换用 createrepo 做本地源。做本地源的方法也不复杂在包目录里执行cd /opt/docker-offline/rpm createrepo .然后在 /etc/yum.repos.d/local.repo 里写[local-docker] nameLocal Docker Repo baseurlfile:///opt/docker-offline/rpm enabled1 gpgcheck0接着刷新缓存并安装yum clean all yum makecache yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这种本地源方式最大的好处是yum 会自动处理依赖不用关心顺序。而且后续要补装其他 rpm 包只要把包丢进这个目录再执行一次createrepo . yum makecache就能直接 install。安装完成后可以用rpm -qa | grep docker确认安装结果。通常能看到 docker-ce、docker-ce-cli、containerd.io、docker-buildx-plugin 等包。接下来启用服务systemctl enable docker --now systemctl status docker如果服务能起来说明 rpm 方式安装成功。3.3 方式B实操二进制包部署与 systemd 服务配置用静态二进制包方式安装核心就是两步把可执行文件放到 PATH 环境变量能找到的位置然后写 systemd unit 文件让 dockerd 变成系统服务。先把压缩包拷到目标机器并解压tar -xzf docker-26.1.4.tgz cp docker/* /usr/bin/确认一下关键文件都到位docker --version dockerd --version containerd --version能输出版本号说明二进制文件没问题。接着创建 systemd 服务文件。把下面内容写入 /etc/systemd/system/docker.service[Unit] DescriptionDocker Application Container Engine Documentationhttps://docs.docker.com Afternetwork-online.target firewalld.service containerd.service Wantsnetwork-online.target [Service] Typenotify ExecStart/usr/bin/dockerd ExecReload/bin/kill -s HUP $MAINPID LimitNOFILE1048576 LimitNPROCinfinity LimitCOREinfinity TimeoutStartSec0 Delegateyes KillModeprocess Restarton-failure StartLimitBurst3 StartLimitInterval60s [Install] WantedBymulti-user.target这个文件里面的内容我解释一下。Typenotify 表示 dockerd 启动完成时会通过 sd_notify 通知 systemd这样 systemctl start docker 能准确等到服务真正就绪。Delegateyes 是把 CPU、内存等 cgroup 管理权委托给 Docker这是容器资源隔离的基础。Restarton-failure 是让 dockerd 挂掉后自动拉起生产环境非常实用。写完服务文件后还需要配置 containerd 的 systemd 文件否则 docker 内部的 containerd 服务不太好管理。Docker 官方其实提供了 containerd.service 模板但二进制包里没有。你可以直接使用我上面这个 docker.service 文件然后在启动时通过 ExecStart 里的参数指定 containerd socket 路径。不过更省事的方式是让 dockerd 自动管理内部的 containerd不需要额外维护 containerd.service。这一点在 Docker 较新的版本里已经处理得很好不用担心。如果你确实需要单独启动 containerd比如你在用 crictl 或者 Kubernetes可以另外写一个 /etc/systemd/system/containerd.service。但只是跑 Docker 容器的话不写也能正常用。最后启用服务systemctl daemon-reload systemctl enable docker --now systemctl status docker如果状态显示 active (running)二进制方式部署就算成功了。3.4 写 daemon.json数据目录、日志与镜像源一次配好无论 rpm 方式还是二进制方式安装好后都要写 /etc/docker/daemon.json。这个文件控制着 docker 的核心运行时配置一开始就配好能省掉后面大量麻烦。一个生产环境可用的最小配置{ exec-opts: [native.cgroupdriversystemd], log-driver: json-file, log-opts: { max-size: 100m, max-file: 3 }, storage-driver: overlay2, data-root: /data/docker }解释几个关键项。native.cgroupdriversystemd是让 Docker 使用 systemd cgroup 驱动如果你后面要接 Kubernetes这个配置几乎必备。max-size和max-file限制单个容器日志文件的大小和数量默认配置下日志无限增长能把磁盘写满这个我踩过惨痛教训。>systemctl status docker --no-pager确认 active (running)。第二步看版本和系统信息docker version docker infodocker version里 Client 和 Server 两段都正常输出才说明 dockerd 跟客户端通信没问题。docker info里重点看存储驱动、数据目录、Cgroup 驱动这几个字段跟 daemon.json 配的是否一致。第三步如果你带了 hello-world 之类的镜像包直接加载并运行docker load -i /opt/docker-offline/images/hello-world.tar docker run --rm hello-world看到 “Hello from Docker!” 的输出说明整个链路都通了。没有镜像包时可以先用docker ps和docker images验证 Docker API 是否正常响应。只要命令不报 connection refused 或者 permission denied基本没问题。4.2 离线环境真正有用的几条 docker 命令离线环境下没有外网拉镜像日常操作跟在线环境有很大区别。最核心的就是 load 和 save。镜像打包在源机器上执行docker save nginx:1.24 -o nginx-1.24.tar批量打包多个镜像docker save nginx:1.24 redis:7.0 mysql:8.0 -o app-images.tar在目标机器上加载docker load -i nginx-1.24.tar docker load -i app-images.tar关于 save 和 export 的区别我发现很多人容易搞混。docker save保存的是镜像的分层结构包括历史信息可以完整恢复到 Docker 中用于创建容器。docker export导出的是容器的文件系统快照不含镜像历史适合做轻量备份但不适合镜像分发。离线部署场景一定要用docker save和docker load。另外docker tag在离线环境也很有用。内网部署时镜像仓库地址通常变了比如原来是 registry.example.com/project/app:latest内网环境可能是 192.168.0.10:5000/prod/app:latest可以用 tag 重新打标签再推送不用重新构建。4.3 离线环境下镜像导入导出的小技巧实际操作中镜像文件可能非常大一个应用镜像四五个 GB 很正常。传输这类大文件有几个技巧。一是用分卷压缩。tar 包太大时用 split 分卷gzip -c app-images.tar | split -b 2G - app-images.tar.gz.到了目标机器上合并解压cat app-images.tar.gz.* | gunzip -c app-images.tar docker load -i app-images.tar二是镜像瘦身。打包前检查一下镜像里有没有无用的层用docker system df看看空间占用尽量复用基础镜像不要每个应用打一个完全独立的镜像这样离线包体积能省不少。三是优先使用 docker save 的压缩格式。新版 Docker 支持docker save --output配合 gzip 管道但兼容性最好的还是先存 tar 再压缩。我一般直接在传输前做 gzip 压缩十几 GB 的镜像压完能缩到 5GB 左右对 U 盘传输场景特别友好。5. 常见问题与排查技巧实录这几类坑你大概率也会踩5.1 依赖错误libltdl、container-selinux 这类顽固分子离线装 rpm 版 Docker 最常见的报错就是依赖冲突。我遇到过的典型报错长这样Error: Package: docker-ce-xx.x.x.el7.x86_64 (local) Requires: container-selinux 2.107这个错误说明缺 container-selinux而且版本要大于等于指定值。解决办法就是前面 2.1 节说的单独下载对应版本的 container-selinux rpm 包或者把 epel 源里的包直接拉到离线包目录。还有一个容易踩的坑是 libltdl 相关依赖。有些旧版本 Docker 依赖 libltdl.so.7你明明觉得系统应该有但一检查 /usr/lib64 下可能装的是 libltdl.so.7 但链接有问题。这时候可以确认一下依赖库是否完整ldd /usr/bin/dockerd | grep not found任何 “not found” 的库都说明缺依赖把对应 rpm 包补齐即可。用 yumdownloader --resolve 拉包时这个库一般会被自动带上但如果你是在网上手动找的包很容易漏掉。另一个常见场景是用了rpm -ivh --nodeps强行装表面上装上了但 dockerd 跑不起来报各种动态库缺失。所以我强烈建议不要用 --nodeps老老实实用 yum localinstall 或本地源方式。强行跳过依赖是给自己埋雷。5.2 daemon.json 配置不生效的排查修改 daemon.json 后执行systemctl restart docker结果发现配置没生效这类问题我见过很多次。最常见的两个原因一是 JSON 语法错误。daemon.json 要求严格的 JSON 格式多一个逗号或者少一个引号都会导致 dockerd 启动失败。检查方法python -m json.tool /etc/docker/daemon.json如果输出正常显示 JSON 内容说明语法没问题。如果报错会提示具体是第几行出了问题照着改就行。二是 dockerd 启动时没有加载这个文件。有些场景下 docker 是通过自定义脚本启动的没有走 systemd 单元文件这时候 daemon.json 就不会被自动加载。检查 systemd 单元文件里 ExecStart 有没有显式参数覆盖路径。正常情况下/usr/bin/dockerd启动会自动读取 /etc/docker/daemon.json。还有一个细节改完 daemon.json 后如果只执行systemctl reload docker而不是 restart某些配置可能不会重新加载。比如数据目录>systemctl status docker --no-pager -l journalctl -u docker --no-pager -n 100这两条命令能给出详细的错误日志。我遇到最多的几种一是端口被占用。dockerd 默认监听 /var/run/docker.sock如果之前装过其他版本的 docker 没有清理干净sock 文件冲突会导致启动失败。解决办法是把旧的 docker 相关目录清掉systemctl stop docker rm -rf /var/lib/docker /var/run/docker.sock systemctl start docker二是防火墙规则冲突。如果系统上有其他容器运行时比如 containerd 单独起的占用了一些 iptables 链dockerd 启动时初始化 iptables 会失败。这种时候可以先临时停掉 firewalld 看是否恢复systemctl stop firewalld systemctl start docker如果确认是防火墙导致再针对 Docker 放行相关端口而不是简单粗暴地停防火墙。三是 overlay 模块未加载。CentOS 7 的 overlay2 存储驱动需要内核模块支持modprobe overlay echo overlay /etc/modules-load.d/overlay.conf内核模块加载后重启 docker一般就能解决。5.4 网络与权限问题firewalld、iptables、SELinux 的坑离线环境经常因为网络策略太严格导致容器网络出问题。最典型的表现是容器起来后外访不通或者宿主机访问不到容器的端口映射。先检查 firewalld 是否拦截了容器通信。Docker 在启动时会往 iptables 里写规则如果 firewalld 的富规则跟容器网段冲突容器网络就会异常。排查方法iptables -L -n | grep DOCKER如果 DOCKER 链不存在说明 dockerd 创建网络时 iptables 规则没写成功。常见的诱因是 iptables 版本跟内核不匹配或者 nf_tables 与 iptables 共存导致冲突。CentOS 7 上比较稳妥的做法是统一用 iptables 服务关闭 firewalldsystemctl stop firewalld systemctl disable firewalldSELinux 干扰也比较常见。当 getenforce 输出 Enforcing而容器的挂载或端口映射出现 permission denied 时可以先临时设为 Permissive 验证setenforce 0 docker run --rm -p 8080:80 nginx能正常跑就是 SELinux 的问题。长期方案有两种一是保持 SELinux 开启但正确配置容器上下文策略二是在合规允许的前提下设置 /etc/selinux/config 里的 SELINUXpermissive。具体选哪种要看你客户环境的安全基线要求。最后再说一个容易被忽略的点权限问题。装完 Docker 后普通用户执行 docker 命令会提示 permission denied因为 /var/run/docker.sock 的属组是 root:docker。要么把用户加入 docker 组usermod -aG docker youruser newgrp docker要么所有命令都用 sudo 执行。管理类服务器上我不推荐给太多人 docker 组权限因为 docker 组权限约等于 root 权限能操作 docker 就意味着能挂载宿主机目录等于拿到了宿主机控制权。这是一个需要提前跟团队对齐的安全边界问题。我个人在实际操作中还有一个习惯装完 Docker 后立刻做一次镜像目录的备份策略。不管是用定时任务把 /data/docker 里的重要容器数据定期打包还是提前规划好容器数据卷的挂载位置都建议在部署当天就定下来。Docker 本身不提供数据备份能力这块儿依赖人工规划。提前把目录规范定好后面无论是补镜像、扩磁盘还是迁移主机都会省非常多事。
返回列表