1 - Limit Range

デフォルトでは、コンテナは、Kubernetesクラスター上の計算リソースの消費を制限されずに実行されます。リソースクォータを利用すれば、クラスター管理者はリソースの消費と作成を名前空間ベースで制限することができます。名前空間内では、Podやコンテナは名前空間のリソースクォータで定義された範囲内でできるだけ多くのCPUとメモリーを消費できてしまうため、1つのPodまたはコンテナが利用可能なすべてのリソースを専有してしまう恐れがあります。LimitRangeを利用すれば、このような名前空間内での(Podやコンテナへの)リソースの割り当てを制限するポリシーを定めることができます。

LimitRangeを利用すると、次のような制約を課せるようになります。

  • 名前空間内のPodまたはコンテナごとに、計算リソースの使用量の最小値と最大値を強制する。
  • 名前空間内のPersistentVolumeClaimごとに、ストレージリクエストの最小値と最大値を強制する。
  • 名前空間内で、リソースのrequestとlimitの割合を強制する。
  • 名前空間内の計算リソースのデフォルトのrequest/limitの値を設定して、実行時にコンテナに自動的に注入する。

LimitRangeを有効にする

Kubernetes 1.10以降では、LimitRangeのサポートはデフォルトで有効になりました。

LimitRangeが特定の名前空間内で強制されるのは、その名前空間内にLimitRangeオブジェクトが存在する場合です。

LimitRangeオブジェクトの名前は、有効なDNSサブドメイン名でなければなりません。

Limit Rangeの概要

  • 管理者は、1つの名前空間に1つのLimitRangeを作成します。
  • ユーザーは、Pod、コンテナ、PersistentVolumeClaimのようなリソースを名前空間内に作成します。
  • LimitRangerアドミッションコントローラーは、計算リソース要求が設定されていないすべてのPodとコンテナに対して、デフォルト値と制限値を強制します。そして、リソースの使用量を追跡し、名前空間内に存在するすべてのLimitRangeで定義された最小値、最大値、割合を外れないことを保証します。
  • LimitRangeの制約を破るようなリソース(Pod、コンテナ、PersistentVolumeClaim)の作成や更新を行うと、APIサーバーへのリクエストがHTTPステータスコード403 FORBIDDENで失敗し、破られた制約を説明するメッセージが返されます。
  • 名前空間内でLimitRangeがcpumemoryなどの計算リソースに対して有効になっている場合、ユーザーはrequestsやlimitsに値を指定しなければなりません。指定しなかった場合、システムはPodの作成を拒否する可能性があります。
  • LimitRangeの検証は、Podのアドミッションステージでのみ発生し、実行中のPodでは発生しません。

以下は、LimitRangeを使用して作成できるポリシーの例です。

  • 8GiBのRAMと16コアのCPUの容量がある2ノードのクラスター上で、名前空間内のPodに対して、CPUには100mのrequestと最大500mのlimitの制約を課し、メモリーには200Miのrequestと600Miのlimitの制約を課す。
  • Spec内のrequestsにcpuやmemoryを指定せずに起動したコンテナに対して、CPUにはデフォルトで150mのlimitとrequestを、メモリーにはデフォルトで300Miのrequestをそれぞれ定義する。

名前空間のlimitの合計が、Podやコンテナのlimitの合計よりも小さくなる場合、リソースの競合が起こる可能性があります。その場合、コンテナやPodは作成されません。

LimitRangeに対する競合や変更は、すでに作成済みのリソースに対しては影響しません。

次の項目

より詳しい情報は、LimitRangerの設計ドキュメントを参照してください。

制限の使用例については、以下のページを読んでください。

2 - リソースクォータ

複数のユーザーやチームが決められた数のノードを持つクラスターを共有しているとき、1つのチームが公平に使えるリソース量を超えて使用するといった問題が出てきます。

リソースクォータはこの問題に対処するための管理者向けツールです。

ResourceQuotaオブジェクトによって定義されるリソースクォータは、名前空間ごとの総リソース消費を制限するための制約を提供します。リソースクォータは同じ名前空間のクラスター内でタイプごとに作成できるオブジェクト数や、名前空間内のリソースによって消費されるコンピュートリソースの総量を制限できます。

リソースクォータは下記のように働きます。

  • 異なる名前空間で異なるチームが存在するとき。現時点ではこれは自主的なものですが、将来的にはACLsを介してリソースクォータの設定を強制するように計画されています。
  • 管理者は各名前空間で1つのResourceQuotaを作成します。
  • ユーザーが名前空間内でリソース(Pod、Serviceなど)を作成し、クォータシステムがResourceQuotaによって定義されたハードリソースリミットを超えないことを保証するために、リソースの使用量をトラッキングします。
  • リソースの作成や更新がクォータの制約に違反しているとき、そのリクエストはHTTPステータスコード403 FORBIDDENで失敗し、違反した制約を説明するメッセージが表示されます。
  • cpumemoryといったコンピューターリソースに対するクォータが名前空間内で有効になっているとき、ユーザーはそれらの値に対するrequestslimitsを設定する必要があります。設定しないとクォータシステムがPodの作成を拒否します。 ヒント: コンピュートリソースの要求を設定しないPodに対してデフォルト値を強制するために、LimitRangerアドミッションコントローラーを使用してください。この問題を解決する例はwalkthroughで参照できます。

ResourceQuotaのオブジェクト名は、有効なDNSサブドメイン名である必要があります.

名前空間とクォータを使用して作成できるポリシーの例は以下の通りです。

  • 32GiB RAM、16コアのキャパシティーを持つクラスターで、Aチームに20GiB、10コアを割り当て、Bチームに10GiB、4コアを割り当て、将来の割り当てのために2GiB、2コアを予約しておく。
  • "testing"という名前空間に対して1コア、1GiB RAMの使用制限をかけ、"production"という名前空間には制限をかけない。

クラスターの総キャパシティーが、その名前空間のクォータの合計より少ない場合、リソースの競合が発生する場合があります。このとき、リソースの先着順で処理されます。

リソースの競合もクォータの変更も、作成済みのリソースには影響しません。

リソースクォータを有効にする

多くのKubernetesディストリビューションにおいてリソースクォータはデフォルトで有効になっています。APIサーバーで--enable-admission-plugins=の値にResourceQuotaが含まれるときに有効になります。

特定の名前空間にResourceQuotaがあるとき、そのリソースクォータはその名前空間に適用されます。

コンピュートリソースクォータ

特定の名前空間において、コンピュートリソースの合計に上限を設定できます。

下記のリソースタイプがサポートされています。

リソース名 説明
limits.cpu 停止していない状態の全てのPodで、CPUリミットの合計がこの値を超えることができません。
limits.memory 停止していない状態の全てのPodで、メモリーの合計がこの値を超えることができません。
requests.cpu 停止していない状態の全てのPodで、CPUリクエストの合計がこの値を超えることができません。
requests.memory 停止していない状態の全てのPodで、メモリーリクエストの合計がこの値を超えることができません。
hugepages-<size> 停止していない状態の全てのPodで, 指定されたサイズのHuge Pageリクエスト数がこの値を超えることができません。
cpu requests.cpuと同じ。
memory requests.memoryと同じ。

拡張リソースのためのリソースクォータ

上記で取り上げたリソースに加えて、Kubernetes v1.10において、拡張リソースのためのリソースクォータのサポートが追加されました。

拡張リソースに対するオーバーコミットが禁止されているのと同様に、リソースクォータで拡張リソース用にrequestslimitsの両方を指定しても意味がありません。現在、拡張リソースに対してはrequests.というプレフィックスのついたクォータアイテムのみ設定できます。

GPUリソースを例にすると、もしリソース名がnvidia.com/gpuで、ユーザーが名前空間内でリクエストされるGPUの上限を4に指定するとき、下記のようにリソースクォータを定義します。

  • requests.nvidia.com/gpu: 4

さらなる詳細はクォータの確認と設定を参照してください。

ストレージのリソースクォータ

特定の名前空間においてストレージリソースの総数に上限をかけることができます。

さらに、関連するストレージクラスに基づいて、ストレージリソースの消費量に上限をかけることもできます。

リソース名 説明
requests.storage 全てのPersistentVolumeClaimにおいて、ストレージのリクエストの合計がこの値を超えないようにします。
persistentvolumeclaims 特定の名前空間内で作成可能なPersistentVolumeClaimの総数。
<storage-class-name>.storageclass.storage.k8s.io/requests.storage ストレージクラス名<storage-class-name>に関連する全てのPersistentVolumeClaimにおいて、ストレージリクエストの合計がこの値を超えないようにします。
<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims ストレージクラス名<storage-class-name>に関連する全てのPersistentVolumeClaimにおいて、特定の名前空間内で作成可能なPersistentVolumeClaimの総数。

例えば、もし管理者がgoldストレージクラスをbronzeストレージクラスと分けてリソースクォータを設定するとき、管理者はリソースクォータを下記のように指定できます。

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

Kubernetes v1.8において、ローカルのエフェメラルストレージに対するリソースクォータのサポートがα版の機能として追加されました。

リソース名 説明
requests.ephemeral-storage 名前空間内の全てのPodで、ローカルのエフェメラルストレージのリクエストの合計がこの値を超えないようにします。
limits.ephemeral-storage 名前空間内の全てのPodで、ローカルのエフェメラルストレージのリミットの合計がこの値を超えないようにします。
ephemeral-storage requests.ephemeral-storageと同じ。

オブジェクト数に対するクォータ

下記のシンタックスを使用して、名前空間に紐づいた全ての標準であるリソースタイプの中の特定のリソースの総数に対するリソースクォータを設定できます。

  • count/<resource>.<group> コアでないグループのリソース用
  • count/<resource> コアグループのリソース用

オブジェクト数に対するクォータでユーザーが設定するリソースの例は下記の通りです。

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

カスタムリソースに対して同じシンタックスを使用できます。例えば、example.comというAPIグループ内のwidgetsというカスタムリソースのリソースクォータを設定するにはcount/widgets.example.comと記述します。

count/*リソースクォータの使用において、オブジェクトがサーバーストレージに存在するときオブジェクトはクォータの計算対象となります。このようなタイプのリソースクォータはストレージリソース浪費の防止に有効です。例えば、もしSecretが大量に存在するとき、そのSecretリソースの総数に対してリソースクォータの制限をかけたい場合です。クラスター内でSecretが大量にあると、サーバーとコントローラーの起動を妨げることになります。適切に設定されていないCronJobから保護するためにジョブのクォータを設定できます。名前空間内で大量のJobを作成するCronJobは、サービスを利用不可能にする可能性があります。

また、限定されたリソースのセットにおいて汎用オブジェクトカウントのリソースクォータを実行可能です。

下記のタイプのリソースがサポートされています。

リソース名 説明
configmaps 名前空間内で存在可能なConfigMapの総数。
persistentvolumeclaims 名前空間内で存在可能なPersistentVolumeClaimの総数。
pods 名前空間内で存在可能な停止していないPodの総数。.status.phase in (Failed, Succeeded)がtrueのとき、Podは停止状態にあります。
replicationcontrollers 名前空間内で存在可能なReplicationControlerの総数。
resourcequotas 名前空間内で存在可能なResourceQuotaの総数。
services 名前空間内で存在可能なServiceの総数。
services.loadbalancers 名前空間内で存在可能なtype:LoadBalancerであるServiceの総数。
services.nodeports 名前空間内で存在可能なtype:NodePortであるServiceの総数。
secrets 名前空間内で存在可能なSecretの総数。

例えば、podsのリソースクォータはPodの総数をカウントし、特定の名前空間内で作成されたPodの総数の最大数を設定します。またユーザーが多くのPodを作成し、クラスターのPodのIPが枯渇する状況を避けるためにpodsのリソースクォータを名前空間に設定したい場合があります。

クォータのスコープについて

各リソースクォータには関連するscopeのセットを関連づけることができます。クォータは、列挙されたscopeの共通部分と一致する場合にのみリソースの使用量を計測します。

スコープがクォータに追加されると、サポートするリソースの数がスコープに関連するリソースに制限されます。許可されたセット以外のクォータ上でリソースを指定するとバリデーションエラーになります。

スコープ 説明
Terminating .spec.activeDeadlineSeconds >= 0であるPodに一致します。
NotTerminating .spec.activeDeadlineSecondsがnilであるPodに一致します。
BestEffort ベストエフォート型のサービス品質のPodに一致します。
NotBestEffort ベストエフォート型のサービス品質でないPodに一致します。
PriorityClass 指定された優先度クラスと関連付いているPodに一致します。

BestEffortスコープはリソースクォータを次のリソースに対するトラッキングのみに制限します:

  • pods

TerminatingNotTerminatingNotBestEffortPriorityClassスコープは、リソースクォータを次のリソースに対するトラッキングのみに制限します:

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

同じクォータでTerminatingNotTerminatingの両方のスコープを指定することはできず、また同じクォータでBestEffortNotBestEffortの両方のスコープを指定することもできないことに注意してください。

scopeSelectoroperator フィールドにおいて下記の値をサポートしています。:

  • In
  • NotIn
  • Exists
  • DoesNotExist

scopeSelectorの定義においてscopeNameに下記のいずれかの値を使用する場合、operatorExistsを指定してください。

  • Terminating
  • NotTerminating
  • BestEffort
  • NotBestEffort

operatorInまたはNotInの場合、valuesフィールドには少なくとも1つの値が必要です。例えば以下のように記述します:

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

operatorExistsまたはDoesNotExistの場合、valuesフィールドは指定しないでください

PriorityClass毎のリソースクォータ

FEATURE STATE: Kubernetes v1.17 [stable]

Podは特定の優先度で作成されます。リソースクォータのSpec内にあるscopeSelectorフィールドを使用して、Podの優先度に基づいてPodのシステムリソースの消費をコントロールできます。

リソースクォータのSpec内のscopeSelectorによってPodが選択されたときのみ、そのリソースクォータが一致し、消費されます。

リソースクォータがscopeSelectorフィールドを使用して優先度クラスに対してスコープされる場合、リソースクォータのオプジェクトは、次のリソースのみトラッキングするように制限されます:

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

この例ではリソースクォータのオブジェクトを作成し、特定の優先度を持つPodに一致させます。この例は下記のように動作します。

  • クラスター内のPodは"low"、"medium"、"high"の3つの優先度クラスのうち1つをもちます。
  • 1つのリソースクォータのオブジェクトは優先度毎に作成されます。

下記のYAMLをquota.ymlというファイルに保存します。

apiVersion: v1
kind: List
items:
- 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 ./quota.yml
resourcequota/pods-high created
resourcequota/pods-medium created
resourcequota/pods-low created

kubectl describe quotaを実行してUsedクォータが0であることを確認します。

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"であるPodを作成します。下記の内容をhigh-priority-pod.ymlというファイルに保存します。

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でマニフェストを適用します。

kubectl create -f ./high-priority-pod.yml

pods-highという名前のプライオリティーが"high"のクォータにおける"Used"項目の値が変更され、それ以外の2つの値は変更されていないことを確認してください。

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

リクエスト vs リミット

コンピュートリソースを分配する際に、各コンテナはCPUとメモリーそれぞれのリクエストとリミット値を指定します。クォータはそれぞれの値を設定できます。

クォータにrequests.cpurequests.memoryの値が指定されている場合は、コンテナはそれらのリソースに対する明示的な要求を行います。同様に、クォータにlimits.cpulimits.memoryの値が指定されている場合は、コンテナはそれらのリソースに対する明示的な制限を行います。

クォータの確認と設定

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

クォータとクラスター容量

ResourceQuotaはクラスター容量に依存しません。またユニット数の絶対値で表されます。そのためクラスターにノードを追加したことにより、各名前空間が自動的により多くのリソースを消費するような機能が提供されるわけではありません

下記のようなより複雑なポリシーが必要な状況があります。

  • 複数チーム間でクラスターリソースの総量を分けあう。
  • 各テナントが必要な時にリソース使用量を増やせるようにするが、偶発的なリソースの枯渇を防ぐために上限を設定する。
  • 1つの名前空間に対してリソース消費の需要を検出し、ノードを追加し、クォータを増加させる。

このようなポリシーは、クォータの使用量の監視と、他のシグナルにしたがってクォータのハードの制限を調整する"コントローラー"を記述することにより、ResourceQuotaをビルディングブロックのように使用して実装できます。

リソースクォータは集約されたクラスターリソースを分割しますが、ノードに対しては何の制限も行わないことに注意して下さい。例: 複数の名前空間のPodは同一のノード上で稼働する可能性があります。

デフォルトで優先度クラスの消費を制限する

例えば"cluster-services"のように、条件に一致するクォータオブジェクトが存在する場合に限り、特定の優先度のPodを名前空間で許可することが望ましい場合があります。

このメカニズムにより、オペレーターは特定の高優先度クラスの使用を限られた数の名前空間に制限することができ、全ての名前空間でこれらの優先度クラスをデフォルトで使用することはできなくなります。

これを実施するには、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"]

なお、"cluster-services"Podは、条件に一致するscopeSelectorを持つクォータオブジェクトが存在する名前空間でのみ許可されます。

    scopeSelector:
      matchExpressions:
      - scopeName: PriorityClass
        operator: In
        values: ["cluster-services"]

次の項目

3 - ノードリソースマネージャー

レイテンシーが致命的なワークロードや高スループットのワークロードをサポートするために、Kubernetesは一連のリソースマネージャーを提供します。リソースマネージャーはCPUやデバイス、メモリ(hugepages)などのリソースの特定の要件が設定されたPodのためにノードリソースの調整と最適化を目指しています。

メインのマネージャーであるトポロジーマネージャーはポリシーに沿って全体のリソース管理プロセスを調整するKubeletコンポーネントです。

個々のマネージャーの設定は下記のドキュメントで詳しく説明されてます。