はじめに
今回はResource Quotaによるリソース制御の動作を確認したいと思います。
Resource Quotaは「namespace単位」でリソースを制御することができます。
制御できる種類としては、以下の3つがあります。
- Compute Resource Quota
- Storage Resource Quota
- Object Count Quota
Compute Resource Quota
Compute Resource Quotaは、CPU/メモリ/GPUに対して上限(limits)/下限(requests)を設定します。
Resource Quotaの作成
以下のマニフェストのResource Quotaを作成します。ここでは「namespace: dev」に対して制限をかけます。
namespaceはあらかじめ作成済みです。
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-resource-quota
namespace: dev
spec:
hard:
limits.cpu: 0.5
limits.memory: 500Mi
requests.cpu: 0.2
requests.memory: 200Mi
$ kubectl apply -f computeResourceQuota.yaml
resourcequota/compute-resource-quota created
$ kubectl describe -n dev resourcequotas compute-resource-quota
Name: compute-resource-quota
Namespace: dev
Resource Used Hard
-------- ---- ----
limits.cpu 0 500m
limits.memory 0 500Mi
requests.cpu 0 200m
requests.memory 0 200Mi
動作確認
Pod(Deployment)をデプロイします。まずはlimits/requestsを設定しないでapplyしてみます。
$ kubectl apply -f nginx_redis.yaml
deployment.apps/resource-quota created
$ kubectl -n dev get deployments.apps
NAME READY UP-TO-DATE AVAILABLE AGE
resource-quota 0/2 0 0 108s
$ kubectl -n dev describe replicasets.apps resource-quota-77d577889b
Name: resource-quota-77d577889b
Namespace: dev
・・・
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedCreate 2m1s replicaset-controller Error creating: pods "resource-quota-77d577889b-6xn5d" is forbidden: failed quota: compute-resource-quota: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
Warning FailedCreate 2m1s replicaset-controller Error creating: pods "resource-quota-77d577889b-5dhb6" is forbidden: failed quota: compute-resource-quota: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
Warning FailedCreate 2m1s replicaset-controller Error creating: pods "resource-quota-77d577889b-bv84g" is forbidden: failed quota: compute-resource-quota: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
Warning FailedCreate 2m1s replicaset-controller Error creating: pods "resource-quota-77d577889b-hbn26" is forbidden: failed quota: compute-resource-quota: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
Warning FailedCreate 2m1s replicaset-controller Error creating: pods "resource-quota-77d577889b-h6vdw" is forbidden: failed quota: compute-resource-quota: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
Warning FailedCreate 2m1s replicaset-controller Error creating: pods "resource-quota-77d577889b-9brcv" is forbidden: failed quota: compute-resource-quota: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
Warning FailedCreate 2m1s replicaset-controller Error creating: pods "resource-quota-77d577889b-tmh2h" is forbidden: failed quota: compute-resource-quota: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
Warning FailedCreate 2m1s replicaset-controller Error creating: pods "resource-quota-77d577889b-7ll7x" is forbidden: failed quota: compute-resource-quota: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
Warning FailedCreate 2m replicaset-controller Error creating: pods "resource-quota-77d577889b-bqtjx" is forbidden: failed quota: compute-resource-quota: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
Warning FailedCreate 39s (x6 over 119s) replicaset-controller (combined from similar events): Error creating: pods "resource-quota-77d577889b-brs24" is forbidden: failed quota: compute-resource-quota: must specify limits.cpu,limits.memory,requests.cpu,requests.memor
limits.cpuなどを設定するのは必須だとエラーになっていますね。
今度は各コンテナにリソース制御を設定します。1つのPodに2つのコンテナが構成されていて、各コンテナのCPU/メモリのlimits/requestsの合計がResourceQuotaの設定値になるようにしています。
apiVersion: apps/v1
kind: Deployment
metadata:
name: resource-quota
namespace: dev
spec:
replicas: 2
selector:
matchLabels:
app: app1
template:
metadata:
labels:
app: app1
spec:
containers:
- name: nginx
image: nginx:latest
resources:
requests:
memory: 100Mi
cpu: 100m
limits:
memory: 200Mi
cpu: 200m
- name: redis
image: redis:latest
resources:
requests:
memory: 100Mi
cpu: 100m
limits:
memory: 300Mi
cpu: 300m
$ kubectl apply -f nginx_redis.yaml
deployment.apps/resource-quota created
$ kubectl -n dev get pod
NAME READY STATUS RESTARTS AGE
resource-quota-57b9c6c54c-dsn7v 2/2 Running 0 46s
replica数が「2」なのに、Podが一つしか起動していないですね。
マニュアルをよく読むと、全てのPodの合計がResource Quotaで設定した値に収まらないといけないですね。
Compute Resource Quota
Across all pods in a non-terminal state, the sum of CPU limits cannot exceed this value.
各コンテナのrequests/limitsの値を調整して収まるようにすると、デプロイに成功しました。
$ kubectl apply -f nginx_redis.yaml
deployment.apps/resource-quota created
$ kubectl -n dev get pod
NAME READY STATUS RESTARTS AGE
resource-quota-7dcf5bc48-22fcr 2/2 Running 0 13s
resource-quota-7dcf5bc48-zvfkr 2/2 Running 0 13s
$ kubectl -n dev describe resourcequotas
Name: compute-resource-quota
Namespace: dev
Resource Used Hard
-------- ---- ----
limits.cpu 400m 500m
limits.memory 400Mi 500Mi
requests.cpu 200m 200m
requests.memory 200Mi 200Mi
Storage Resource Quota
Storage Resource Quotaは、PVC(PersistentVolumeClaim)の容量とStorageClassに対して制限をかけることができます。
Resource Quotaの作成
以下のマニフェストのResource Quotaを作成します。ここでは「namespace: dev」に対して制限をかけます。
apiVersion: v1
kind: ResourceQuota
metadata:
name: storage-resource-quota
namespace: dev
spec:
hard:
requests.storage: 50Mi
zfs-sc.storageclass.storage.k8s.io/requests.storage: 100Mi
$ kubectl apply -f storageResourceQuota.yaml
resourcequota/storage-resource-quota created
$ kubectl -n dev describe resourcequotas
Name: storage-resource-quota
Namespace: dev
Resource Used Hard
-------- ---- ----
requests.storage 0 50Mi
zfs-sc.storageclass.storage.k8s.io/requests.storage 0 100Mi
なお、StorageClassは以前作成したものを利用しています。
参考:[Kubernetes]Dynamic Provisioningの動作を確認する
$ kubectl get storageclasses.storage.k8s.io
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
zfs-sc gentics.com/zfs Retain Immediate false 5d23h
動作確認
PVC
まずはrequests.storageの設定に対して、PVCの動作を確認してみます。
以下のマニフェストのPVを作成します。
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0001
namespace: dev
spec:
capacity:
storage: 50Mi
volumeMode: Filesystem
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /share
server: k8s-client
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0002
namespace: dev
spec:
capacity:
storage: 100Mi
volumeMode: Filesystem
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /share
server: k8s-client
$ kubectl apply -f pv.yaml
persistentvolume/pv0001 created
persistentvolume/pv0002 created
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv0001 50Mi RWX Recycle Available slow 12s
pv0002 100Mi RWX Recycle Available slow 12s
requests.storageは「50Mi」で設定しました。pv0002はその値を超えていますが、この時点ではエラーにはなってないですね。
次にPVCを作成します。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim1
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 50Mi
storageClassName: slow
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim2
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 100Mi
storageClassName: slow
$ kubectl apply -f pvc.yaml
persistentvolumeclaim/myclaim1 created
Error from server (Forbidden): error when creating "pvc.yaml": persistentvolumeclaims "myclaim2" is forbidden: exceeded quota: storage-resource-quota, requested: requests.storage=100Mi, used: requests.storage=50Mi, limited: requests.storage=50Mi
1つ目のPVCはClaimできましたが、2つ目は制限を超えたためエラーになりました。
2つ目は100MiでClaimしましたので、仮に1つだけだったとしてもエラーになります。また、requests.storageの設定値は「合計値」ですので、2つ目のPVCが50Mi以下だったとしても、合計値で制限を超えてしまいます。
StorageClass
今度はzfs-sc.storageclass.storage.k8s.io/requests.storageの設定値に対して動作を確認します。
以下のマニフェストでDynamic ProvisioningでPVCを作成します。
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: zfs-pvc001
namespace: dev
annotations:
volume.beta.kubernetes.io/storage-class: "zfs-sc"
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 100Mi
$ kubectl apply -f pvc-sc.yaml
Error from server (Forbidden): error when creating "pvc-sc.yaml": persistentvolumeclaims "zfs-pvc001" is forbidden: exceeded quota: storage-resource-quota, requested: requests.storage=100Mi, used: requests.storage=50Mi, limited: requests.storage=50Mi
StorageClassに対しては「100Mi」で制限をかけていますが、同じnamespaceでPVCに対して50Miで制限をかけていますので、こちらの制限に引っかかってますね。
なので、PVCを削除して再度applyしてもエラーになります。
$ kubectl delete -f pvc.yaml
persistentvolumeclaim "myclaim1" deleted
Error from server (NotFound): error when deleting "pvc.yaml": persistentvolumeclaims "myclaim2" not found
$ kubectl describe -n dev resourcequotas
Name: storage-resource-quota
Namespace: dev
Resource Used Hard
-------- ---- ----
requests.storage 0 50Mi
zfs-sc.storageclass.storage.k8s.io/requests.storage 0 100Mi
$ kubectl apply -f pvc-sc.yaml
Error from server (Forbidden): error when creating "pvc-sc.yaml": persistentvolumeclaims "zfs-pvc001" is forbidden: exceeded quota: storage-resource-quota, requested: requests.storage=100Mi, used: requests.storage=0, limited: requests.storage=50Mi
storageのrequestsを「100Mi -> 30Mi」に変更して再度applyします。
$ kubectl apply -f pvc-sc.yaml
persistentvolumeclaim/zfs-pvc001 created
$ kubectl -n dev get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
zfs-pvc001 Bound pvc-d4a48e0f-e70d-484a-a4ff-b760694daebb 30Mi RWX zfs-sc 11s
$ kubectl describe -n dev resourcequotas
Name: storage-resource-quota
Namespace: dev
Resource Used Hard
-------- ---- ----
requests.storage 30Mi 50Mi
zfs-sc.storageclass.storage.k8s.io/requests.storage 30Mi 100Mi
今度は成功しました。
Storage Resource Quotaは2つの設定が競合しますので、両方設定する際には注意が必要ですね。
Object Count Quota
Compute/Storage Resource Quotaは「リソースの使用量」に対して制限をかけています。対してObject Count Quotaは、「Object(リソース)の数」に対して制限をかけます。
設定方法はKubernetesのバージョンによって以下の二通りがありますが、v1.9以降であればどちらの方法も可能です。
Kubernetes v1.9以降
v1.9以降では、count/<resource>.<group>の形式で指定できるようになりました。
指定できるリソースは以下になります。
https://kubernetes.io/docs/concepts/policy/resource-quotas/#object-count-quota
- 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
- count/deployments.extensions
Resource Quotaの作成
以下のマニフェストのResource Quotaを作成します。ここでは「namespace: dev」に対して制限をかけます。
apiVersion: v1
kind: ResourceQuota
metadata:
name: object-count-quota
namespace: dev
spec:
hard:
count/deployments.apps: 3
count/jobs.batch: 3
$ kubectl apply -f objectCountQuota.yaml
resourcequota/object-count-quota created
$ kubectl -n dev describe resourcequotas
Name: object-count-quota
Namespace: dev
Resource Used Hard
-------- ---- ----
count/deployments.apps 0 3
count/jobs.batch 0 3
動作確認
ここではDeploymentを作成して動作を確認します。
$ kubectl apply -f nginx-dev.yaml
deployment.apps/nginx1 created
deployment.apps/nginx2 created
deployment.apps/nginx3 created
Error from server (Forbidden): error when creating "nginx-dev.yaml": deployments.apps "nginx4" is forbidden: exceeded quota: object-count-quota, requested: count/deployments.apps=1, used: count/deployments.apps=3, limited: count/deployments.apps=3
4つ目のDeploymentで失敗しました。
DeploymentとResource Quotaの状態を確認します。
$ kubectl get -n dev deployments.apps
NAME READY UP-TO-DATE AVAILABLE AGE
nginx1 2/2 2 2 24s
nginx2 2/2 2 2 24s
nginx3 2/2 2 2 24s
$ kubectl describe -n dev resourcequotas
Name: object-count-quota
Namespace: dev
Resource Used Hard
-------- ---- ----
count/deployments.apps 3 3
count/jobs.batch 0 3
3つだけDeploymentが作成されていますね。
念のため「namespace: default」のDeploymentに制限がかかっていないことを確認します。
$ kubectl apply -f nginx-default.yaml
deployment.apps/nginx-default1 created
deployment.apps/nginx-default2 created
deployment.apps/nginx-default3 created
deployment.apps/nginx-default4 created
$ kubectl get deployments.apps -A
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
default nginx-default1 2/2 2 2 28s
default nginx-default2 2/2 2 2 28s
default nginx-default3 2/2 2 2 28s
default nginx-default4 2/2 2 2 28s
dev nginx1 2/2 2 2 24m
dev nginx2 2/2 2 2 24m
dev nginx3 2/2 2 2 24m
「namespace: default」には制限がかかっていませんね。
Kubernetes v1.9未満
v1.9未満の場合は、以下のリソースタイプを指定できます。
- configmaps
- persistentvolumeclaims
- pods
- replicationcontrollers
- resourcequotas
- services
- services.loadbalancers
- services.nodeports
- secrets
Resource Quotaの作成
以下のマニフェストのResource Quotaを作成します。ここでは「namespace: stg」に対して制限をかけます。
apiVersion: v1
kind: ResourceQuota
metadata:
name: object-count-quota-previous
namespace: stg
spec:
hard:
services.nodeports: 3
pods: 3
$ kubectl apply -f objectCountQuota-previous.yaml
resourcequota/object-count-quota-previous created
$ kubectl -n stg describe resourcequotas
Name: object-count-quota-previous
Namespace: stg
Resource Used Hard
-------- ---- ----
pods 0 3
services.nodeports 0 3
動作確認
ここでは、Deploymentのreplica数を「4」にして確認してみます。
$ kubectl apply -f nginx-stg.yaml
deployment.apps/nginx-stg created
$ kubectl -n stg get deployments.apps
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-stg 3/4 3 3 20s
$ kubectl -n stg get replicasets.apps
NAME DESIRED CURRENT READY AGE
nginx-stg-7f54869d4d 4 3 3 33s
$ kubectl -n stg get pod
NAME READY STATUS RESTARTS AGE
nginx-stg-7f54869d4d-9mtzs 1/1 Running 0 16s
nginx-stg-7f54869d4d-9v7l2 1/1 Running 0 16s
nginx-stg-7f54869d4d-qmww6 1/1 Running 0 16s
$ kubectl -n stg describe resourcequotas
Name: object-count-quota-previous
Namespace: stg
Resource Used Hard
-------- ---- ----
pods 3 3
services.nodeports 0 3
replica数が「4」の設定ですが、Podは3つしかデプロイされていませんね。replicasetの詳細を確認します。
$ kubectl describe -n stg replicaset nginx-stg-7f54869d4d
Name: nginx-stg-7f54869d4d
Namespace: stg
・・・
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 3m57s replicaset-controller Created pod: nginx-stg-7f54869d4d-9v7l2
Normal SuccessfulCreate 3m57s replicaset-controller Created pod: nginx-stg-7f54869d4d-9mtzs
Normal SuccessfulCreate 3m57s replicaset-controller Created pod: nginx-stg-7f54869d4d-qmww6
Warning FailedCreate 3m57s replicaset-controller Error creating: pods "nginx-stg-7f54869d4d-wkxhl" is forbidden: exceeded quota: object-count-quota-previous, requested: pods=1, used: pods=3, limited: pods=3
Warning FailedCreate 3m57s replicaset-controller Error creating: pods "nginx-stg-7f54869d4d-4x2zr" is forbidden: exceeded quota: object-count-quota-previous, requested: pods=1, used: pods=3, limited: pods=3
Warning FailedCreate 3m57s replicaset-controller Error creating: pods "nginx-stg-7f54869d4d-wfr9t" is forbidden: exceeded quota: object-count-quota-previous, requested: pods=1, used: pods=3, limited: pods=3
Warning FailedCreate 3m57s replicaset-controller Error creating: pods "nginx-stg-7f54869d4d-x8dtb" is forbidden: exceeded quota: object-count-quota-previous, requested: pods=1, used: pods=3, limited: pods=3
Warning FailedCreate 3m57s replicaset-controller Error creating: pods "nginx-stg-7f54869d4d-hnvrq" is forbidden: exceeded quota: object-count-quota-previous, requested: pods=1, used: pods=3, limited: pods=3
Warning FailedCreate 3m57s replicaset-controller Error creating: pods "nginx-stg-7f54869d4d-97tnw" is forbidden: exceeded quota: object-count-quota-previous, requested: pods=1, used: pods=3, limited: pods=3
Warning FailedCreate 3m57s replicaset-controller Error creating: pods "nginx-stg-7f54869d4d-xkg2p" is forbidden: exceeded quota: object-count-quota-previous, requested: pods=1, used: pods=3, limited: pods=3
Warning FailedCreate 3m57s replicaset-controller Error creating: pods "nginx-stg-7f54869d4d-rvkg5" is forbidden: exceeded quota: object-count-quota-previous, requested: pods=1, used: pods=3, limited: pods=3
Warning FailedCreate 3m56s replicaset-controller Error creating: pods "nginx-stg-7f54869d4d-92jl8" is forbidden: exceeded quota: object-count-quota-previous, requested: pods=1, used: pods=3, limited: pods=3
Warning FailedCreate 3m34s (x11 over 3m56s) replicaset-controller (combined from similar events): Error creating: pods "nginx-stg-7f54869d4d-jghnm" is forbidden: exceeded quota: object-count-quota-previous, requested: pods=1, used: pods=3, limited: pods=3
4つ目のPodをデプロイするときに、Podの上限「3」を超えたため、エラーになっていますね。
なお、v1.9以降とv1.9未満の両方の形式を一つのマニフェストにまとめて記載することも可能です。
apiVersion: v1
kind: ResourceQuota
metadata:
name: object-count-quota-consolidate
namespace: prd
spec:
hard:
# v1.9以降の記載
count/deployments.apps: 3
count/jobs.batch: 3
# v1.9未満の記載
pods: 5
services.loadbalancers: 5
$ kubectl apply -f objectCountQuota-consolidate.yaml
resourcequota/object-count-quota-consolidate created
$ kubectl -n prd describe resourcequotas
Name: object-count-quota-consolidate
Namespace: prd
Resource Used Hard
-------- ---- ----
count/deployments.apps 0 3
count/jobs.batch 0 3
pods 0 5
services.loadbalancers 0 5
まとめ
今回のResource Quotaだけでなく、これまでKubernetesのリソース制御方法を確認してきました。
クラスタ管理者がLimitRangeやResource Quotaで各namespaceの上限/下限を設定して、各アプリの開発者がその範囲内でデプロイするPodのリソースを設定するのかなと検証しながら思っていました。
LimitRangeとResource Quotaで設定できる対象や値が微妙に異なりますので、システムやアプリの要件に応じて適切に設定しないといけないですね。ベストプラクティスを考えながらこれからも検証したいと思います。