Kubernetesのリソースクォータとリソース制限
Kubernetesのリソースクォータとリソース制限
本記事では、Kubernetesにおいてコンテナが使用するCPUやメモリ等の計算資源を制限する方法を説明します。
なお、「計算資源」の英語訳は「リソース」(Resource)ですが、Kubernetesが管理するオブジェクトもまたリソースと呼ばれるため、以下では「リソース」という言葉は、後者のKubernetesが管理するオブジェクトを指すものとします。
Kubernetesリソースの分類
Kubernetesリソースは、概ね5つのカテゴリに分類されており、本記事で取り上げるリソースの対応関係は、次の通りです。
番号 | カテゴリ | 説明 | リソース |
---|---|---|---|
1 | Cluster APIs | セキュリティや計算資源制限に関連するリソース群 | Node, Namespace, Resource Quota |
2 | Config & Storage APIs | 設定や永続化のためのストレージに関連するリソース群 | |
3 | Metadata APIs | リソース制御に関連するリソース群 | LimitRange |
4 | Service APIs | コンテナの外部公開に関連するリソース群 | |
5 | Workloads APIs | コンテナの実行に関連するリソース群 |
Nodeリソース
Nodeは普段、直接操作することはないリソースですが、ノード(サーバ)が持つ計算資源や現在使用中の計算資源を確認できる点では重要です。
まず、Node一覧を表示してみます。
kubectl get nodes
次に、特定のNodeの概要情報をYAMLに出力します。
kubectl get node <Node名> -o yaml
Nodeが持つ物理的な計算資源は、status.capacity
で確認できます。
このcpu
やmemory
の値は、ノードに実際に搭載されたCPUコア数やメモリ容量です。
[前略]
status:
[中略]
capacity:
cpu: "6"
ephemeral-storage: 61255492Ki
hugepages-1Gi: "0"
hugepages-2Mi: "0"
memory: 2032964Ki
pods: "110"
[後略]
status.allocatable
では、Kubernetes自身が必要とする計算資源を差し引いたものが表示されています。
[前略]
status:
[中略]
allocatable:
cpu: "6"
ephemeral-storage: "56453061334"
hugepages-1Gi: "0"
hugepages-2Mi: "0"
memory: 1930564Ki
pods: "110"
[後略]
現在使用中の計算資源を調べるため、特定のNodeの詳細情報を表示します。
kubectl describe node <Node名>
Nodeの詳細情報例です。
[前略]
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits Age
--------- ---- ------------ ---------- --------------- ------------- ---
kube-system coredns-78fcd69978-f78q8 100m (1%) 0 (0%) 70Mi (3%) 170Mi (9%) 231d
kube-system coredns-78fcd69978-q9zgw 100m (1%) 0 (0%) 70Mi (3%) 170Mi (9%) 231d
[中略]
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
Resource Requests Limits
-------- -------- ------
cpu 850m (14%) 0 (0%)
memory 240Mi (12%) 340Mi (18%)
ephemeral-storage 0 (0%) 0 (0%)
hugepages-1Gi 0 (0%) 0 (0%)
hugepages-2Mi 0 (0%) 0 (0%)
[後略]
現在割り当てが可能な、余剰の計算資源は、上述のstatus.allocatable
から、この詳細情報のAllocated resources
を差し引いたものになります。
Eviction Manager
Eviction Managerは、Nodeリソースのkube-reserved
, system-reserved
, Allcatable
の合計値がEviction Thresholdを超えていないかを常時確認し、超えている場合にはPodを強制的に除外(Evict)する処理を行なっています。
これにより、Kubernetesクラスターが高負荷になり続けるのを防いでいます。
Eviction Thresholdには、softとhardの2つのリミットが定義されています。
- soft: リミットを超えたあと、
terminationGracePeriodSeconds
での設定時間経過後に、Podに対してSIGTERM
シグナルが送られます - hard: リミットを超えた直後にPodに対して
SIGKILL
が送られます
Namespaceリソース
Kubernetesには、複数のNamespaceを作成し、Namespace間をセキュリティ的に分離する機能があります。
1つのNamespaceに複数のDeploymentやServiceを含めることができるので、単純には、Namespaceは他のリソースをまとめるものと捉えても構いません。
Namespaceに対しては、後述するResourceQuotaを使い、Namespaceに含まれるリソース全体に計算資源の制限を掛けることが可能です。
Namespaceの作成
Namespaceは次のコマンドで作成できます。
kubectl create namespace example-namespace
または、以下のようなマニフェストを作成し、
apiVersion: v1
kind: Namespace
metadata:
name: example-namespace
次のコマンドを実行することでも作成できます。
kubectl create -f example-namespace.yml
Namespaceの指定
kubectl
では-n
オプションにより、Namespaceを指定します。
kubectl get pods -n example-namespace
逆に、すべてのNamespaceにまたがった操作を行う場合は、-A
または--all-namespaces
を指定します。
kubectl get pods -A
ResourceQuota
ResourceQuotaは、Namespaceの計算資源を制限する際に使用します。
以下は旧記法でのResourceQuotaの記述例です。
apiVersion: v1
kind: ResourceQuota
metadata:
namespace: default
name: example-quota
spec:
hard:
# リソース数の上限値
pods: 4
persitentvolumeclaims: 4
# 計算資源要求合計の上限値
requests.cpu: "2" # 計算資源要求の合計がこれを超えるとコンテナ起動は拒否されます
requests.memory: "2Gi"
requests.storage: "10Gi"
# 計算資源最大値合計の上限値
limits.cpu: "8" # 計算資源最大値の合計がこれを超えるとコンテナ起動は拒否されます
limits.memory: "8Gi"
ResourceQuotaで設定した項目は、あとで説明するコンテナの計算資源割り当ての際に設定必須になります。コンテナでの設定を忘れると、Podが起動しません。
新記法での記述例です。
apiVersion: v1
kind: ResourceQuota
metadata:
namespace: default
name: example-quota
spec:
hard:
# リソース数の上限値
count/pods: 4
count/persitentvolumeclaims: 4
なお、ResourceQuotaの作成、変更によってその制限に引っ掛かる状態になっても、既に作成済みのリソースは影響を受けず、それらのリソースは動作を継続することができます。
LimitRange
LimitRangeは、個別のNamespaceに対して、Container/Pod/PersistentVolumeClaimに計算資源の制限を掛ける際に使用します。
Container/Podに対するLimitRange
apiVersion: v1
kind: LimitRange
metadata:
namespace: default
name: example-container-limit
spec:
limits:
- type: Container
min: # 下限値
cpu: "125m" # これより小さい計算資源を要求するコンテナ起動は拒否されます
memory: "128Mi"
max: # 上限値
cpu: "2" # これより大きい計算資源の最大値を設定するコンテナ起動は拒否されます
memory: "2Gi"
defaultRequest: # デフォルト値
cpu: "500m" # 計算資源要求(resources.requests)が設定されていないコンテナのデフォルト値になります
memory: "512Mi"
default: # 計算資源要求の上限値
cpu: "1" # これより大きい計算資源を要求するコンテナ起動は拒否されます
memory: "1Gi"
maxLimitRequestRatio: # 計算資源最大値/要求値の上限値
cpu: 2 # これより大きい計算資源比のコンテナ起動は拒否されます
memory: 2
PersistentVolumeClaimに対するLimitRange
apiVersion: v1
kind: LimitRange
metadata:
namespace: default
name: example-pvc-limitrange
spec:
limits:
- type: PersistentVolumeCraim
min: # 下限値
storage: "1Gi" # これより小さい計算資源を要求するコンテナ起動は拒否されます
max: # 上限値
storage: "20Gi" # これより大きい計算資源を要求するコンテナ起動は拒否されます
Containerごとの計算資源割り当て
個別のContainerごとに計算資源の割り当てを行う際には、以下のようにDeploymentのresources.requests
とresources.limits
を設定します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-app-deployment
spec:
replicas: 2
selector:
matchLabels:
app: example-app
template:
metadata:
labels:
app: example-app
spec:
containers:
- name: example-app-container
image: example-app:v1.0.0-alpha
resources:
requests: # このコンテナが使用する計算資源の最小値を設定
cpu: "500m" # vCPU数
memory: "512Mi" # メモリ容量
ephemeral-storage: "1Gi" # 一時ストレージ容量
limits: # このコンテナが使用する計算資源の最大値を設定
cpu: "1" # vCPU数(1=1000m)
memory: "1Gi" # メモリ容量(1Gi=1024Mi)
ephemeral-storage: "2Gi"
ports:
- containerPort: 3000
vCPUとは仮想コアで、物理的なCPUでは論理コアに等しいです。
例えば、Intel社のCPUでHyper-Threadingが有効化されているものは、
1物理コア = 2論理コア = 2vCPU
となります。
resources.requests
は、コンテナ起動時に割り当てられる計算資源の初期値にもなります。
もし、Kubernetesクラスターを構成するノードにこの初期値を割り当てられるだけの計算資源が余っていないときは、コンテナ起動がスケジューリングされません。
一方resources.limits
では、ノードにこの最大値を割り当てられるだけの計算資源が余っていない場合でも、コンテナ起動はスケジューリングされます。
なお、LimitRangeにより計算資源の上限が設定されていないNamespaceにあるDeploymentにおいては、resources.limits
を設定しないと、ノードが割り当て可能な上限いっぱいまで資源を消費しようとするので、注意が必要です。
ノードの計算資源が逼迫した状況では、他のPodの動作に重大な悪影響を及ぼします。
例えば、メモリが枯渇するとOut of Memory(OOM)によって、プロセス停止が発生することがあります。
resources.limits
を設定した場合、resources.requests
のデフォルト値はresources.limits
に設定されます。
Ephemeral Storage
ephemeral-storage
には、コンテナを再起動すると破棄されてしまう、一時的なストレージの容量を設定します。
Ephemeral Storage容量としてカウントされるものとしては、次の3つがあります。
- コンテナが出力するログデータ
- emptyDirに書き込まれたデータ(ただし、
medium: Memory
指定のemptyDirを除く) - コンテナの書き込み可能レイヤーに書き込まれたデータ
なお、emptyDirは複数のコンテナで共有可能なため、例えばコンテナAとBでresources.limits.ephemeral-storage
を2Gi
に設定しているとき、emptyDirには合計4Gi
まで書き込みが可能です。
Podの計算資源割り当て量
Kubernetesでは、複数コンテナを含む1つのPod単位で起動がスケジューリングされるため、個々のコンテナでの計算資源割り当て量から、Podの計算資源割り当て量を計算する必要が出てきます。
Podの計算資源割り当て量は、次の計算式で求められます。
すべてのInitContainerでの最大値と、Containerの計算資源の合計値のうち、どちらか小さくない方
PodのQoS Class
requests, limitsの設定に応じて、Podには次のようなQoS Classが自動で付与されます。
条件 | QoS Class |
---|---|
requests, limitsがどちらも未指定 | BestEffort |
requestsとlimitsの値が同じで、cpuとmemoryの両方が指定済み | Guaranteed |
Guranteedの条件を満たさず、1つ以上のrequests, limitsが指定済み | Burstable |
QoS Classはkubectl get pods
で確認できます。
kubectl get pods -o custom-columns="Name:{.meta.name},QoS Class:{.status.qosClass}"
Discussion