학습·이미지 빌드·서빙은 k3s 의 파드로 실행. 명령은 모두 서버에서 실행하며, 소스는 /opt/geo-mlops 에 있다고 가정.

1. NVIDIA 드라이버와 Container Toolkit

k3s 는 시작할 때 nvidia-container-runtime 유무를 확인하고 containerd 에 nvidia 런타임 등록. 드라이버와 NVIDIA Container Toolkit 을 k3s 보다 먼저 설치(설치 방법은 NVIDIA 공식 문서 참고). 이미 k3s 가 실행 중이면 Toolkit 설치 뒤 sudo systemctl restart k3s 로 다시 읽게 함.

nvidia-smi                       # 드라이버 확인
which nvidia-container-runtime   # Toolkit 확인

2. k3s 설치

  1. k3s 설치(공식 설치 스크립트).

    curl -sfL https://get.k3s.io | sh -
  2. 노드가 Ready 인지, kubeconfig 가 생성되었는지 확인. 앱 컨테이너가 이 파일을 읽음.

    sudo k3s kubectl get nodes
    ls -l /etc/rancher/k3s/k3s.yaml
  3. RuntimeClass nvidia 유무 확인. 없으면 생성.

    sudo k3s kubectl get runtimeclass nvidia || \
    cat <<'EOF' | sudo k3s kubectl apply -f -
    apiVersion: node.k8s.io/v1
    kind: RuntimeClass
    metadata:
      name: nvidia
    handler: nvidia
    EOF

3. NVIDIA device plugin

RuntimeClass 만으로도 파드에 GPU 할당은 가능하지만, 클러스터가 GPU 점유 상태를 알지 못함. GPU 한 장에 두 파드가 배치되어 둘 다 메모리 부족으로 비정상 종료. device plugin 은 노드가 GPU 를 nvidia.com/gpu 자원으로 광고(클러스터에 GPU 수를 알림)하게 함. 그러면 사용 가능한 GPU 가 없을 때 파드가 Pending 상태로 대기. 앱의 GPU 슬롯 화면도 이 광고값을 집계.

설치 파일은 두 가지. 둘 중 하나만 적용(같은 DaemonSet 이름 사용). 파일 전문과 항목 설명은 NVIDIA device plugin 레퍼런스 참고.

파일사용 시점
scripts/serving/nvidia-device-plugin.ymlGPU 한 장 = 슬롯 하나. GPU 가 여러 장이고 파드마다 한 장씩 할당하면 되는 경우
scripts/serving/nvidia-device-plugin-timeSlicing.ymlGPU 한 장을 여러 슬롯(기본 8) 으로 나눠 광고. GPU 가 한두 장인데 학습·서빙을 여러 개 동시에 실행해야 하는 경우
  1. 하나를 적용. kubeconfig 만 필요.

    sudo k3s kubectl apply -f /opt/geo-mlops/scripts/serving/nvidia-device-plugin-timeSlicing.yml
  2. 노드마다 광고되는 GPU 수 확인(시분할이면 GPU 수 × 8).

    sudo k3s kubectl get nodes -o custom-columns='NAME:.metadata.name,ALLOC:.status.allocatable.nvidia\.com/gpu'
  3. 앱에 전역 관리자로 로그인해 시스템 콘솔의 GPU 슬롯 화면으로 이동. device plugin 이 Ready 이고 총 슬롯이 위 숫자와 같으면 정상. 테넌트가 하나도 없으면 웹 화면이 열리지 않으므로, 이 확인은 첫 로그인과 초기 설정에서 첫 테넌트를 만든 뒤 진행.

    GPU 슬롯: 노드가 광고하는 GPU 와 device plugin 상태

replicas(기본 8) 변경 시 ConfigMap 을 수정해 다시 적용한 뒤 반드시 롤아웃. 롤아웃하지 않으면 반영되지 않음.

sudo k3s kubectl apply -f /opt/geo-mlops/scripts/serving/nvidia-device-plugin-timeSlicing.yml
sudo k3s kubectl -n kube-system rollout restart ds/nvidia-device-plugin-daemonset

동시에 실행되는 학습 수

슬롯 수와 학습 동시 실행 수는 별도 설정. 서버는 모든 테넌트를 통틀어 GEO_MLOPS_TRAINING_MAX_CONCURRENT(기본 1)개까지만 학습을 실행하고, 나머지는 QUEUED 상태로 대기. 기본값이면 여유 슬롯이 있어도 학습은 하나씩 실행. 서빙에는 이 한도 없음. 동시에 실행할 학습 수만큼 값을 높이고 앱 컨테이너 재생성.

# /etc/geo-mlops/app.env
GEO_MLOPS_TRAINING_MAX_CONCURRENT=4
docker compose -f /opt/geo-mlops/docker-compose.yml up -d app

4. k3s 의 레지스트리 pull 설정

앱의 내장 레지스트리(/v2)는 평문 HTTP. 이미지 전송 경로는 두 가지.

방향주체기본 주소설정
push빌드 파드 내부의 BuildKit10.42.0.1:10000BuildKit 에 insecure 가 이미 활성화되어 있음(GEO_MLOPS_SERVING_REGISTRY_INSECURE=true)
pull노드의 containerdlocalhost:10000containerd 는 localhost 를 insecure 로 취급하므로 단일 노드면 추가 설정 불필요

서버 한 대 구성에서는 registries.yaml 불필요. pull 주소를 localhost 가 아닌 값으로 바꾼 경우(노드가 여럿이라 GEO_MLOPS_SERVING_REGISTRY_PULL 을 호스트:포트 로 지정한 경우)에만 각 노드의 /etc/rancher/k3s/registries.yaml 에 그 주소를 insecure 로 등록.

# /etc/rancher/k3s/registries.yaml: <registry-host>:10000 을 실제 pull 주소로
mirrors:
  "<registry-host>:10000":
    endpoint: ["http://<registry-host>:10000"]
configs:
  "<registry-host>:10000":
    tls:
      insecure_skip_verify: true
sudo systemctl restart k3s    # registries.yaml 은 k3s 가 시작할 때 읽는다

다음: 필수 보안 설정

2026-10-02 기준 플랫폼으로 작성

© MLOps Central Platform