【Amazon EKS】第四回 Cluster Autoscalerを使ってEKSノードのAuto Scalingをしてみた!
投稿者:内堀 雄真
はじめに
こんにちは!ハイブリッドクラウド部の内堀です。
ハイブリッドクラウド部では、Amazon EKSについてのテックブログを投稿しています!
前回記事では「CodePipelineでJavaアプリケーションのEKSへのデプロイを自動化する」
手順をまとめました。
今回は「Cluster AutoscalerでEKSノードのAuto Scalingを実行する」手順を解説します。
目次
Cluster Autoscaler とは
Cluster Autoscalerはノードのスケーリングを行います。
利用するためには、Cluster AutoscalerのコンポーネントをEKSクラスターにデプロイする必要があります。
※Cluster Autoscalerの導入でデプロイします。
このコンポーネントは、具体的に以下のような機能を担当します。
- Podがスケジュールされるのに十分なリソースがない場合、ノードを追加する必要があるかどうかを判断する
- 需要に応じて新しいノードを追加したり、使用されていないノードを削除する
また、今回は触れませんが以下のような機能も所有しています。
- 複数のノードグループを管理し、Podのリソース要求の大小に合わせてどのノードグループを使用するかを決定する
どのように動作するのか
まずはスケールアウトを行う際の動作です。
今回使用するノードはCPUが4Core、アプリケーション用PodのCPU Requestsは1Coreです。

ノード内にアプリケーション用Podを3個作成します。
アプリケーション用Podは1CoreのCPUリソースをリクエストしているため、ノード内の3/4Coreを確保することになります。
この状態で4個目のPodを作成しようとした場合、StatusがPendingとなり起動が出来ません。
アプリケーション用Podを実行するためのCPUリソースがノードに確保できないためです。
(kube-system上で動作するPodのリソースも確保する必要があるため、1ノードで実行できるアプリケーション用Podは最大3個になります。)

このように、新しいPodを起動するためのリソースがノード内に無くなり、ノードを追加する必要がある場合にCluster Autoscalerは動作します。
Pending状態のPodを検知してノードのスケールアウトを行い、新しいノードを作成します。

その後、Pending状態のPodを新しいノード上で起動させることでノードのスケールアウトが実現されます。

次にスケールインを行う際の動作です。
スケールインを行うトリガーは、「Podを他のノードに移動することが可能で、ノードをシャットダウンしても全体のパフォーマンスに影響を与えないと判断された場合」となります。
先ほどと同じ条件のノードが3台起動していて、図のようにPodが起動しているとします。

Podの数をスケールインし、6個まで減らします。

この時、1番右にあるノードで実行されている2個のPodは他のノードに移動することが出来ます。
図の左と中央にあるノードにPodを移動できる場合、右のノードで動作するPodは0個になりシャットダウンしても全体パフォーマンスに影響を与えなくなります。

この時右のノードはアイドル状態(削除しても問題がない状態)とCluster Autoscalerに判断され、スケールイン処理が開始されます。

まず、Cluster Autoscalerはアイドルノード上で動いているPodを削除します。
その後、他のノードにPodが正常にスケジューリングされたことを確認し、ノードを削除することでスケールインを実現します。

スケールイン・スケールアウト判定の詳細は下記の公式FAQページを参考にしてください。
環境
クラスタ環境は引き続き第1回で作成したものを使用します。
Kubernetesバージョン:1.26
ノード数の最大値・最小値設定
増減するノード数の最大値・最小値を設定します。
Cluster Autoscaler は Auto Scaling グループで設定した最小値と最大値を尊重し、自動的に希望のサイズのみを調整する性質があります。
EKS >クラスター > コンピューティング > ノードグループ



ノードグループの画面で編集をクリックします。

希望のサイズ・最小サイズ・最大サイズを指定して保存します。
最小サイズ:1
最大サイズ:5
ノードのスケールアウトから確認するため、希望のサイズは2に設定しておきます。

ノードグループの詳細画面でこのように表示されればOKです。

対象のAuto Scalingグループにも反映されているか確認します。

IAMポリシーの作成
Cluster Autoscalerを使用するためには、AWSのAuto Scalingグループを調査・変更する権限が必要になります。
必要な権限を持ったポリシーを作成し、ノードグループのIAMロールに追加していきます。
(クラスターのIAMロールではないので気を付けてください)
ロールに紐づけるIAMポリシーは大きく分けて2種類あります。
・クラスターオートスケーラーのすべての機能を許可する。(推奨)
・クラスターオートスケーラーの限定された機能を許可する。
今回は推奨されているすべての機能を許可するIAMポリシーを使用します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"autoscaling:DescribeAutoScalingGroups",
"autoscaling:DescribeAutoScalingInstances",
"autoscaling:DescribeLaunchConfigurations",
"autoscaling:DescribeScalingActivities",
"autoscaling:DescribeTags",
"ec2:DescribeInstanceTypes",
"ec2:DescribeLaunchTemplateVersions"
],
"Resource": ["*"]
},
{
"Effect": "Allow",
"Action": [
"autoscaling:SetDesiredCapacity",
"autoscaling:TerminateInstanceInAutoScalingGroup",
"ec2:DescribeImages",
"ec2:GetInstanceTypesFromInstanceRequirements",
"eks:DescribeNodegroup"
],
"Resource": ["*"]
}
]
}
上記の内容でIAMポリシーを作成します。
IAM > ポリシー
ポリシーの作成

JSONを選択し、ポリシーエディタに先ほどの許可設定を書き込み次へ。

「Cluster-Autoscaler-policy」のような分かりやすい名前を付け、定義されている許可を確認してから作成します。


ノードグループの画面から関連付けられているIAMロールのページに移動します。

IAMロールの設定画面に移動したら、許可ポリシーのセクションで「許可を追加 > ポリシーをアタッチ」をクリックします。

作成したポリシーを追加します。

Cluster Autoscalerの導入
下記のコマンドでCluster Autoscalerを導入するマニフェストファイルを取得します。
# マニフェストをダウンロード
$ wget https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/aws/examples/cluster-autoscaler-autodiscover.yaml
ダウンロードしたマニフェストファイル
# cluster-autoscaler-autodiscover.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-addon: cluster-autoscaler.addons.k8s.io
k8s-app: cluster-autoscaler
name: cluster-autoscaler
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-autoscaler
labels:
k8s-addon: cluster-autoscaler.addons.k8s.io
k8s-app: cluster-autoscaler
rules:
- apiGroups: [""]
resources: ["events", "endpoints"]
verbs: ["create", "patch"]
- apiGroups: [""]
resources: ["pods/eviction"]
verbs: ["create"]
- apiGroups: [""]
resources: ["pods/status"]
verbs: ["update"]
- apiGroups: [""]
resources: ["endpoints"]
resourceNames: ["cluster-autoscaler"]
verbs: ["get", "update"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["watch", "list", "get", "update"]
- apiGroups: [""]
resources:
- "namespaces"
- "pods"
- "services"
- "replicationcontrollers"
- "persistentvolumeclaims"
- "persistentvolumes"
verbs: ["watch", "list", "get"]
- apiGroups: ["extensions"]
resources: ["replicasets", "daemonsets"]
verbs: ["watch", "list", "get"]
- apiGroups: ["policy"]
resources: ["poddisruptionbudgets"]
verbs: ["watch", "list"]
- apiGroups: ["apps"]
resources: ["statefulsets", "replicasets", "daemonsets"]
verbs: ["watch", "list", "get"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses", "csinodes", "csidrivers", "csistoragecapacities"]
verbs: ["watch", "list", "get"]
- apiGroups: ["batch", "extensions"]
resources: ["jobs"]
verbs: ["get", "list", "watch", "patch"]
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["create"]
- apiGroups: ["coordination.k8s.io"]
resourceNames: ["cluster-autoscaler"]
resources: ["leases"]
verbs: ["get", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: cluster-autoscaler
namespace: kube-system
labels:
k8s-addon: cluster-autoscaler.addons.k8s.io
k8s-app: cluster-autoscaler
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["create", "list", "watch"]
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["cluster-autoscaler-status", "cluster-autoscaler-priority-expander"]
verbs: ["delete", "get", "update", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-autoscaler
labels:
k8s-addon: cluster-autoscaler.addons.k8s.io
k8s-app: cluster-autoscaler
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-autoscaler
subjects:
- kind: ServiceAccount
name: cluster-autoscaler
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: cluster-autoscaler
namespace: kube-system
labels:
k8s-addon: cluster-autoscaler.addons.k8s.io
k8s-app: cluster-autoscaler
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: cluster-autoscaler
subjects:
- kind: ServiceAccount
name: cluster-autoscaler
namespace: kube-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cluster-autoscaler
namespace: kube-system
labels:
app: cluster-autoscaler
spec:
replicas: 1
selector:
matchLabels:
app: cluster-autoscaler
template:
metadata:
labels:
app: cluster-autoscaler
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '8085'
spec:
priorityClassName: system-cluster-critical
securityContext:
runAsNonRoot: true
runAsUser: 65534
fsGroup: 65534
seccompProfile:
type: RuntimeDefault
serviceAccountName: cluster-autoscaler
containers:
- image: registry.k8s.io/autoscaling/cluster-autoscaler:v1.26.2
name: cluster-autoscaler
resources:
limits:
cpu: 100m
memory: 600Mi
requests:
cpu: 100m
memory: 600Mi
command:
- ./cluster-autoscaler
- --v=4
- --stderrthreshold=info
- --cloud-provider=aws
- --skip-nodes-with-local-storage=false
- --expander=least-waste
- --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/<YOUR CLUSTER NAME>
volumeMounts:
- name: ssl-certs
mountPath: /etc/ssl/certs/ca-certificates.crt # /etc/ssl/certs/ca-bundle.crt for Amazon Linux Worker Nodes
readOnly: true
imagePullPolicy: "Always"
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
volumes:
- name: ssl-certs
hostPath:
path: "/etc/ssl/certs/ca-bundle.crt"
マニフェストファイルが取得出来たら、Deploymentの149行目(spec.template.spec.containers.image)のバージョンがクラスターのk8sバージョンのマイナーバージョンまで一致しているかを確認します。
今回使用するEKS環境は1.26を使用しているためデフォルトのままにします。
修正の必要がある場合、このページのImagesセクションを参考に行ってください。
次に、165行目(spec.template.spec.containers.command.–node-group-auto-discovery)行末の<YOUR CLUSTER NAME>にEKSのクラスター名を入力します。
この行では、ノードグループのスケーリングを担当しているオートスケーリンググループのタグを指定しています。
タグを設定することで、Cluster Autoscalerは指定したオートスケーリンググループを検出できるようになり、希望のノードグループに新しいノード(インスタンス)を追加できるようになります。
148 containers:
149 - image: registry.k8s.io/autoscaling/cluster-autoscaler:v1.26.2
150 name: cluster-autoscaler
151 resources:
152 limits:
153 cpu: 100m
154 memory: 600Mi
155 requests:
156 cpu: 100m
157 memory: 600Mi
158 command:
159 - ./cluster-autoscaler
160 - --v=4
161 - --stderrthreshold=info
162 - --cloud-provider=aws
163 - --skip-nodes-with-local-storage=false
164 - --expander=least-waste
165 - --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/NIC_Cluster
2か所の修正が終わったら保存して環境にデプロイします。
$ kubectl apply -f cluster-autoscaler-autodiscover.yaml
kube-systemの中にcluster-autoscaler-~という名前のPodが作成されていることを確認し、Cluster Autoscalerの導入は完了です。
$ kubectl get pods -n kube-system
NAME READY STATUS
cluster-autoscaler-db8d5788d-t4zzj 1/1 Running 0 3m14s
動作確認
手動でpod数を増減させて、Cluster Autoscalerによるノードのスケーリングを確認していきます。
Pod作成は第3回のアプリケーション用 Deploymentを使用して行います。
Pod1個につき、1CoreのCPUリソースを使用するように設定します。
# アプリケーション用Deployment
# Deploymentリソースの定義に使用されるKubernetes APIのバージョンとグループ
apiVersion: apps/v1
# 作成するリソースの種類
kind: Deployment
# メタデータ
metadata:
name: tomcat-app # 作成するリソースの名前
namespace: sample-app # リソースを作成する名前空間
# リソースの仕様
spec:
replicas: 2 # Deploymentが管理するPodのレプリカ数
selector: # 適用するリソースの選択
matchLabels: # ラベルの一致を条件とする
app: tomcat
template: # 作成するpodの定義
metadata:
labels:
app: tomcat
spec:
containers: # pod内のコンテナの仕様
- name: tomcat-app # コンテナの名前
image: ***.dkr.ecr.ap-northeast-1.amazonaws.com/testapp:version # コンテナイメージ
ports: # コンテナが公開するポート(内部向け)
- name: tcp
containerPort: 8080
resources:
limits:
cpu: 1
memory: 512Mi
requests:
cpu: 1 #1つのPodで1CoreのCPUリソースを使用するように設定
memory: 512Mi
スケーリング前はPodが2個・ノードが2台動いている状態です。
それぞれのノードにPodが1個づつ起動していることが分かります。
$ k get pods -o wide -n sample-app
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
tomcat-app-8599ffb699-nkz2l 1/1 Running 0 3m50s 10.0.20.7 ip-10-0-20-116.ap-northeast-1.compute.internal <none> <none>
tomcat-app-8599ffb699-v4l8v 1/1 Running 0 24m 10.0.21.89 ip-10-0-21-243.ap-northeast-1.compute.internal <none> <none>
$ k get nodes
NAME STATUS ROLES AGE VERSION
ip-10-0-20-116.ap-northeast-1.compute.internal Ready <none> 59m v1.26.6-eks-a5565ad
ip-10-0-21-243.ap-northeast-1.compute.internal Ready <none> 59m v1.26.6-eks-a5565ad
まずはレプリカ数を10に設定し、スケールアウトが行われることを確認します。
$ kubectl scale --replicas=10 deployment tomcat-app -n sample-app
Pod数が10になり、4つのPodがPending状態になりました。
スケジューリング出来ず、Pending状態になってしまったPodはNODE欄が<none>になっていることが確認できます。
$ kubectl get pods -n sample-app -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
tomcat-app-8599ffb699-hg6jg 1/1 Running 0 12s 10.0.20.254 ip-10-0-20-116.ap-northeast-1.compute.internal <none> <none>
tomcat-app-8599ffb699-hwvzl 0/1 Pending 0 12s <none> <none> <none> <none>
tomcat-app-8599ffb699-kxgt8 1/1 Running 0 12s 10.0.21.235 ip-10-0-21-243.ap-northeast-1.compute.internal <none> <none>
tomcat-app-8599ffb699-kxnfv 0/1 Pending 0 12s <none> <none> <none> <none>
tomcat-app-8599ffb699-nkz2l 1/1 Running 0 15m 10.0.20.7 ip-10-0-20-116.ap-northeast-1.compute.internal <none> <none>
tomcat-app-8599ffb699-p5jbb 1/1 Running 0 12s 10.0.21.188 ip-10-0-21-243.ap-northeast-1.compute.internal <none> <none>
tomcat-app-8599ffb699-s2ljw 0/1 Pending 0 12s <none> <none> <none> <none>
tomcat-app-8599ffb699-v4l8v 1/1 Running 0 35m 10.0.21.89 ip-10-0-21-243.ap-northeast-1.compute.internal <none> <none>
tomcat-app-8599ffb699-vrt66 1/1 Running 0 12s 10.0.20.221 ip-10-0-20-116.ap-northeast-1.compute.internal <none> <none>
tomcat-app-8599ffb699-wtcjq 0/1 Pending 0 12s <none> <none> <none> <none>
しばらく待機することでノード数がスケールアウトして4台になり、すべてのPodが起動していることが確認できます。
先ほどPending状態だったPodも、スケールアウトによって増えたNODEへと割り当てられています。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-10-0-20-116.ap-northeast-1.compute.internal Ready <none> 80m v1.26.6-eks-a5565ad
ip-10-0-20-157.ap-northeast-1.compute.internal Ready <none> 9m48s v1.26.6-eks-a5565ad
ip-10-0-21-174.ap-northeast-1.compute.internal Ready <none> 9m52s v1.26.6-eks-a5565ad
ip-10-0-21-243.ap-northeast-1.compute.internal Ready <none> 80m v1.26.6-eks-a5565ad
$ kubectl get pods -n sample-app -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
tomcat-app-8599ffb699-hg6jg 1/1 Running 0 9m28s 10.0.20.254 ip-10-0-20-116.ap-northeast-1.compute.internal <none> <none>
tomcat-app-8599ffb699-hwvzl 1/1 Running 0 9m28s 10.0.21.9 ip-10-0-21-174.ap-northeast-1.compute.internal <none> <none>
tomcat-app-8599ffb699-kxgt8 1/1 Running 0 9m28s 10.0.21.235 ip-10-0-21-243.ap-northeast-1.compute.internal <none> <none>
tomcat-app-8599ffb699-kxnfv 1/1 Running 0 9m28s 10.0.20.72 ip-10-0-20-157.ap-northeast-1.compute.internal <none> <none>
tomcat-app-8599ffb699-nkz2l 1/1 Running 0 24m 10.0.20.7 ip-10-0-20-116.ap-northeast-1.compute.internal <none> <none>
tomcat-app-8599ffb699-p5jbb 1/1 Running 0 9m28s 10.0.21.188 ip-10-0-21-243.ap-northeast-1.compute.internal <none> <none>
tomcat-app-8599ffb699-s2ljw 1/1 Running 0 9m28s 10.0.21.80 ip-10-0-21-174.ap-northeast-1.compute.internal <none> <none>
tomcat-app-8599ffb699-v4l8v 1/1 Running 0 45m 10.0.21.89 ip-10-0-21-243.ap-northeast-1.compute.internal <none> <none>
tomcat-app-8599ffb699-vrt66 1/1 Running 0 9m28s 10.0.20.221 ip-10-0-20-116.ap-northeast-1.compute.internal <none> <none>
tomcat-app-8599ffb699-wtcjq 1/1 Running 0 9m28s 10.0.21.36 ip-10-0-21-174.ap-northeast-1.compute.internal <none> <none>
ノードの詳細情報を見てみます。
ノードのcpu数は4で、Pod1個あたりのCPU Requestsは1(全体の25%)になっています。
Deploymentで設定した内容が反映されていることが確認できます。
3個のアプリケーションPod(Cpu使用率75%) + kube-system上で動作するPod(Cpu使用率25%以内)
という構成で1つのノードが構成されていることが読み取れます。
# kubectl describe nodes
-------------------
Capacity:
cpu: 4
ephemeral-storage: 104845292Ki
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 8022496Ki
pods: 58
-------------------
Non-terminated Pods: (10 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits Age
--------- ---- ------------ ---------- --------------- ------------- ---
kube-system aws-node-pfc85 25m (0%) 0 (0%) 0 (0%) 0 (0%) 82m
kube-system csi-secrets-store-secrets-store-csi-driver-g2xxb 70m (1%) 400m (10%) 140Mi (2%) 400Mi (5%) 82m
kube-system efs-csi-controller-648794959f-wxfht 0 (0%) 0 (0%) 0 (0%) 0 (0%) 16h
kube-system efs-csi-node-k5x6m 0 (0%) 0 (0%) 0 (0%) 0 (0%) 82m
kube-system kube-proxy-m9fpw 100m (2%) 0 (0%) 0 (0%) 0 (0%) 82m
kube-system secrets-provider-aws-secrets-store-csi-driver-provider-awst9h99 50m (1%) 50m (1%) 100Mi (1%) 100Mi (1%) 81m
olm opencloud-operators-2nnl9 10m (0%) 0 (0%) 50Mi (0%) 0 (0%) 36m
sample-app tomcat-app-8599ffb699-kxgt8 1 (25%) 1 (25%) 512Mi (7%) 512Mi (7%) 12m
sample-app tomcat-app-8599ffb699-p5jbb 1 (25%) 1 (25%) 512Mi (7%) 512Mi (7%) 12m
sample-app tomcat-app-8599ffb699-v4l8v 1 (25%) 1 (25%) 512Mi (7%) 512Mi (7%) 47m
次にスケールインを確認します。
ノードが4台・サンプルアプリケーションPodが10個動作している状態で行い、レプリカ数を2に設定します。
$ kubectl scale --replicas=2 deployment tomcat-app -n sample-app
20分ほど待機することで2台の不要なノードが削除され、動作確認を行う前の状態(ノードが2台・Podが2つ起動)に戻りました。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-10-0-20-214.ap-northeast-1.compute.internal Ready 58m v1.26.6-eks-a5565ad
ip-10-0-21-36.ap-northeast-1.compute.internal Ready 59m v1.26.6-eks-a5565ad
$ kubectl get pods -n sample-app
NAME READY STATUS RESTARTS AGE
tomcat-app-8599ffb699-bl6kb 1/1 Running 0 31m
tomcat-app-8599ffb699-cmnc2 1/1 Running 0 11m
※デフォルトでCluster Autoscalerはノードをアイドル判定してから10分後にノードを削除することになっています。
(–scale-down-unneeded-timeというCluster Autoscalerのパラメータが10分に設定されているためです。変更したい場合はこのページを参考に、Cluster Autoscaler用マニフェストのDeployment内にあるspec.template.spec.containers.command.へ書き足してください。)
まとめ
今回は、「Cluster AutoscalerでEKSノードのAuto Scalingを実行する」手順をまとめました。
- ノード数の最大値・最小値設定
- IAMポリシーの作成
- Cluster Autoscalerの導入
これらのステップにより、需要の変化に合わせてノードを自動的に増減させることが出来ました。この記事が皆さまのEKSの設定に役立つ一助となれば幸いです。この機会に、皆さまもぜひEKSを触ってみてください!
第5回では、「EKSをバージョンアップする方法」について記事を書く予定です!次回も楽しみにお待ちください!
AWS社とのAWSソリューションプロバイダープログラム契約を結び、AWSのアカウントの手配から設計、環境構築、システム運用までワンストップのソリューションを提供いたします。
コンテナ技術には特に力を入れており、Amazon EKSだけでなく、Red Hat OpenShiftのソリューションもございます。
OpenShiftのソリューションページや、運用ソリューションであるNI+C Multicloud MSPの紹介ページもありますので、一読していただけますと幸いです。