TT Lab
블로그 시작하기
배우기 러닝패스 코스

쿠버네티스 GPU 스케줄링과 AI 배치

가짜 GPU 로 장치 플러그인의 광고와 할당을 본다

TT Lab 에서 이어서 보기

이 실습은 진짜 쿠버네티스에서 돕니다

VM 안에 k3s 한 대가 떠 있고, fake-gpu-operator 가 그 노드에 가짜 GPU 2장을 광고합니다. KWOK 컨트롤러도 미리 올려 두었습니다. 처음 뜨는 데 3분쯤 걸립니다. 세션이 끝나면 VM 과 파일은 사라집니다.

목표

장치 플러그인이 노드에 무엇을 적고 컨테이너에 무엇을 넘기는지 직접 보고, 확장 자원의 규칙과 GPU 장부 계산을 손에 익힙니다.

왜 중요한가

GPU 파드가 Pending 일 때 가장 먼저 볼 곳은 「노드가 몇 장을 광고하는가」 와 「이미 몇 장이 배치됐는가」 입니다. 스케줄러는 그 두 숫자의 차만 봅니다. 이 실습에서 그 장부를 손으로 계산해 보면, 나중에 큐·쿼터·갱 스케줄링이 끼어들어도 맨 아래에서 무엇이 결정되는지 잃지 않습니다. 그리고 가짜 GPU 가 진짜와 어디서 갈라지는지(컨테이너 안)를 함께 확인합니다.

단계

  1. 실제 노드가 광고하는 GPU 를 /root/gsb-fake/advertised.txt 에 node=, capacity=, allocatable=, product= 네 줄로 적습니다.
  2. fake-lab 네임스페이스에 GPU 1장짜리 Pod gpu-1 을 /root/gsb-fake/gpu-1.yaml 로 만들어 Running 으로 둡니다.
  3. gpu-1 안의 환경 변수와 nvidia-smi 출력을 /root/gsb-fake/inside.txt 에 담습니다.
  4. requests 1·limits 2 인 파드와 limits 0.5 인 파드를 만들어 보고 거절 메시지를 /root/gsb-fake/rules.txt 에 모읍니다.
  5. gpu-2, gpu-3 을 만들어 gpu-3 이 Pending 이 되게 하고 사유를 /root/gsb-fake/pending.txt 에 담습니다.
  6. 풀 크기를 4장으로 바꾼 값을 /root/gsb-fake/values.yaml 에 적어 helm upgrade 하고, 노드가 실제로 4장을 광고해 gpu-3 이 Running 이 되게 합니다.
  7. KWOK 노드 gpu-a 를 /root/gsb-fake/kwok-node.yaml 로 만들고 GPU 1장짜리 Pod far-1 을 그 위에 둡니다.
  8. GPU 장부를 /root/gsb-fake/report.json 에 nodes, allocatable_total, requested, free 로 적습니다.

참고

노드가 광고하는 GPU 를 읽는다

실제 노드(KWOK 가 아닌 노드)가 광고하는 GPU 를 「/root/gsb-fake/advertised.txt」 에 node=, capacity=, allocatable=, product= 네 줄로 적으세요. product 는 노드 라벨 nvidia.com/gpu.product 의 값입니다.

GPU 수는 노드 오브젝트의 status.capacity 와 status.allocatable 에, 모델 이름은 라벨에 있습니다. 확장 자원 이름에 점이 있어서 jsonpath 에서는 점 앞에 역슬래시가 필요합니다.

파드 하나에 GPU 한 장을 준다

fake-lab 네임스페이스에 GPU 1장을 limits 로 요청하는 Pod gpu-1 을 「/root/gsb-fake/gpu-1.yaml」 로 만들어 적용하세요. 이미지는 registry.k8s.io/e2e-test-images/busybox:1.36.1-1 이고, Running 이어야 합니다.

확장 자원은 limits 에만 적어도 됩니다. requests 는 자동으로 같은 값이 됩니다. 컨테이너가 바로 끝나면 Running 을 볼 수 없으니 오래 자는 명령을 주세요.

컨테이너는 무엇을 받았나

gpu-1 컨테이너 안의 환경 변수 전체와 nvidia-smi 출력을 「/root/gsb-fake/inside.txt」 한 파일에 담으세요. MOCK_NVIDIA_VISIBLE_DEVICES= 줄이 있어야 합니다.

kubectl exec 로 컨테이너 안에서 env 와 nvidia-smi 를 차례로 실행합니다. nvidia-smi 는 장치 플러그인이 Allocate 응답으로 넣어 준 가짜입니다.

확장 자원의 두 규칙을 일부러 어긴다

두 파드를 만들어 보고 API 서버가 낸 거절 메시지를 「/root/gsb-fake/rules.txt」 에 모으세요. (가) requests 1·limits 2 로 요청하는 파드 (나) limits 를 0.5 로 요청하는 파드. 둘 다 거절되어야 하고 fake-lab 에 남으면 안 됩니다.

CPU 는 0.5 도, requests 와 limits 를 다르게 주는 것도 됩니다. 확장 자원은 둘 다 안 됩니다. 거절 메시지는 표준 오류로 나오므로 2>&1 로 함께 담습니다.

노드를 꽉 채워 Pending 을 만든다

GPU 1장씩 요청하는 Pod gpu-2, gpu-3 을 fake-lab 에 차례로 만드세요. 노드가 2장뿐이라 gpu-3 은 Pending 이 됩니다. 스케줄러가 남긴 사유를 「/root/gsb-fake/pending.txt」 에 담으세요.

사유는 파드의 PodScheduled 조건 메시지나 이벤트에 있습니다. 노드가 하나뿐이라 문장이 짧습니다.

광고 수를 늘려 기다리던 파드를 풀어 준다

fake-gpu-operator 의 풀 크기를 4장으로 바꾸세요. 값은 「/root/gsb-fake/values.yaml」 에 적고(runtimeClass.enabled: false 포함) 그 파일로 helm upgrade 합니다. 노드가 실제로 4장을 광고하고 gpu-3 이 Running 이 되어야 합니다. 차트는 oci://ghcr.io/run-ai/fake-gpu-operator/fake-gpu-operator 판 0.2.1, 릴리스 이름 gpu-operator, 네임스페이스 gpu-operator 입니다.

helm upgrade 만으로는 이미 등록된 노드가 바뀌지 않습니다. 노드마다 만들어 둔 토폴로지 ConfigMap 을 status-updater 가 다시 계산하지 않기 때문입니다. 그 ConfigMap 을 지운 뒤 status-updater 를, 그다음 device-plugin 을 다시 띄워 보세요. 실측에서 노드의 광고 수는 device-plugin 을 다시 띄운 뒤에야 바뀌었습니다.

kubelet 없는 노드에도 GPU 를 붙인다

KWOK 노드 gpu-a 를 「/root/gsb-fake/kwok-node.yaml」 로 만들어 적용하세요. 풀 라벨 run.ai/simulated-gpu-node-pool: default 와 KWOK 테인트가 있어야 합니다. 그다음 GPU 1장을 요청하는 Pod far-1 을 fake-lab 에 만들어 gpu-a 위에 Running 으로 두세요.

KWOK 컨트롤러는 kwok.x-k8s.io/node: fake 주석이 붙은 노드만 맡습니다. 파드가 그 노드로 가려면 테인트를 견디는 톨러레이션과, 실제 노드가 아니라 그 노드를 고르게 하는 노드 선택이 필요합니다.

GPU 장부를 직접 계산한다

지금 클러스터의 GPU 장부를 「/root/gsb-fake/report.json」 에 적으세요. nodes(GPU 를 광고하는 노드 이름 → 장수), allocatable_total(그 합), requested(노드에 배치된 Pending·Running 파드가 limits 로 요청한 합, 모든 네임스페이스), free(앞의 둘의 차) 네 키입니다.

노드 쪽은 status.allocatable, 파드 쪽은 spec.nodeName 이 있는 파드의 컨테이너 limits 를 더합니다. 아직 노드를 못 받은 파드는 장부에 들어가지 않습니다.