EKSのAuto Scalingを試してみた

2019.11.26

こんにちは。EKS Workshop で実際に手を動かしながら機能を学んでいます。
やはり手を動かしながら学べるので本当に楽しいですよね。

さて今回はEKS Workshopでも取り扱っていた 「Horizontal Pod Autoscaler (HPA)」と「Cluster Autoscaler (CA)」を 実際に試してみたのでブログに書いてみました。

Horizontal Pod Autoscaler (HPA) を試してみる

さてまずはHPAから試していきます。
HPAはKubernetesのリソースの1つで、CPU使用率、API Objectのメトリクスなどをモニタリングし、 スケーリングが必要になったタイミングで、Replication ControllerやDeploymentなどのPod数をスケーリングします。 DaemonSetのようにスケールしないしない物に関してはHPAの対象とすることはできません。

概要を抑えたところで実際に手を動かしていきましょう。

環境

  • Kubernetes: 1.14
  • helm: 3.0.0
  • eksctl: 0.10.2

1. クラスタの作成

eksctlを使って東京リージョンにさくさくっと作り上げます。

$ export AWS_DEFAULT_REGION=ap-northeast-1

$ eksctl create cluster \
  --name=eksworkshop-hpa \
  --nodes=3 \
  --node-type=t3.small

2. Metrics Serverのインストール

HPAがスケーリングの指示をするにあたって、メトリクスを収集する必要があります。
Kubernetesでは一般的には Metrics Server を使用します。
今回はMetrics ServerをHelmを使用してデプロイします。
またこちらの手順 でHelmを使用せずにデプロイすることもできます。

$ helm install stable/metrics-server \
  --generate-name \
  --namespace kube-system

	NAME: metrics-server-1574319962

	LAST DEPLOYED: Thu Nov 21 16:06:04 2019
	NAMESPACE: kube-system
	STATUS: deployed
	REVISION: 1
	NOTES:
	The metric server has been deployed. 
	
	In a few minutes you should be able to list metrics using the following
	command:
	
	kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes"

こちらもEKSクラスタと同様にサクッとデプロイができました。
表示されたコマンドを実行して問題なく動作しているかの確認をします。

$ kubectl get apiservice v1beta1.metrics.k8s.io -o yaml
	apiVersion: apiregistration.k8s.io/v1
	kind: APIService
	metadata:
	  creationTimestamp: "2019-11-21T07:06:04Z"
	  labels:
	    app: metrics-server
	    chart: metrics-server-2.8.8
	~~~

一部表示を端折っていますが問題なさそうですね。

3. サンプルアプリケーションのデプロイ

GETリクエストに対して「ok!」とレスポンスを返すアプリケーションを元にDeploymentの作成とServiceで公開します。

$ kubectl run php-apache --image=k8s.gcr.io/hpa-example --requests=cpu=200m --expose --port=80
	service/php-apache created
	deployment.apps/php-apache created

4. HPAのデプロイ

次にkubectl autoscaleを実行してHPAをデプロイします。
先ほど作成したDeploymentで稼働しているPodのCPU使用率が平均して50%になるようにスケーリングを行います。

$ kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10

HPAの状況を確認してみます。
アプリケーションに対して負荷をかけていない状態なのでメトリクスは0%/50%Relicasも1になっています。

$ kubectl get hpa -w
	NAME         REFERENCE               TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
	php-apache   Deployment/php-apache   0%/50%    1         10        1          3m32s

5. スケーリングさせる

別途Busyboxを用意してそこから先ほど作成したアプリケーションに対して負荷をかけます。

$ while true; do wget -q -O - http://php-apache; done
	OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!

アプリケーションはOK!とレスポンスを返すので問題なく動作してますね。

この時HPAの状態を確認してみましょう。

$ kubectl get hpa -w
	NAME         REFERENCE               TARGETS    MINPODS   MAXPODS   REPLICAS   AGE
	php-apache   Deployment/php-apache   271%/50%   1         10        1          5m

スケールアウトできておらず、1つのPodにかなりの負荷がかかっています。
この状態から暫く時間が経ってからもう一度確認します。

$ kubectl get hpa -w
	NAME         REFERENCE               TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
	php-apache   Deployment/php-apache   47%/50%   1         10        10         8m8s

十分にスケールされてCPU負荷も50%付近を保っています。
ここでBusyboxからのリクエストを停止させます。

$ kubectl get hpa -w
	NAME         REFERENCE               TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
	php-apache   Deployment/php-apache   0%/50%    1         10        10         10m

この状態から数分経過した後に、Pod数が1つに戻ります。

$ kubectl get hpa -w
	NAME         REFERENCE               TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
	php-apache   Deployment/php-apache   0%/50%    1         10        1          15m

thrashingと呼ばれる、スケール管理時にメトリクスによって頻繁なレプリカ数の変化が発生する可能性があります。 Kubernetesではこれを防ぐためにHPAでスケールインするまでの待機時間を設定することができ、 これにより設定されているためにスケールインに関しては大きく時間がかかっています。

6. クラスタの削除

HPAの動作がわかったところでEKSクラスタを削除していきます。

$ eksctl delete cluster \
  --name=eksworkshop-hpa

Cluster Autoscaler(CA)を試す

次にCAを試していきます。今回はEKSWorkshopの手順とかなり違う順序でやっていますのでご了承ください。 HPAとは違いノードをスケールさせます。
またメトリクスもCPU使用率とかではなくpending状態のPodに基づいてスケールします。EKSの場合だとクラスタのメトリクスを元にASGに対して指示を出すことで実現します。

環境

  • Kubernetes: 1.14
  • eksctl: 0.10.2

1. クラスタの作成

eksctlを使って東京リージョンにさくさくっと作り上げます。

$ export AWS_DEFAULT_REGION=ap-northeast-1

$ eksctl create cluster \
  --name=eksworkshop-eksctl \
  --nodes=3 \
  --node-type=t3.small

2. ASGの更新

eksctlで作成したASGを探して最小、最大量の変更を行います。

$ aws autoscaling update-auto-scaling-group \
  --auto-scaling-group-name <ASG NAME> \
  --min-size 2 \
  --max-size 8

3. IAM Policyの作成

クラスタからAutoScalingの状態を変更確認できるように、IAMロールにポリシーを付与します。 なのでまず初めにポリシーファイルを作成します。

asg-policy.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "autoscaling:DescribeAutoScalingGroups",
        "autoscaling:DescribeAutoScalingInstances",
        "autoscaling:SetDesiredCapacity",
        "autoscaling:TerminateInstanceInAutoScalingGroup",
        "autoscaling:DescribeTags"
      ],
      "Resource": "*"
    }
  ]
}

次にそのファイルを元にIAM Roleにアタッチします。

$ STACK_NAME=$(eksctl get nodegroup --cluster eksworkshop-eksctl -o json | jq -r '.[].StackName')
$ INSTANCE_PROFILE_ARN=$(aws cloudformation describe-stacks --stack-name $STACK_NAME | jq -r '.Stacks[].Outputs[] | select(.OutputKey=="InstanceProfileARN") | .OutputValue')
$ ROLE_NAME=$(aws cloudformation describe-stacks --stack-name $STACK_NAME | jq -r '.Stacks[].Outputs[] | select(.OutputKey=="InstanceRoleARN") | .OutputValue' | cut -f2 -d/)

$ aws iam put-role-policy --role-name $ROLE_NAME --policy-name ASG-Policy-For-Worker --policy-document file://asg-policy.json

4. CAのデプロイ

CA用のServiceAccountの作成とDeploymentを作成します。 EKSWorkshop用に準備されたマニュフェストはありますが、一部変更が必要なので変更します。
少しわかりづらい記載で申し訳ないですが、の部分を実際のASGの名前に書き換えてください。

$ curl -O https://eksworkshop.com/scaling/deploy_ca.files/cluster_autoscaler.yml
$ sed -i "s/<AUTOSCALING GROUP NAME>/<ASG NAME>/" cluster_autoscaler.yml
$ sed -i "s/us-west-2/ap-northeast-1/" cluster_autoscaler.yml

最後に出来上がったマニュフェストをデプロイします。

$ kubectl apply -f cluster_autoscaler.yml

5. スケールさせる

Nginxを使用したPodで、replicasの値を1にしたDeploymentをデプロイします。
まずはマニュフェストを記載していきます。

nginx.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-to-scaleout
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        service: nginx
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx-to-scaleout
        resources:
          limits:
            cpu: 500m
            memory: 512Mi
          requests:
            cpu: 500m
            memory: 512Mi

これをデプロイします。

$ kubectl apply -f nginx.yaml

この段階ではPod数が1つなので当然pendingも発生しておらずワーカーノードのスケールも発生していません。

$ kubectl get deployments
	NAME                READY   UP-TO-DATE   AVAILABLE   AGE
	nginx-to-scaleout   1/1     1            1           11s

$ kubectl get pods
	NAME                                 READY   STATUS    RESTARTS   AGE
	nginx-to-scaleout-84f9cdbd84-tsptk   1/1     Running   0          41s

ワーカーノードのスケールを発生させるためにDeploymentsのPod数をスケールさせます。

$ kubectl scale --replicas=10 deployment/nginx-to-scaleout

この段階でPodがpending状態になっていることが確認できます。

$ kubectl get pods
	NAME                                 READY   STATUS    RESTARTS   AGE
	nginx-to-scaleout-84f9cdbd84-2tb9x   1/1     Running   0          32s
	nginx-to-scaleout-84f9cdbd84-8vvp7   1/1     Running   0          32s
	nginx-to-scaleout-84f9cdbd84-8w6fh   1/1     Running   0          32s
	nginx-to-scaleout-84f9cdbd84-kxx6p   1/1     Running   0          32s
	nginx-to-scaleout-84f9cdbd84-lkwth   1/1     Running   0          32s
	nginx-to-scaleout-84f9cdbd84-m2jfq   1/1     Running   0          32s
	nginx-to-scaleout-84f9cdbd84-rzvqr   1/1     Running   0          32s
	nginx-to-scaleout-84f9cdbd84-tsptk   1/1     Running   0          3m11s
	nginx-to-scaleout-84f9cdbd84-w4hj7   0/1     Pending   0          32s
	nginx-to-scaleout-84f9cdbd84-xmgvb   1/1     Running   0          32s

ワーカーノードのスケーリングが終わり4台に増えた段階でpendingが解消されることも確認できました。

$ kubectl get nodes
	NAME                                                STATUS   ROLES    AGE   VERSION
	ip-192-168-19-201.ap-northeast-1.compute.internal   Ready    <none>   33m   v1.14.7-eks-1861c5
	ip-192-168-48-242.ap-northeast-1.compute.internal   Ready    <none>   17m   v1.14.7-eks-1861c5
	ip-192-168-58-249.ap-northeast-1.compute.internal   Ready    <none>   33m   v1.14.7-eks-1861c5
	ip-192-168-77-50.ap-northeast-1.compute.internal    Ready    <none>   33m   v1.14.7-eks-1861c5

$ kubectl get pods
	NAME                                 READY   STATUS    RESTARTS   AGE
	nginx-to-scaleout-84f9cdbd84-2tb9x   1/1     Running   0          2m43s
	nginx-to-scaleout-84f9cdbd84-8vvp7   1/1     Running   0          2m43s
	nginx-to-scaleout-84f9cdbd84-8w6fh   1/1     Running   0          2m43s
	nginx-to-scaleout-84f9cdbd84-kxx6p   1/1     Running   0          2m43s
	nginx-to-scaleout-84f9cdbd84-lkwth   1/1     Running   0          2m43s
	nginx-to-scaleout-84f9cdbd84-m2jfq   1/1     Running   0          2m43s
	nginx-to-scaleout-84f9cdbd84-rzvqr   1/1     Running   0          2m43s
	nginx-to-scaleout-84f9cdbd84-tsptk   1/1     Running   0          5m22s
	nginx-to-scaleout-84f9cdbd84-w4hj7   1/1     Running   0          2m43s
	nginx-to-scaleout-84f9cdbd84-xmgvb   1/1     Running   0          2m43s

Cluster AutoscalerでもHPAと同様に、スケーリングを行なった後の待機時間が設定されているのでそれが終わるまでノード数は減っていきません。

6. クラスタの削除

まずアタッチしたポリシーを削除してからクラスタを削除します。

$ aws iam delete-role-policy \
  --role-name $ROLE_NAME \
  --policy-name ASG-Policy-For-Worker

$ eksctl delete cluster \
  --name=eksworkshop-eksctl

さいごに

kubectlを実際に実行しながら進めるのでEKSWorkshopはEKSを理解するのに非常に良いチュートリアルです。 お手すきの際に実際にお試しください