kubernetes 환경에서 GPU 서빙 | GPU인식
들어가면서
AI시대에 회사에서 GPU를 어떻게 서빙할지에 대한 고민이 있습니다.
이를 위해, GPU가 어떻게 Kubernetes와 결합해서 서빙 되는지 이해하고자, 조사하게 되었습니다.
여러 Inference Accelerator(AWS Inferentia, Google TPU등)들이 있지만, 여기서는 회사에서 사용하고 있는, Nvidia GPU를 기준으로 알아보겠습니다.
Kubernetes에서 GPU 인식시키기
GPU는 개별 Node의 자원이기 때문에, 크게 아래의 2개 단계로 나누어 해결하게 됩니다.
- GPU를 갖고 있는 Node에 Pod를 할당합니다.
- Node안에서 GPU 하드웨어를 Pod에 연결해줍니다.
Kubernetes의 Device Plugin framework
Kubernetes에서의 GPU인식은, ‘Device Plugin’이라는 기능을 Base로 작동합니다.
Device Plugin?
Kubernetes는 Hardware(이하 HW)의 추상화(Abstraction)을 제공합니다. 이 과정에서 수 많은 하드웨어를 지원하는 Interface가 ‘Device plugin’입니다.
‘Device Plugin’을 통해, Hardware Vendor(하드웨어 공급자, Nvidia같은)는 Kubernetes내부 Code를 변경하지 않고, Node의 kubelet에 HW를 인식시킬 수 있습니다(kubelet이 인식한 HW를 Cluster에 Advertise합니다).
단, Vendor사는 Resource name을
vendor-domain/resourcetype와 같은 규칙으로 정의해야 합니다.
대표적인 예시로,nvidia.com/gpu가 있습니다.
Device Plugin Workflow
Device Plugin은 크게 등록(Registration), 모니터링(ListAndWatch), 할당(Allocate)의 3단계로 작동합니다.
Device Plugin 작동 Flow | from github.com/kubernetes/design-proposals-archive
이 Workflow를 위해, ‘Device Plugin’은 다음과 같은 Function이 구현되어야 합니다.
GetDevicePluginOptions
초기 연결 시 호출됩니다. 특정 옵션(예:PreStartContainer실행 여부)을 지원하는지 확인합니다.ListAndWatch (필수)
핵심 중의 핵심입니다.
gRPC 스트림을 열어둡니다. Plugin은 이 연결을 통해 장치 목록(ID)과 상태(Healthy/Unhealthy)를 실시간으로 Kubelet에 쏩니다. 장치에 장애가 발생하면 이 함수를 통해 즉시 보고됩니다.Allocate (필수)
Pod가 GPU를 사용하려고 할 때 호출됩니다. Kubelet은 “GPU ID 0번을 쓰려고 하니 필요한 정보를 줘”라고 요청하고, Plugin은 해당 GPU를 사용하기 위한 환경 변수, 볼륨 마운트 경로, 장치 노드(/dev/nvidia0 등) 정보를 응답합니다.PreStartContainer (선택)
컨테이너가 시작되기 직전에 실행됩니다. 장치를 초기화하거나 특정 설정을 초기화해야 할 때 사용합니다.GetPreferredAllocation (선택)
여러 개의 장치 중 어떤 것을 우선적으로 할당할지 결정할 때 사용합니다 (Topology-aware 할당 등).
Kubelet, Device Plugin, Hardware로 이어지는 통신
‘kubelet과 ‘Device Plugin’의 통신
Device Plugins are expected to communicate with Kubelet through gRPC on an Unix socket.
When starting the gRPC server, they are expected to create a unix socket at the following host path: /var/lib/kubelet/device-plugins/.
- from: kubernetes/design-proposal-archive
‘Device Plugin’과 ‘kubelet’은 gRPC(Unix Socket기반)로 통신하게 됩니다.
때문에, Host OS에서 /var/lib/kubelet/device-plugins/ 경로로 가면, 이 통신에 사용하는 Socket을 확인할 수 있습니다.
1
2
3
4
5
6
7
root@***:/var/lib/kubelet/device-plugins# ls -al
total 12
drwxr-xr-x 2 root root 4096 Feb 27 06:51 .
drw-r--r-- 10 root root 4096 Feb 28 00:06 ..
srwxr-xr-x 1 root root 0 Feb 27 06:51 kubelet.sock <-- kubelet이 device plugin용으로 열어둔 Socket입니다.
..
srwxr-xr-x 1 root root 0 Feb 27 06:51 nvidia-mig-3g.20gb.sock <-- 등록을 마친 GPU가 kubelet이 접속할 수 있게 열어둔 Socket입니다
‘Device Plugin’과 Hardware의 통신
‘Device Plugin’은 OS의 HW 드라이버와 연결됩니다.
이때, Pod의 Container는 Driver를 포함하지 않습니다. Host(여기선 Node를 의미)의 Driver파일 Container에 Mount시켜서 사용합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
kind: Pod
apiVersion: v1
metadata:
name: nvidia-device-plugin-daemonset-88s6m
...
spec:
volumes:
...
- name: driver-install-dir
hostPath:
path: /run/nvidia/driver
type: DirectoryOrCreate
...
Q: Host의 드라이버를 사용하면, Host의 OS와 Container의 OS가 일치해야 하는것 아닌가?
A: Container 자체가 Host와 Kernel를 공유하기 때문에, 이미 Kernel을 일치합니다. OS는 달라도 됩니다.
Nvidia Device Plugin
‘Nvidia Device Plugin’은 위의 Device Plugin Framework를 이용해, Nvidia GPU Resource를 Kubernetes에 조달해줍니다.
Nvidia Device Plugin의 작동 Flow
‘Nvidia Device Plugin’은 일반적인 Device Plugin과 달리, ‘NVIDIA Container Stack(과거 Nvidia Container Runtime)’와의 상호작용이 핵심입니다.
‘Nvidia Container Stack’은 Container Runtime(kubelet이 Container 생성을 지시하는 대상) 수준에서, 별도의 GPU를 사용할 수 있는 Container Runtime을 만들어냅니다.
GPU를 사용하기 위해선, 이 Runtime을 사용해야 합니다.
탐색(Discovery)
Nvidia Device Plugin의 Discovery 과정. | from alibabacloud.com/blog- Plugin이 실행되면 호스트의 NVML(NVIDIA Management Library)을 호출합니다.
이를 통해, Node에 장착되어 있는 GPU 모델, 갯수, 드라이버 상태를 확인하게 됩니다.
- Plugin이 실행되면 호스트의 NVML(NVIDIA Management Library)을 호출합니다.
- 할당(Allocate) & 환경 변수 주입
- 사용자가
resources: limits: nvidia.com/gpu: 1를 요청하면, GPU의 UUID(Host에서 관리하는 Device ID)를 환경변수(NVIDIA_VISIBLE_DEVICES=UUID)에 주입합니다.
이렇게 각 Pod로 GPU가 분산, 할당됩니다.
- 사용자가
- 런타임 가로채기 (The Hook)
- Kubelet이 컨테이너를 생성할 때, ‘NVIDIA Container Runtime’이 이 과정을 가로챕니다.
런타임(NVIDIA Container Runtime)은 ‘2’번 과정에 주입된 환경변수(NVIDIA_VISIBLE_DEVICES)를 확인하고,
Container내부에/dev/nvidia0와 같이 Device와 구동에 필요한 라이브러리(.so파일들)를 직접 Mount해 줍니다.
- Kubelet이 컨테이너를 생성할 때, ‘NVIDIA Container Runtime’이 이 과정을 가로챕니다.
CDI(Container Device Interface)
최근 NVIDIA는 기존의 복잡한 환경 변수/볼륨 마운트 방식 대신, 더 표준화된 방식인 CDI로 전환하고 있습니다.
- CDI는
- 위의 ‘Container Runtime’을 별도로 생성하는 방식이 아닌, 표준화된 방식으로, GPU와 같은 외부 Device를 Container에 연결하기 위한 표준입니다.
- HW를 Container에 연결하는 과정을 단순하게 만들어줍니다.
References
- Kubernetes Device Plugin | kubernetes.io
- Device Plugins
- Kubernetes Device Plugin Proposal | github.com/kubernetes/design-proposals-archive
- ‘Device Plugin’의 세부 설계가 가장 잘 나와있는 문서입니다.
- https://github.com/kubernetes/design-proposals-archive/blob/main/resource-management/device-plugin.md
- Nvidia device plugin
- https://github.com/NVIDIA/k8s-device-plugin
- Container Device Interface | docs.nvidia.com
- Container Device Interface (CDI) Support in the GPU Operator — NVIDIA GPU Operator
