简介:在无外网环境中为 CentOS 7.6 部署容器运行时,常因依赖缺失与 GPG 校验受阻。该离线安装包专为此场景设计,面向运维工程师及需搭建 GPU 加速容器的开发者,涵盖 Docker CE 19.03 与 NVIDIA Docker 2.4 的完整 RPM 依赖链,无需联网即可完成安装。压缩包共 30 个文件,体积约 91.98MB,以 20 个 RPM 安装包为核心,辅以 GZ/BZ2 压缩库、repo 元数据、XML 仓库描述、JSON 配置及 GPG 密钥文件,结构清晰可直接用于内网批量部署。文中附带的 daemon.json 预设了 GPU 运行时配置路径,安装后仅需替换配置并重启 Docker 即可生效。已有 3312 人学习使用,尤其适合服务器离线初始化、AI 推理环境快速交付等场景,可帮助避开 Yum 源不可达、依赖循环等典型坑点,显著缩短部署排错时间。
1. 离线机房里那台装了GPU的服务器,等的不只是docker-ce
一台centos7.6服务器,两张T4卡,要在一个彻底上不了公网的机房里部署推理服务。我到现场一看,系统是7.6,驱动已经用run文件装好,宿主机nvidia-smi输出正常,偏偏容器环境完全是空的——docker没装,nvidia-docker2更是没人碰过。没有外网,yum直接失效,照着在线教程一步步走根本走不通。
这篇笔记要解决的问题很具体:在联网备机上把docker-ce 19.03和nvidia-docker2连同全部依赖收集成rpm包,拷进离线机,搭本地yum源,完成安装并让容器里能跑通nvidia-smi。整套流程不需要目标机器碰公网一次,适合刚接手内网GPU服务器的人,也适合给产线批量交付同样镜像环境的运维。下面每一步都是我实际落过地的命令和参数,坑也一并写出来。
2. 离线安装前先想清楚:为什么锁死19.03和nvidia-docker2这对组合
2.1 版本选择的三个理由:内核、驱动与容器运行时的三角关系
centos7.6的默认内核是3.10.0-957,cgroup还是v1体系。docker-ce官方仓库里,19.03是兼容这套内核的最后一批稳定版本线,也是第一个原生支持--gpus参数的版本线,后续的20.10虽然也能用,但依赖的containerd.io版本更激进,在7.6的cgroup v1上偶发资源统计异常。离线环境里排查一个运行时异常的成本极高,所以选19.03不是情怀,是求稳。
nvidia-docker2这个包名对应的是NVIDIA官方的GPU容器运行时方案。它做的事情是给docker注册一个名为nvidia的runtime,在容器启动时把宿主机的显卡驱动、CUDA driver库透传进去。docker本身不认显卡,只有加上这层运行时,--gpus all才有意义。19.03提供命令行入口,nvidia-docker2提供底层能力,两者是组合关系,不是二选一。
还有一个隐含前提必须提前确认:宿主机上必须已经装好NVIDIA显卡驱动,且nvidia-smi在宿主机能正常输出。nvidia-docker2只是透传驱动,不是替代驱动。驱动没装好,后面容器里报的错会非常难排查,这个坑我在第5章会专门细说。
2.2 离线环境的资源清单:要有哪些文件、不能缺哪一类
动手收集之前,先列清楚一份完整的离线资源项,缺一项后面都会卡壳。
| 资源 | 内容 | 说明 |
|---|---|---|
| rpm包目录 | docker-ce、docker-ce-cli、containerd.io及全部依赖 | 这是docker本体 |
| rpm包目录 | nvidia-docker2、nvidia-container-toolkit、libnvidia-container1、libnvidia-container-tools | 这是GPU运行时链 |
| 本地repo配置 | 指向rpm目录的.repo文件 | 让yum在离线机认识这些包 |
| CUDA基础镜像tar包 | nvidia/cuda镜像的save产物 | 容器里跑nvidia-smi用,必须离线导入 |
很多人漏掉的是最后一项:离线机上docker pull一定失败,必须在备机上docker save成tar包再拷过去docker load。镜像tar和rpm包属于两条线,前者负责容器内容,后者负责容器运行能力,我在第4章里会完整演示这条链路。
另外还要注意架构问题。备机和离线机都应该是x86_64,用uname -m先确认。arm架构机器的rpm包和镜像要单独收集,不能拿x86的包硬装,这和离线安装mysql时下载arm版rpm是同一个道理。
2.3 在联网备机上用repotrack收集依赖包:命令与筛选
备机需要能访问公网,并且在备机上先把docker官方和nvidia官方两套centos7仓库加进来。收集依赖包我推荐用repotrack而不是yumdownloader:yumdownloader加--resolve参数只会解析一层依赖,repotrack会把整棵依赖树递归拉下来。离线安装最怕的就是半路冒出一个缺失的-el7.x86_64.rpm,所以收集阶段宁可多收,不可少收。
# 在联网备机上执行 yum install -y yum-utils createrepo # 添加docker官方centos7仓库 yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 添加nvidia官方centos7仓库 yum-config-manager --add-repo https://nvidia.github.io/nvidia-docker/centos7/nvidia-docker.repo mkdir -p /opt/gpu-docker-offline/rpms cd /opt/gpu-docker-offline/rpms # 收集docker-ce 19.03及依赖,指定小版本或版本线均可 repotrack --destdir=/opt/gpu-docker-offline/rpms docker-ce-19.03 docker-ce-cli containerd.io # 收集nvidia-docker2整条依赖链 repotrack --destdir=/opt/gpu-docker-offline/rpms nvidia-docker2 libnvidia-container1 libnvidia-container-tools nvidia-container-toolkit逻辑说明:repotrack执行完不会安装任何包,只是把匹配到的rpm文件下载到指定目录。docker-ce-19.03这种写法会匹配yum源里所有19.03开头的小版本,装的时候yum自动选一个合适版本,避免自己死记19.03.15这种具体版本号。后面四条nvidia包是把完整运行时链点名拉下来,缺一条都会在离线机安装时报依赖错误。
参数说明:--destdir必须放在命令前面,指定输出目录,否则文件落在当前目录管理起来很乱。备机上收集到的rpm包数量一般有几十个,除了docker自身,还包括container-selinux、policycoreutils-python这类系统依赖。收集完成后,这套操作和离线安装node时把所有二进制包和依赖目录一次性拷过去是一个思路,只是yum的依赖关系比node_modules更绕,少一个包都装不上。
3. 把离线rpm包变成本地yum源,再装docker-ce 19.03
3.1 用createrepo生成repodata:最小命令
备机上收集好的rpm包只是一个文件堆,yum要靠repodata索引才能解析依赖。createrepo的作用就是在rpm目录里生成这个索引。
cd /opt/gpu-docker-offline createrepo rpms/执行后rpms/目录下会多出repodata/子目录,里面有repomd.xml等索引文件。这一步完成后把整个/opt/gpu-docker-offline目录打成压缩包,拷到离线机任意路径解压。我在打包时习惯把rpm目录单独放,不要和后续的镜像tar混在一个目录里,repodata是按目录生成的,目录路径变了索引路径也要跟着变。
3.2 本地repo文件怎么写:gpgcheck和baseurl细节
离线机上要把本地目录变成yum认识的源,在/etc/yum.repos.d/下新建一个repo文件,内容指向解压出来的rpm目录。
[gpu-docker-local] name=GPU Docker Offline Repo baseurl=file:///opt/gpu-docker-offline/rpms enabled=1 gpgcheck=0baseurl必须是file://开头,路径指向包含repodata/的那个目录,不是tar包解压的根目录。第一次搭这个源时我把路径多写了一层,结果yum install docker-ce一直报找不到包,后来用yum repolist一查,这个源显示的包数量是0,问题马上暴露。
gpgcheck设为0是离线环境下的省事做法。如果保留gpgcheck=1,就必须把docker官方和nvidia官方的GPG key也拷进离线机并执行rpm --import,否则本地源里的包全部因签名未知被拒绝。内网环境没有外部攻击面,我一般直接关掉。
配置完成后让yum重建缓存:
yum clean all && yum makecache3.3 用yum localinstall安装docker-ce:为什么不用rpm -ivh
离线机的repo源就绪后,安装docker本体。
cd /opt/gpu-docker-offline/rpms # 推荐用localinstall,让yum自动处理依赖顺序 yum localinstall -y docker-ce-*.rpm --nogpgcheck # 安装完成后启动并验证 systemctl start docker systemctl enable docker docker version这里有一个明显的细节差异:rpm -ivh *.rpm会把所有rpm按文件名字母序安装,docker-ce主包会在containerd.io前面先装,结果就是报依赖缺失或冲突。yum localinstall会读取rpm目录里的依赖关系,把containerd.io、container-selinux这些都按正确顺序装好。离线环境里请忘掉rpm -ivh *.rpm,这是很多人翻车的起点。
--nogpgcheck和repo文件里的gpgcheck=0作用相同,双保险。docker version输出里如果Client和Server两段都能看到19.03.x,docker本体就已经通了。Server段起不来时报的是Cannot connect to the Docker daemon,这时先看journalctl -u docker,不要反复systemctl start,原因基本在日志里已经写明。
4. nvidia-docker2的离线安装链:runtime注册与GPU容器验证
4.1 先看清nvidia-docker2的依赖关系,别只装一个包
nvidia-docker2不是独立软件,它是一整条依赖链的最上层。完整的链条是:nvidia-docker2依赖nvidia-container-runtime,这个运行时由nvidia-container-toolkit提供,toolkit又依赖libnvidia-container1和libnvidia-container-tools做底层库调用。任何一层缺失,效果都是docker启动容器时找不到nvidia runtime。
在备机收集阶段,repotrack已经把这条链上的包都拉下来了。到了离线机安装目录里,先确认这些包都在:
ls -l | grep -E 'nvidia-(docker|container|libnvidia)'看到nvidia-docker2、nvidia-container-toolkit、libnvidia-container1、libnvidia-container-tools四类包齐全再往下走。这套依赖关系在NVIDIA官方仓库的rpm包里是固定的,版本不同链条不变。我见过有人图省事只装nvidia-docker2一个rpm,结果docker info里永远看不到nvidia这个runtime。
4.2 安装nvidia-docker2并确认daemon.json被正确写入
cd /opt/gpu-docker-offline/rpms # 一次装齐整条链 yum localinstall -y nvidia-docker2-*.rpm libnvidia-container*.rpm --nogpgcheck # 安装完成后检查daemon.json cat /etc/docker/daemon.json # 重启docker让配置生效 systemctl restart docker # 确认docker已经认识nvidia runtime docker info | grep -i runtime逻辑说明:nvidia-docker2的安装脚本会自动把nvidia runtime的配置合并进/etc/docker/daemon.json。但这里有个隐患:如果离线机上原本就存在daemon.json,比如配置过registry-mirror或私有仓库地址,安装脚本的合并逻辑偶尔会失败且不报错。所以安装后必须cat实际内容确认。
如果daemon.json里没有nvidia runtime配置,需要手动补全。这是唯一的配置正解:
{ "runtimes": { "nvidia": { "path": "/usr/bin/nvidia-container-runtime", "runtimeArgs": [] } } }手动写入后执行systemctl restart docker。这里的path指向的二进制由nvidia-container-toolkit包提供,如果这个文件不存在,docker启动容器时会报unknown runtime nvidia。所以写配置前先ls /usr/bin/nvidia-container-runtime确认文件在不在。
4.3 离线导入GPU镜像并跑通容器里的nvidia-smi
docker和nvidia runtime都就绪后,还差一个能在容器里运行的基础镜像。这一步必须在备机完成pull和save,离线机只负责load。整个逻辑和wsl离线导入ubuntu镜像包一样,先在一个地方准备好完整镜像,再传输导入。
# 在联网备机执行 docker pull nvidia/cuda:11.0-base docker save nvidia/cuda:11.0-base -o nvidia-cuda-11.0-base.tar # 把tar包拷到离线机,然后在离线机执行 docker load -i nvidia-cuda-11.0-base.tar # 用GPU运行时启动容器并验证 docker run --rm --gpus all nvidia/cuda:11.0-base nvidia-smi参数说明:--gpus all表示把宿主机全部GPU设备传入容器,nvidia runtime会拦截容器启动过程,自动执行nvidia-container-cli的挂载逻辑,把显卡设备和驱动库映射进去。容器内nvidia-smi的输出应该和宿主机一致,能看到显卡型号、驱动版本以及CUDA版本。
这里有个匹配问题需要留意:镜像tag里的CUDA版本要能被宿主机驱动支持。比如CUDA 11.0的基础镜像要求宿主机驱动版本不低于450.x。如果容器里报CUDA版本不支持,要么换更老的基础镜像,要么升级宿主机驱动,不要以为这是nvidia-docker2装错了。
5. 离线安装避坑:让GPU容器翻车的5个典型问题
5.1 依赖报错:包明明在目录里,yum却说不认识
现象:yum localinstall时提示缺少某个-el7.x86_64.rpm,打开rpms目录确认文件就在那里。
原因:yum没有成功索引本地源。最常见是baseurl路径写错,比如指向了tar包解压根目录而不是包含repodata的子目录;或者往rpms目录追加了新rpm后忘了重新执行createrepo,yum的索引还是旧的。
解决:检查repo文件的baseurl,指向repodata/所在目录;如果目录内容有变动,重新执行createrepo rpms/,然后yum clean all && yum makecache再装。
5.2 docker服务起不来:报iptables failed
现象:systemctl start docker报Error creating default "bridge" network: iptables failed。
原因:离线最小化安装的centos7.6通常没有装iptables服务,或者firewalld处于disabled状态导致NAT链缺失,docker创建默认bridge网络时调用iptables失败。
解决:先装iptables组件,yum install -y iptables iptables-services,再systemctl enable iptables,最后重新启动docker。如果确认这是纯内网环境且容器对外网络需求极低,可以在daemon.json里加"iptables": false绕开,但GPU容器一般要联网拉推理数据,我不建议关。
5.3 docker run --gpus all报unknown runtime "nvidia"
现象:启动容器时docker daemon返回Error response from daemon: unknown runtime "nvidia"。
原因:daemon.json里没有nvidia runtime配置,或者配置里的path指向的二进制不存在。nvidia-docker2安装脚本合并配置失败是高频原因,特别是在原daemon.json里已经手动写过runtimes字段的情况下。
解决:先ls /usr/bin/nvidia-container-runtime确认二进制在,再手动写入完整runtime配置到daemon.json,systemctl restart docker后docker info查看Runtimes段应该包含nvidia。这个坑在自动化和手动混合配置时出现最多,我每次都先检查这步才继续跑容器。
5.4 容器里nvidia-smi报couldn't find libcuda
现象:容器启动成功,但执行nvidia-smi时报找不到libcuda,或者显示NVIDIA driver not loaded。
原因:宿主机显卡驱动是用.run脚本安装的,装完后没有把动态库路径暴露给系统。nvidia-container-cli在容器启动时找不到libcuda.so.xxx,透传失败。
解决:到宿主机上执行ldconfig,确认/usr/lib64/nvidia/下的libcuda库被系统识别。如果没识别,写入/etc/ld.so.conf.d/nvidia.conf并再执行一次ldconfig。排查时可以用nvidia-container-cli info看完整报错,这条命令会直接告诉你哪一层找不到库。
5.5 在离线机上习惯性执行docker pull,超时后才想起来没网
现象:离线机上docker pull nvidia/cuda:11.0-base一直转圈超时,到最后才反应过来这台机器根本没有外网。
原因:这是最不值得踩的坑,纯粹是操作习惯问题。离线机的镜像来源只能是tar包,没有别的路径。
解决:回到备机执行pull和save,tar包拷进离线机后load。顺带再提醒一次:备机和离线机的架构必须一致。x86_64备机拉下来的镜像到arm64离线机上load是能成功的,但运行时直接exec format error。这和离线安装mysql时下载arm版rpm是同类问题,先uname -m对一下再拷贝。
6. 离线安装后的收尾技巧:一键脚本与三步验证
6.1 把整套安装流程固化成一个可复用的脚本
同一套离线环境和机型,我给每个机房交付时都会带一个小脚本,把第3章和第4章的安装步骤串起来。脚本设计成可重复执行,跑烂了也不怕,重跑一遍就能把环境拉回到可用状态。
#!/bin/bash # offline-docker-install.sh 适用场景:rpms目录已拷入离线机 RPMS_DIR="/opt/gpu-docker-offline/rpms" cd "$RPMS_DIR" || exit 1 # 第一步:安装docker本体 yum localinstall -y docker-ce-*.rpm --nogpgcheck systemctl enable docker && systemctl start docker # 第二步:安装nvidia docker运行时链 yum localinstall -y nvidia-docker2-*.rpm libnvidia-container*.rpm --nogpgcheck # 第三步:确保daemon.json里已经有nvidia runtime,没有则写入 if ! grep -q '"nvidia"' /etc/docker/daemon.json 2>/dev/null; then echo '{"runtimes":{"nvidia":{"path":"/usr/bin/nvidia-container-runtime","runtimeArgs":[]}}}' > /etc/docker/daemon.json fi systemctl restart docker docker info | grep -i runtime nvidia-container-cli info这段脚本有一点要注意:第三步里如果用echo直接覆盖daemon.json,原来是registry-mirror这类自定义配置会被清掉。如果离线机原本有daemon.json内容,先把原文件备份一份,再用python或jq做字段合并,不要直接覆盖。脚本末尾的nvidia-container-cli info是快速体检项,它能给出驱动库、设备节点、运行时权限三方面的状态,比直接跑容器定位问题快得多。
6.2 用三步验证法确认整个链路可用
我习惯的最终验证不是装完跑一次容器就结束,而是分三步走:第一步宿主机nvidia-smi确认驱动正常;第二步docker info | grep -i runtime确认docker认识nvidia runtime;第三步docker run --rm --gpus all执行一次容器内nvidia-smi,输出与宿主机一致才算闭环。三步中任何一步异常,都按第5章对应条目去查,不要跳步。
这套离线安装方案我已经在不同机房的GPU服务器上重复用过多轮,最大的教训始终是同一个:配置daemon.json之前先看一眼原文件内容,改完先docker info再跑容器。机器越是没有网,越要在每一步留痕,日志和输出就是你唯一的判断依据。希望这个流程能帮你在没网的GPU服务器上少走弯路,一步到位把docker-ce 19.03和nvidia-docker2跑起来。
本文还有配套的精品资源,点击获取