테마 전환

Docker는 radiance를 호스트에 설치하지 않고 올리는 가장 깔끔한 길이다. 호스트에 필요한 것은 Docker와 ROCm 10.1을 지원하기만 한 amdgpu 커널 드라이버뿐이다. ROCm도 컴파일러도 FFmpeg도 호스트에는 필요 없다. 이 글은 docs/DOCKER.md를 따라간다.
이미지에는 무엇이 들어 있나
저장소는 엔진, 플러그인, 도구, 레시피를 Docker 이미지 하나로 빌드한다. 이미지는 설치 트리에서 조립된다 — 설치 트리가 실제로 로드하는 공유 라이브러리, 정적 busybox, hfget.sh가 쓸 curl, jq, CA 번들. 배포 이미지는 FROM scratch로, 배포판도 패키지 관리자도 없고 busybox 셸(docker exec -it <name> sh)만 있다.
Dockerfile의 빌드 단계는 여섯 개다.
| 단계 | 내용 |
|---|---|
ffmpeg | 엔진이 실제로 쓰는 디mux·디코더·인코더만 켠 FFmpeg 8.1 (배포판 빌드는 엔진이 안 쓰는 패키지 200개를 들여온다) |
llvm | ROCm 10.1의 clang은 같은 소스를 두 번 컴파일해 같은 코드를 내지 않는 문제(VOPD 페어링)가 있어, 이를 고친 패치를 적용한 libLLVM을 다시 빌드한다 |
build | rocm/dev-ubuntu-24.04:10.1.0-full에서 장치(device) 코드 컴파일, /opt/radiance에 설치 (레시피 포함) |
test | 카드 없이 도는 모든 테스트(`ctest -LE 'gpu |
rootfs | 설치 트리를 chroot 안에서 전수 검사해 빠져나간 라이브러리가 없으면 실패 |
runtime | FROM scratch + rootfs. 배포되는 이미지 |
이미지에는 RDNA4(gfx1201), RDNA3(gfx11-generic), Strix Halo(gfx1151)용 커널이 들어 있다. 카드를 더 받거나 다른 커밋으로 쓰려면 직접 빌드한다. 참고로 이미지에 가장 큰 파일은 HIP 런타임이 디바이스를 초기화하는 데 필요한 LLVM/Clang 라이브러리(약 130MB, 90MB)다.
풀어서 실행하기: docker run 옵션의 이유
compose 없이 직접 실행할 때의 전체 명령이다. 항목마다 이유가 있다.
sh
docker run -d --name radiance --network host --init --ulimit memlock=-1 \
--device /dev/kfd --device /dev/dri --security-opt seccomp=unconfined \
--group-add "$(getent group render | cut -d: -f3)" \
--group-add "$(getent group video | cut -d: -f3)" \
-v /srv/models/rad:/models:ro \
stilldeadcode/radiance --model /models/m.rad --tp 2 --host :: --port 8000--device /dev/kfd --device /dev/dri+render/video그룹 — 컨테이너에 GPU를 넘긴다.--security-opt seccomp=unconfined— Docker 기본 seccomp 프로파일이 io_uring과 ROCm 런타임의 NUMA 바인딩 호출을 막는다. Qwen3.8-Flash-Next의 n-gram 임베딩 표는 io_uring으로 컨테이너에서 읽히므로, 이게 없으면 그 모델이 서빙되지 않는다.--init— PID 1의 init 프로세스가 SIGTERM을 엔진에 전달한다. 엔진은 스텝을 끝내고 카드를 반환한 뒤 종료한다.--ulimit memlock=-1— 장치 커널이 n-gram 표를 모으는 곳이 없는 실행(호스트 백엔드)에서--ngram-placement ram은 표 사본(Flash-Next 기준 47.7GiB)을 RAM에 고정한다. Docker 기본 memlock 한도를 훨씬 넘기 때문에 한도를 올려야 한다. 카드에서 도는 사본은 카드가 읽는 고정 메모리라 이 한도의 계산 대상이 아니다.deploy/compose/common.yaml도 이 값을 설정한다.--network host— NAT 없이 호스트 주소를 직접 바인딩한다(IPv4·IPv6).-v /srv/models/rad:/models:ro— 모델 디렉터리를 읽기 전용으로 마운트한다. 이미지는GPU_MAX_ALLOC_PERCENT=100,GPU_MAX_HEAP_SIZE=100을 설정한다.
entrypoint는 radiance이고, rad-convert, rad-info, rad-kbench, rad-tune, rad-schemas, rad-tokcheck도 PATH에 있다. 레시피는 /opt/radiance/share/radiance/recipes/에 있다. 같은 이미지로 변환만 돌리는 방법:
sh
docker run --rm -v /srv/models/rad:/models --entrypoint rad-convert stilldeadcode/radiance ...compose로 모델 올리기
deploy/compose/에는 모델별 compose 파일과 공통으로 확장되는 common.yaml이 있다. 표 전체는 README의 모델 목록과 거의 같다 — flashnext.yaml, flashnext-tp1.yaml, qwen3.8-27b-fp8.yaml, gemma4-31b-w4a8.yaml, qwen-image-2.1.yaml, minimax-h3.yaml, breeze-tts2.yaml, embeddinggemma2.yaml 등. 사용법은 세 줄이다.
sh
cp deploy/compose ~/radiance-compose && cd ~/radiance-compose
cp .env.example .env # RADIANCE_MODELS, RADIANCE_API_KEY, UID/GID, 렌더·비디오 GID 등
docker compose -f minicpm5-2b.yaml up -d
docker compose -f minicpm5-2b.yaml logs -f
docker compose -f minicpm5-2b.yaml down꼭 알아 둘 것:
- 모든 모델이 같은 포트를 쓰고 대부분 카드를 둘 다 잡는다. 한 번에 하나씩 — 다른 모델을 올리려면 먼저
down. - 모델 디렉터리는 direct I/O가 되는 로컬 파일시스템(ext4, XFS, btrfs)에 있어야 한다. Flash-Next의 n-gram 표를 컨테이너에서 그 방식으로 읽는다. btrfs 압축 디렉터리는 동작은 하되 direct read 이점이 사라진다.
/health,/ping, 대시보드는 API 키 없이 답한다. 헬스체크는 busyboxwget으로/health를 가져온다./models는 읽기 전용 마운트이므로 컨테이너 안에서--model <org/repo>를 쓸 거라면--download-dir가 쓰기 가능한 마운트를 가리키게 해야 한다.
이미지를 직접 빌드하기
다른 카드나 다른 커밋이 필요하면 docker/build.sh를 쓴다.
sh
docker/build.sh # HEAD → radiance:<version>, :<commit>, :latest
docker/build.sh -t 'gfx1200;gfx1201' # GPU 아키텍처 (기본 'gfx1201;gfx11-generic;gfx1151')
docker/build.sh -j 8 # 컴파일 잡 제한 (hipcc 아래 장치 커널 하나는 2-3GB)
docker/build.sh -s gpu-host # 커밋을 ssh로 스트리밍해 원격에서 빌드
docker/build.sh --target build # 특정 단계에서 멈춤빌드 컨텍스트는 git archive 결과라 커밋된 것만 이미지에 들어간다. 빌드 컨테이너는 카드를 열거할 수 없으므로 GPU 아키텍처를 반드시 이름으로 지정해야 한다(베어메탈의 ./build.sh는 자동 탐지). 빌드 이미지의 ROCm 버전은 호스트 커널 드라이버가 지원해야 하며, ROCM_VERSION 빌드 인자로 바꾼다. 카드를 필요로 하는 GPU 테스트는 build 단계 이미지에서 별도 실행한다(docker/build.sh --target build 후 --device와 seccomp=unconfined로 ctest -L gpu).
직접 만든 플러그인 얹기
다른 카드용 커널 라이브러리, 새 아키텍처, 새 양자화기 같은 자기 플러그인은 마운트해서 검색 경로 앞에 놓는다. 이미지 안의 것은 아무것도 바꾸지 않는다.
sh
docker run ... -v /srv/radiance-plugins:/plugins:ro \
-e RADIANCE_HOME=/plugins:/opt/radiance/share/radiance \
radiance --model /models/m.rad/plugins에는 같은 radiance 버전에 대해 빌드한 kernels/, architectures/, quantizers/ 중 원하는 것을 넣는다. 엔진 이미지는 모든 AMD GPU families에서 시작한다 — GPU_TARGETS는 동봉 커널 라이브러리 libr4d의 컴파일 대상일 뿐, 엔진이 도는 카드의 제한이 아니다.
자주 듣는 질문
- 이미지 라이선스는? radiance는 Apache-2.0이고, 이미지는 glibc·ROCm 런타임·FFmpeg·busybox·curl·jq 등을 각자 라이선스(GPL·LGPL 포함)로 담는다. 이미지의
/usr/share/doc/THIRD-PARTY가 각 항목의 버전과 라이선스 파일, 소스를 적는다. 빌드는 이미지 안 파일을 추적 가능하게 못 만들면 실패한다. - 카드를 여러 개 쓰는데 한 컨테이너만? 기본 설계다. 대부분의 compose 파일이
--tp 2를 쓴다. 카드 한 장용 파일(*-tp1.yaml)과 네·여덟 장용(glm53-flash.yaml)도 있다. - JPEG XL 입력은? 디코더(libjxl)를 넣지 않아 이름으로 거부한다.
이제 Docker를 직접 빌드는 단계로 넘어갈 준비가 됐다. 다음 글은 이미지 없이 맨 소스에서 빌드하는 길을 다룬다.