Skip to content

feat(container): add GPU (CUDA) serving image variant + build CI - #27

Merged
teri0411 merged 2 commits into
mainfrom
feat/gpu-serving-image
Jul 1, 2026
Merged

feat(container): add GPU (CUDA) serving image variant + build CI#27
teri0411 merged 2 commits into
mainfrom
feat/gpu-serving-image

Conversation

@teri0411

@teri0411 teri0411 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Add a GPU (CUDA) variant of the serving container plus an image build CI, and broaden the base system dependencies for common ML libraries.

The mlplatform GPU inference deployment needs a CUDA-capable serving image. Like the CPU image, this variant does not bake torch — start_server.sh installs the model's own requirements.txt at runtime via uv, so the CUDA build of torch comes from the model itself. The GPU base only supplies the CUDA runtime + cuDNN and the NVIDIA container-runtime hooks so those wheels can reach the GPU.

Changes

  • container/Dockerfile.gpu (new): nvidia/cuda:12.8.1-cudnn-runtime-ubuntu22.04 base, same uv + mlflow-skinny orchestrator toolchain and start_server.sh entrypoint as the CPU image.
  • .github/workflows/container-image.yml (new): matrix build (cpu/gpu) pushing to GHCR on release (and workflow_dispatch). CPU keeps the unsuffixed tag (:<version>, :latest) for backward compatibility; GPU uses the -gpu suffix (:<version>-gpu, :latest-gpu). Floating tags move only on release.
  • Broaden system deps (both CPU + GPU images): add build-essential/pkg-config (C/C++ ext without wheels), git (git+https requirements), ffmpeg + libsndfile1 (audio/video), libsm6/libxext6 (opencv/X runtime libs).
  • container/README.md: document both variants, tag scheme, runtime-torch note, and known gaps.

Known gaps (documented in README)

  • Source-compiled CUDA extensions (building flash-attn/apex/mmcv from source) need nvcc + CUDA headers, which are only in the -cudnn-devel- base (~2x size). The GPU image uses the -runtime- base, so prefer models that install prebuilt CUDA wheels.
  • CUDA driver / torch-wheel compatibility (GPU): the base targets CUDA 12.8 (NVIDIA_REQUIRE_CUDA=cuda>=12.8). A model's CUDA framework wheel must match the node driver — e.g. a torch wheel built for CUDA 13.0 (+cu130) fails on a node whose driver only supports CUDA 12.8. Pin torch to a cu126/cu128 build.

Test plan

  • container/Dockerfile.gpu builds successfully (local build, ~5.6GB image).
  • .github/workflows/container-image.yml parses (valid YAML, cpu/gpu matrix).
  • Live on an H100 node: image pulls + runs, nvidia-smi sees the MIG device, CUDA 12.8 runtime + cuDNN present, and a driver-compatible torch (torch==2.7.1+cu126) reports cuda.is_available() == True and runs a GPU matmul on a MIG slice.
  • Published to GHCR as ghcr.io/nubison/nubison-model:0.0.7-gpu (public, anonymous pull HTTP 200).

Additional (manual review)

  • CI matrix build/publish on an actual GitHub release (workflow only runs on release / workflow_dispatch, not on PR).

이상윤 added 2 commits July 1, 2026 09:21
Add a GPU variant of the serving container on an NVIDIA CUDA runtime base so a
model's CUDA-enabled framework wheels (e.g. torch) can use the GPU at serve time.
Like the CPU variant, torch is not baked; start_server.sh installs the model's
requirements.txt via uv at runtime, so the CUDA torch wheel comes from the model.

- container/Dockerfile.gpu: nvidia/cuda:12.8.1-cudnn-runtime-ubuntu22.04 base,
  same uv + mlflow-skinny orchestrator toolchain and start_server.sh entrypoint.
- .github/workflows/container-image.yml: matrix build (cpu/gpu) pushing to GHCR
  on release (and workflow_dispatch). CPU keeps the unsuffixed tag for backward
  compatibility; GPU uses the -gpu suffix.
- container/README.md: document both variants, tag scheme, and runtime torch note.
The serving image installs each model's requirements.txt at runtime, so the base
must carry the system deps that common ML wheels need. Add a build toolchain
(build-essential, pkg-config), git (git+https requirements), ffmpeg + libsndfile1
(audio/video), and libsm6/libxext6 to both CPU and GPU images.

Document remaining gaps in container/README.md: source-compiled CUDA extensions
(need the -devel- base / nvcc) and CUDA driver vs torch-wheel compatibility on GPU
(the base targets CUDA 12.8; a +cu130 torch wheel fails on a 12.8 node driver).
@teri0411
teri0411 merged commit c07009a into main Jul 1, 2026
3 checks passed
@teri0411
teri0411 deleted the feat/gpu-serving-image branch July 1, 2026 02:50
@claude

claude Bot commented Jul 1, 2026

Copy link
Copy Markdown

리뷰

프로덕션 GPU 서빙 이미지 + 배포 CI를 추가하는 PR이라 보안/일관성 위주로 확인했습니다. 전반적으로 CPU 이미지와의 패리티를 잘 유지했고, README에 알려진 제약사항(known gaps)까지 명시한 점이 좋습니다.

🔴 보안: container-image.yml에서 GitHub Actions 스크립트 인젝션 패턴

- name: Resolve version
  id: ver
  run: |
    if [ "${{ github.event_name }}" = "release" ]; then
      V="${{ github.event.release.tag_name }}"
    elif [ -n "${{ inputs.version }}" ]; then
      V="${{ inputs.version }}"
    ...

github.event.release.tag_nameinputs.version처럼 외부에서 제어 가능한 값을 ${{ }}run: 스크립트 안에 직접 문자열 치환하고 있습니다. GitHub Actions에서 이 패턴은 잘 알려진 스크립트 인젝션 벡터입니다 (예: 릴리스 태그명이나 workflow_dispatch 입력값에 백틱-커맨드나 세미콜론을 포함시키면 러너에서 임의 셸 명령이 실행될 수 있음). 이 워크플로는 packages: write 권한과 secrets.GITHUB_TOKEN(GHCR 로그인)을 사용하므로, 인젝션이 성공하면 GHCR에 임의 이미지를 푸시하거나 토큰을 탈취하는 등의 영향으로 이어질 수 있습니다.

릴리스 태그/워크플로 입력값은 보통 신뢰된 협업자만 생성할 수 있어 실제 악용 난이도는 낮지 않지만, GitHub 공식 가이드/CodeQL에서 권장하는 방식대로 env: 를 경유해 값을 넘기면 방어적으로 안전합니다:

- name: Resolve version
  id: ver
  env:
    RELEASE_TAG: ${{ github.event.release.tag_name }}
    INPUT_VERSION: ${{ inputs.version }}
  run: |
    if [ "${{ github.event_name }}" = "release" ]; then
      V="$RELEASE_TAG"
    elif [ -n "$INPUT_VERSION" ]; then
      V="$INPUT_VERSION"
    else
      V="$(git rev-parse --short HEAD)"
    fi
    V="${V#v}"
    echo "version=$V" >> "$GITHUB_OUTPUT"

🟡 참고: PR 시점 CI 검증 미실행 (이미 known gap으로 명시됨)

PR 설명에 이미 체크박스로 표시돼 있지만, 이 워크플로는 release/workflow_dispatch에서만 실행되어 PR 시점에는 빌드 실패를 잡아낼 수 없습니다. 이후에 pull_request 트리거로 push: false 빌드(스모크 테스트)를 추가하면 Dockerfile 문법 오류를 머지 전에 조기에 발견할 수 있을 것 같습니다. (블로킹 사항은 아닙니다.)

🟢 사소한 의견: Dockerfile/Dockerfile.gpu 중복

두 Dockerfile이 시스템 패키지 목록, orchestrator 설치, ENV 블록 등 대부분 내용을 그대로 복제하고 있고, 이를 "parity 유지"라는 주석으로 수동 동기화하고 있습니다. 지금 범위에서는 문제없지만, 추후 목록이 늘어나면 두 파일이 drift될 위험이 있으니 (예: 공통 apt-get install 목록을 별도 스크립트/ARG로 뽑아내는 등) 참고해두면 좋을 것 같습니다.

전체적으로 기능 자체는 잘 구현되어 있고 H100에서 실제 검증까지 마쳐서 안심이 됩니다. 위 스크립트 인젝션 항목만 반영해주시면 좋을 것 같습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant