この記事は NTTコムウェア Advent Calender 2021 12日目の記事です。
はじめに
NTTコムウェアのtonomuraです。
本記事では、AWSのマネージドサービスであるAmazon EKSにおけるリソース使用料の節約を試みた内容を紹介します。
想定読者
・Amazon EKSをステージング環境で利用していて、利用料金に悩んでいる方
・平日日勤帯以外などの未使用時間にサーバを止めておきたい方
・コスト削減したいけどAWS Fargateへの移行に踏み切れない方
Amazon EKSとは
Amazon Elastic Kubernetes Service の略であり、AWS上でkubernetes環境を簡易に構築・運用できるマネージド・サービスです。
似たようなサービスにAmazon ECSやAmazon EKS on Fargateなどがあり、これらはサーバを意識することなくコンテナをデプロイできるサービスになります。
対して、Amazon EKSはワーカーノードと呼ばれる、コンテナがデプロイされるサーバ(EC2インスタンス)は自分たちで管理する必要があり、従量課金で使った分だけ課金されてしまいます。
本記事の内容
そこで、主にステージング環境用途で使用する場合において、少しでも利用料金を節約するために、使っていない間にEC2インスタンスを落としておく方法を紹介します。
また、EC2インスタンスの復旧までにどのくらい時間がかかるのか、kubernetesリソースの状態はどのように遷移するのか、コマンド実行結果と合わせて紹介します。
前提条件
・本記事で扱うアプリケーションは、コンテナ化されたWeb三層構造のサンプルアプリです
・複数のサービスを組み合わせている場合、Podの起動順序等の制約により、正しく動作しない場合があります
・EKSクラスター(eksctlを利用して構築)やAuto Scalingグループは予め設定済みの状態です
・使用するツールとワーカーノードの構成は以下のとおりです
$ aws --version
aws-cli/2.2.12 Python/3.8.8 Linux/4.14.77-70.59.amzn1.x86_64 exe/x86_64.amzn.2018 prompt/off
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-10-**-**-**.region.compute.internal Ready <none> 38h v1.20.7-eks-*****
ip-10-**-**-**.region.compute.internal Ready <none> 38h v1.20.7-eks-*****
ip-10-**-**-**.region.compute.internal Ready <none> 38h v1.20.7-eks-*****
ip-10-**-**-**.region.compute.internal Ready <none> 38h v1.20.7-eks-*****
ノード名は伏せていますが、2つのノードグループで構成されており、各ノードグループに対してデプロイされるPodを振り分けています。
初期状態
まずはどのようなPodが立ち上がっているかを確認します。
今回の環境では、サンプルアプリ以外にもPodのログを収集するContainer Insights のリソースが動いています。
$ kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
amazon-cloudwatch cloudwatch-agent-fjfdz 1/1 Running 0 20m
amazon-cloudwatch cloudwatch-agent-jz4wf 1/1 Running 0 36m
amazon-cloudwatch cloudwatch-agent-x28qt 1/1 Running 0 27m
amazon-cloudwatch cloudwatch-agent-z5c5j 1/1 Running 0 27m
amazon-cloudwatch fluentd-cloudwatch-dgt4x 1/1 Running 0 20m
amazon-cloudwatch fluentd-cloudwatch-gj9lf 1/1 Running 0 27m
amazon-cloudwatch fluentd-cloudwatch-jqtcz 1/1 Running 0 36m
amazon-cloudwatch fluentd-cloudwatch-xqdnk 1/1 Running 0 27m
test-ap test-ap-5ffc6fc4cc-29cmv 1/1 Running 0 44d
test-ap test-ap-5ffc6fc4cc-c4h9f 1/1 Running 0 44d
test-ap test-ap-5ffc6fc4cc-msrtp 1/1 Running 0 44d
test-ap test-ap-5ffc6fc4cc-v7cdx 1/1 Running 0 44d
test-ap nginx-8c489f6cf-968ps 1/1 Running 0 29m
test-ap nginx-8c489f6cf-cb26n 1/1 Running 0 29m
test-ap nginx-8c489f6cf-fzxc9 1/1 Running 0 29m
test-ap nginx-8c489f6cf-nsdph 1/1 Running 0 29m
kube-system alb-ingress-controller-68f5cb8b4b-rklj2 1/1 Running 0 29m
kube-system aws-node-hj5gf 1/1 Running 0 27m
kube-system aws-node-jfqps 1/1 Running 0 37m
kube-system aws-node-w2sbs 1/1 Running 0 27m
kube-system aws-node-zxctr 1/1 Running 0 21m
kube-system coredns-5fd8748bdd-5jhct 1/1 Running 0 29m
kube-system coredns-5fd8748bdd-kkr2x 1/1 Running 0 29m
kube-system efs-csi-node-6kq4b 3/3 Running 0 37m
kube-system efs-csi-node-g9lss 3/3 Running 0 27m
kube-system efs-csi-node-jzbpf 3/3 Running 0 27m
kube-system efs-csi-node-v4p7w 3/3 Running 0 21m
kube-system kube-proxy-46xxl 1/1 Running 0 21m
kube-system kube-proxy-gbktt 1/1 Running 0 27m
kube-system kube-proxy-hbnff 1/1 Running 0 37m
kube-system kube-proxy-xq275 1/1 Running 0 27m
※補足
マネージドサービスのため、etcd
やapiserver
などのリソースは参照できないため登場しません。
ノードを落としてみる
早速、この状態からワーカーノードを落としてみます。
落とす方法としては、Auto Scalingグループの設定の内、「希望する容量」を変更し、ノードが強制的に0台になるようにします。
コマンドは以下のとおりです。
#ノードグループ1 の希望する容量を「0」にする
$ aws autoscaling set-desired-capacity --auto-scaling-group-name eksctl-test-ap-nodegroup-z1-nodeg-NodeGroup-1PXBUVII5GDBS \
--desired-capacity 0
#ノードグループ2 の希望する容量を「0」にする
$ aws autoscaling set-desired-capacity --auto-scaling-group-name eksctl-test-ap-nodegroup-z2-nodeg-NodeGroup-1IHPM2OH5CEW9 \
--desired-capacity 0
ノードの状態を確認してみます。
$ kubectl get nodes
No resources found in default namespace.
リソースが見つからない状態になりました。
この時点で、EC2インスタンスは停止状態のため、EC2料金は課金されなくなりました。
続いて、PodのSTATUS(状態)を確認してみます。
$ kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
test-ap test-ap-5ffc6fc4cc-4lc8c 0/1 Pending 0 2s
test-ap test-ap-5ffc6fc4cc-d756p 0/1 Pending 0 2s
test-ap test-ap-5ffc6fc4cc-gjkt9 0/1 Pending 0 2s
test-ap test-ap-5ffc6fc4cc-ljmp7 0/1 Pending 0 2s
test-ap nginx-8c489f6cf-dthxl 0/1 Pending 0 22s
test-ap nginx-8c489f6cf-lsb8q 0/1 Pending 0 23s
test-ap nginx-8c489f6cf-p878k 0/1 Pending 0 2s
test-ap nginx-8c489f6cf-vdcg9 0/1 Pending 0 2s
kube-system alb-ingress-controller-68f5cb8b4b-mshkq 0/1 Pending 0 2s
kube-system coredns-5fd8748bdd-cf584 0/1 Pending 0 2s
kube-system coredns-5fd8748bdd-zdzkm 0/1 Pending 0 2s
一部のPodが見えなくなり、STATUS(状態)がPending
(保留)に変わりました。
この状態で試しにアプリにアクセスしてみます。
$ curl -k https://xxxx/test-ap/login/ -o /dev/null -w '%{http_code}\n' -s
503
503エラーとなりアクセスできなくなりました。
ノードを復旧させる
復旧させる方法は、Auto Scalingグループの設定を同じコマンドでもとに戻すだけです。
#ノードグループ1 の希望する容量を「2」にする
$ aws autoscaling set-desired-capacity --auto-scaling-group-name eksctl-test-ap-nodegroup-z1-nodeg-NodeGroup-1PXBUVII5GDBS \
--desired-capacity 2
#ノードグループ2 の希望する容量を「2」にする
$ aws autoscaling set-desired-capacity --auto-scaling-group-name eksctl-test-ap-nodegroup-z2-nodeg-NodeGroup-1IHPM2OH5CEW9 \
--desired-capacity 2
ノードのSTATUS(状態)を確認します。
$ kubectl get nodes
No resources found in default namespace.
実行直後はまだノードの情報は出力されません。
しばらく-w
オプションで待機すると、およそ2分ほどでノードの状態が確認できるようになりました。
$ kubectl get nodes -w
NAME STATUS ROLES AGE VERSION
ip-xx-xx-xx-xx.region.compute.internal NotReady <none> 0s v1.20.7-eks-xxxxxx
ip-xx-xx-xx-xx.region.compute.internal NotReady <none> 0s v1.20.7-eks-xxxxxx
ip-xx-xx-xx-xx.region.compute.internal NotReady <none> 0s v1.20.7-eks-xxxxxx
ip-xx-xx-xx-xx.region.compute.internal NotReady <none> 0s v1.20.7-eks-xxxxxx
この時点ではSTATUS(状態)はNotReady
ですが、しばらくすると、
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-xx-xx-xx-xx.region.compute.internal Ready <none> 45s v1.20.7-eks-xxxxxx
ip-xx-xx-xx-xx.region.compute.internal Ready <none> 45s v1.20.7-eks-xxxxxx
ip-xx-xx-xx-xx.region.compute.internal Ready <none> 45s v1.20.7-eks-xxxxxx
ip-xx-xx-xx-xx.region.compute.internal Ready <none> 45s v1.20.7-eks-xxxxxx
STATUS(状態)がReady
になりました。
その後、Podの状態を確認します。
$ kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
amazon-cloudwatch cloudwatch-agent-79tfh 1/1 Running 0 3m41s
amazon-cloudwatch cloudwatch-agent-fqgzd 1/1 Running 0 3m51s
amazon-cloudwatch cloudwatch-agent-gbvbk 1/1 Running 0 4m1s
amazon-cloudwatch cloudwatch-agent-nprzt 1/1 Running 0 4m11s
amazon-cloudwatch fluentd-cloudwatch-4gfqf 1/1 Running 0 3m51s
amazon-cloudwatch fluentd-cloudwatch-5rxrk 1/1 Running 0 4m11s
amazon-cloudwatch fluentd-cloudwatch-cd52v 1/1 Running 0 3m41s
amazon-cloudwatch fluentd-cloudwatch-p7tx4 1/1 Running 0 4m1s
test-ap test-ap-5ffc6fc4cc-4lc8c 1/1 Running 0 7m31s
test-ap test-ap-5ffc6fc4cc-d756p 1/1 Running 0 7m31s
test-ap test-ap-5ffc6fc4cc-gjkt9 1/1 Running 0 7m31s
test-ap test-ap-5ffc6fc4cc-ljmp7 1/1 Running 0 7m31s
test-ap nginx-8c489f6cf-dthxl 1/1 Running 0 7m51s
test-ap nginx-8c489f6cf-lsb8q 1/1 Running 0 7m52s
test-ap nginx-8c489f6cf-p878k 1/1 Running 0 7m31s
test-ap nginx-8c489f6cf-vdcg9 1/1 Running 0 7m31s
kube-system alb-ingress-controller-68f5cb8b4b-mshkq 1/1 Running 0 7m31s
kube-system aws-node-54975 1/1 Running 0 4m31s
kube-system aws-node-54gjt 1/1 Running 0 4m31s
kube-system aws-node-m4lwc 1/1 Running 0 4m32s
kube-system aws-node-xw6zn 1/1 Running 0 4m31s
kube-system coredns-5fd8748bdd-cf584 1/1 Running 0 7m31s
kube-system coredns-5fd8748bdd-zdzkm 1/1 Running 0 7m31s
kube-system efs-csi-node-4gvj4 3/3 Running 0 4m32s
kube-system efs-csi-node-5v556 3/3 Running 0 4m31s
kube-system efs-csi-node-8fln9 3/3 Running 0 4m31s
kube-system efs-csi-node-dzfqt 3/3 Running 0 4m31s
kube-system kube-proxy-4wdrm 1/1 Running 0 4m31s
kube-system kube-proxy-4xtr8 1/1 Running 0 4m31s
kube-system kube-proxy-zndfc 1/1 Running 0 4m31s
kube-system kube-proxy-zvvxs 1/1 Running 0 4m31s
すべてのPodが元のRunning
に戻りました。
再びアプリにアクセスしてみます。
$ curl -k https://xxxx/test-ap/login/ -o /dev/null -w '%{http_code}\n' -s
200
無事に元どおりアクセス可能な状態に戻りました。
復旧までにかかる時間はトータルで10分程度ではありますが、すべてのノードをコマンド一つで停止し、復旧することができました。
まとめ
Auto Scalingグループの設定を変更するだけで、Amazon EKS 上のEC2インスタンスを停止し、リソース使用料を節約できました。
一時的にPodはPending
になるものの、ノード復旧後も正常にアプリにアクセスすることができました。
その他の使い方
コマンドも簡易であるため、AWS Lambdaなどと組み合わせ、任意のイベントをトリガーとして活用できます。
例えば、チャットに「業務終了」と発言したタイミングでステージング環境を落としたり、定期的に自動実行させることも可能かと思います。