Container Device Interface (CDI)
El Container Device Interface (CDI) es una especificación diseñada para estandarizar cómo se exponen y utilizan los dispositivos (como GPUs, FPGAs y otros aceleradores de hardware) en los contenedores. El objetivo es proporcionar un mecanismo más consistente y seguro para utilizar dispositivos de hardware en entornos de contenedores, abordando los desafíos asociados con las configuraciones y preparaciones específicas de cada dispositivo.
Además de permitir que el contenedor interactúe con el nodo del dispositivo, CDI también te permite especificar configuraciones adicionales para el dispositivo, como variables de entorno, montajes del host (como objetos compartidos) y hooks ejecutables.
Primeros pasos
Para comenzar con CDI, necesitas tener configurado un entorno compatible. Esto incluye tener instalado Docker v27+ con CDI configurado y Buildx v0.22+.
También necesitas crear las especificaciones de dispositivos usando archivos JSON o YAML en una de las siguientes ubicaciones:
/etc/cdi/var/run/cdi/etc/buildkit/cdi
NoteLa ubicación se puede cambiar configurando la opción
specDirsen la seccióncdidel archivo de configuraciónbuildkitd.tomlsi estás utilizando BuildKit directamente. Si estás compilando usando el demonio de Docker con el controladordocker, consulta la documentación de Configurar dispositivos CDI.
NoteSi estás creando un builder de contenedores en WSL, debes asegurarte de que Docker Desktop esté instalado y que la Paravirtualización de GPU en WSL 2 esté habilitada. También se requiere Buildx v0.27+ para montar las bibliotecas de WSL en el contenedor.
Compilar con una especificación CDI simple
Comencemos con una especificación CDI simple que inyecta una variable de entorno
en el entorno de compilación y la escribe en /etc/cdi/foo.yaml:
cdiVersion: "0.6.0"
kind: "vendor1.com/device"
devices:
- name: foo
containerEdits:
env:
- FOO=injectedInspecciona el builder default para verificar que vendor1.com/device sea detectado
como un dispositivo:
$ docker buildx inspect
Name: default
Driver: docker
Nodes:
Name: default
Endpoint: default
Status: running
BuildKit version: v0.23.2
Platforms: linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/amd64/v4, linux/386
Labels:
org.mobyproject.buildkit.worker.moby.host-gateway-ip: 172.17.0.1
Devices:
Name: vendor1.com/device=foo
Automatically allowed: false
GC Policy rule#0:
All: false
Filters: type==source.local,type==exec.cachemount,type==source.git.checkout
Keep Duration: 48h0m0s
Max Used Space: 658.9MiB
GC Policy rule#1:
All: false
Keep Duration: 1440h0m0s
Reserved Space: 4.657GiB
Max Used Space: 953.7MiB
Min Free Space: 2.794GiB
GC Policy rule#2:
All: false
Reserved Space: 4.657GiB
Max Used Space: 953.7MiB
Min Free Space: 2.794GiB
GC Policy rule#3:
All: true
Reserved Space: 4.657GiB
Max Used Space: 953.7MiB
Min Free Space: 2.794GiB
Ahora creemos un Dockerfile para usar este dispositivo:
# syntax=docker/dockerfile:1-labs
FROM busybox
RUN --device=vendor1.com/device \
env | grep ^FOO=Aquí usamos la
instrucción RUN --device
y establecemos vendor1.com/device, lo cual solicita el primer dispositivo disponible en la
especificación. En este caso utiliza foo, que es el primer dispositivo en
/etc/cdi/foo.yaml.
NoteLa instrucción
RUN --devicesolo está disponible en el canallabsdesde Dockerfile frontend v1.14.0-labs y aún no está disponible en la sintaxis estable.
Ahora compilemos este Dockerfile:
$ docker buildx build .
[+] Building 0.4s (5/5) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 155B 0.0s
=> resolve image config for docker-image://docker/dockerfile:1-labs 0.1s
=> CACHED docker-image://docker/dockerfile:1-labs@sha256:9187104f31e3a002a8a6a3209ea1f937fb7486c093cbbde1e14b0fa0d7e4f1b5 0.0s
=> [internal] load metadata for docker.io/library/busybox:latest 0.1s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
ERROR: failed to build: failed to solve: failed to load LLB: device vendor1.com/device=foo is requested by the build but not allowed
Falla porque la compilación no permite automáticamente el dispositivo vendor1.com/device=foo,
como se muestra en la salida de buildx inspect anterior:
Devices:
Name: vendor1.com/device=foo
Automatically allowed: falsePara permitir el dispositivo, puedes usar la
opción --allow
con el comando docker buildx build:
$ docker buildx build --allow device .
O puedes establecer la anotación org.mobyproject.buildkit.device.autoallow en
la especificación CDI para permitir automáticamente el dispositivo en todas las compilaciones:
cdiVersion: "0.6.0"
kind: "vendor1.com/device"
devices:
- name: foo
containerEdits:
env:
- FOO=injected
annotations:
org.mobyproject.buildkit.device.autoallow: trueAhora ejecutando la compilación nuevamente con la opción --allow device:
$ docker buildx build --progress=plain --allow device .
#0 building with "default" instance using docker driver
#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 159B done
#1 DONE 0.0s
#2 resolve image config for docker-image://docker/dockerfile:1-labs
#2 DONE 0.1s
#3 docker-image://docker/dockerfile:1-labs@sha256:9187104f31e3a002a8a6a3209ea1f937fb7486c093cbbde1e14b0fa0d7e4f1b5
#3 CACHED
#4 [internal] load metadata for docker.io/library/busybox:latest
#4 DONE 0.1s
#5 [internal] load .dockerignore
#5 transferring context: 2B done
#5 DONE 0.0s
#6 [1/2] FROM docker.io/library/busybox:latest@sha256:f85340bf132ae937d2c2a763b8335c9bab35d6e8293f70f606b9c6178d84f42b
#6 CACHED
#7 [2/2] RUN --device=vendor1.com/device env | grep ^FOO=
#7 0.155 FOO=injected
#7 DONE 0.2s
La compilación es exitosa y la salida muestra que la variable de entorno FOO
se inyectó en el entorno de compilación como se especificó en la especificación CDI.
Configurar un builder de contenedores con soporte para GPU
En esta sección, te mostraremos cómo configurar un builder de contenedores
utilizando GPUs de NVIDIA. Desde Buildx v0.22, al crear un nuevo builder de contenedores, se
añade automáticamente una solicitud de GPU al builder de contenedores si el host tiene instalados
los controladores de GPU en el kernel. Esto es similar a usar
--gpus=all con el comando docker run.
Ahora vamos a crear un builder de contenedores llamado gpubuilder usando Buildx:
$ docker buildx create --name gpubuilder --driver-opt "image=moby/buildkit:buildx-stable-1-gpu" --bootstrap
#1 [internal] booting buildkit
#1 pulling image moby/buildkit:buildx-stable-1-gpu
#1 pulling image moby/buildkit:buildx-stable-1-gpu 1.0s done
#1 creating container buildx_buildkit_gpubuilder0
#1 creating container buildx_buildkit_gpubuilder0 8.8s done
#1 DONE 9.8s
gpubuilder
NoteCreamos una imagen de BuildKit especialmente diseñada porque la imagen de lanzamiento actual de BuildKit está basada en Alpine, la cual no es compatible con los controladores de NVIDIA. La siguiente imagen está basada en Ubuntu, instala las bibliotecas de cliente de NVIDIA y genera la especificación CDI para tu GPU en el builder de contenedores si se solicita un dispositivo durante una compilación.
Inspeccionemos este builder:
$ docker buildx inspect gpubuilder
Name: gpubuilder
Driver: docker-container
Last Activity: 2025-07-10 08:18:09 +0000 UTC
Nodes:
Name: gpubuilder0
Endpoint: unix:///var/run/docker.sock
Driver Options: image="moby/buildkit:buildx-stable-1-gpu"
Status: running
BuildKit daemon flags: --allow-insecure-entitlement=network.host
BuildKit version: v0.26.2
Platforms: linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
Labels:
org.mobyproject.buildkit.worker.executor: oci
org.mobyproject.buildkit.worker.hostname: d6aa9cbe8462
org.mobyproject.buildkit.worker.network: host
org.mobyproject.buildkit.worker.oci.process-mode: sandbox
org.mobyproject.buildkit.worker.selinux.enabled: false
org.mobyproject.buildkit.worker.snapshotter: overlayfs
Devices:
Name: nvidia.com/gpu
On-Demand: true
GC Policy rule#0:
All: false
Filters: type==source.local,type==exec.cachemount,type==source.git.checkout
Keep Duration: 48h0m0s
Max Used Space: 488.3MiB
GC Policy rule#1:
All: false
Keep Duration: 1440h0m0s
Reserved Space: 9.313GiB
Max Used Space: 93.13GiB
Min Free Space: 188.1GiB
GC Policy rule#2:
All: false
Reserved Space: 9.313GiB
Max Used Space: 93.13GiB
Min Free Space: 188.1GiB
GC Policy rule#3:
All: true
Reserved Space: 9.313GiB
Max Used Space: 93.13GiB
Min Free Space: 188.1GiB
Podemos ver que el proveedor nvidia.com/gpu es detectado como un dispositivo en el builder, lo
que significa que se detectaron los controladores.
Opcionalmente, puedes verificar si los dispositivos GPU de NVIDIA están disponibles en el contenedor
usando nvidia-smi:
$ docker exec -it buildx_buildkit_gpubuilder0 nvidia-smi -L
GPU 0: Tesla T4 (UUID: GPU-6cf00fa7-59ac-16f2-3e83-d24ccdc56f84)
Compilar con soporte para GPU
Creemos un Dockerfile simple que utilizará el dispositivo GPU:
# syntax=docker/dockerfile:1-labs
FROM ubuntu
RUN --device=nvidia.com/gpu nvidia-smi -LAhora ejecuta la compilación utilizando el builder gpubuilder que creamos anteriormente:
$ docker buildx --builder gpubuilder build --progress=plain .
#0 building with "gpubuilder" instance using docker-container driver
...
#7 preparing device nvidia.com/gpu
#7 0.000 > apt-get update
...
#7 4.872 > apt-get install -y gpg
...
#7 10.16 Downloading NVIDIA GPG key
#7 10.21 > apt-get update
...
#7 12.15 > apt-get install -y --no-install-recommends nvidia-container-toolkit-base
...
#7 17.80 time="2025-04-15T08:58:16Z" level=info msg="Generated CDI spec with version 0.8.0"
#7 DONE 17.8s
#8 [2/2] RUN --device=nvidia.com/gpu nvidia-smi -L
#8 0.527 GPU 0: Tesla T4 (UUID: GPU-6cf00fa7-59ac-16f2-3e83-d24ccdc56f84)
#8 DONE 1.6s
Como habrás notado, el paso #7 está preparando el dispositivo nvidia.com/gpu
instalando las bibliotecas del cliente y el kit de herramientas para generar las
especificaciones CDI para la GPU.
Luego, el comando nvidia-smi -L se ejecuta en el contenedor utilizando el dispositivo GPU.
La salida muestra el UUID de la GPU.
Puedes comprobar la especificación CDI generada dentro del builder de contenedores con el siguiente comando:
$ docker exec -it buildx_buildkit_gpubuilder0 cat /etc/cdi/nvidia.yaml
Para la instancia EC2 g4dn.xlarge
utilizada aquí, se ve de la siguiente manera:
cdiVersion: 0.6.0
containerEdits:
deviceNodes:
- path: /dev/nvidia-modeset
- path: /dev/nvidia-uvm
- path: /dev/nvidia-uvm-tools
- path: /dev/nvidiactl
env:
- NVIDIA_VISIBLE_DEVICES=void
hooks:
- args:
- nvidia-cdi-hook
- create-symlinks
- --link
- ../libnvidia-allocator.so.1::/usr/lib/x86_64-linux-gnu/gbm/nvidia-drm_gbm.so
hookName: createContainer
path: /usr/bin/nvidia-cdi-hook
- args:
- nvidia-cdi-hook
- create-symlinks
- --link
- libcuda.so.1::/usr/lib/x86_64-linux-gnu/libcuda.so
hookName: createContainer
path: /usr/bin/nvidia-cdi-hook
- args:
- nvidia-cdi-hook
- enable-cuda-compat
- --host-driver-version=570.133.20
hookName: createContainer
path: /usr/bin/nvidia-cdi-hook
- args:
- nvidia-cdi-hook
- update-ldcache
- --folder
- /usr/lib/x86_64-linux-gnu
hookName: createContainer
path: /usr/bin/nvidia-cdi-hook
mounts:
- containerPath: /run/nvidia-persistenced/socket
hostPath: /run/nvidia-persistenced/socket
options:
- ro
- nosuid
- nodev
- bind
- noexec
- containerPath: /usr/bin/nvidia-cuda-mps-control
hostPath: /usr/bin/nvidia-cuda-mps-control
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /usr/bin/nvidia-cuda-mps-server
hostPath: /usr/bin/nvidia-cuda-mps-server
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /usr/bin/nvidia-debugdump
hostPath: /usr/bin/nvidia-debugdump
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /usr/bin/nvidia-persistenced
hostPath: /usr/bin/nvidia-persistenced
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /usr/bin/nvidia-smi
hostPath: /usr/bin/nvidia-smi
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /usr/lib/x86_64-linux-gnu/libcuda.so.570.133.20
hostPath: /usr/lib/x86_64-linux-gnu/libcuda.so.570.133.20
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /usr/lib/x86_64-linux-gnu/libcudadebugger.so.570.133.20
hostPath: /usr/lib/x86_64-linux-gnu/libcudadebugger.so.570.133.20
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /usr/lib/x86_64-linux-gnu/libnvidia-allocator.so.570.133.20
hostPath: /usr/lib/x86_64-linux-gnu/libnvidia-allocator.so.570.133.20
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /usr/lib/x86_64-linux-gnu/libnvidia-cfg.so.570.133.20
hostPath: /usr/lib/x86_64-linux-gnu/libnvidia-cfg.so.570.133.20
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /usr/lib/x86_64-linux-gnu/libnvidia-gpucomp.so.570.133.20
hostPath: /usr/lib/x86_64-linux-gnu/libnvidia-gpucomp.so.570.133.20
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.570.133.20
hostPath: /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.570.133.20
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /usr/lib/x86_64-linux-gnu/libnvidia-nscq.so.570.133.20
hostPath: /usr/lib/x86_64-linux-gnu/libnvidia-nscq.so.570.133.20
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /usr/lib/x86_64-linux-gnu/libnvidia-nvvm.so.570.133.20
hostPath: /usr/lib/x86_64-linux-gnu/libnvidia-nvvm.so.570.133.20
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /usr/lib/x86_64-linux-gnu/libnvidia-opencl.so.570.133.20
hostPath: /usr/lib/x86_64-linux-gnu/libnvidia-opencl.so.570.133.20
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /usr/lib/x86_64-linux-gnu/libnvidia-pkcs11-openssl3.so.570.133.20
hostPath: /usr/lib/x86_64-linux-gnu/libnvidia-pkcs11-openssl3.so.570.133.20
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /usr/lib/x86_64-linux-gnu/libnvidia-pkcs11.so.570.133.20
hostPath: /usr/lib/x86_64-linux-gnu/libnvidia-pkcs11.so.570.133.20
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /usr/lib/x86_64-linux-gnu/libnvidia-ptxjitcompiler.so.570.133.20
hostPath: /usr/lib/x86_64-linux-gnu/libnvidia-ptxjitcompiler.so.570.133.20
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /lib/firmware/nvidia/570.133.20/gsp_ga10x.bin
hostPath: /lib/firmware/nvidia/570.133.20/gsp_ga10x.bin
options:
- ro
- nosuid
- nodev
- bind
- containerPath: /lib/firmware/nvidia/570.133.20/gsp_tu10x.bin
hostPath: /lib/firmware/nvidia/570.133.20/gsp_tu10x.bin
options:
- ro
- nosuid
- nodev
- bind
devices:
- containerEdits:
deviceNodes:
- path: /dev/nvidia0
name: "0"
- containerEdits:
deviceNodes:
- path: /dev/nvidia0
name: GPU-6cf00fa7-59ac-16f2-3e83-d24ccdc56f84
- containerEdits:
deviceNodes:
- path: /dev/nvidia0
name: all
kind: nvidia.com/gpu¡Felicitaciones por tu primera compilación utilizando un dispositivo GPU con BuildKit y CDI!