KubernetesのPodとNodeのAuto Scalingについて


PodのHorizontal Pod Autoscaling(水平podオートスケーリング)

podのHorizontal pod autoscaling(水平podオートスケーリング)とは、podの負荷に応じて自動的にpodの数を増減させることを指す。

HorizontalPodAutoscalerリソースを定義することで水平オートスケーリングが可能になる。

まずDeployment, ReplicaSet, ReplicationController, StatefulSetはscaled resource objectと呼ばれる。

これらがauto scalingが可能なリソース。

HorizontalPodAutoscalerリソースの定義例:

apiVersion: autoscaling/v2beta1

kind: HorizontalPodAutoscaler
metadata:
name: php-hpa
namespace: default
spec:
scaleTargetRef: # ここでautoscale対象となる`scaled resource object`を指定
apiVersion: apps/v1
kind: Deployment
name: php-deploy
minReplicas: 1 # 最小レプリカ数
maxReplicas: 5 # 最大レプリカ数
metrics:
- type: Resource
resource:
name: cpu
targetAverageUtilization: 50 # CPU使用率が常に50%になるように指定

autoscalingプロセスは以下の3つで行われる。


  • 指定したscaled resource objectによって管理されている全てのpodのメトリックを取得する

  • メトリックを定義されたターゲット値に近づけるためにはどのくらいのpodが必要か計算する

  • scaled resource objectのreplicas fieldを更新する。

ポイントとしては、auto scalingはtarget valueに近づくようにpod数が調整されるということ。

target valueが80なら 全体(全podの合計)のCPU使用率が80%になるように調整される。たとえば全体のCPU使用率が90%ならpod数は増えるし、50%ならpod数は減らされる。

スケールアウトのための計算方法は以下 (詳しくはAutoscaling Algorithmを参照)

必要なレプリカ数 = ceil(sum(Podの現在のCPU使用率) / targetAverageUtilization)

重要な点は、定期的(30秒に1回)にメトリックの取得をしているため、すぐにはスケーリングが行われないということ。少し時間がかかる。


  • スケールアウト(pod数を増やす)イベントは3分おきに発生する。

  • スケールイン(pod数を減らす)イベントは5分おきに発生する。


オートスケールされやすいようにCPUを100%使うようなpodを作成する

$ cat hpa-test-deploy.yaml

kind: Deployment
metadata:
name: hpa-test-deploy
spec:
replicas: 3
template:
metadata:
name: hpa-app
labels:
app: hpa-app
spec:
containers:
- image: busybox
command: ["dd", "if=/dev/zero", "of=/dev/null"] # CPUを100%使うように

$ kubectl apply -f hpa-test-deploy.yaml

deployment "hpa-test-deploy" created

3つpodができていることを確認

$ kubectl get pod

NAME READY STATUS RESTARTS AGE
hpa-test-deploy-69487b74dc-bfk6s 1/1 Running 0 9s
hpa-test-deploy-69487b74dc-rc4k2 1/1 Running 0 9s
hpa-test-deploy-69487b74dc-zs2j7 1/1 Running 0 9s

しばらくするとtopコマンドでメトリックスが取得可能状態になる

$ kubectl top pod

NAME CPU(cores) MEMORY(bytes)
hpa-test-deploy-69487b74dc-rc4k2 945m 0Mi
hpa-test-deploy-69487b74dc-zs2j7 957m 0Mi
hpa-test-deploy-69487b74dc-bfk6s 950m 0Mi

CPU使用率を50%に保つHorizontalPodAutoscalerリソースを作る

$ cat hpa-test.yaml

apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: hpa-test
namespace: default
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: hpa-test-deploy
minReplicas: 1
maxReplicas: 5
metrics:
- type: Resource
resource:
name: cpu
targetAverageUtilization: 50

$ kubectl apply -f hpa-test.yaml

horizontalpodautoscaler "hpa-test" created

すぐにはメトリックスを取得しないので最初はTARGETSの左がになる

$ kubectl get HorizontalPodAutoscaler hpa-test

NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hpa-test Deployment/hpa-test-deploy <unknown> / 50% 1 5 0 27s

しばらくするとTARGETSの左にメトリックスが表示される

$ kubectl get HorizontalPodAutoscaler hpa-test

NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hpa-test Deployment/hpa-test-deploy 966% / 50% 1 5 3 30s

describeしてみると、Events欄にSuccessfulRescaleと表示され、pod数が5つになったことが分かる

$ kubectl describe HorizontalPodAutoscaler hpa-test

Name: hpa-test
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"autoscaling/v2beta1","kind":"HorizontalPodAutoscaler","metadata":{"annotations":{},"name":"hpa-test","namespace":"default"},"spec":{"max...
CreationTimestamp: Fri, 20 Apr 2018 22:38:53 +0900
Reference: Deployment/hpa-test-deploy
Metrics: ( current / target )
resource cpu on pods (as a percentage of request): 966% (966m) / 50%
Min replicas: 1
Max replicas: 5
Conditions:
Type Status Reason Message
---- ------ ------ -------
AbleToScale True SucceededRescale the HPA controller was able to update the target scale to 5
ScalingActive True ValidMetricFound the HPA was able to succesfully calculate a replica count from cpu resource utilization (percentage of request)
ScalingLimited True TooManyReplicas the desired replica count was more than the maximum replica count
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulRescale 3s horizontal-pod-autoscaler New size: 5; reason: cpu resource utilization (percentage of request) above target

podの数を見てみると5つ(HorizontalPodAutoscalerで指定したMAXのpod replica数)になっていることが分かる

$ kubectl get pod

NAME READY STATUS RESTARTS AGE
hpa-test-deploy-69487b74dc-6sp7l 1/1 Running 0 10s
hpa-test-deploy-69487b74dc-bfk6s 1/1 Running 0 2m
hpa-test-deploy-69487b74dc-d69mt 1/1 Running 0 10s
hpa-test-deploy-69487b74dc-rc4k2 1/1 Running 0 2m
hpa-test-deploy-69487b74dc-zs2j7 1/1 Running 0 2m


他のメトリックスによるAuto Scaling

custom metricsを用いてCPUメトリックス以外のメトリックスでオートスケーリングを行うことができる。

HorizontalPodAutoscalerリソースには以下の4種類を指定することができる。


  • Resource


    • Resource Requests, Resource Limitsのようなメトリックス



  • Pods


    • Podのメトリックス。例えばQueries Per Second(QPS) や message broker’s queueやコネクションの数。



  • Object


    • Podとは直接関係ないメトリックス。たとえばIngressのHit/sやlatency。



  • External


    • Kubernetes外のメトリックス(例: LBのQPS、Cloud Pus/Subの溜まっているメッセージ数)




どんなメトリックスがAuto Scalingに適しているか判断する


  • podのメモリ消費量はオートスケーリングメトリックスに向かない。
    podが増えるほど全体のメモリ消費量は増え、使えるメモリ量が逆に減るからだ。

  • メトリックスの一つとして、Queries per Second (QPS)がある。(※Requests per Secondと同義か?)

  • オートスケーリングメトリックスとしてcustom metricsを使う前に、podが増減したらそのメトリックスがどのように振る舞うかよく考えること。


PodのVertical Pod Autoscaling(垂直podオートスケーリング)

Vertical Pod Autoscalerは、コンテナに割り当てるCPU、メモリのリソースの割り当てを自動的にスケールさせるリソースです。

必要とするリソース(CPU、メモリ)量があらかじめ推測しにくいアプリケーションに対して、実績に基づいてそれらしい値を決めたい場合に効果を発揮します。

猫でもわかる Vertical Pod Autoscaler がわかりやすいです。


Nodeの水平オートスケーリング

Kubernetesは、cloud providerが提供していれば、nodeのauto scalingもでき、Cluster Autoscalerという機能が担っている。

Cluster Autoscalerは、リソースが枯渇して既存のnodeにpodがschedulingできない場合に、nodeを追加する。

またリソース負荷が低いならnodeの削除も行う。

Cluster Autoscalerはnodeを追加する前にそのnodeにpodがscheduleできるかを確認する。じゃないとnodeを追加してもpodがscheduleできない事態になってしまう。

nodeを削除するとき(scale downするとき)system podsが実行されているかチェックし、されているならnodeの削除を行わない。ただしsystem podsがDaemonSetの場合を除く。DaemonSetは各nodeで実行されているためnodeからpodを削除しても問題ないため。

また unmanaged pod(replicasetやdeploymentによって管理されていないpod)やlocal storageを使っているpodが存在する場合もnodeを削除しない。

nodeを削除するさいには、nodeにunschedulableを設定する。これによりpodがscheduleされなくなる。

そしてnode上のpodが追い出される。

追い出されたpodはreplicasetやdeploymentの管理下なので他のnodeへ再scheduleされる。


Tips



  • kubectl cordon <node>でnodeにunschedulableを設定できる。unschedulableが設定されたnodeにはpodがscheduleされなくなる。


  • kubectl drain <node>でnodeにunschedulableを設定し、node上のpodを追い出す。


  • kubectl uncordon <node>を実行すればnodeのunschedulableを解除できる。


cluster scale down時にサービスの中断を抑える

nodeが突然死んだ場合はどうしようもないが、手動もしくはCluster Autoscalerでnodeの削除が行われる場合は、サービスを中断させないことができる。

PodDisruptionBudgetリソースを作成すればよい。

参考: https://qiita.com/tkusumi/items/946b0f31931d21a78058

PodDisruptionBudget とは Node を計画的に停止したい場合に、Pod の状況を見ながら退去 (evict) させる機能です」


まとめ


  • podの水平スケーリング(scale out)はHorizontalPodAutoscalerリソースを作成し、それをDeployment, ReplicaSetに指定し、対象CPU使用量を指定することで簡単にできる。

  • podのCPU使用量の他に、アプリケーションが提供するcustom metricsやclusterにdeployされている他のリソースのmetricsをもとにオートスケーリングすることができる。

  • (cloud providerから提供されていれば)cluster nodeもオートスケーリングできる。


参考