4. 在Jetson上安装Docker

Jetson平台与普通x86服务器在Docker部署上存在本质差异:其GPU(NVIDIA Tegra系列)无法通过标准nvidia-docker2直接驱动,必须借助NVIDIA官方为嵌入式平台定制的nvidia-container-runtime,将GPU、VPU(视频编解码单元)及CUDA库以容器内设备节点的方式暴露给应用。本章将完整演示在JetPack 5.x/6.x(Ubuntu 20.04/22.04)环境下,通过apt源安装Docker Engine、配置NVIDIA容器运行时,并执行三层验证(版本、GPU可见性、实际算力调用)的标准化流程。

Jetson Docker 部署链路 ① apt 安装 Docker Engine • 添加 Docker 官方 apt 源 • 安装 docker-ce / docker-ce-cli • containerd.io 运行时 • 配置用户组与开机自启 ② 配置 nvidia-container-runtime • 添加 NVIDIA 官方 apt 源 • 安装 nvidia-container-runtime • 修改 /etc/docker/daemon.json • 指定默认运行时为 nvidia ③ 三层验证 • docker version 版本检查 • nvidia-smi 设备可见性 • 运行 CUDA 容器测试算力 • 检查 /dev/nvidia* 节点 关键路径与风险点 • 设备节点映射:/dev/nvidia0、/dev/nvidiactl、/dev/nvidia-modeset 必须显式挂载 • 库文件映射:/usr/lib/aarch64-linux-gnu/libcuda.so 等需通过 LD_LIBRARY_PATH 注入 • 运行时选择:docker run --runtime=nvidia 或设置默认运行时,二选一 • 版本匹配:JetPack 5.x 对应 CUDA 11.4,JetPack 6.x 对应 CUDA 12.2,镜像标签需对应 • 权限验证:容器内执行 nvidia-smi 应输出与宿主机一致的 GPU 信息

4.1 核心原理:为什么Jetson不能直接使用标准nvidia-docker2

在x86服务器上,NVIDIA GPU通过PCIe总线与CPU通信,驱动直接管理硬件。而Jetson的GPU(如Ampere架构的GA10B)集成在Tegra SoC内,与CPU共享统一内存(Unified Memory),且其设备节点、固件加载路径与x86平台完全不同。标准nvidia-docker2依赖nvidia-container-cli去探测PCIe设备并注入/dev/nvidia*节点,但Jetson上这些节点并不存在——取而代之的是/dev/nvidia-uvm、/dev/nvidia-usb等Tegra特有节点。

NVIDIA为此提供了nvidia-container-runtime的Jetson专用分支,它通过读取/etc/nvidia-container-runtime/config.toml中的nvidia-driver-versions和nvidia-require-jetson标记,自动将Jetson的GPU设备节点、CUDA用户态库(libcuda.so、libnvidia-ptxjitcompiler.so等)以及VPU固件映射到容器内。其核心机制是:

  • 设备节点注入:运行时启动时扫描/dev下与NVIDIA相关的节点,通过--device参数动态挂载到容器。
  • 库文件绑定:将宿主机的/usr/lib/aarch64-linux-gnu/tegra、/usr/lib/aarch64-linux-gnu/libcuda.so.1等路径绑定到容器内相同位置。
  • 环境变量传递:自动设置NVIDIA_VISIBLE_DEVICES=all、NVIDIA_DRIVER_CAPABILITIES=compute,utility等变量,供容器内应用感知GPU。

架构对比速查表

对比项x86服务器Jetson平台
GPU接口PCIe独立显卡SoC集成,统一内存
设备节点/dev/nvidia0, /dev/nvidiactl/dev/nvidia-uvm, /dev/nvidia-usb
驱动加载内核模块 nvidia.ko内核模块 nvgpu.ko + 固件
容器运行时nvidia-container-runtime(标准版)nvidia-container-runtime(Jetson分支)
CUDA库路径/usr/lib/x86_64-linux-gnu/usr/lib/aarch64-linux-gnu/tegra
典型镜像nvidia/cuda:12.2-base-ubuntu20.04nvcr.io/nvidia/l4t-base:r36.2.0

4.2 操作流程:从apt源到运行时配置

以下步骤在Jetson Orin NX(JetPack 6.0,Ubuntu 22.04)上验证通过,适用于所有Jetson系列(Xavier NX、AGX Orin等)。全程需要sudo权限,建议先执行sudo apt update确保系统源正常。

步骤1:安装Docker Engine(apt方式)

# 1. 安装依赖包
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common

# 2. 添加Docker官方GPG密钥(注意:Jetson是arm64架构)
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# 3. 添加apt源(使用arm64架构标识)
echo "deb [arch=arm64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list

# 4. 安装Docker Engine
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# 5. 将当前用户加入docker组(避免每次sudo)
sudo usermod -aG docker $USER
newgrp docker

# 6. 启动并设置开机自启
sudo systemctl enable docker --now

安装完成后,执行docker version应能看到Client和Server版本信息。若Server部分报错,检查sudo systemctl status docker查看日志。

步骤2:配置nvidia-container-runtime

# 1. 添加NVIDIA官方apt源(Jetson专用)
curl -s -L https://nvidia.github.io/nvidia-container-runtime/gpgkey | sudo apt-key add -
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-container-runtime/$distribution/nvidia-container-runtime.list | sudo tee /etc/apt/sources.list.d/nvidia-container-runtime.list

# 2. 安装运行时
sudo apt update
sudo apt install -y nvidia-container-runtime

# 3. 验证运行时安装
which nvidia-container-runtime
# 应输出 /usr/bin/nvidia-container-runtime

# 4. 修改Docker守护进程配置
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<'EOF'
{
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": []
        }
    },
    "default-runtime": "nvidia"
}
EOF

# 5. 重启Docker使配置生效
sudo systemctl restart docker

关键注意点:Jetson的daemon.json中不要添加"nvidia-container-cli"相关参数(如nvidia-container-cli --no-persistenced),因为Jetson分支不使用CLI模式,而是直接通过运行时hook注入设备。若添加会导致容器启动失败,报错nvidia-container-cli: initialization error。

步骤3:验证安装(三层检查)

# 第一层:Docker版本与运行时识别
docker version --format '{{.Server.Runtimes}}'
# 应输出 map[nvidia:0x4000012345] 或类似,确认nvidia运行时已注册

# 第二层:GPU设备可见性(使用官方L4T基础镜像)
docker run --rm --runtime=nvidia nvcr.io/nvidia/l4t-base:r36.2.0 nvidia-smi
# 应输出Jetson GPU信息,包括名称、显存(统一内存)、CUDA版本

# 第三层:实际算力调用(运行CUDA向量加法示例)
docker run --rm --runtime=nvidia nvcr.io/nvidia/l4t-cuda:11.4.0-devel-ubuntu20.04 \
    sh -c "echo '#include <cuda_runtime.h>
    #include <iostream>
    int main() {
        int count; cudaGetDeviceCount(&count);
        std::cout << \"CUDA devices: \" << count << std::endl;
        return 0;
    }' > test.cu && nvcc test.cu -o test && ./test"
# 应输出 CUDA devices: 1

4.3 案例拆解:从“容器内看不到GPU”到完整运行YOLOv5

背景与约束:某团队在Jetson AGX Orin 64GB上部署YOLOv5s目标检测服务,JetPack 5.1.2,已按常规流程安装Docker和nvidia-container-runtime。但执行docker run --rm nvcr.io/nvidia/l4t-base:r35.4.1 nvidia-smi时报错:

docker: Error response from daemon: failed to create task for container: failed to initialize nvidia container runtime: no devices found.

初始现象:运行时无法找到GPU设备。检查宿主机ls /dev/nvidia*发现只有/dev/nvidia-uvm和/dev/nvidia-usb,缺少/dev/nvidia0和/dev/nvidiactl。

分析判断过程:

  1. 首先确认宿主机GPU驱动正常:执行nvidia-smi,输出正常,说明驱动层无问题。
  2. 检查/etc/nvidia-container-runtime/config.toml,发现nvidia-require-jetson = false,这是关键错误——Jetson分支必须显式启用该选项。
  3. 进一步查看daemon.json,发现误添加了"nvidia-container-cli"参数,导致运行时走了x86路径。

实际操作步骤:

# 1. 修正config.toml
sudo sed -i 's/nvidia-require-jetson = false/nvidia-require-jetson = true/' /etc/nvidia-container-runtime/config.toml

# 2. 清理daemon.json中的多余参数
sudo tee /etc/docker/daemon.json <<'EOF'
{
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": []
        }
    },
    "default-runtime": "nvidia"
}
EOF

# 3. 重启服务
sudo systemctl restart docker
sudo systemctl restart nvidia-container-runtime

# 4. 重新验证
docker run --rm --runtime=nvidia nvcr.io/nvidia/l4t-base:r35.4.1 nvidia-smi

结果对比:修复后,nvidia-smi正确输出AGX Orin的GPU信息(Ampere架构,64GB统一内存,CUDA 11.4)。随后拉取ultralytics/yolov5:latest镜像,挂载/dev/video0摄像头设备,成功运行实时检测,帧率稳定在45FPS。

风险与代价:该问题导致团队排查耗时3小时,主要原因是NVIDIA官方文档对Jetson分支的config.toml修改说明分散在多个页面。复盘经验:Jetson上安装nvidia-container-runtime后,必须检查nvidia-require-jetson是否为true,这是与x86平台最大的配置差异。

经验提醒:若使用JetPack 6.x(CUDA 12.2),镜像标签应使用nvcr.io/nvidia/l4t-base:r36.2.0或l4t-cuda:12.2.0-devel。JetPack 5.x对应r35.x系列,切勿混用,否则会出现libcuda.so.1版本不匹配错误。

4.4 常见误区与总结复盘

误区错误表现正确做法
直接安装nvidia-docker2apt报错找不到包,或安装后容器无法启动只安装nvidia-container-runtime,不安装nvidia-docker2
daemon.json中设置nvidia-container-cli参数容器启动报nvidia-container-cli: initialization error仅设置runtime路径,不添加CLI参数
使用x86的CUDA镜像容器内报exec format error必须使用nvcr.io/nvidia/l4t-*或arm64架构镜像
忘记设置default-runtimedocker run不带--runtime=nvidia时无法使用GPU在daemon.json中设置"default-runtime": "nvidia"
忽略nvidia-require-jetson配置运行时找不到设备节点确保/etc/nvidia-container-runtime/config.toml中该选项为true

总结复盘:Jetson的Docker部署本质是“设备节点+库文件+环境变量”三重映射的工程。与x86相比,最大的区别在于:

  • 设备节点差异:Jetson没有/dev/nvidia0,而是/dev/nvidia-uvm,运行时通过nvidia-require-jetson识别并映射。
  • 库文件路径:CUDA库位于/usr/lib/aarch64-linux-gnu/tegra,而非标准路径,运行时需显式绑定。
  • 镜像架构:必须使用arm64架构的L4T镜像,x86镜像无法运行。

建议在完成安装后,将以下命令保存为verify-jetson-docker.sh脚本,便于快速验证环境:

#!/bin/bash
echo "=== 1. Docker Runtime ==="
docker version --format '{{.Server.Runtimes}}'
echo "=== 2. GPU Device ==="
docker run --rm --runtime=nvidia nvcr.io/nvidia/l4t-base:r36.2.0 nvidia-smi
echo "=== 3. CUDA Compute ==="
docker run --rm --runtime=nvidia nvcr.io/nvidia/l4t-cuda:12.2.0-devel-ubuntu22.04 \
    sh -c "nvcc --version | tail -1"

该脚本可在每次系统更新或Docker升级后执行,确保运行时配置未被覆盖。若输出异常,优先检查daemon.json和config.toml两个配置文件。