GPU Operator 와 타임슬라이싱 · 오퍼랜드는 라벨이 켜는 만큼만 뜬다 · 실습
라벨 한 줄을 내렸더니 파드가 사라졌다
목표
GPU Operator 가 펼치는 데몬셋 다섯 개를 노드 라벨로 배치하고, 노드 하나에서 오퍼랜드 하나만 빼 보고, '있어야 할 오퍼랜드가 다 있는가' 와 '순서가 어긋난 노드가 있는가' 를 판정하는 도구를 만듭니다.
왜 중요한가
GPU Operator 를 설치하면 파드가 열 개 넘게 뜹니다. 그 파드들은 하나의 프로그램이 아니라 오퍼레이터가 ClusterPolicy 를 읽고 펼친 데몬셋 여러 개입니다. 드라이버, 컨테이너 툴킷, 장치 플러그인, GPU Feature Discovery, DCGM exporter, 검증기, MIG 매니저가 각자 자기 데몬셋을 갖고 있고, 각 데몬셋은 nodeSelector 로 nvidia.com/gpu.deploy.<이름> 라벨을 봅니다. 이 구조를 알면 운영에서 할 수 있는 일이 달라집니다 — 노드 한 대만 오퍼랜드에서 빼는 것도, 드라이버가 이미 깔린 노드에 드라이버만 안 올리는 것도, 문제가 난 노드 하나를 격리하는 것도 라벨 한 줄입니다. 모르면 반대가 됩니다. 라벨 하나가 잘못 붙어 툴킷 없이 장치 플러그인만 도는 노드가 생기면, 자원은 광고되고 스케줄러는 성공하는데 워크로드만 장치를 못 잡습니다. 아무도 오류를 내지 않으니 몇 시간이 그냥 갑니다.
단계
1. /root/gpuops 에서 작업합니다(export KUBECONFIG=/root/.kube/config). 네임스페이스 gpu-operator 를 만드세요. lab-node-0(GPU 노드, 드라이버 없음)에 feature.node.kubernetes.io/pci-10de.present=true, nvidia.com/gpu.present=true 와 함께 nvidia.com/gpu.deploy.driver=true, nvidia.com/gpu.deploy.container-toolkit=true, nvidia.com/gpu.deploy.device-plugin=true, nvidia.com/gpu.deploy.gpu-feature-discovery=true, nvidia.com/gpu.deploy.dcgm-exporter=true 를 붙이세요. lab-node-1(GPU 노드, 드라이버가 호스트에 이미 깔려 있음)에는 같은 라벨을 붙이되 nvidia.com/gpu.deploy.driver 만 false 로 둡니다. lab-node-2(GPU 없음)에는 nvidia.com/gpu.deploy. 로 시작하는 라벨을 하나도 붙이지 마세요. 그리고 /root/gpuops/out/roster.txt 에 다섯 줄을 적으세요 — <오퍼랜드이름>=<그 오퍼랜드를 켜는 라벨 키> 꼴로 driver·container-toolkit·device-plugin·gpu-feature-discovery·dcgm-exporter 다섯 개입니다.
2. /root/gpuops/k8s/toolkit-ds.yaml 에 데몬셋 nvidia-container-toolkit-daemonset 을 쓰세요 — 네임스페이스 gpu-operator, 파드 라벨과 셀렉터는 app: nvidia-container-toolkit-daemonset, nodeSelector 는 nvidia.com/gpu.deploy.container-toolkit: "true", 컨테이너 이미지는 nvcr.io/nvidia/k8s/container-toolkit:v1.16.2, 메모리 요청은 128Mi 입니다. 적용한 뒤 desiredNumberScheduled 가 2 가 되고 파드가 lab-node-0 과 lab-node-1 에 하나씩 뜨는 것을 확인하세요.
3. /root/gpuops/k8s/driver-ds.yaml 에 데몬셋 nvidia-driver-daemonset 을 쓰세요 — 같은 네임스페이스, 파드 라벨과 셀렉터는 app: nvidia-driver-daemonset, nodeSelector 는 nvidia.com/gpu.deploy.driver: "true", 이미지는 nvcr.io/nvidia/driver:550.90.07-ubuntu22.04, 메모리 요청은 512Mi 입니다. 적용한 뒤 GPU 노드가 둘인데도 이 데몬셋만 한 노드에 뜨는 것을 확인하고, /root/gpuops/out/driver.txt 에 두 줄을 적으세요 — DESIRED=<숫자> 와 SKIPPED=<빠진 노드 이름>.
4. 데몬셋 둘을 더 만드세요. /root/gpuops/k8s/device-plugin-ds.yaml 의 nvidia-device-plugin-daemonset 은 nodeSelector nvidia.com/gpu.deploy.device-plugin: "true", 이미지 nvcr.io/nvidia/k8s-device-plugin:v0.16.2, 메모리 요청 128Mi 입니다. /root/gpuops/k8s/gfd-ds.yaml 의 gpu-feature-discovery 는 nodeSelector nvidia.com/gpu.deploy.gpu-feature-discovery: "true", 같은 이미지, 같은 메모리 요청입니다. 둘 다 파드 라벨과 셀렉터는 app: <데몬셋 이름> 입니다. 적용한 뒤 두 데몬셋의 desiredNumberScheduled 가 각각 2 인 것을 확인하세요.
5. /root/gpuops/k8s/dcgm-ds.yaml 에 데몬셋 nvidia-dcgm-exporter 를 쓰세요 — nodeSelector nvidia.com/gpu.deploy.dcgm-exporter: "true", 이미지 nvcr.io/nvidia/k8s/dcgm-exporter:3.3.7-3.5.0-ubuntu22.04, 메모리 요청 128Mi, 파드 라벨과 셀렉터는 app: nvidia-dcgm-exporter 입니다. 적용해 두 노드에 뜨는 것을 확인한 다음, lab-node-1 의 nvidia.com/gpu.deploy.dcgm-exporter 를 false 로 바꾸고 파드가 사라지는 것을 확인하세요. /root/gpuops/out/optout.txt 에 두 줄을 적으세요 — BEFORE=<바꾸기 전 desiredNumberScheduled> 와 AFTER=<바꾼 뒤 값>.
6. /root/gpuops/operand-audit.sh 를 만드세요. 클러스터의 모든 노드를 돌며, 그 노드에 nvidia.com/gpu.deploy.<이름> 라벨이 true 로 붙어 있는 오퍼랜드마다 그 데몬셋의 파드가 그 노드에서 Running 인지 봅니다. 없으면 <노드> MISSING <오퍼랜드이름> 을 한 줄 내고, 그 노드에 빠진 것이 하나도 없으면 <노드> OK 를 한 줄 냅니다. 하나라도 빠졌으면 종료 코드 1, 아니면 0 입니다. 오퍼랜드 이름과 데몬셋 이름의 짝은 1단계의 다섯 개입니다. 만든 뒤 지금 클러스터에 돌려 출력을 /root/gpuops/out/audit.txt 에 저장하세요. 노드 이름을 스크립트 안에 적어 두지 마세요 — 채점기가 노드를 하나 더 만들어 놓고 부릅니다.
7. 누군가 lab-node-2 에 nvidia.com/gpu.deploy.device-plugin=true 만 손으로 붙였다고 칩시다. 그 라벨을 실제로 붙이고(다른 deploy 라벨은 붙이지 않습니다) 장치 플러그인 파드가 그 노드에 뜨는 것을 확인하세요. 그리고 /root/gpuops/order-check.sh <노드이름> 을 만드세요 — 그 노드에 장치 플러그인 파드는 Running 인데 컨테이너 툴킷 파드가 없으면 device-plugin-without-toolkit 한 줄과 종료 코드 1, 그 밖의 경우에는 ok 한 줄과 0 입니다. 세 노드에 차례로 돌려 /root/gpuops/out/order.txt 에 <노드> <결과> 꼴로 세 줄을 적으세요.
8. /root/gpuops/out/matrix.txt 에 노드마다 한 줄씩 세 줄을 적으세요. 형식은 <노드> driver=<yes|no> toolkit=<yes|no> device-plugin=<yes|no> gfd=<yes|no> dcgm=<yes|no> 이고, yes 는 그 오퍼랜드의 데몬셋 파드가 그 노드에서 Running 이라는 뜻입니다. 줄 순서는 lab-node-0, lab-node-1, lab-node-2 입니다. 그리고 네 번째 줄에 TOTAL_PODS=<gpu-operator 네임스페이스에서 Running 인 데몬셋 파드 총수> 를 적으세요. 숫자와 yes/no 는 API 에 물어서 채우세요 — 앞 단계의 기억으로 적으면 어긋납니다.
참고
export KUBECONFIG=/root/.kube/config로 시작합니다. 노드는 lab-node-0/1/2 셋이고 모든 산출물은/root/gpuops아래, 오브젝트는 네임스페이스gpu-operator에 둡니다.- 이 환경에는 GPU 도 GPU Operator 도 없습니다. 그래서 오퍼레이터가 만들어 낼 데몬셋을 여러분이 직접 씁니다. 대신 데몬셋 컨트롤러와 스케줄러는 진짜라, 라벨에 따라 파드가 생기고 사라지는 것은 실물입니다.
- 파드는 실제로 실행되지 않고 Ready 로 위조됩니다. 드라이버가 정말 설치됐는지는 볼 수 없고 보지도 않습니다 — 이 실습이 판정하는 것은 '어느 노드에 무엇이 떴는가' 뿐입니다.
- 데몬셋을 적용하거나 라벨을 바꾼 직후에는 컨트롤러가 반응할 시간이 몇 초 필요합니다. 숫자가 예전 같으면 잠시 뒤 다시 보세요.
- 흔한 실수: nodeSelector 의 값에서 따옴표를 빼는 것. 라벨 값은 문자열이라
"true"로 적어야 합니다. - 흔한 실수: 판정을
desiredNumberScheduled로만 하는 것. 라벨은 맞는데 파드가 못 뜬 노드는 그 숫자에 안 잡힙니다. - [GPU Operator: Getting Started](https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/latest/getting-started.html) · [Installing the NVIDIA GPU Operator](https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/latest/install-gpu-operator.html) · [DaemonSet](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/)
단계 8개
- 오퍼랜드를 켜고 끄는 라벨을 노드마다 다르게 둔다
- 첫 오퍼랜드를 라벨로 배치한다
- 드라이버만 빠지는 노드
- 장치 플러그인과 GFD 를 함께 올린다
- 라벨을 내리자 파드가 사라진다
- 있어야 할 오퍼랜드가 다 있는지 판정하는 도구
- 말이 안 되는 조합을 찾아낸다
- 노드와 오퍼랜드의 표를 실제 상태에서 뽑는다