LabHub
배우기 러닝패스 코스

GPU Operator 와 타임슬라이싱 · GPU 노드라는 사실은 라벨로 적혀 있다 · 실습

라벨이 없으면 아무 오류 없이 아무 일도 일어나지 않는다

LabHub 에서 이어서 보기

목표

GPU 노드를 만드는 라벨 세 벌(NFD·GFD·오퍼레이터)을 출처별로 세우고, 라벨 값 규약을 API 서버에게 직접 거절당하며 배우고, In·NotIn·Exists·Gt 와 term 목록으로 파드의 자리를 정하고, 발견 결과를 되돌리는 동기화기와 라벨 규약 점검기를 손으로 만듭니다.

왜 중요한가

GPU Operator 는 노드에 꽂힌 카드를 직접 보지 않습니다. 라벨을 봅니다. nfd-worker 가 PCI 벤더 ID 0x10de 를 찾아 feature.node.kubernetes.io/pci-10de.present=true 를 붙이고, 오퍼레이터는 그 라벨이 붙은 노드에만 오퍼랜드를 올리고, gpu-feature-discovery 가 모델·장수·메모리·드라이버 판을 라벨로 더 붙이면 그때부터 사람과 스케줄러가 그 라벨로 자리를 고릅니다. 이 사슬의 무서운 점은 끊어져도 오류가 나지 않는다는 것입니다. 라벨이 없으면 오퍼랜드가 안 뜨고, 안 뜨니 자원이 광고되지 않고, 광고가 없으니 파드는 그냥 Pending 입니다. 어디에도 '라벨이 없어서' 라고 적히지 않습니다. 그래서 GPU 클러스터를 다루는 사람의 첫 손버릇은 kubectl get node -o json 으로 라벨부터 읽는 것이 되고, 두 번째 손버릇은 라벨 규약을 검사하는 작은 도구를 갖고 다니는 것이 됩니다. 이 실습이 그 둘을 만듭니다.

단계

1. /root/gpunfd 에서 작업합니다(export KUBECONFIG=/root/.kube/config). 세 노드에 라벨을 붙여 '발견이 끝난 클러스터' 를 세우세요. lab-node-0 에는 feature.node.kubernetes.io/pci-10de.present=true, feature.node.kubernetes.io/kernel-version.major=5, nvidia.com/gpu.present=true, nvidia.com/gpu.product=NVIDIA-A100-SXM4-40GB, nvidia.com/gpu.count=4, nvidia.com/gpu.memory=40537, nvidia.com/cuda.driver.major=550 을 붙입니다. lab-node-1 에는 같은 키로 pci-10de.present=true, kernel-version.major=5, gpu.present=true, gpu.product=NVIDIA-A10, gpu.count=2, gpu.memory=22731, cuda.driver.major=535 를 붙입니다. lab-node-2 에는 feature.node.kubernetes.io/kernel-version.major=5 하나만 붙이고 pci-10de.presentnvidia.com/ 로 시작하는 라벨도 붙이지 마세요(GPU 가 없는 노드입니다). 그리고 /root/gpunfd/out/sources.txt 에 세 줄을 적으세요 — 어느 라벨을 어느 컴포넌트가 붙이는지입니다. feature.node.kubernetes.io/pci-10de.present=nfd-worker, nvidia.com/gpu.product=gpu-feature-discovery, nvidia.com/gpu.deploy.driver=gpu-operator 를 한 줄씩 적습니다.
2. 장치 이름 NVIDIA A100-SXM4-40GB 를 그대로 라벨 값으로 넣어 보세요 — kubectl patch node lab-node-1 -p '{"metadata":{"labels":{"nvidia.com/gpu.product":"NVIDIA A100-SXM4-40GB"}}}' 를 실행하고 그 출력을 표준 오류까지 함께 /root/gpunfd/out/reject.txt 에 저장하세요(거절당해야 정상이며 라벨은 바뀌지 않습니다). 그리고 /root/gpunfd/sanitize.sh 를 만드세요 — 인자로 받은 문자열을 라벨 값으로 쓸 수 있게 다듬어 한 줄로 출력합니다. 규칙은 셋입니다. (1) 영숫자와 - _ . 이 아닌 문자는 모두 - 로 바꾼다, (2) 63자를 넘으면 63자로 자른다, (3) 양 끝이 영숫자가 아니면 영숫자가 나올 때까지 떼어 낸다. 만든 뒤 bash /root/gpunfd/sanitize.sh "NVIDIA A100-SXM4-40GB"NVIDIA-A100-SXM4-40GB 를 내는지 확인하세요.
3. 네임스페이스 nfd-lab 를 만들고 /root/gpunfd/k8s/a100-job.yaml 에 파드 a100-job 을 쓰세요. 컨테이너 이름은 trainer, 이미지는 nvcr.io/nvidia/pytorch:24.07-py3 입니다. spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution 하나로 nvidia.com/gpu.productNVIDIA-A100-SXM4-40GB 인 노드만 고르게 하세요. nodeSelectornodeName 은 쓰지 않습니다. 적용한 뒤 파드가 어느 노드로 갔는지 확인하세요.
4. /root/gpunfd/k8s/any-gpu-job.yaml 에 파드 any-gpu-job 을 쓰세요(컨테이너 trainer, 같은 이미지). 조건은 한 개의 term 안에 matchExpressions 두 개입니다 — nvidia.com/gpu.presentExists 이고, 동시에 nvidia.com/gpu.productNVIDIA-A10아닌(NotIn) 노드. 적용한 뒤 파드가 lab-node-0 으로 가는 것을 확인하세요. Exists 에는 values 를 적지 않는다는 점에 주의하세요.
5. 파드 둘을 더 만드세요. /root/gpunfd/k8s/gt-job.yamlgt-job 은 term 하나에 조건 하나 — nvidia.com/gpu.countGt 3 인 노드입니다. /root/gpunfd/k8s/or-job.yamlor-jobterm 두 개입니다 — 첫째는 gpu.count Gt 3, 둘째는 gpu.product In [NVIDIA-A10]. 둘 다 컨테이너는 trainer, 이미지는 같습니다. 적용한 뒤 gt-job 은 lab-node-0 으로만 갈 수 있고 or-job 은 GPU 노드 둘 중 하나로 간다는 것을 확인하고, /root/gpunfd/out/placement.txt 에 두 줄을 <파드이름> <노드이름> 꼴로 적으세요.
6. /root/gpunfd/features/ 아래에 노드마다 파일 하나씩 lab-node-0.env·lab-node-1.env 를 만드세요. 각 줄은 <라벨키>=<값> 이고 1단계에서 붙인 nvidia.com/ 라벨들을 그대로 담습니다(워커가 보고한 사실입니다). 그리고 /root/gpunfd/nfd-sync.sh 를 만드세요 — 각 파일의 줄과 실제 노드 라벨을 대조해 다르면 파일 쪽 값으로 되돌리고, 되돌린 것마다 <노드> <키> <값> 한 줄을 출력합니다. 모두 같으면 아무것도 출력하지 않고 0 으로 끝납니다. 만든 뒤 kubectl label node lab-node-1 nvidia.com/gpu.count=9 --overwrite 로 값을 하나 망가뜨리고 bash /root/gpunfd/nfd-sync.sh 를 돌려 그 출력을 /root/gpunfd/out/sync.txt 에 저장하세요. 끝난 뒤 라벨은 원래 값으로 돌아와 있어야 합니다.
7. lab-node-1 에 feature.node.kubernetes.io/custom-gpu-training=true 를 붙이세요(NFD 의 사용자 정의 규칙이 붙였다고 칩니다). /root/gpunfd/k8s/train-a.yaml 에 파드 train-a 를 쓰고(컨테이너 trainer, 같은 이미지) 그 라벨이 true 인 노드를 required nodeAffinity 로 요구하게 한 뒤 적용해 lab-node-1 에서 뜨는 것을 확인하세요. 그 다음 그 라벨을 lab-node-1 에서 지우고, 같은 내용에 이름만 train-b 로 바꾼 매니페스트를 /root/gpunfd/k8s/train-b.yaml 로 만들어 적용하세요. 마지막으로 /root/gpunfd/out/ignored.txt 에 정확히 두 줄을 적으세요 — train-a=Running:lab-node-1train-b=Pending.
8. /root/gpunfd/label-audit.sh 를 만드세요. 클러스터의 모든 노드를 돌며 nvidia.com/gpu.presenttrue 인 노드에 대해 네 가지를 봅니다 — (1) nvidia.com/gpu.product·nvidia.com/gpu.count·nvidia.com/gpu.memory 가 모두 있는가, (2) gpu.countgpu.memory 가 숫자로만 되어 있는가, (3) gpu.product 가 63자 이하인가, (4) gpu.product 가 영숫자로 시작해 영숫자와 - _ . 로만 이루어졌는가. 어긋난 것마다 <노드> <라벨키> <이유> 를 한 줄씩 내고 하나라도 있으면 종료 코드 1 로 끝냅니다. 아무 문제가 없으면 OK 한 줄만 내고 0 으로 끝냅니다. 이유는 missing·not-a-number·too-long·bad-format 중 하나를 씁니다. 만든 뒤 지금 클러스터에 돌려 그 출력을 /root/gpunfd/out/audit.txt 에 저장하세요. 노드 목록을 스크립트 안에 적어 두지 마세요 — 채점기가 노드를 하나 더 만들어 놓고 부릅니다.

참고

단계 8개

  1. 발견 결과를 세운다 — 라벨 세 벌은 출처가 다르다
  2. 벤더가 주는 이름은 그대로 라벨이 되지 못한다
  3. 모델 이름으로 자리를 고른다
  4. 한 term 안의 조건들은 모두 참이어야 한다
  5. term 목록은 OR 이고, 숫자 라벨은 크기로 견줄 수 있다
  6. 손으로 바꾼 라벨은 왜 되돌아오는가
  7. 라벨을 지워도 이미 뜬 파드는 쫓겨나지 않는다
  8. 규약을 지키는지 검사하는 도구를 만든다