DRA는 어떻게 작동할까
DRA는 어떻게 작동할까 (How DRA Works)
앞서 동적 리소스 할당(DRA)을 소개했는데, 이번에는 그 내부가 어떻게 돌아가는지 살펴볼게요. 쿠버네티스가 어떻게 장치를 워크로드에 할당하는지, 그리고 미리 스케줄링된(Pod가 이미 spec.nodeName이 설정된) Pod는 이 흐름과 어떻게 상호작용하는지 설명합니다.
DRA로 리소스 할당이 작동하는 방식
사용자 워크플로 (Workflow for users)
- 드라이버 생성 (Driver creation): 장치 소유자나 서드파티 주체가 클러스터에서 ResourceSlice를 만들고 관리할 수 있는 드라이버를 만듭니다. 이 드라이버는 선택적으로 장치 범주와 요청 방법을 정의하는 DeviceClass도 만듭니다.
- 클러스터 설정 (Cluster configuration): 클러스터 관리자가 클러스터를 만들고, 장치를 노드에 부착하고, DRA 장치 드라이버를 설치해요. 관리자는 선택적으로 장치 범주를 정의하는 DeviceClass를 만듭니다.
- 리소스 클레임 (Resource claims): 워크로드 운영자가 특정 DeviceClass 안의 장치 설정을 요청하는 ResourceClaimTemplate이나 ResourceClaim을 만듭니다. 같은 단계에서 워크로드 운영자는 그 클레임을 요청하도록 쿠버네티스 매니페스트를 수정해요.
쿠버네티스 워크플로 (Workflow for Kubernetes)
- ResourceSlice 생성: 클러스터의 드라이버가 비슷한 장치의 관리 풀(pool)을 나타내는 ResourceSlice를 생성합니다.
- 워크로드 생성: 클러스터 컨트롤 플레인이 새 워크로드에 ResourceClaimTemplate이나 특정 ResourceClaim 참조가 있는지 확인해요.
- 워크로드가 ResourceClaimTemplate을 쓰면,
resourceclaim-controller라는 컨트롤러가 워크로드를 위해 ResourceClaim을 생성합니다. - 워크로드가 특정 ResourceClaim을 쓰면, 쿠버네티스는 그 클레임이 클러스터에 존재하는지 확인해요. 없으면 Pod는 배포되지 않습니다.
- 워크로드가 ResourceClaimTemplate을 쓰면,
- ResourceSlice 필터링: 각 Pod마다 쿠버네티스는 클러스터의 ResourceSlice를 확인해 다음 기준을 모두 만족하는 장치를 찾아요:
- 리소스에 접근할 수 있는 노드가 Pod를 실행할 자격이 있을 것.
- ResourceSlice에 Pod의 ResourceClaim 요구사항과 일치하는 할당되지 않은 리소스가 있을 것.
- 리소스 할당: Pod의 ResourceClaim에 적합한 ResourceSlice를 찾으면 쿠버네티스 스케줄러가 그 할당 세부사항으로 ResourceClaim을 업데이트합니다. 스케줄러는 first-fit 전략을 쓰고, 풀과 ResourceSlice를 이름의 사전 순으로 평가해요. 드라이버는 이름을 적절히 지어 특정 슬라이스나 풀에 우선순위를 줄 수 있습니다.
- Pod 스케줄링: 리소스 할당이 끝나면 스케줄러는 할당된 리소스에 접근할 수 있는 노드에 Pod를 배치합니다. 그 노드의 장치 드라이버와
kubelet은 gRPC로 조정해 장치와 Pod의 장치 접근을 설정합니다. 단, 드라이버가 노드 로컬 준비·정리가 필요 없는 장치에 대해 선택적 노드 작업을 선언한 경우는 예외예요.
사전 스케줄링된 Pod (Pre-scheduled Pods)
여러분(또는 다른 API 클라이언트)이 spec.nodeName이 이미 설정된 Pod를 만들면 스케줄러는 우회됩니다. 그 Pod가 필요로 하는 ResourceClaim이 아직 없거나, 할당되지 않았거나, Pod를 위해 예약되지 않았다면 kubelet은 Pod 실행에 실패하고 주기적으로 다시 확인합니다. 그 요구사항이 나중에 충족될 수 있기 때문이에요.
이런 상황은 Pod 스케줄링 당시 스케줄러에 동적 리소스 할당 지원이 켜져 있지 않았을 때(버전 불일치, 설정, 기능 게이트 등)도 생길 수 있어요. kube-controller-manager는 이를 감지하고 필요한 ResourceClaim을 예약해 Pod를 실행 가능하게 만들려고 합니다. 하지만 이는 그 클레임이 스케줄러에 의해 다른 Pod를 위해 할당된 경우에만 작동해요.
스케줄러를 우회하는 건 피하는 게 좋아요. 노드에 배정된 Pod는 멈춰 있는 동안 일반 리소스(RAM, CPU)를 점유해서 다른 Pod가 못 쓰기 때문이죠. 특정 노드에서 실행하면서도 정상 스케줄링 흐름을 거치게 하려면, 정확히 원하는 노드와 일치하는 node selector로 Pod를 만드세요:
apiVersion: v1
kind: Pod
metadata:
name: pod-with-cats
spec:
nodeSelector:
kubernetes.io/hostname: name-of-the-intended-node
...
입력되는 Pod를 admission 시점에 변경해 .spec.nodeName 필드를 해제하고 대신 node selector를 쓰게 할 수도 있어요.
장치 바인딩 조건 (Device binding conditions)
기능 상태: 쿠버네티스 v1.36에서 Beta, 기본 활성화
Device Binding Conditions(장치 바인딩 조건)는 패브릭에 부착된 GPU나 재프로그래밍 가능한 FPGA 같은 외부 리소스가 준비되었는지 확인될 때까지 쿠버네티스 스케줄러가 Pod 바인딩을 지연시킬 수 있게 해줍니다.
이 대기 동작은 스케줄링 프레임워크의 PreBind 단계에서 구현됩니다. 이 단계에서 스케줄러는 바인딩을 진행하기 전에 필요한 모든 장치 조건이 충족되었는지 확인해요.
이 기능은 조기 바인딩을 피해 스케줄링 신뢰성을 높이고, 외부 장치 컨트롤러와의 조정을 가능하게 합니다.
이 기능을 쓰려면 장치 드라이버가 ResourceSlice의 Device 섹션에 다음 필드를 게시해야 하고, 클러스터 관리자가 스케줄러가 이 필드를 존중하도록 DRADeviceBindingConditions와 DRAResourceClaimDeviceStatus 기능 게이트를 켜야 해요.
bindingConditions: Pod가 바인딩되기 전에 관련 ResourceClaim의.status.conditions필드에서 True가 되어야 하는 조건 타입의 목록. 이 조건들은 보통 DeviceAttached나 DeviceInitialized 같은 준비 신호를 나타냅니다.bindingFailureConditions: 관련 ResourceClaim의 status.conditions 필드에서 True로 설정되면 실패 상태를 나타내는 조건 타입 목록. 이 중 하나라도 True면 스케줄러는 바인딩을 중단하고 Pod를 재스케줄링해요.bindsToNode:true로 설정하면 스케줄러는 선택한 노드 이름을 ResourceClaim의status.allocation.nodeSelector필드에 기록합니다. 이는 Pod의spec.nodeSelector에는 영향을 주지 않아요. 대신 ResourceClaim 안에 node selector를 설정해서, 외부 컨트롤러가 장치 부착·준비 같은 노드별 작업을 수행할 때 사용할 수 있게 합니다.
bindingConditions와 bindingFailureConditions에 나열된 모든 조건 타입은 ResourceClaim의 status.conditions 필드에서 평가됩니다. 외부 컨트롤러는 표준 쿠버네티스 조건 의미론(type, status, reason, message, lastTransitionTime)을 사용해 이 조건들을 업데이트해야 해요.
스케줄러는 모든 bindingConditions가 True가 될 때까지 최대 600초(기본값)를 기다립니다. 타임아웃에 도달하거나 bindingFailureConditions 중 하나가 True가 되면, 스케줄러는 할당을 비우고 Pod를 재스케줄링해요. 클러스터 관리자는 kube-scheduler 설정 파일을 편집해 이 타임아웃 시간을 설정할 수 있습니다.
KubeSchedulerConfiguration에서 이 타임아웃을 설정하는 예시:
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default-scheduler
pluginConfig:
- name: DynamicResources
args:
apiVersion: kubescheduler.config.k8s.io/v1
kind: DynamicResourcesArgs
bindingTimeout: 60s
예시 (Example)
바인딩 조건을 지원하는 DRA 드라이버가 쓰이는 클러스터에서 볼 수 있는 ResourceSlice 예시:
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: gpu-slice-1
spec:
driver: dra.example.com
nodeSelector:
nodeSelectorTerms:
- matchExpressions:
- key: accelerator-type
operator: In
values:
- "high-performance"
pool:
name: gpu-pool
generation: 1
resourceSliceCount: 1
devices:
- name: gpu-1
attributes:
vendor:
string: "example"
model:
string: "example-gpu"
bindsToNode: true
bindingConditions:
- dra.example.com/is-prepared
bindingFailureConditions:
- dra.example.com/preparing-failed
이 예시 ResourceSlice의 특징:
accelerator-type=high-performance라벨이 붙은 노드를 대상으로 삼아, 스케줄러가 특정 자격 있는 노드 집합만 사용하게 합니다.- 스케줄러는 선택된 그룹에서 노드 하나(예:
node-3)를 고르고, ResourceClaim의status.allocation.nodeSelector필드를 그 노드 이름으로 설정해요. dra.example.com/is-prepared바인딩 조건은 장치gpu-1이 바인딩 전에 준비되어야 함(is-prepared조건이True)을 나타냅니다.gpu-1장치 준비가 실패하면(preparing-failed조건이True), 스케줄러는 바인딩을 중단해요.- 스케줄러는 장치가 준비될 때까지 최대 600초(기본)를 기다립니다.
- 외부 컨트롤러는 ResourceClaim의 node selector를 사용해 선택된 노드에서 노드별 설정을 수행할 수 있어요.
장치 바인딩 조건은 kube-apiserver와 kube-scheduler의 DRADeviceBindingConditions 기능 게이트로 제어됩니다.
노드 할당 가능 리소스 (Node allocatable resources)
기능 상태: 쿠버네티스 v1.36에서 Alpha, 기본 비활성화
DRA가 관리하는 장치는 cpu, memory, hugepages 같은 노드 할당 가능 리소스로 구성된 기본 발자국(footprint) 을 가질 수 있어요. 이 기능은 DRA 기반 요청을 일반 Pod spec 요청과 함께 스케줄러의 표준 회계(accounting)에 통합합니다.
DRA 드라이버는 두 가지 모델로 장치가 노드 할당 가능 리소스를 소비하는 방식을 정의합니다:
- 직접 리소스 매핑(
mapping): DRA 장치가 표준 노드 리소스(예: 커스텀 CPU 코어 풀, 메모리 블록)를 직접 제공해요. 클레임 할당이 노드의 표준 CPU·메모리 용량에 직접 매핑됩니다. - 보조 장치 오버헤드(
overhead): DRA 장치(GPU나 가속기 같은)가 Pod·컨테이너에 할당되었을 때 동작하기 위해 호스트 리소스(예: 호스트 RAM)를 보조 오버헤드로 요구합니다.
이런 장치 타입의 클레임을 쓰는 PodSpec을 작성할 때 알아둘 점:
- Pod 수준 리소스를 쓰면 스케줄러가 이를 컨테이너 요청·한도 모두에 대해 엄격히 검증합니다:
- 모든 컨테이너 요청과 DRA 클레임 리소스의 합이 Pod 수준 요청을 초과하면 안 됩니다. 초과하면 Pod는 스케줄링에 실패해요.
- 각 컨테이너의 한도와 DRA 할당의 합이 Pod 수준 한도를 초과하면 안 됩니다. 초과하면 스케줄링에 실패합니다.
- 컨테이너의 총 리소스 요구량은 컨테이너 수준 리소스와 관련 리소스 클레임의 노드 할당 가능 리소스의 합입니다.
DRA 드라이버는 ResourceSlice 안 장치의 nodeAllocatableResources 필드로 이 노드 할당 가능 리소스 발자국을 선언합니다. 이 필드는 요청된 DRA 장치·용량을 노드의 status.allocatable에서 추적되는 표준 리소스로 변환하는 것을 정의해요(확장 리소스는 이 필드에 지원되지 않음). CPU나 Memory DRA 드라이버처럼 네이티브 리소스를 직접 노출하는 드라이버와, 호스트 메모리가 필요한 가속기처럼 보조 노드 의존성이 필요한 장치 양쪽에 유용합니다.
nodeAllocatableResources 필드는 두 가지 유스케이스를 지원해요:
- 매핑(Mapping): DRA 장치가 표준 리소스(예: CPU·Memory DRA 드라이버)를 직접 나타낼 때. 스케줄러는
capacityMultiplier로 용량을, 또는deviceMultiplier로 장치 수를 스케일링해 정확한 수량을 계산합니다. - 오버헤드(Overhead): 장치가 보조 노드 의존성(GPU가 소비하는 호스트 메모리 등)을 요구할 때. 이를 참조하는 컨테이너 수에 선형으로 늘어나는 평평한
perPod비용이나 가변perContainer비용으로 정의할 수 있어요.
예시: CPU DRA 드라이버 (매핑)
CPU DRA 드라이버가 DRA 소비 가능 용량으로 CPU 소켓을 128개 CPU 풀로 노출하는 예시입니다. capacityKey는 소비되는 cpu.example.com/cpu 용량을 노드의 표준 cpu 할당 가능 리소스에 직접 연결해요.
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: my-node-cpus
spec:
driver: cpu.example.com
nodeName: my-node
pool:
name: socket-cpus
generation: 1
resourceSliceCount: 1
devices:
- name: socket0cpus
allowMultipleAllocations: true
capacity:
"cpu.example.com/cpu": "128"
nodeAllocatableResources:
mapping:
cpu:
capacityKey: "cpu.example.com/cpu"
- name: socket1cpus
allowMultipleAllocations: true
capacity:
"cpu.example.com/cpu": "128"
nodeAllocatableResources:
mapping:
cpu:
capacityKey: "cpu.example.com/cpu"
capacityMultiplier: 1
예시: 보조 리소스가 있는 가속기 (오버헤드)
가속기가 Pod당 8Gi 메모리를 추가로 요구하는 리소스 슬라이스 예시입니다.
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: my-node-xpus
spec:
driver: xpu.example.com
nodeName: my-node
pool:
name: xpu-pool
generation: 1
resourceSliceCount: 1
devices:
- name: xpu-model-x-001
attributes:
example.com/model:
string: "model-x"
nodeAllocatableResources:
overhead:
memory:
perPod: "8Gi"
Pod가 노드에 성공적으로 바인딩되면, DRA로 할당된 노드 할당 가능 리소스의 정확한 수량이 kube-scheduler에 의해 집계되고 Pod의 status.nodeAllocatableResourceClaimStatuses 필드에 직접 삽입됩니다. 이는 스케줄러에서 kubelet으로 명확하고 영속적인 인계(handoff)를 제공해요.
중요한 점은 kubelet이 이 API를 네이티브로 소비해 시스템 수준 경계를 완벽히 정렬한다는 것입니다:
- cgroups: Pod·컨테이너 cgroup에 이제 DRA 기반 할당이 포함되어, 워크로드가 커널에 의해 인위적으로 제한(throttled)되는 것을 방지합니다.
- OOM 스코어:
kubelet은 컨테이너의 DRA 메모리 요청을 유효 메모리 요청에 반영합니다.
노드 할당 가능 리소스는 alpha 기능이며, kube-apiserver, kube-scheduler, kubelet에서 DRANodeAllocatableResources 기능 게이트가 켜졌을 때 활성화됩니다.