빼낸 GPU를 되붙이는 법 — Jetson 컨테이너에 CDI로 CUDA 얹기

Jetson 양산 OS 개발기 ③ · 바이코드(ByCode)

지난 편에서 우리는 meta-tegra 기반 Yocto 이미지를 Jetson Orin Nano에 올리고 첫 부팅을 검증했다. 그 이미지는 의도적으로 슬림하다. GPU 연산에 필요한 CUDA도, 추론 엔진 TensorRT도 base에 넣지 않았다. 그렇다면 이 OS에서 GPU는 어떻게 쓰나? 이번 편의 답은 “컨테이너로 되붙인다”이다.

먼저 분명히 해둔다. CUDA·TensorRT·컨테이너 런타임은 모두 NVIDIA가 만든 것이다. 우리가 개발했다고 말하지 않는다. 우리가 한 일은 base 이미지를 슬림하게 유지하면서, 그 위에 표준 방식으로 GPU 컨테이너를 붙이고 실제 추론까지 도는지 검증한 것이다.

왜 base에서 GPU 런타임을 빼는가

양산 OS는 작을수록 좋다. 업데이트가 가벼워지고, 검증할 표면과 공격 표면이 줄기 때문이다. 그래서 우리는 base에 GPU 드라이버 라이브러리와 컨테이너 실행에 필요한 최소 요소만 남겼다. CUDA 툴킷과 TensorRT는 base가 아니라 컨테이너 이미지가 제공하도록 했다.

이 분리에는 실용적 이유도 있다. 응용마다 요구하는 CUDA·TensorRT 버전이 다르다. 이 둘을 컨테이너에 두면, base OS를 건드리지 않고 응용별로 런타임을 교체할 수 있다. OS와 응용의 수명 주기를 분리하는 것이다.

레거시가 아니라 CDI를 쓰는 이유

데스크톱이나 서버 GPU에서는 docker run --gpus all이 익숙하다. 이 경로는 NVML이라는 관리 라이브러리에 기댄다. 그런데 Jetson의 통합 GPU(iGPU)에서는 이 레거시 경로가 우리 환경에서 동작하지 않았다. 대신 NVIDIA Container Toolkit이 제공하는 CDI 방식으로 붙였다. NVIDIA Container Toolkit

CDI(Container Device Interface)는 GPU 같은 장치를 컨테이너에 주입하는 방법을 기술한 벤더 중립 규격이다. 붙이는 절차는 두 단계다. 먼저 daemon.jsonfeatures.cdi를 켜고, nvidia-ctk cdi generate --mode=csv로 장치 명세 파일(/etc/cdi/nvidia.yaml)을 만든다. 그 다음 컨테이너를 docker run --device nvidia.com/gpu=all로 실행한다. 레거시 --gpus--runtime nvidia가 아니다.

한 가지 더. Tegra iGPU에서는 nvidia-smi가 동작하지 않는다. 이는 고장이 아니라 정상이다. 그래서 GPU가 컨테이너에 제대로 잡혔는지는 nvidia-smi가 아니라 컨테이너 안에서 CUDA로 직접 확인해야 한다.

인식과 추론, 두 단계로 확인한다

검증은 두 단계로 나눴다. 장치가 보이는가(인식), 그리고 실제로 계산하는가(추론)다.

인식 단계에서는 NVIDIA의 l4t-jetpack:r36.4.0 컨테이너 안에서 작은 CUDA 프로그램을 돌렸다. 결과는 Detected 1 CUDA device, 그리고 Orin, CC 8.7, 8 SM이었다. iGPU가 컨테이너 경계를 넘어 보인다는 뜻이다.

추론 단계에서는 같은 컨테이너의 trtexec로 ResNet50 FP16 엔진을 빌드해 실행했다. 결과는 &&&& PASSED, throughput 471 qps, median latency 약 2.12 ms였다. 첫 엔진 빌드는 타이밍 캐시가 없어 약 102초가 걸렸고, 이후에는 캐시로 단축된다.

여기서 슬림화의 대가가 드러났다. 처음에 trtexec는 “Loading standard plugins” 단계에서 멈췄다. 원인은 추론 플러그인 라이브러리가 또 다른 라이브러리(libnvdla_compiler)를 필요로 하는데, 슬림 base에서 그것이 빠져 있었기 때문이다. 해당 패키지를 이미지에 넣자 통과했다. 슬림화는 공짜가 아니라, 무엇을 남기고 무엇을 뺄지 아는 작업이다.

덧붙이면, 이 수치는 특정 모델(ResNet50)의 추론 성능이지 부팅 속도나 이미지 크기가 아니다. 그런 지표는 기준과 반복 횟수를 정해 다른 편에서 따로 측정한다.

재플래시가 지우는 것

함정이 하나 있다. 위에서 만든 CDI 설정(daemon.json, /etc/cdi/nvidia.yaml)은 런타임에 생성한 상태이지 이미지 안에 든 것이 아니다. 그래서 보드를 다시 플래시하면 이 설정이 사라지고, 컨테이너가 could not select device driver "cdi"로 실패한다. 개발 중에는 매번 다시 설정하면 되지만, 양산 제품에서는 그럴 수 없다.

방향은 분명하다. 이 설정을 이미지에 굽는 것이다. daemon.json을 이미지에 동봉하고, 첫 부팅 때 systemd로 CDI 명세를 자동 생성하게 만든다. 그러면 OS가 부팅 즉시 컨테이너 GPU를 쓸 수 있다. 이것은 개발 키트의 문제가 아니라 재현성과 양산의 문제다.

정리

슬림 base에서 뺀 GPU 능력을, NVIDIA의 컨테이너와 표준 CDI로 되붙였다. 컨테이너 안에서 Orin의 iGPU를 인식했고, TensorRT 추론까지 통과했다. 그 과정에서 슬림화의 대가(빠진 라이브러리)와 런타임 상태의 휘발성(재플래시)이라는, 양산에서 반드시 다뤄야 할 두 가지가 함께 드러났다.

개발 키트에서 컨테이너 GPU가 돈다는 것은 출발점이다. 다음 편에서는 이 컨테이너를 무인으로 업데이트하고, 문제가 생기면 자동으로 이전 상태로 되돌리는 OTA v0를 다룬다.

참고 자료

  1. NVIDIA, Container Toolkit (nvidia-ctk · CDI 생성 도구)
  2. NVIDIA, NGC l4t-jetpack 컨테이너
  3. CNCF, Container Device Interface (CDI) 스펙
  4. OE4T, meta-tegra — NVIDIA Jetson 플랫폼용 BSP layer
  5. 자체 실측(2026년 6월): CDI deviceQuery 로그 · trtexec ResNet50 결과 · 빌드 설정

이 글은 바이코드가 Jetson 기반 양산용 Edge OS를 만드는 과정을 공개하는 개발기 시리즈의 세 번째 편입니다.