简介:在无外网或内网隔离环境中,为CentOS 7.6配置容器运行环境常因依赖缺失而受阻。该资源包完整收录docker-ce-19.03与nvidia-docker2离线安装所需材料,面向系统运维、深度学习平台搭建及GPU容器化部署人员,解决离线条件下安装Docker及NVIDIA Container Toolkit的难题。压缩包共30个文件,约91.98MB,以20个rpm安装包为核心,涵盖docker主程序、CLI、containerd及nvidia-container-toolkit全套组件,并附有本地仓库元数据、gpg校验文件、daemon.json配置模板与说明txt,既可直接rpm批量安装,也可作为内网yum源使用,便于校验包体完整性并快速完成运行配置。安装流程与依赖处理思路已写入说明文档,可指导读者完成rpm安装、服务启用与配置生效。已有3312人学习下载,适合内网环境复现GPU容器能力的运维与开发人员直接取用。
1. 离线装 Docker 19.03 + nvidia-docker2:为什么离线环境里这套组合最难,也最值得装
一台 CentOS 7.6 的 GPU 服务器,外网被墙得死死的,但任务要求你在上面跑基于容器的深度学习推理。你第一个想到的就是 docker-ce 19.03 加 nvidia-docker2:前者让容器编排变干净,后者让容器能用上显卡。这个组合真正的难点不在 docker-ce 本身,而在“离线”两个字——yum 默认源全部连不上,rpm 依赖链却一环套一环。我第一次在离线机装这套东西时,光处理本地 repo 的 metadata 就花了三个小时。
这个场景适合内网机房、科研集群、有安全合规要求的离线 GPU 服务器。下面这套做法,我会讲清楚怎么在联网机器上把整套 rpm 拉齐,怎么搬到离线机做本地源,以及 nvidia-docker2 安装后最容易翻车的几个点。你按步骤走,基本一次成。
2. 离线的根基:先核对 CentOS 7.6 内核、驱动和已有容器环境,再决定下载什么包
2.1 这台离线机到底缺什么:内核版本、gcc、驱动和 nvidia-smi 先查一遍
离线安装最忌讳拿到机器就直接去下载 rpm。如果目标机内核太老,或者显卡驱动根本没装,后面所有步骤都是在白费。我一般先登录离线机,把下面四个现状查清楚:
cat /etc/redhat-release uname -r which gcc && gcc --version | head -1 which nvidia-smi && nvidia-smi第一个命令确认系统版本一定是 7.X,因为 docker-ce-19.03 的 rpm 包在 el7 和 el8 上是分开的,你要是把 el8 的包硬塞给 CentOS 7.6,yum 会直接报os release is not supported。第二个命令看内核,CentOS 7.6 默认内核是 3.10.0-957,docker 19.03 要求内核不低于 3.10,这个没问题。第三个命令查 gcc,不是必须,但如果你后面要编译内核模块或者装某些 NVIDIA 驱动组件,没有 gcc 会很痛苦。第四个命令最关键:nvidia-smi能跑,说明 NVIDIA 驱动加载正常;查出来的驱动版本和 CUDA 版本,直接决定了你在容器里能跑什么镜像。
如果这台机器上已经装过其他版本的 docker,先别急着卸载。先执行docker --version看看是不是已经存在 19.03。如果存在旧版,需要把/var/lib/docker里的容器数据备份好,或者卸载干净,否则新装的 dockerd 启动时会因为老数据库格式不兼容而崩溃。我遇到过一台机器上同时存在 docker-ce 和 docker-io 的残留,导致dockerd启动时报unrecognized daemon option,排查了半天。
另外,务必确认/dev/nvidia*设备节点是否存在。ls -l /dev/nvidia*能列出 nvidia0、nvidiactl、nvidia-modeset 等设备。这个检查很重要,因为 nvidia-docker2 是在容器启动时动态向里面注入这些设备,如果设备节点不存在,即使 runtime 配置正确,容器里依然跑不了 nvidia-smi。
2.2 备一台联网同版本机器,用 repotrack 把 docker-ce-19.03 全套依赖拉下来
离线安装说白了就是“在能上网的机器上把目标机的软件世界平移过去”。所以我强烈建议你准备一台和离线机完全相同的 CentOS 7.6 联网机,哪怕是一台虚拟机。不要在这台机器上装 docker,只用来下载 rpm。然后安装 yum-utils 和 createrepo,这两个工具包是生产离线源的基础。
临时添加 docker-ce 官方源和 nvidia-docker 的源。这里需要说明:docker-ce 源加进去之后,yumdownloader才能认出 docker-ce 包;nvidia-docker 源加进去之后,nvidia-docker2 等包才可被下载。命令如下:
yum install -y yum-utils createrepo rpm --import https://download.docker.com/linux/centos/gpg yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo curl -s -L https://nvidia.github.io/nvidia-docker/centos7/nvidia-docker.repo -o /etc/yum.repos.d/nvidia-docker.repo yum makecache加完源之后,先看一眼 docker-ce 19.03 在仓库里的具体版本号。这一步很关键,因为你直接写docker-ce-19.03时,yumdownloader会把当前仓库里最新的 19.03.x 拉下来,但 19.03 的小版本可能已经迭代到 19.03.15 甚至更高。不同小版本对 containerd.io 的依赖要求不完全一致,所以我们要锁定一个具体版本。
yum list docker-ce --showduplicates | grep "19.03"从输出里挑一个你想要的版本号,例如docker-ce-19.03.15-3.el7.x86_64。然后用yumdownloader --resolve下载。--resolve参数会让 yum 自动把依赖的包也下载进来,这是离线安装最重要的参数。我把 docker-ce 和 nvidia-docker2 相关的包一次下载齐全:
mkdir -p /data/docker-offline-rpms yumdownloader --resolve --destdir=/data/docker-offline-rpms \ docker-ce-19.03.15-3.el7.x86_64 \ docker-ce-cli-19.03.15-3.el7.x86_64 \ containerd.io \ nvidia-docker2 \ nvidia-container-runtime \ libnvidia-container1 \ libnvidia-container-tools下载完成后,用createrepo /data/docker-offline-rpms生成本地仓库元数据。这一步是必须的,因为离线机上 yum 访问本地目录时,需要依赖 repodata 目录下的一堆.xml.gz文件。之后再把这个目录打包传输过去即可。
这里有一个常见的操作误区:很多人图省事,直接rpm -ivh *.rpm去装。这会在依赖缺失时给你列出长长一串错误。正确做法是让 yum 从本地 repo 安装,让它自动按依赖顺序处理。所以createrepo生成的 repodata 不只是给联网机看的,更是给离线机 yum 识别用的。记得把整个目录包括 repodata 一起拷贝,别只拷 rpm 文件。
3. 制作本地 yum 源并离线安装 docker-ce-19.03:最小命令与三个必调参数
3.1 在离线机上创建本地 repo,用 yum install 装 docker-ce
把打包好的 rpm 目录拷贝到离线机任意路径,我习惯放在/data/docker-offline-rpms。然后写一个 local.repo,让 yum 优先从本地目录里找包。这里要注意:不要改动系统原有的 CentOS-Base.repo 等文件,离线机没有外网时,它们会拖慢 yum 甚至导致超时。我们可以通过--disablerepo='*'在命令行里屏蔽所有外部源,只显式启用 local。
cat > /etc/yum.repos.d/local.repo <<'EOF' [local] name=local-offline-docker baseurl=file:///data/docker-offline-rpms enabled=1 gpgcheck=0 EOFgpgcheck=0 是因为离线环境里你可能没有 docker 官方 GPG 密钥文件。如果你们内网有严格的 rpm 签名要求,可以另想办法导入密钥,但大多数业务场景下这个参数最省事。写完后执行yum clean all && yum makecache刷新缓存,随后运行安装命令:
yum -y --disablerepo='*' --enablerepo=local install docker-ce docker-ce-cli containerd.io安装过程应该没有任何外网请求,所有包都从/data/docker-offline-rpms里找到。如果日志里出现Could not resolve host之类的内容,说明你还有外部源没屏蔽干净,去看一下/etc/yum.repos.d/里是不是有.repo没禁用。安装完成后,可以用rpm -qa | grep docker确认版本:
rpm -qa | grep -E "docker-ce|containerd"输出里应该能看到 docker-ce-19.03.15-3.el7.x86_64 和 containerd.io-1.6.x 之类的包名。这里有个细节:19.03 的 docker-ce 包依赖的 containerd.io 版本不能太高,官方在 19.03 时代推荐的是 1.2.10 系列,但后来为了修 CVE,也会允许 1.4 甚至 1.6。只要 yum 能在本地 repo 里解析出满足依赖的 containerd.io,安装就能成功。如果解析到一半断掉,多半是下载时没有把 containerd.io 的依赖也一并--resolve下来。
3.2 启动 docker 并验证 CLI:这里最容易碰上版本不匹配和 cgroup 驱动问题
docker-ce 安装完成后别急着启动,先编辑/etc/docker/daemon.json。如果没有这个文件,需要手动创建。在离线场景下,我一般建议至少设置三处参数:exec-opts 里的 cgroup 驱动、storage-driver 和 insecure-registries。
cat > /etc/docker/daemon.json <<'EOF' { "exec-opts": ["native.cgroupdriver=systemd"], "storage-driver": "overlay2", "insecure-registries": ["registry.local:5000"] } EOFcgroup 驱动是这里最容易翻车的。CentOS 7.6 默认的 init 系统是 systemd,而 docker 默认 cgroup 驱动是 cgroupfs。如果两者不一致,会在启动时看到非常隐蔽的错误,比如容器能创建但无法看到 PID。设成native.cgroupdriver=systemd能避免这个麻烦。storage-driver 设为 overlay2,因为 CentOS 7.6 的 xfs 文件系统默认支持 d_type,overlay2 性能最稳。insecure-registries 是给内网私有仓库用的,如果你们有 HTTP 镜像仓库,这行必须配置;没有的话可以去掉。
配置好之后启动服务并检查:
systemctl enable --now docker systemctl status docker docker info | grep -E "Cgroup Driver|Storage Driver|Server Version"docker info里能看到Server Version: 19.03、Storage Driver: overlay2、Cgroup Driver: systemd。这里如果Server Version显示的是18.09或20.10,那说明你装错了源,或者本地 repo 里有其他版本被 yum 选中了。这种情况下不要启动容器,立刻回联网机重新下载正确的包。
另一个常见现象是 docker-cli 版本和 dockerd 版本不一致。比如你只装上了 docker-ce-cli 20.10,而 daemon 是 19.03,docker version会出现Client: 20.10和Server: 19.03。它们的协议是兼容的,但--gpus参数的行为会有差异。所以确认rpm -qa | grep docker-ce里前面的 docker-ce 和 docker-ce-cli 小版本必须完全一致,都是 19.03.15,否则后面跑 nvidia-docker2 会不确定。
4. nvidia-docker2 离线安装:从 nvidia-container-runtime 到 Docker 的 Runtime 配置
4.1 nvidia-docker2 的依赖关系,为什么必须先装 libnvidia-container
nvidia-docker2 其实是一个元包,真正干活的是 nvidia-container-runtime,而它又依赖 libnvidia-container1 和 libnvidia-container-tools。libnvidia-container 负责在容器启动时探测宿主机驱动、设备节点和库文件,然后把它们绑定到容器里。理解这个依赖链你才能知道离线打包时缺一对少三。
用户名下经常有人只安装 nvidia-docker2,却忽略了 libnvidia-container 系列。安装时 yum 会给出缺少依赖提示,但如果你用了--nogpgcheck强制安装,最后会导致 nvidia-container-runtime 起不来,docker run --gpus直接报nvidia-container-cli: initialization error: driver library version mismatch。所以在联网机下载 rpm 时,必须把 libnvidia-container 相关的四个包都下载进来。这些包不依赖外网,体积也不大,拉齐很轻松。
另外注意 nvidia-docker2 的 rpm 源里可能包含nvidia-container-toolkit这个包。在 docker 19.03 上它是可选组件,它提供的是docker run --gpus的辅助脚本。如果下载源里有,也一并放进本地 repo。这样离线安装时,yum 能自动判断依赖,不会因为缺少一个可选包而中断。
4.2 把 nvidia-docker2 的 rpm 离线传进去,用 rpm 还是 yum:指定本地 repo 安装
既然我们已经有了local.repo,离线安装 nvidia-docker2 就不要用rpm -ivh一个个装,那是给自己找麻烦。直接在相同本地 repo 里执行:
yum -y --disablerepo='*' --enablerepo=local install nvidia-docker2 nvidia-container-runtime libnvidia-container1 libnvidia-container-tools这个命令会让 yum 自动检查 docker-ce 是否已安装、版本是否为 19.03。如果 docker-ce 版本太低或太高,yum 都会提示冲突。这里务必注意:nvidia-docker2 在安装时会运行自己的 RPM 脚本,它可能会检查系统里的 docker 路径并在/etc/docker/daemon.json里写入 nvidia runtime 配置。如果之前你已经有一个 daemon.json,并且里面写了一堆 registry-mirrors 和 storage-driver,这个脚本不会做“增量修改”,而是直接覆盖或者跳到冲突状态。
我遇到过的情况是:原本 daemon.json 里配置了私有仓库,装完 nvidia-docker2 后,文件里只剩 nvidia runtime 一段,私有仓库配置丢了,导致之后 pull 镜像失败。后来我养成了习惯:在执行这条安装命令之前,先cp /etc/docker/daemon.json /etc/docker/daemon.json.bak,装完再检查一遍文件内容,把丢失的配置补回去。
安装完成后,用rpm -qa | grep nvidia验证一下包列表,你至少能看到nvidia-container-runtime和libnvidia-container1这两个包。如果你下载时漏了libnvidia-container-tools,输出里会少它,容器启动 hook 可能找不到nvidia-container-cli工具,随后就是一连串诡异报错。
4.3 配置 /etc/docker/daemon.json 启用 nvidia runtime,并用 nvidia-smi 容器验证
如果安装脚本没有自动写好 nvidia runtime,我们就手动维护 daemon.json。手动写其实更可控。完整配置如下:
cat > /etc/docker/daemon.json <<'EOF' { "default-runtime": "nvidia", "runtimes": { "nvidia": { "path": "nvidia-container-runtime", "runtimeArgs": [] } }, "exec-opts": ["native.cgroupdriver=systemd"], "storage-driver": "overlay2", "insecure-registries": ["registry.local:5000"] } EOF重点解释 runtime 段落:default-runtime 设成 nvidia 之后,你不需要每条命令都加--runtime=nvidia也能用 GPU;path必须是 nvidia-container-runtime 的绝对路径,你可以先which nvidia-container-runtime确认,如果在安装时没有生成这个可执行文件,多半是 libnvidia-container-tools 没装好。runtimeArgs 留空即可。
配置好之后执行systemctl restart docker。然后先用命令验证 runtime 是否注册成功:
docker info | grep -A2 "Runtimes"输出要能看到nvidia这个 runtime,并且它后面带了path=nvidia-container-runtime。如果少这一行,说明 daemon.json 没被 docker 读到,多半是文件里多了一个不可见字符,或者 last section 没闭合。接着做最直接的验证,用本地已有的 CUDA 镜像跑一个容器,执行nvidia-smi。如果离线机没有镜像,先通过内网 registry 拉取一个,不要指望从 Docker Hub 拉。
docker run --rm --gpus all <你的cuda镜像> nvidia-smi容器里能打印出 GPU 型号和驱动版本,就代表 nvidia-docker2 生效。如果宿主机的驱动已经加载,但容器 nvidia-smi 报错,先回宿主机执行nvidia-smi确认正常,然后检查/dev/nvidia*节点权限,这一步我在避坑章节里展开。
5. 离线安装避坑:19.03 与 nvidia-docker2 的五个常见翻车现场
5.1 现象:yum 报错 Error: Failed to download metadata for repo ‘base’
很多离线机一执行 yum 命令就开始卡住,几分钟后报错Failed to download metadata for repo 'base'。这个现象的直接原因是系统默认的 CentOS-Base.repo 指向了外网镜像,而离线机无法访问。但更深层的原因是你没有意识到 yum 每次都会刷新 metadata,即使你只是安装一个本地 rpm。解决方法是彻底屏蔽非本地源。我习惯用如下操作:
mkdir -p /var/tmp/yum-bak mv /etc/yum.repos.d/CentOS-Base.repo /var/tmp/yum-bak/ mv /etc/yum.repos.d/*.repo /var/tmp/yum-bak/ 2>/dev/null || true把原来所有 repo 文件移走之后,重新创建 local.repo。这样不用在每条命令后面加--disablerepo,也不会报 metadata 错误。注意移动后yum clean all && yum makecache一定要重新执行,否则旧的元数据缓存还在本地,yum 依然会试图去访问已失效的源。
5.2 现象:docker 装上了,systemctl start docker 时报 Failed to start Docker
这类启动失败是最让人头疼的,因为它不会直接告诉你具体原因。现象就是systemctl start docker显示失败,但docker info什么都没输出。最干的做法是执行journalctl -u docker -n 20看日志。常见原因集中在/var/lib/docker目录残留和 iptables 冲突。如果在安装 docker-ce 之前系统里已有 docker 容器数据,一定要先备份并清空/var/lib/docker。
另一个隐藏坑是 CentOS 7.6 自带的 iptables 版本和 docker 的防火墙规则不兼容,启动时 dockerd 创建 docker0 网桥失败导致退出。解决方法是先确认systemctl status firewalld,如果 firewalld 在运行,重启 docker 前先systemctl restart firewalld或直接systemctl stop firewalld。这听起来像玄学,但实际是因为 docker 尝试追加 iptables 规则时锁定了 fw 表格。如果你不想关闭防火墙,至少要把 docker 网段加到 firewalld 的 trusted 区域,否则容器网络会莫名断网。
5.3 现象:docker run --gpus 报 unknown runtime specification nvidia
这个报错我见了不下十次。现象是执行docker run --gpus all时,docker CLI 提示unknown runtime specification nvidia。原因很简单:daemon.json 里没有配置 nvidia runtime,或者 default-runtime 写成了"default-runtime": "nvidia"但缺少"runtimes"段。docker 19.03 的 CLI 端支持--gpus,但 daemon 端必须能通过 runtime 找到nvidia-container-runtime可执行文件。
解决方法是先which nvidia-container-runtime,确认这个二进制在 PATH 里。如果不存在,说明 nvidia-container-runtime 包没装上,重新用本地 yum 安装。如果二进制存在,编辑 daemon.json,确保 runtimes 段落里的 path 使用绝对路径/usr/bin/nvidia-container-runtime,然后systemctl restart docker。再执行docker info看 Runtimes 列表里是否出现 nvidia。
5.4 现象:容器里 nvidia-smi 报 Failed to initialize NVML
容器里能执行 nvidia-smi,但输出Failed to initialize NVML: Driver/library version mismatch。这个现象的原因多半是宿主机 NVIDIA 驱动升级过,但内核模块没有重新加载,或者宿主机的其他进程还占用着旧的版本库。在离线机上短期解决就是重启一次宿主机,确保内核模块重新加载最新版本。如果你不能重启机器,可以尝试重新加载驱动模块:
rmmod nvidia_drm nvidia_modeset nvidia_uvm nvidia modprobe nvidia注意卸载模块前要把所有 CUDA 相关进程停掉。另外,如果宿主机上的/dev/nvidia*设备节点权限不正确,容器内也会出现 NVML 初始化失败。解决方法是创建 udev 规则:把/lib/udev/rules.d/80-nvidia-gpu.rules拷贝到/etc/udev/rules.d/,然后重载 udev。这一步对离线环境尤其重要,因为你可能用的是无人维护的老机器,驱动装了之后节点权限一直是默认的 root-only。
5.5 现象:yum localinstall 时本地 repo 的包冲突,出现 gpg key not found
安装 docker-ce 或 nvidia-docker2 时,yum 提示The GPG keys listed for the "local" repository are not installed。这个原因是 local.repo 里没有指定 gpgkey,或者你从联网机下载的 rpm 包里没有带回 GPG 密钥。最简单直接的解决是让本地仓库关闭校验。修改 local.repo:
sed -i 's/gpgcheck=1/gpgcheck=0/' /etc/yum.repos.d/local.repo如果你对内网安全要求很严,不想关闭校验,那就把 docker 官方 GPG key 也复制到离线机,并在 local.repo 里设置gpgkey=file:///etc/pki/rpm-gpg/...。不过我用的方案一直是 gpgcheck=0,因为离线仓库本身是自建的,完整拷贝过来的 rpm 只要在联网机上校验过即可。
6. 把离线安装做成能复现的脚本:一个 200 行内的部署脚本与验证套路
6.1 用本地 rpm 目录 + yum localinstall 来跳过源依赖,脚本化安装
一旦上面的流程走通,我建议你把所有命令收敛到一个脚本里。这样团队里的同事不会再手动敲几十条命令,也避免他们再次踩到 5.1 和 5.4 的坑。脚本的核心是复用/data/docker-offline-rpms和/etc/yum.repos.d/local.repo,把安装动作固化成同一段逻辑:
#!/bin/bash set -euo pipefail RPMS_DIR=/data/docker-offline-rpms if [ ! -d "$RPMS_DIR/repodata" ]; then echo "缺少 repodata,请在联网机上用 createrepo 生成后再拷贝" exit 1 fi cat > /etc/yum.repos.d/local.repo <<EOF [local] name=local-offline-docker baseurl=file://$RPMS_DIR enabled=1 gpgcheck=0 EOF yum clean all yum makecache yum install -y --disablerepo='*' --enablerepo=local \ docker-ce docker-ce-cli containerd.io \ nvidia-docker2 nvidia-container-runtime \ libnvidia-container1 libnvidia-container-tools if [ ! -f /etc/docker/daemon.json ]; then cp /etc/docker/daemon.json /etc/docker/daemon.json.bak || true fi cat > /etc/docker/daemon.json <<EOF { "default-runtime": "nvidia", "runtimes": { "nvidia": { "path": "/usr/bin/nvidia-container-runtime", "runtimeArgs": [] } }, "exec-opts": ["native.cgroupdriver=systemd"], "storage-driver": "overlay2" } EOF systemctl daemon-reload systemctl enable --now docker脚本前面的set -euo pipefail能让你第一时间发现哪个步骤失败,避免在缺包的机器上硬着头皮继续跑。脚本里我特意加了 repodata 检查,因为实际环境里有人只拷了 rpm 目录忘记拷 repodata,导致安装一开始就报错。daemon.json 的覆盖逻辑里先备份再写入,能保住原先的私有仓库配置。
6.2 装完后用 --gpus 跑一遍 CUDA 镜像做冒烟测试,顺便验证 docker info 里的 runtime
脚本跑完不能算成功,还要有一个冒烟测试的收尾动作。我会在同一个脚本后面追加两段验证,一段看 docker info,一段跑 GPU 容器:
docker info | grep -A2 Runtimes docker run --rm --gpus all <cuda-image> nvidia-smi >/dev/null 2>&1 \ && echo "GPU runtime is OK" \ || echo "GPU runtime check FAILED"第一行是确认 docker daemon 能识别 nvidia runtime。第二行用本地已有的 CUDA 镜像执行 nvidia-smi,输出只要不报错,就代表整条链路通了。如果失败,脚本输出里会直接告诉你。
我最开始在离线机上踩的最难查的一次,是 daemon.json 里写了"gpus": []这种无效字段,导致 docker info 打开后 Runtimes 正常,但docker run --gpus依然报错。后来我把 daemon.json 里除了 runtime 和默认参数之外的所有字段全部删掉,用最小配置跑通,再逐个加回私有仓库配置。这个习惯帮我排掉了大量无头绪的故障。离线安装这件事,最怕的不是依赖多,而是你不知道哪一层配置被覆盖了。希望这套脚本化的思路能帮到你。
本文还有配套的精品资源,点击获取