简介:这是一套专为CentOS系统离线安装Docker 1.12.6而打包的rpm软件包集合,适合内网环境或不便联网的服务器运维人员使用,帮助用户免去自行寻找依赖包的麻烦。整个压缩包共16个文件,以docker、docker-client、docker-common等核心rpm安装包为主体,同时附带repodata仓库元数据(含xml.gz、sqlite.bz2等格式),可用于yum本地源的配置与依赖校验,整体体积约19.27MB,小巧易传输。资源当前已有381人学习下载,说明其在同类离线部署场景中具有一定参考价值。通过这份软件包,用户能一次性获得安装Docker所需的必要组件和依赖信息,按常规rpm安装或搭建本地yum源即可完成部署,适合初学者快速上手,也方便运维人员批量分发安装。
1. 离线安装 Docker 的 rpm/tar 包:一套流程把内网机器的容器环境从零拉起来
内网服务器要装 Docker,在线环境一条yum install docker的事,放到隔离网络里往往能折腾一整个下午。这份 docker.rpm.tar 软件包就是干这个用的:它把 Docker 的服务端、客户端以及依赖组件打成 rpm 集合,把可用的镜像打成 tar 包,让没有外网的 CentOS/RHEL 机器也能把整套容器环境搭起来。适合的读者很明确——做交付部署的运维、搞内网实验的工程师、在比赛机房临时搭环境的队伍。我拿它在客户现场装过好几次,最大的感受是:真正难的从来不是执行命令,而是搞清楚 rpm 和 tar 各负责哪一块,顺序错一步,后面全是坑。
2. rpm 包与 tar 包的分工:先分清程序文件和镜像文件再动手
2.1 为什么离线交付首选 rpm 集合而不是一个安装脚本
在线环境下,yum 会直接从软件源拉取 Docker 的 rpm 包,并且自动解析依赖链。依赖缺失时它会提示你缺什么、问你要不要一起装;依赖冲突时它会拒绝执行。这一切的前提是机器能访问软件源。内网机器没有这个条件,所以交付方通常会把 Docker 本体拆成一组 rpm 包——docker-ce、docker-ce-cli、containerd.io、docker-compose-plugin这些组件全都在里面,一起拷到目标机器上,再通过本地安装方式把它们装好。
那镜像为什么用 tar 而不是 rpm?因为镜像本身不是“软件包”,它是一套分层文件系统,由多个只读层堆叠而成。rpm 只管安装程序文件、注册 systemd 服务,它没办法把镜像装进 Docker 的存储目录。镜像是通过docker save导出成 tar 归档,再在目标机器上用docker load导回本地镜像库。所以这套软件包里的 rpm 负责让 Docker 变成可执行程序,tar 负责让 Docker 里有可跑的镜像,两者各管一段,缺一个都不行。
2.2 动手前先摸清现场:三个命令判断这台机器适不适合装
我拿到这种离线包,第一件事不是急着解压,而是先看目标机器是什么系统、有没有装过 Docker、SELinux 是什么状态。这三个问题没确认之前就安装,后面几乎一定会翻车。
# 确认系统发行版本和内核,判断这份 rpm 包是否匹配 cat /etc/redhat-release # 检查机器上有没有装过其他版本的 docker rpm -qa | grep docker # 查看 SELinux 当前状态,enforcing 状态常导致容器启动失败 getenforce第一条命令看系统版本。这份 rpm 包面向 CentOS/RHEL 7 或 8 是常见做法,你至少要知道目标系统是哪个大版本,因为yum localinstall的行为在 7 和 8 上不一样,rpm 依赖关系也会有差异。第二条命令看已有安装:如果机器上已经装了旧版docker-ce,直接再装一份新的,大概率出现版本冲突或者旧服务抢占端口。第三条命令看 SELinux,enforcing状态下 Docker 的 overlay 存储驱动可能会被安全策略拦下来,导致容器创建失败。常见做法是临时用setenforce 0切到 permissive,确认能跑起来之后再决定要不要固化这个配置。
2.3 安装 Docker 本体:yum localinstall 批量处理 rpm 依赖
确认现场没问题之后,进入存放 rpm 包的目录,执行安装命令。我见过不少同事在这一步直接用rpm -ivh *.rpm,然后被一堆依赖报错砸懵。正确的姿势是用yum localinstall,让 yum 帮你做依赖分析。
# 进入 rpm 包所在目录 cd /root/docker-packages # 用 yum 本地安装,自动解析 rpm 之间的依赖关系 yum localinstall -y docker-ce-*.rpm docker-ce-cli-*.rpm containerd.io-*.rpmlocalinstall是 yum 针对本地 rpm 文件设计的安装模式,它会把目录里的 rpm 作为数据源来解析依赖,找到docker-ce依赖containerd.io,就一起装或者按顺序装。-y参数跳过交互确认,脚本化部署时必须有它。文件名里的*是为了匹配带版本号的完整文件名,比如docker-ce-24.0.x.el7.x86_64.rpm。这里要强调一个边界:如果这份包里还带了docker-compose-plugin或docker-buildx-plugin,我一般会一起写上,避免后面用docker compose时才发现命令不存在。
有人问为什么不直接rpm -ivh *.rpm?因为 rpm 工具本身不做依赖解析,它只负责按你给的顺序一个个装。顺序不对时它会直接报libcgroup is needed by docker-ce这类错误然后中止,不会像 yum 那样帮你自动调整安装顺序。离线环境下手动排 rpm 依赖顺序是纯玄学,不如直接交给 yum。
2.4 如果系统里压根没有 rpm 命令
搜索“没找到 rpm 命令”能找到一堆人问这个问题。-bash: rpm: command not found通常有两种情况:一是机器不是 RHEL 系发行版,rpm命令从未安装;二是 PATH 环境变量被改过了,/usr/bin不在里面。确认方法很简单:
# 查看 rpm 命令的位置 which rpm # 如果没有输出,检查 PATH echo $PATH如果which rpm没有结果,先看/etc/os-release里写的是什么发行版。如果是 Ubuntu/Debian 系,这份 rpm 包根本不适用,Debian 系用的是dpkg和.deb包,你需要找对应格式的离线包。如果系统确实是 CentOS 但 rpm 缺失,常见做法是用yum install -y rpm先把包管理器补上。另外也要提醒一点:有人喜欢在 Windows 上先解压这份 tar 包看看里面有什么,Windows 10 以上自带的tar.exe确实能解压,但解压出来的 rpm 文件在 Windows 上毫无意义,它必须回到 Linux 环境里用 yum/rpm 安装。
3. tar 包加载与镜像恢复:从 docker save 产物到容器能跑起来
3.1 先看 tar 包里到底装了什么:三步快速识图
rpm 装完,Docker 本体是有了,但镜像还在 tar 包里躺着。拿到一个 tar 包,我从不直接tar -xvf全解出来,而是先看它的目录结构,因为这决定了接下来怎么处理。
# 查看 tar 包内文件列表,只看前 20 行 tar -tf images.tar.gz | head -20 # 查看文件类型,确认是不是 docker save 生成的归档 file images.tar.gz如果列表里出现manifest.json、repositories这类文件,以及一堆十六进制命名的目录,这基本可以断定是docker save的标准产物,里面装的是镜像层数据。这种情况你完全不需要手动解压,直接让 Docker 自己读。如果列表里是docker/、dockerd、containerd/这类可执行文件路径,那就是把 Docker 程序文件打了包,这种 tar 走的是另一套用法,后面 3.3 节单独说。file命令的输出可以告诉你这个文件是 gzip 压缩还是裸 tar,压缩过的在加载前可能要多一步解压。
3.2 docker load 的正确姿势:不要先解压再导入
很多人会习惯性地先tar -xvf把镜像解压出来,再想办法导入,这是绕了远路。docker load本身就支持直接读压缩或未压缩的 tar 归档,你把目录解出来反而会破坏 Docker 期望的目录结构。我一般是直接交给 load:
# 直接加载镜像归档,Docker 会自动识别层结构 docker load -i images.tar.gz # 加载完成后立刻查看镜像列表,确认有没有导入成功 docker images # 如果镜像名是 <none>,手动打上自己习惯的 tag docker tag <IMAGE_ID> myapp:latest-i参数指定输入文件,支持 gzip 压缩的 tar,Docker 内部会处理解压逻辑。docker images的作用是验证:成功的输出会有一行Loaded image: xxx:latest,同时docker images里能看到对应的 REPOSITORY 和 TAG。注意这里有个细节——如果对方导出镜像时用的是镜像 ID 而不是仓库名+标签,docker load完成后你的镜像列表里会出现一堆<none>:<none>的悬空镜像。这种镜像不是坏的,它只是缺少名字,可以用docker tag重新打上一个 tag。如果你不做这一步,后面docker run就没有一个可以直接引用的名字,只能用IMAGE_ID,操作起来非常别扭。
3.3 如果 tar 包不是 save 产物,而是 docker 程序包
docker save导出的 tar 是镜像,但还有一种情况:对方把整个 Docker 程序目录,包括docker、dockerd、containerd这些二进制文件直接打包成 tar 给你。这种情况在应急交付里也常见。处理方式是解压到系统目录,然后手动配置服务。
# 解压到临时目录,看清楚里面的结构 mkdir -p /tmp/docker-bin && tar -xvf docker-binary.tar -C /tmp/docker-bin # 把二进制放到标准目录,并建立 PATH 软链 mv /tmp/docker-bin/docker /usr/bin/docker mv /tmp/docker-bin/dockerd /usr/bin/dockerd # 配置 systemd 服务,让 docker 开机自启 cat > /etc/systemd/system/docker.service <<'EOF' [Unit] Description=Docker daemon After=network-online.target [Service] Type=notify ExecStart=/usr/bin/dockerd ExecReload=/bin/kill -s HUP $MAINPID Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable --now docker这个 systemd 服务文件是离线场景下最简可用版本:ExecStart指向 dockerd 的实际路径,Type=notify是 docker 官方常用的服务类型,要求 dockerd 启动完成后通过 sd_notify 通知 systemd,Restart=on-failure保证进程异常退出后会自动拉起。文件写完后必须daemon-reload,否则 systemd 不会识别新服务。这种二进制 tar 包的劣势也提一下:它缺少containerd、runc这类底层组件的话,dockerd 可能起不来,而且没有 rpm 包的版本记录,后面想卸载只能手动删文件,很难清干净。
3.4 加载前检查磁盘空间:镜像体积和你想的往往不一样
内网服务器上磁盘空间不足导致 load 失败,是我遇到过最多的问题之一。docker load不是简单拷贝 tar 文件,它要把镜像的每一层解包写入/var/lib/docker的存储目录,消耗的空间通常是 tar 包体积的 1.2 到 1.5 倍。加载前先看一眼磁盘,成本极低。
# 查看 Docker 数据目录所在分区的空间 df -h /var/lib/docker # 对比 tar 包体积 du -sh images.tar.gz # 清理不再使用的镜像层,释放空间 docker system prune -fdf -h看可用空间,du -sh看 tar 包实际大小,两者一对比就知道够不够装。如果空间紧张,docker system prune -f是常用手段,它会清理所有悬空镜像和停止的容器、无用网络。注意它不会删除你正在使用的镜像,也不会删带 tag 的镜像,安全边界比较明确。另一个容易被忽略的点是内核版本:overlay2 存储驱动要求内核支持,老机器内核版本过低时,即使镜像 load 成功,容器运行也可能报failed to mount overlay。我一般会顺带执行uname -r确认内核版本在 3.10 以上,CentOS 7 默认内核在这个边界上,docker 官方对 overlay2 的支持从内核 4.0 开始才算完整。
4. 离线部署避坑指南:五个必踩的坑,含 rpm 依赖和 tar 权限问题
4.1 现象:rpm -ivh *.rpm 装到一半报 libcgroup is needed
有同事第一次装这份离线包,直接rpm -ivh docker-ce-*.rpm,装到一半报错:error: Failed dependencies: libcgroup is needed by docker-ce。我另一个同事更头铁,把 rpm 传参顺序换了一下,结果缺的依赖从 libcgroup 变成了别的。原因:rpm 工具本身不做依赖解析,它按你命令行里给的顺序逐个安装,后一个包依赖前一个包时,一旦顺序不对就会立即中止;而且 Docker 的 rpm 包依赖一堆底层库,靠人肉排序根本不现实。解决:统一改用yum localinstall -y *.rpm,yum 会把目录里所有 rpm 作为候选源,自动解析依赖关系并排好安装顺序,一次性装完。从那以后我再也不在 CentOS 上手工排列 rpm 顺序了。
4.2 现象:docker load 成功,但 docker run 提示 no such image
docker load -i images.tar输出Loaded image ID: sha256:xxxx,看起来一切正常,但执行docker run nginx:latest提示Unable to find image 'nginx:latest' locally。原因:对方在导出镜像时本地只有镜像 ID,没有仓库名和 tag,docker save导出的 tar 里就没有nginx:latest这个引用,load 进来后只能看到一个<none>镜像。解决:先用docker images查看镜像 ID,再手动打 tag:
docker images docker tag <IMAGE_ID> nginx:latest docker run -d --name web -p 8080:80 nginx:latestdocker tag的第一个参数是镜像 ID,第二个参数是你要赋予的仓库名和标签。这一步是 load 之后最容易遗漏的操作,漏了它后面所有引用镜像名的命令全都会找不到。我现在每次 load 完的习惯是先跑docker images看 REPOSITORY 列是否全是<none>,是就立刻补 tag。
4.3 现象:docker 命令提示 permission denied,连接不上 docker api
在服务器上输入docker ps,报错内容里有permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock。原因:docker 客户端连接的是/var/run/docker.sock这个 unix socket,当前用户不在docker用户组里,没有读写权限。很多内网机器的管理员习惯用 root 装完 docker 后直接把 root 给交付人员,交付人员又用普通用户登录操作,就撞上这个权限墙。解决:把当前用户加进 docker 组,重新登录会话。
usermod -aG docker $USER newgrp docker docker ps-aG表示追加用户到 docker 组,newgrp docker用于当前会话立即刷新组权限,不用退出重新登录。我处理客户现场问题时还会顺手检查另一种情况:如果连 root 执行docker ps都报一样的错,多半是 docker 服务本身没起来,先用systemctl status docker看服务状态再对 socket 做排查。
4.4 现象:tar 包解压出来的 docker 二进制启动时报 Cannot connect to the Docker daemon
拿到的是 docker 程序 tar 包,解压完执行docker version,输出 Client 版本正常,但 Server 部分显示Cannot connect to the Docker daemon。原因:你解压的只是客户端二进制,dockerd 守护进程压根没启动,或者你用的docker是客户端,它连接后台服务时才去检查守护进程;二进制 tar 包不像 rpm 包会自带 systemd 服务文件,不会自动注册开机自启。解决:确认/usr/bin/dockerd是否存在并手动拉起服务。
# 确认 dockerd 二进制存在 ls -l /usr/bin/dockerd # 手工启动 dockerd,前台运行观察日志 dockerd --host=unix:///var/run/docker.sock # 确认可以连接后,Ctrl+C 结束,用 systemd 管理 systemctl start docker这里有个常见误解要说明:docker命令和dockerd是两个程序,前者是客户端,后者是守护进程;客户端永远只负责把请求发给守护进程,它自己不会去启动容器。如果你解压出来的 tar 包里只有docker而没有dockerd,那它只算半个交付包。我在 3.3 节写过最简 systemd 服务文件的写法,遇到这种包先把服务文件补上再谈后面的使用。
4.5 现象:镜像加载进去了,但容器一启动就立即退出
docker run -d nginx执行成功,容器状态却显示Exited (0)或者Exited (1)。原因分两类:一类是镜像本身没问题,但启动命令需要前台进程,你手动指定的命令把进程跑后台了;另一类是内网机器 SELinux 处于 enforcing 状态,容器挂载或网络初始化被策略拦截,退出码是 1。解决:先看日志,再查 SELinux,不要上来就重建容器。
# 查看容器退出日志 docker logs <CONTAINER_ID> # 检查 SELinux 状态 getenforce # 临时切换到 permissive 模式,确认是不是 SELinux 拦截 setenforce 0 docker start <CONTAINER_ID>docker logs是第一步,它能告诉你进程退出前最后写了什么;setenforce 0是排查手段,切到 permissive 后如果容器能正常启动,那问题就锁定在 SELinux 策略上。内网生产环境不建议长期 permissive,更合适的做法是把需要用到的端口通过semanage port -a -t docker_port_t -p tcp <端口>加入策略,或者按 docker 官方文档给存储目录设置正确的 SELinux 标签。
5. 把离线安装流程固化成脚本:从手工 step 到一条命令交付
踩过上面这些坑之后,我给客户交付离线包时不再手工一步步敲命令,而是把整个流程写进一个安装脚本,和 rpm/tar 包放同一目录。这个脚本现在是我离线部署的标准起点。
#!/bin/bash set -e cd "$(dirname "$0")" echo "==> [1/4] 安装 Docker rpm 包" yum localinstall -y docker-*.rpm echo "==> [2/4] 启动并设置开机自启" systemctl enable docker systemctl start docker echo "==> [3/4] 加载目录下所有镜像 tar 包" find . -maxdepth 1 -name "*.tar" | xargs -n1 docker load -i echo "==> [4/4] 验证版本与镜像列表" docker version --format 'Server: {{.Server.Version}}' docker images脚本里的细节值得说一下。set -e让脚本在任意一步失败时立即退出,避免装到一半没发现错误继续往后跑。cd "$(dirname "$0")"保证无论在哪个目录执行脚本,都能切换到脚本所在目录,也就是 rpm 包和 tar 包所在的目录。第三步用find + xargs而不是for循环,是因为xargs -n1每次只传一个文件给docker load -i,文件名里有空格也不会出错;我用for i in *.tar时踩过文件名带空格的坑,$i会被拆成两个参数,load 直接失败。第四步的docker version --format只输出 Server 版本字段,方便自动化脚本后面做版本判断。
这个脚本不是万能药:如果目标机器的 yum 源本身有问题,yum localinstall可能尝试去外网拉依赖;如果这份离线包里的 rpm 不完整,第二步systemctl start docker会失败。我通常在步骤二后面再加一段检查逻辑,用systemctl is-active docker判断服务状态,不活跃就输出错误并退出。从那以后我每次交付离线 Docker 环境都强制走一遍这条流程:环境检查、yum 本地安装、服务拉起、镜像加载、版本验证,五步串成一行命令。这套脚本帮我少处理了至少十次现场突发的依赖问题,也把交接成本压到了最低,希望帮到你。
本文还有配套的精品资源,点击获取