리소스 쿼터

리소스 쿼터 (Resource Quotas)

여러 사용자나 팀이 고정된 수의 노드를 가진 클러스터를 공유할 때, 한 팀이 공정한 몫 이상의 리소스를 사용할 수 있다는 우려가 있어요. 리소스 쿼터는 관리자에게 이 우려를 해결하기 위한 도구예요.

ResourceQuota 객체로 정의되는 리소스 쿼터는 네임스페이스별 총 리소스 소비를 제한하는 제약을 제공해요. ResourceQuota는 또한 네임스페이스에서 API 유형별로 생성될 수 있는 객체 수를 제한할 수 있으며(#quota-on-object-count), 그 네임스페이스에 있는 API 객체가 소비할 수 있는 총 인프라 리소스 양도 제한할 수 있어요.

출처: 문서

본문

Kubernetes ResourceQuota가 동작하는 방식 (How Kubernetes ResourceQuotas work)

ResourceQuota는 다음과 같이 동작해요:

  • 서로 다른 팀이 서로 다른 네임스페이스에서 작업해요. 이 분리는 RBAC 또는 다른 인가 메커니즘으로 강제할 수 있어요.
  • 클러스터 관리자가 각 네임스페이스에 대해 최소 하나의 ResourceQuota를 만들어요. 강제가 유지되도록 하기 위해 클러스터 관리자는 ValidatingAdmissionPolicy를 정의하는 것 같은 방식으로 그 ResourceQuota를 삭제하거나 업데이트하는 접근을 제한해야 해요.
  • 사용자가 네임스페이스에서 리소스(파드, 서비스 등)를 만들고, 쿼터 시스템이 사용량을 추적해 ResourceQuota에 정의된 하드 리소스 한도를 초과하지 않도록 보장해요. ResourceQuota에 스코프를 적용해 적용되는 곳을 제한할 수 있어요.
  • 리소스를 만들거나 업데이트하는 것이 쿼터 제약을 위반하면 컨트롤 플레인이 HTTP 상태 코드 403 Forbidden으로 그 요청을 거부해요. 오류에는 위반되었을 제약을 설명하는 메시지가 포함돼요.
  • 네임스페이스에서 cpumemory 같은 리소스에 대해 쿼터가 활성화되면 사용자는 파드를 정의할 때 그 값에 대한 requests 또는 limits를 지정해야 해요. 그렇지 않으면 쿼터 시스템이 파드 생성을 거부할 수 있어요. 리소스 쿼터 연습은 이 문제를 피하는 방법의 예시를 보여줘요.
  • 컴퓨트 리소스 요구 사항을 만들지 않는 파드에 기본값을 강제하도록 LimitRange를 정의할 수 있어요(사용자가 그것을 기억할 필요가 없도록).

파드를 직접 만드는 경우는 많지 않아요. 예를 들어 Deployment 같은 워크로드 관리 객체를 더 자주 만들어요. 사용 가능한 것보다 더 많은 리소스를 사용하려는 Deployment를 만들면 Deployment(또는 다른 워크로드 관리 객체)의 생성은 성공하지만, Deployment는 관리하는 모든 파드가 존재하게 하는 데 실패할 수 있어요. 그 경우 kubectl describe 같은 것으로 Deployment의 상태를 확인해 무슨 일이 일어났는지 볼 수 있어요.

  • cpumemory 리소스의 경우 ResourceQuota는 그 네임스페이스의 모든 (새) 파드가 그 리소스에 대한 한도를 설정하도록 강제해요. 네임스페이스에서 cpu 또는 memory에 대해 리소스 쿼터를 강제하면 여러분과 다른 클라이언트가 제출하는 모든 새 파드에 대해 그 리소스에 대한 requests 또는 limits를 지정해야 해요. 그렇지 않으면 컨트롤 플레인이 그 파드의 admission을 거부할 수 있어요.
  • 다른 리소스의 경우: ResourceQuota는 그 리소스에 대한 한도나 요청을 설정하지 않은 네임스페이스의 파드를 무시하고 동작해요. 즉 리소스 쿼터가 이 네임스페이스의 임시 스토리지를 제한하더라도 임시 스토리지에 대한 한도/요청 없이 새 파드를 만들 수 있다는 뜻이에요.

LimitRange를 사용해 이 리소스에 대한 기본 요청을 자동으로 설정할 수 있어요.

ResourceQuota 객체의 이름은 유효한 DNS 하위 도메인 이름이어야 해요.

네임스페이스와 쿼터로 만들 수 있는 정책의 예는 다음과 같아요:

  • 32 GiB RAM과 16 코어 용량의 클러스터에서 팀 A가 20 GiB와 10 코어를 사용하게 하고, B가 10GiB와 4 코어를 사용하게 하며, 향후 할당을 위해 2GiB와 2 코어를 예비로 보유하기.
  • "testing" 네임스페이스를 1 코어와 1GiB RAM 사용으로 제한하기. "production" 네임스페이스가 임의의 양을 사용하게 하기.

클러스터의 총 용량이 네임스페이스 쿼터의 합보다 작은 경우 리소스에 대한 경합이 있을 수 있어요. 이것은 선착순으로 처리돼요.

리소스 쿼터 활성화 (Enabling Resource Quota)

ResourceQuota 지원은 많은 Kubernetes 배포에서 기본적으로 활성화돼 있어요. API 서버--enable-admission-plugins= 플래그가 인자 중 하나로 ResourceQuota를 가질 때 활성화돼요.

해당 네임스페이스에 ResourceQuota가 있을 때 리소스 쿼터가 특정 네임스페이스에서 강제돼요.

리소스 쿼터의 유형 (Types of resource quota)

ResourceQuota 메커니즘은 다양한 종류의 한도를 강제하게 해줘요. 이 섹션은 강제할 수 있는 한도의 유형을 설명해요.

인프라 리소스 쿼터 (#compute-resource-quota)

주어진 네임스페이스에서 요청될 수 있는 컴퓨트 리소스의 총합을 제한할 수 있어요.

다음 리소스 유형이 지원돼요:

리소스 이름 설명
limits.cpu 비종료 상태의 모든 파드에서 CPU 한도의 합이 이 값을 초과할 수 없음
limits.memory 비종료 상태의 모든 파드에서 메모리 한도의 합이 이 값을 초과할 수 없음
requests.cpu 비종료 상태의 모든 파드에서 CPU 요청의 합이 이 값을 초과할 수 없음
requests.memory 비종료 상태의 모든 파드에서 메모리 요청의 합이 이 값을 초과할 수 없음
hugepages- 비종료 상태의 모든 파드에서 지정된 크기의 huge page 요청 수가 이 값을 초과할 수 없음
cpu requests.cpu와 동일
memory requests.memory와 동일

확장 리소스 쿼터 (#quota-for-extended-resources)

위에서 언급한 리소스 외에도 릴리스 1.10에서 확장 리소스에 대한 쿼터 지원이 추가됐어요.

확장 리소스에는 오버커밋이 허용되지 않으므로 쿼터에서 같은 확장 리소스에 대해 requests와 limits를 모두 지정하는 것은 의미가 없어요. 그래서 확장 리소스의 경우 requests. 접두사가 있는 쿼터 항목만 허용돼요.

GPU 리소스를 예로 들면, 리소스 이름이 nvidia.com/gpu이고 네임스페이스에서 요청된 총 GPU 수를 4로 제한하려면 다음과 같이 쿼터를 정의할 수 있어요:

  • requests.nvidia.com/gpu: 4

자세한 내용은 쿼터 보기와 설정을 참고해요.

DRA 리소스 클레임 쿼터 (#quota-for-dra-resource-claims)

DRA(동적 리소스 할당) 리소스 클레임은 디바이스 클래스로 DRA 리소스를 요청할 수 있어요. examplegpu라는 이름의 예시 디바이스 클래스에 대해 네임스페이스에서 요청된 총 GPU 수를 4로 제한하려면 다음과 같이 쿼터를 정의할 수 있어요:

  • examplegpu.deviceclass.resource.k8s.io/devices: 4

DRA에 의한 확장 리소스 할당이 활성화되면 같은 이름의 디바이스 클래스 examplegpu가 확장 리소스로 요청될 수 있어요. 디바이스 클래스의 ExtendedResourceName 필드가 example.com/gpu로 주어지면 명시적으로 요청할 수 있고, 다음과 같이 쿼터를 정의할 수 있어요:

  • requests.example.com/gpu: 4

또는 디바이스 클래스 이름 examplegpu에서 파생된 확장 리소스 이름을 암시적으로 사용해 다음과 같이 쿼터를 정의할 수 있어요:

  • requests.deviceclass.resource.kubernetes.io/examplegpu: 4

리소스 클레임 또는 확장 리소스에서 요청된 모든 디바이스는 위에 나열된 세 가지 쿼터 모두에 계산돼요. 확장 리소스 쿼터(예: requests.example.com/gpu: 4)는 디바이스 플러그인이 제공하는 디바이스도 계산해요.

자세한 내용은 쿼터 보기와 설정을 참고해요.

스토리지 쿼터 (#quota-for-storage)

주어진 네임스페이스에서 요청될 수 있는 볼륨에 대한 스토리지 총합을 제한할 수 있어요.

또한 연관된 StorageClass에 기반해 스토리지 리소스 소비를 제한할 수 있어요.

리소스 이름 설명
requests.storage 모든 영구 볼륨 클레임에서 스토리지 요청의 합이 이 값을 초과할 수 없음
persistentvolumeclaims 네임스페이스에 존재할 수 있는 PersistentVolumeClaim의 총 수
.storageclass.storage.k8s.io/requests.storage <storage-class-name>과 연관된 모든 영구 볼륨 클레임에서 스토리지 요청의 합이 이 값을 초과할 수 없음
.storageclass.storage.k8s.io/persistentvolumeclaims <storage-class-name>과 연관된 모든 영구 볼륨 클레임에서 네임스페이스에 존재할 수 있는 영구 볼륨 클레임의 총 수

예를 들어 bronze StorageClass와 별도로 gold StorageClass로 스토리지 쿼터를 정하고 싶다면 다음과 같이 쿼터를 정의할 수 있어요:

  • gold.storageclass.storage.k8s.io/requests.storage: 500Gi
  • bronze.storageclass.storage.k8s.io/requests.storage: 100Gi

로컬 임시 스토리지 쿼터 (#quota-for-local-ephemeral-storage)

리소스 이름 설명
requests.ephemeral-storage 네임스페이스의 모든 파드에서 로컬 임시 스토리지 요청의 합이 이 값을 초과할 수 없음
limits.ephemeral-storage 네임스페이스의 모든 파드에서 로컬 임시 스토리지 한도의 합이 이 값을 초과할 수 없음
ephemeral-storage requests.ephemeral-storage와 동일

CRI 컨테이너 런타임을 사용할 때 컨테이너 로그는 임시 스토리지 쿼터에 계산돼요. 이것은 스토리지 쿼터를 소진한 파드의 예기치 않은 축출을 초래할 수 있어요. 자세한 내용은 로깅 아키텍처를 참고해요.

객체 수 쿼터 (#quota-on-object-count)

다음 구문을 사용해 Kubernetes API에서 특정 리소스 종류의 총 수에 대한 쿼터를 설정할 수 있어요:

  • 비코어 API 그룹의 리소스에 대해 count/<resource>.<group>
  • 코어 API 그룹의 리소스에 대해 count/<resource>

예를 들어 PodTemplate API는 코어 API 그룹에 있으므로 네임스페이스의 PodTemplate 객체 수를 제한하려면 count/podtemplates를 사용해요.

이러한 유형의 쿼터는 컨트롤 플레인 스토리지의 소진을 방지하는 데 유용해요. 예를 들어 큰 크기를 감안해 서버의 Secret 수를 제한하고 싶을 수 있어요. 클러스터의 Secret이 너무 많으면 실제로 서버와 컨트롤러가 시작되는 것을 방지할 수 있어요. 잘못 구성된 CronJob으로부터 보호하기 위해 Jobs에 대한 쿼터를 설정할 수 있어요. 네임스페이스에서 너무 많은 Job을 만드는 CronJob은 서비스 거부로 이어질 수 있어요.

이 방식으로 쿼터를 정의하면 API 서버의 일부인 Kubernetes API와 CustomResourceDefinition이 지원하는 모든 커스텀 리소스에 적용돼요. 예를 들어 example.com API 그룹의 widgets 커스텀 리소스에 대한 쿼터를 만들려면 count/widgets.example.com을 사용해요. API 집계를 사용해 CustomResourceDefinition으로 정의되지 않은 추가·커스텀 API를 추가하면 코어 Kubernetes 컨트롤 플레인은 집계된 API에 대한 쿼터를 강제하지 않아요. 확장 API 서버가 커스텀 API에 적절하면 쿼터 강제를 제공할 것으로 기대돼요.

객체 수 쿼터 아래에 두고 싶을 수 있는 객체 종류의 일반적인 예시 목록이에요. 사용할 구성 문자열로 나열돼요.

  • count/pods
  • count/persistentvolumeclaims
  • count/services
  • count/secrets
  • count/configmaps
  • count/deployments.apps
  • count/replicasets.apps
  • count/statefulsets.apps
  • count/jobs.batch
  • count/cronjobs.batch

같은 유형의 쿼터만 설정하는 또 다른 구문이 있는데, 특정 API 종류에만 동작해요. 다음 유형이 지원돼요:

리소스 이름 설명
configmaps 네임스페이스에 존재할 수 있는 ConfigMap의 총 수
persistentvolumeclaims 네임스페이스에 존재할 수 있는 PersistentVolumeClaim의 총 수
pods 네임스페이스에 존재할 수 있는 비종료 상태 파드의 총 수. .status.phase in (Failed, Succeeded)가 true이면 파드는 종료 상태
replicationcontrollers 네임스페이스에 존재할 수 있는 ReplicationController의 총 수
resourcequotas 네임스페이스에 존재할 수 있는 ResourceQuota의 총 수
services 네임스페이스에 존재할 수 있는 Service의 총 수
services.loadbalancers 네임스페이스에 존재할 수 있는 LoadBalancer 유형 Service의 총 수
services.nodeports 네임스페이스에 존재할 수 있는 NodePort 또는 LoadBalancer 유형 Service에 할당된 NodePort의 총 수
secrets 네임스페이스에 존재할 수 있는 Secret의 총 수

예를 들어 pods 쿼터는 단일 네임스페이스에서 종료되지 않은 생성된 파드 수의 최대값을 계산하고 강제해요. 사용자가 작은 파드를 많이 만들어 파드 IP의 클러스터 공급을 소진하는 경우를 피하기 위해 네임스페이스에 pods 쿼터를 설정하고 싶을 수 있어요.

더 많은 예시는 쿼터 보기와 설정에서 찾을 수 있어요.

쿼터 보기와 설정 (Viewing and Setting Quotas)

kubectl은 쿼터의 생성, 업데이트, 보기를 지원해요:

kubectl create namespace myspace
cat <<EOF > compute-resources.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
spec:
  hard:
    requests.cpu: "1"
    requests.memory: "1Gi"
    limits.cpu: "2"
    limits.memory: "2Gi"
    requests.nvidia.com/gpu: 4
EOF
kubectl create -f ./compute-resources.yaml --namespace=myspace
cat <<EOF > object-counts.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
spec:
  hard:
    configmaps: "10"
    persistentvolumeclaims: "4"
    pods: "4"
    replicationcontrollers: "20"
    secrets: "10"
    services: "10"
    services.loadbalancers: "2"
EOF
kubectl create -f ./object-counts.yaml --namespace=myspace
kubectl get quota --namespace=myspace
NAME                    AGE
compute-resources       30s
object-counts           32s
kubectl describe quota compute-resources --namespace=myspace
Name:                    compute-resources
Namespace:               myspace
Resource                 Used  Hard
--------                 ----  ----
limits.cpu               0     2
limits.memory            0     2Gi
requests.cpu             0     1
requests.memory          0     1Gi
requests.nvidia.com/gpu  0     4
kubectl describe quota object-counts --namespace=myspace
Name:                   object-counts
Namespace:              myspace
Resource                Used    Hard
--------                ----    ----
configmaps              0       10
persistentvolumeclaims  0       4
pods                    0       4
replicationcontrollers  0       20
secrets                 1       10
services                0       10
services.loadbalancers  0       2

kubectl은 또한 count/<resource>.<group> 구문을 사용해 모든 표준 네임스페이스 리소스에 대한 객체 수 쿼터를 지원해요:

kubectl create namespace myspace
kubectl create quota test --hard=count/deployments.apps=2,count/replicasets.apps=4,count/pods=3,count/secrets=4 --namespace=myspace
kubectl create deployment nginx --image=nginx --namespace=myspace --replicas=2
kubectl describe quota --namespace=myspace
Name:                         test
Namespace:                    myspace
Resource                      Used  Hard
--------                      ----  ----
count/deployments.apps        1     2
count/pods                    2     3
count/replicasets.apps        1     4
count/secrets                 1     4

쿼터와 클러스터 용량 (Quota and Cluster Capacity)

ResourceQuota는 클러스터 용량과 독립적이에요. 절대 단위로 표현돼요. 그래서 클러스터에 노드를 추가해도 각 네임스페이스가 자동으로 더 많은 리소스를 소비할 수 있게 되지는 않아요.

때로는 더 복잡한 정책이 원해질 수 있어요. 예:

  • 총 클러스터 리소스를 여러 팀에게 비례적으로 나누기.
  • 각 테넌트가 필요에 따라 리소스 사용량을 늘리게 하되, 우발적 리소스 소진을 방지하기 위한 넉넉한 한도를 두기.
  • 한 네임스페이스의 수요를 감지하고, 노드를 추가하고, 쿼터를 늘리기.

이러한 정책은 쿼터 사용량을 감시하고 다른 신호에 따라 각 네임스페이스의 쿼터 하드 한도를 조정하는 "컨트롤러"를 작성함으로써 ResourceQuota를 구성 요소로 사용해 구현할 수 있어요.

리소스 쿼터는 총 클러스터 리소스를 나누지만 노드에 대한 제한은 만들지 않는다는 점에 유의해요: 여러 네임스페이스의 파드가 같은 노드에서 실행될 수 있어요.

쿼터 스코프 (Quota scopes)

각 쿼터는 연관된 scopes 집합을 가질 수 있어요. 쿼터는 나열된 스코프의 교집합과 일치할 때만 리소스에 대한 사용량을 측정해요.

스코프가 쿼터에 추가되면 지원하는 리소스 수를 그 스코프와 관련된 것으로 제한해요. 허용된 집합 밖의 쿼터에 지정된 리소스는 검증 오류를 초래해요.

Kubernetes 1.37은 다음 스코프를 지원해요:

스코프 설명
BestEffort (#quota-scope-best-effort) best effort 서비스 품질을 가진 파드와 일치
CrossNamespacePodAffinity (#cross-namespace-pod-affinity-scope) 네임스페이스를 넘는 파드 (반)어피니티 항을 가진 파드와 일치
NotBestEffort (#quota-scope-non-best-effort) best effort 서비스 품질을 가지지 않은 파드와 일치
NotTerminating (#quota-scope-non-terminating) .spec.activeDeadlineSeconds가 nil인 파드와 일치
PriorityClass (#resource-quota-per-priorityclass) 지정된 우선순위 클래스를 참조하는 파드와 일치
Terminating (#quota-scope-terminating) .spec.activeDeadlineSeconds >= 0인 파드와 일치
VolumeAttributesClass (#quota-scope-volume-attributes-class) 지정된 볼륨 속성 클래스를 참조하는 PersistentVolumeClaim과 일치

스코프가 설정된 ResourceQuota는 선택적 scopeSelector 필드를 가질 수도 있어요. 연산자를 지정하는 하나 이상의 일치 표현식과, 관련되면 일치할 값 집합을 정의해요. 예:

scopeSelector:
    matchExpressions:
      - scopeName: BestEffort # Match pods that have best effort quality of service
        operator: Exists # optional; "Exists" is implied for BestEffort scope

scopeSelector는 operator 필드에서 다음 값을 지원해요:

  • In
  • NotIn
  • Exists
  • DoesNotExist

연산자가 In 또는 NotIn이면 values 필드에 최소 하나의 값이 있어야 해요. 예:

scopeSelector:
    matchExpressions:
      - scopeName: PriorityClass
        operator: In
        values:
          - middle

연산자가 Exists 또는 DoesNotExist이면 values 필드를 지정해서는 안 돼요.

Best effort 파드 스코프 (#quota-scope-best-effort)

이 스코프는 파드가 소비한 쿼터만 추적해요. best effort QoS 클래스를 가진 파드만 일치해요. scopeSelector의 연산자는 Exists여야 해요.

Not-best-effort 파드 스코프 (#quota-scope-non-best-effort)

이 스코프는 파드가 소비한 쿼터만 추적해요. Guaranteed 또는 Burstable QoS 클래스를 가진 파드만 일치해요. scopeSelector의 연산자는 Exists여야 해요.

비종료 파드 스코프 (#quota-scope-non-terminating)

이 스코프는 종료되지 않는 파드가 소비한 쿼터만 추적해요. scopeSelector의 연산자는 Exists여야 해요. .spec.activeDeadlineSeconds 필드가 설정되지 않은 파드는 종료되지 않는 것이에요.

이 스코프로 ResourceQuota를 사용해 다음 리소스를 관리할 수 있어요:

  • count.pods
  • pods
  • cpu
  • memory
  • requests.cpu
  • requests.memory
  • limits.cpu
  • limits.memory

종료 파드 스코프 (#quota-scope-terminating)

이 스코프는 종료되는 파드가 소비한 쿼터만 추적해요. scopeSelector의 연산자는 Exists여야 해요. .spec.activeDeadlineSeconds 필드가 어떤 숫자로든 설정되면 파드는 종료되는 것으로 간주돼요.

이 스코프로 ResourceQuota를 사용해 다음 리소스를 관리할 수 있어요:

  • count.pods
  • pods
  • cpu
  • memory
  • requests.cpu
  • requests.memory
  • limits.cpu
  • limits.memory

네임스페이스 간 파드 어피니티 스코프 (#cross-namespace-pod-affinity-scope)

CrossNamespacePodAffinity 쿼터 스코프(#quota-scopes)를 사용해 네임스페이스를 넘는 어피니티 항을 가진 파드를 가질 수 있는 네임스페이스를 제한할 수 있어요. 구체적으로 파드 (반)어피니티 항에서 namespaces 또는 namespaceSelector 필드를 설정할 수 있는 파드를 제어해요.

반어피니티 제약이 있는 파드가 실패 도메인에서 다른 모든 네임스페이스의 파드가 스케줄링되는 것을 막을 수 있으므로 사용자가 네임스페이스 간 어피니티 항을 사용하는 것을 방지하는 것이 바람직할 수 있어요.

이 스코프를 사용하면 (클러스터 관리자로서) 특정 네임스페이스(아래 예시의 foo-ns 같은 것)가 네임스페이스 간 파드 어피니티를 사용하는 파드를 갖지 못하게 할 수 있어요. 그 네임스페이스에 CrossNamespacePodAffinity 스코프와 하드 한도 0으로 ResourceQuota 객체를 만들어 구성해요:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: disable-cross-namespace-affinity
  namespace: foo-ns
spec:
  hard:
    pods: "0"
  scopeSelector:
    matchExpressions:
    - scopeName: CrossNamespacePodAffinity
      operator: Exists

기본적으로 namespacesnamespaceSelector 사용을 금지하고 특정 네임스페이스에만 허용하려면 kube-apiserver 플래그 --admission-control-config-file을 다음 구성 파일의 경로로 설정해 CrossNamespacePodAffinity를 제한 리소스로 구성할 수 있어요:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: "ResourceQuota"
  configuration:
    apiVersion: apiserver.config.k8s.io/v1
    kind: ResourceQuotaConfiguration
    limitedResources:
    - resource: pods
      matchScopes:
      - scopeName: CrossNamespacePodAffinity
        operator: Exists

위 구성으로 파드는 그것이 생성된 네임스페이스가 CrossNamespacePodAffinity 스코프를 가진 리소스 쿼터 객체와 그 필드를 사용하는 파드 수 이상의 하드 한도를 가질 때만 파드 어피니티에서 namespacesnamespaceSelector를 사용할 수 있어요.

PriorityClass 스코프 (#resource-quota-per-priorityclass)

PriorityClass 스코프가 있는 ResourceQuota는 특정 우선순위 클래스를 가진 파드만, 그리고 쿼터 spec의 scopeSelector가 특정 파드를 선택하는 경우에만 일치해요.

파드는 특정 우선순위(/docs/concepts/scheduling-eviction/pod-priority-preemption/#pod-priority)로 생성될 수 있어요. 쿼터 spec의 scopeSelector 필드를 사용해 파드의 우선순위에 기반해 파드의 시스템 리소스 소비를 제어할 수 있어요.

scopeSelector 필드로 쿼터가 PriorityClass에 대해 스코프될 때 ResourceQuota는 다음 리소스만 추적(및 제한)할 수 있어요:

  • pods
  • cpu
  • memory
  • ephemeral-storage
  • limits.cpu
  • limits.memory
  • limits.ephemeral-storage
  • requests.cpu
  • requests.memory
  • requests.ephemeral-storage

예시 (#quota-scope-priorityclass-example)

이 예시는 특정 우선순위의 파드와 일치하는 ResourceQuota를 만들어요. 예시는 다음과 같이 동작해요:

  • 클러스터의 파드는 "low", "medium", "high"라는 세 개의 PriorityClass 중 하나를 가져요. 이것을 시험해 보려면 테스트 클러스터를 사용하고 계속하기 전에 그 세 PriorityClass를 설정해요.
  • 각 우선순위에 대해 하나의 쿼터 객체가 만들어져요.

이 ResourceQuota 집합을 검사해요:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: pods-high
spec:
  hard:
    cpu: "1000"
    memory: "200Gi"
    pods: "10"
  scopeSelector:
    matchExpressions:
    - operator: In
      scopeName: PriorityClass
      values: ["high"]
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: pods-medium
spec:
  hard:
    cpu: "10"
    memory: "20Gi"
    pods: "10"
  scopeSelector:
    matchExpressions:
    - operator: In
      scopeName: PriorityClass
      values: ["medium"]
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: pods-low
spec:
  hard:
    cpu: "5"
    memory: "10Gi"
    pods: "10"
  scopeSelector:
    matchExpressions:
    - operator: In
      scopeName: PriorityClass
      values: ["low"]

kubectl create를 사용해 YAML을 적용해요.

kubectl create -f https://k8s.io/examples/policy/quota.yaml
resourcequota/pods-high created
resourcequota/pods-medium created
resourcequota/pods-low created

Used 쿼터가 0인지 kubectl describe quota로 확인해요.

kubectl describe quota
Name:       pods-high
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     1k
memory      0     200Gi
pods        0     10

Name:       pods-low
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     5
memory      0     10Gi
pods        0     10

Name:       pods-medium
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     10
memory      0     20Gi
pods        0     10

우선순위 "high"를 가진 파드를 만들어요.

apiVersion: v1
kind: Pod
metadata:
  name: high-priority
spec:
  containers:
  - name: high-priority
    image: ubuntu
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo hello; sleep 10;done"]
    resources:
      requests:
        memory: "10Gi"
        cpu: "500m"
      limits:
        memory: "10Gi"
        cpu: "500m"
  priorityClassName: high

파드를 만들려면:

kubectl create -f https://k8s.io/examples/policy/high-priority-pod.yaml

"high" 우선순위 쿼터인 pods-high의 "Used" 통계가 변경되었고 다른 두 쿼터는 변경되지 않았는지 확인해요.

kubectl describe quota
Name:       pods-high
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         500m  1k
memory      10Gi  200Gi
pods        1     10

Name:       pods-low
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     5
memory      0     10Gi
pods        0     10

Name:       pods-medium
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     10
memory      0     20Gi
pods        0     10

기본적으로 PriorityClass 소비 제한하기 (#limiting-priorityclass-consumption-by-default)

"cluster-services" 같은 특정 우선순위의 파드가 일치하는 쿼터 객체가 존재할 때만 네임스페이스에서 허용되는 것이 바람직할 수 있어요.

이 메커니즘으로 운영자는 특정 높은 우선순위 클래스의 사용을 제한된 수의 네임스페이스로 제한할 수 있으며 모든 네임스페이스가 기본적으로 이 우선순위 클래스를 소비할 수는 없게 돼요.

이것을 강제하려면 kube-apiserver 플래그 --admission-control-config-file을 사용해 다음 구성 파일의 경로를 전달해야 해요:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: "ResourceQuota"
  configuration:
    apiVersion: apiserver.config.k8s.io/v1
    kind: ResourceQuotaConfiguration
    limitedResources:
    - resource: pods
      matchScopes:
      - scopeName: PriorityClass
        operator: In
        values: ["cluster-services"]

그런 다음 kube-system 네임스페이스에 리소스 쿼터 객체를 만들어요:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: pods-cluster-services
spec:
  scopeSelector:
    matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["cluster-services"]
kubectl apply -f https://k8s.io/examples/policy/priority-class-resourcequota.yaml -n kube-system
resourcequota/pods-cluster-services created

이 경우 파드 생성은 다음 조건에서 허용돼요:

  • 파드의 priorityClassName이 지정되지 않은 경우.
  • 파드의 priorityClassName이 cluster-services가 아닌 다른 값으로 지정된 경우.
  • 파드의 priorityClassName이 cluster-services로 설정되고 kube-system 네임스페이스에서 생성되며 리소스 쿼터 검사를 통과한 경우.

파드의 priorityClassName이 cluster-services로 설정되고 kube-system 이외의 네임스페이스에서 생성되면 파드 생성 요청이 거부돼요.

VolumeAttributesClass 스코프 (#quota-scope-volume-attributes-class)

이 스코프는 PersistentVolumeClaim이 소비한 쿼터만 추적해요.

PersistentVolumeClaim은 특정 VolumeAttributesClass로 생성될 수 있고, 생성 후 수정될 수도 있어요. 쿼터 spec의 scopeSelector 필드를 사용해 연관된 VolumeAttributesClass에 기반해 PVC의 스토리지 리소스 소비를 제어할 수 있어요.

PVC는 다음 필드로 연관된 VolumeAttributesClass를 참조해요:

  • spec.volumeAttributesClassName
  • status.currentVolumeAttributesClassName
  • status.modifyVolumeStatus.targetVolumeAttributesClassName

ResourceQuota가 PVC를 선택하는 scopeSelector를 가질 때만 관련 ResourceQuota가 일치되고 소비돼요.

scopeSelector 필드로 쿼터가 볼륨 속성 클래스에 대해 스코프될 때 쿼터 객체는 다음 리소스만 추적하도록 제한돼요:

  • persistentvolumeclaims
  • requests.storage

이에 대해 더 배우려면 스토리지 소비 제한을 읽어요.

다음 단계 (What's next)

더 알아보기 (Learn more)