LabHub
배우기 러닝패스 코스

GPU Operator 와 타임슬라이싱 · 드라이버는 애플리케이션이 아니라 커널 모듈이다 · 실습

커널이 한 칸 올라가자 그 노드만 드라이버가 없다

LabHub 에서 이어서 보기

목표

노드 라벨을 사실의 자리로 삼아 미리 컴파일된 드라이버 이미지의 태그를 만드는 도구와 어긋난 노드를 찾아내는 점검기를 만들고, 노드가 늘었을 때 무엇이 먼저 필요한지 겪고, PodDisruptionBudget 이 막는 자리를 지나 드레인부터 uncordon 까지 업그레이드 절차를 끝까지 걸어 봅니다.

왜 중요한가

GPU 드라이버는 애플리케이션이 아니라 커널 모듈입니다. GPU Operator 가 드라이버를 컨테이너로 올리더라도 그 컨테이너가 하는 일은 호스트 커널에 모듈을 적재하는 것이고, 그래서 커널이 바뀌면 드라이버도 그 커널에 맞춰 다시 빌드돼야 합니다. 미리 컴파일된 드라이버 이미지의 태그가 <드라이버브랜치>-<커널판>-<OS태그> 인 것은 이 사실을 이름에 박아 둔 것입니다. 여기서 두 가지가 따라 나옵니다. 첫째, 노드가 한 대 늘거나 커널이 한 칸 올라가는 것은 곧 이미지 작업입니다 — 모르고 지나가면 그 노드에서만 드라이버 파드가 이미지 없음으로 죽습니다. 둘째, 드라이버를 올리려면 그 노드의 GPU 워크로드를 먼저 내려야 하는데, 그 일은 eviction API 를 거치므로 PodDisruptionBudget 이 업그레이드를 막습니다. 업그레이드가 한 노드에서 멈췄다는 신고의 상당수가 드라이버 문제가 아니라 예산 문제이고, 원인을 모르면 드라이버 로그만 몇 시간 읽게 됩니다.

단계

1. /root/gpudrv 에서 작업합니다(export KUBECONFIG=/root/.kube/config). 먼저 네임스페이스 gpu-drv 를 만드세요(뒤 단계의 파드가 여기에 뜹니다). 세 노드에 라벨을 붙이세요. lab-node-0: nvidia.com/gpu.present=true, nvidia.com/cuda.driver.major=550, nvidia.com/cuda.driver.minor=90, nvidia.com/cuda.driver.rev=07, feature.node.kubernetes.io/kernel-version.full=5.15.0-119-generic, feature.node.kubernetes.io/system-os_release.ID=ubuntu, feature.node.kubernetes.io/system-os_release.VERSION_ID=22.04. lab-node-1: 같은 키로 드라이버 535/183/06, 커널 5.15.0-107-generic, ubuntu 22.04. lab-node-2: 드라이버 550/90/07, 커널 6.8.0-45-generic, ubuntu 24.04. 세 노드 모두 nvidia.com/gpu.present=true 입니다.
2. /root/gpudrv/drv-tag.sh <노드이름> 을 만드세요. 그 노드의 라벨을 읽어 미리 컴파일된 드라이버 이미지의 태그를 한 줄로 출력합니다. 형식은 공식 문서의 <드라이버브랜치>-<커널판>-<OS태그> 이고, 브랜치는 nvidia.com/cuda.driver.major, 커널판은 feature.node.kubernetes.io/kernel-version.full, OS 태그는 system-os_release.IDsystem-os_release.VERSION_ID붙여 쓴 것입니다 (예: ubuntu22.04 이면 ubuntu22.04). 세 노드에 차례로 돌려 /root/gpudrv/out/tags.txt<노드> <태그> 꼴로 세 줄을 적으세요. 노드 이름이나 태그를 스크립트 안에 적어 두지 마세요 — 채점기가 노드마다 직접 불러 봅니다.
3. /root/gpudrv/support-matrix.csv 를 만드세요. 첫 줄은 머리글 driver_branch,kernel,os_tag 이고, 그 뒤로 사내 레지스트리에 실제로 빌드해 둔 조합 세 줄을 적습니다 — 550,5.15.0-119-generic,ubuntu22.04, 535,5.15.0-107-generic,ubuntu22.04, 550,6.8.0-45-generic,ubuntu24.04. 그리고 /root/gpudrv/drv-audit.sh 를 만드세요 — nvidia.com/gpu.present=true모든 노드를 돌며 그 노드의 조합이 이 표에 있는지 보고, 없으면 <노드> MISSING <태그> 를 한 줄씩 내고 종료 코드 1 로, 하나도 없으면 OK 한 줄과 0 으로 끝냅니다. 만든 뒤 돌려 출력을 /root/gpudrv/out/audit.txt 에 저장하세요(지금은 OK 여야 합니다).
4. /root/gpudrv/k8s/node3.yaml 로 새 노드 lab-node-3 을 클러스터에 넣으세요. 라벨은 nvidia.com/gpu.present=true, 드라이버 550/90/07, 커널 6.8.0-52-generic, ubuntu 24.04 이고, kwok.x-k8s.io/node: fake 어노테이션과 Ready 조건을 갖춘 가짜 노드입니다(형식은 예시를 보세요). 적용한 뒤 drv-audit.sh 를 돌려 출력을 /root/gpudrv/out/skew.txt 에 저장하세요 — 새 노드가 걸려야 정상입니다. 그 다음 그 조합을 빌드해 레지스트리에 올렸다고 치고 support-matrix.csv 에 줄을 하나 더해, 다시 돌린 출력을 /root/gpudrv/out/skew-fixed.txt 에 저장하세요(이제 OK 여야 합니다).
5. 1단계에서 만든 네임스페이스 gpu-drv 에 파드 둘을 올립니다. /root/gpudrv/k8s/cuda12-job.yaml 에 파드 cuda12-job 을 쓰세요 — 컨테이너 이름 trainer, 이미지 nvcr.io/nvidia/pytorch:24.07-py3, required nodeAffinity 의 한 term 안에 조건 두 개입니다: nvidia.com/cuda.driver.majorIn 550, 그리고 feature.node.kubernetes.io/system-os_release.VERSION_IDIn 22.04. /root/gpudrv/k8s/cuda13-job.yaml 에 파드 cuda13-job 을 쓰세요 — 이미지는 nvcr.io/nvidia/pytorch:25.03-py3, 조건은 nvidia.com/cuda.driver.majorGt 560 하나입니다. 둘 다 적용한 뒤 cuda12-job 은 lab-node-0 에서 뜨고 cuda13-job 은 대기하는 것을 확인하고, cuda13-jobPodScheduled 조건 메시지를 /root/gpudrv/out/cuda.txt 에 저장하세요.
6. /root/gpudrv/k8s/trainer.yaml 에 디플로이먼트 trainer 를 쓰세요 — 네임스페이스 gpu-drv, replicas: 4, 파드 라벨과 셀렉터는 app: trainer, 컨테이너 trainer, 이미지 nvcr.io/nvidia/pytorch:24.07-py3, 그리고 required podAntiAffinitytopologyKey: kubernetes.io/hostname 에 대해 같은 app: trainer 끼리 한 노드에 둘이 못 오게 하세요(노드 네 대에 하나씩 퍼집니다). /root/gpudrv/k8s/pdb.yaml 에 PodDisruptionBudget trainer-pdb 를 쓰세요 — minAvailable: 4, 셀렉터는 app: trainer. 네 파드가 모두 Running 이 된 뒤 kubectl drain lab-node-1 --ignore-daemonsets --delete-emptydir-data --timeout=20s 를 돌리고 출력을 표준 오류까지 함께 /root/gpudrv/out/drain-blocked.txt 에 저장하세요. 거절당해야 정상입니다.
7. 업그레이드 순서를 그대로 밟습니다. (1) lab-node-1 을 드라이버 550.90.07 로 올리면 필요한 태그가 550-5.15.0-107-generic-ubuntu22.04 입니다 — 그 조합을 support-matrix.csv 에 먼저 더하세요(이미지를 확보한 셈입니다). (2) 예산에 여유를 만드세요 — trainer-pdbminAvailable3 으로 낮춥니다. (3) 같은 drain 명령을 다시 돌려 이번에는 성공시키고 출력을 /root/gpudrv/out/drain-ok.txt 에 저장하세요. (4) lab-node-1 의 드라이버 라벨 셋을 550/90/07 로 바꾸세요(드라이버를 새로 올린 셈입니다 — 이 환경에서 실제 설치는 하지 않습니다). (5) kubectl uncordon lab-node-1 로 노드를 되돌리세요. 마지막으로 drv-audit.sh 를 다시 돌려 OK 가 나오는지 확인하세요.
8. GPU Operator 의 업그레이드 컨트롤러는 노드 라벨 nvidia.com/gpu-driver-upgrade-state 로 진행 상태를 나타냅니다. /root/gpudrv/out/upgrade-states.txt 에 그 상태를 문서에 나오는 차례대로 여덟 줄 적으세요 — upgrade-required, cordon-required, pod-deletion-required, drain-required, pod-restart-required, validation-required, uncordon-required, upgrade-done. 그리고 lab-node-1 에 nvidia.com/gpu-driver-upgrade-state=upgrade-done 라벨을 붙이세요. 마지막으로 /root/gpudrv/out/upgrade-report.txt 에 다섯 줄을 적으세요 — NODES=<gpu.present 가 true 인 노드 수>, SKEW=<drv-audit.sh 가 낸 MISSING 줄 수>, LAB_NODE_1_TAG=<drv-tag.sh 가 lab-node-1 에 대해 내는 태그>, MATRIX_ROWS=<support-matrix.csv 의 머리글을 뺀 줄 수>, UPGRADE_STATE=upgrade-done. 숫자와 태그는 지금 상태에서 명령으로 뽑아 채우세요.

참고

단계 8개

  1. 커널 판과 드라이버 판을 노드의 사실로 세운다
  2. 노드 하나가 필요로 하는 드라이버 이미지 태그를 계산한다
  3. 레지스트리에 있는 조합과 대조해 어긋난 노드를 찾는다
  4. 노드가 한 대 늘면 드라이버 이미지부터 모자란다
  5. CUDA 는 앞으로만 호환된다 — 그 요구를 라벨 조건으로 적는다
  6. 업그레이드를 막는 것은 드라이버가 아니라 예산이다
  7. 이미지를 먼저 확보하고, 여유를 만들고, 절차를 끝까지 걷는다
  8. 업그레이드 상태 기계와 마무리 보고