3. 容器运行时选型与安装

在 Jetson 上跑容器,第一件要决定的事不是“装哪个镜像”,而是“用哪条运行时链路”。Jetson 的 GPU 访问依赖 NVIDIA 驱动暴露的 /dev/nvidia* 设备节点、驱动库以及 CUDA 兼容层,普通容器默认看不到这些资源。因此,无论选择 Docker 还是 containerd,都必须把 NVIDIA Container Toolkit 接入运行时,让容器创建时自动完成设备与库的注入。

Docker 与 containerd 的定位差异

Docker 是面向开发者的完整工具链,包含 CLI、镜像构建、网络与卷管理、Compose 等;containerd 是更底层的容器运行时,负责镜像拉取、容器生命周期和快照管理,通常由 Kubernetes 通过 CRI 调用。在 Jetson 单机开发场景中,Docker 的交互效率更高;在边缘集群或 K3s 场景中,containerd 更贴近生产形态。

对比项Docker (dockerd)containerd
调用入口docker CLI / Docker APIctr、crictl、CRI 接口
GPU 注入方式通过 --gpus all 或 --runtime=nvidia通过 CRI 运行时类 nvidia 或 runtimeClassName
典型使用场景单机开发、调试、快速验证K3s/K8s 节点、边缘集群
与 Toolkit 集成点/etc/docker/daemon.json 注册 nvidia runtime/etc/containerd/config.toml 配置 runtime class

两者并非互斥:Docker 内部也使用 containerd 作为底层运行时,只是 Docker 在其上封装了更友好的接口。选型时可按“是否需要 Kubernetes 编排”来判断:需要则优先 containerd,不需要则 Docker 更省心。

NVIDIA Container Toolkit 安装

NVIDIA Container Toolkit 提供 nvidia-container-runtime、nvidia-container-cli 和 nvidia-ctk 等组件,负责在容器启动时挂载 GPU 设备、驱动库和 CUDA 兼容文件。在 Jetson 上,推荐使用 NVIDIA 官方 apt 源安装,避免与 L4T 自带驱动版本冲突。

# 1. 添加 NVIDIA 容器工具链 GPG 密钥与软件源
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
  | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg

curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
  | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
  | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

# 2. 安装工具链
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit

# 3. 确认版本
nvidia-container-toolkit --version
nvidia-ctk --version

安装完成后,系统会新增 /usr/bin/nvidia-container-runtime 与 /usr/bin/nvidia-container-toolkit。此时运行时尚未被 Docker 或 containerd 调用,需要显式注册。

nvidia-container-runtime 配置

配置的核心是让容器运行时在创建容器时调用 nvidia-container-runtime,由它读取镜像的 CUDA 版本、驱动能力并注入对应文件。Docker 场景下使用 nvidia-ctk runtime configure 自动写入配置:

# Docker 场景:注册 nvidia runtime 并设为默认
sudo nvidia-ctk runtime configure --runtime=docker --set-as-default
sudo systemctl restart docker

# 验证 daemon.json 中已出现 nvidia runtime
cat /etc/docker/daemon.json

执行后 /etc/docker/daemon.json 中会出现类似片段:

{
  "runtimes": {
    "nvidia": {
      "path": "nvidia-container-runtime",
      "runtimeArgs": []
    }
  },
  "default-runtime": "nvidia"
}

containerd 场景则使用:

sudo nvidia-ctk runtime configure --runtime=containerd
sudo systemctl restart containerd

该命令会在 /etc/containerd/config.toml 中写入 nvidia runtime class,并配置 BinaryName = "/usr/bin/nvidia-container-runtime"。若使用 K3s,还需在 /var/lib/rancher/k3s/agent/etc/containerd/config.toml.tmpl 中同步该配置,否则节点重启后会被覆盖。

Jetson 容器 GPU 访问链路:运行时选型与 Toolkit 注入 用户入口 docker run / crictl run Docker 路径 dockerd → containerd containerd 路径 CRI / crictl / K3s nvidia-container-runtime 读取镜像 CUDA 版本 nvidia-container-cli 挂载设备与驱动库 Jetson 硬件与驱动资源 /dev/nvidia0 · /dev/nvidiactl · /dev/nvidia-uvm libcuda.so · libnvidia-ml.so · CUDA 兼容库 配置落点 Docker: daemon.json containerd: config.toml K3s: config.toml.tmpl 验证命令 docker run --rm --gpus all nvcr.io/nvidia/l4t-base:r35.4.1 nvidia-smi

验证:从容器内看到 GPU

配置完成后必须验证,否则很容易出现“容器能启动但看不到 GPU”的隐性故障。最直接的验证方式是运行一个带 CUDA 基础环境的镜像,并在容器内执行 nvidia-smi 或 tegrastats。

# Docker 验证:使用 L4T 基础镜像
docker run --rm --gpus all nvcr.io/nvidia/l4t-base:r35.4.1 nvidia-smi

# 若未设置默认 runtime,也可显式指定
docker run --rm --runtime=nvidia nvcr.io/nvidia/l4t-base:r35.4.1 nvidia-smi

预期输出中应能看到 Jetson 的 GPU 型号、驱动版本以及 CUDA 版本。若报错 could not select device driver "" with capabilities: [[gpu]],说明 Docker 未注册 nvidia runtime;若报错 nvidia-container-cli: mount error,通常是驱动库路径或 L4T 版本与镜像不匹配。

containerd 场景可用 crictl 验证:

sudo crictl run --runtime nvidia \
  --image nvcr.io/nvidia/l4t-base:r35.4.1 \
  --command "nvidia-smi"

若使用 K3s,可在 Pod 中指定 runtimeClassName: nvidia,再通过 kubectl logs 查看 nvidia-smi 输出。验证通过后,建议把验证命令固化为部署前的检查项,避免每次换镜像或升级 L4T 后重复排查。

实践建议:Jetson 上镜像的 L4T 版本必须与宿主驱动匹配。例如宿主为 L4T r35.4.1,就优先选择 l4t-base:r35.4.1 或 l4t-cuda:r35.4.1,跨大版本(如 r32 与 r35)混用常导致 libcuda.so 加载失败。
注意:执行 nvidia-ctk runtime configure --set-as-default 后,所有容器默认走 nvidia runtime。若某些容器不需要 GPU,仍可正常启动,但会多一次运行时判断;若追求极致启动速度,可保留默认 runtime 为 runc,仅在需要 GPU 的容器上显式指定 --runtime=nvidia。