Gunosy Tech Blog

Gunosy Tech Blogは株式会社Gunosyのエンジニアが知見を共有する技術ブログです。

AWS Node Termination Handlerの新機能について

はじめに

こんにちは。SRE部の板谷(@SItaya5)です。

Gunosyでは様々なプロダクトでEKS(Amazon Elastic Kubernetes Service)を運用しています。

Worker Nodeはスポットインスタンスを含めたEC2をASG(Auto Scaling Groups)で管理しています。 そのため、インスタンスの停止時にPodを安全に停止・退避ができるようNTH(AWS Node Termination Handler)を稼働させています。

github.com

先日、NTHのv1.9.0版がリリースされ、新機能(Queue Processor)が公開されました。

github.com

本記事では、NTHの新機能について紹介します。 なお、v.1.9.0の新機能はプレビューであり、本番環境での使用は推奨されておりませんのでご注意ください

AWS Node Termination Handlerとは

NTHは、AWS上で運用しているKubernetesクラスタに紐付いたEC2のメタデータを取得します。このメタデータを基に、スポットインスタンスの停止通知を受け取ることができます。停止通知を確認後、 Nodeをスケジューリング対象から除外し、そのNode上で稼働しているPodに対してSIGTERMシグナルを送信して退避します。そのため、Podを安全に停止し、退避させることができます。

これを実現しているのが、NTHのIMDS(Instance Metadata Service Processor)という機能です。 上述の通り、各インスタンスのメタデータを基に処理を行うため、Daemonsetとして稼働させる必要があります。

v.1.9.0では、新たにQueue Processorという機能が追加され、IMDSとQueue Processorのどちらかを選択できるようになりました。 Queue Processorでは、SQSのキューからスポットインスタンスの停止通知を受け取ります。そのためDaemonsetではなく、Deploymentとして稼働させることができ、必要なリソースを減らすことができます。そして1番の目玉はASG Lifecycle Hooksを受け取れるようになったことです。

ASGを使用している場合は、スポットインスタンスの停止だけでなく、インスタンスのローテーション時などに発生するEC2の停止も考慮する必要があります。弊社ではASG Lifecycle Hooksを受け取るためにASG Lifecycle Managerを稼働させています。これは原理的にNTHのQueue Processorと似ており、SQSを通じてASG Lifecycle Hooksを受け取るものです。

Queue Processorを使用することで、スポットインスタンスとASG、双方のインスタンス停止をNTHだけで検知し、Podの停止・退避処理が行えるようになります。

Queue Processorを使ってみる

NTHをデプロイする前に、いくつかのリソースをセットアップしておく必要があります(詳細はREADMEを参照してください)。

リソースの作成

  1. 該当のASGにLifecycle Hookを設定

    • lifecycle-transitionautoscaling:EC2_INSTANCE_TERMINATINGを指定することでインスタンスが停止されるときにフックが実行されます。
  2. ASGにタグを追加

    • Keyをaws-node-termination-handler/managedとしたタグを追加します(Valueの値は任意)。NTHはこのタグが付与されているASGを対象のものと見なします。
    • NTHのオプションでタグのチェックを無効化して全てのASGを対象とすることもできますが、タグを付与しておいた方が無難でしょう。
  3. SQS Queueの作成

    • EventBridgeからインスタンス停止の通知を受け取るQueueです。
  4. EventBridge Ruleの作成

    • ASGのインスタンス停止とスポットインスタンスの停止の2種類のルールを作成し、それぞれのターゲットを先程作成したSQSに設定します。
  5. IAM Roleの作成

    • NTHのPodから呼び出すIAM Roleを作成します。

NTHのデプロイ

上記のリソースが整えばデプロイの準備は完了です。 Helm Chartが公開されているのでここではHelmを使ってデプロイしてみます。

eks-chartsを追加していない場合は以下のコマンドで追加します。

$ helm repo add eks https://aws.github.io/eks-charts

それではデプロイしてみます。以下のコマンドを使用しました。

$ helm upgrade --install aws-node-termination-handler \
    --namespace kube-system \
    --set enableSqsTerminationDraining=true \
    --set queueURL=https://sqs.ap-northeast-1.amazonaws.com/xxxxxxx/queue-name \
    --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=arn:aws:iam::XXXXXXXXXX:role/nth-sample-role \
    eks/aws-node-termination-handler

enableSqsTerminationDraining=trueを設定することでQueue Processorが有効になり、queueURLには先程作成したSQS QueueのURLを指定します。

serviceAccount.annotationsには先程作成したIAM Roleを指定しています。弊社の環境では、PodからIAM Roleを呼び出すためにServiceAccountを使用しています(参考:サービスアカウントの IAM ロール)。この方法では、PodにアタッチされているServiceAccountのAnnotationsにIAM Roleのarnを記載することで、PodからIAM Roleを呼び出すことが可能になります。PodからIAM Roleを呼び出す方法は環境によって異なりますので、環境に合わせた設定が必要になります。

稼働確認

デプロイが完了したらNTHのログを確認してみます。まず、起動に成功すると以下のログが出力されます。

Started watching for interruption events
Kubernetes AWS Node Termination Handler has started successfully!
Started watching for event cancellations
Started monitoring for events event_type=SQS_TERMINATE

それではASGのインスタンスを停止してみましょう。 AWS CLIを使って以下のコマンドを実行します。

$ aws autoscaling terminate-instance-in-auto-scaling-group --instance-id i-xxxxxxxxxxx --no-should-decrement-desired-capacity

するとSQSからインスタンス停止のイベントが通知され、以下のログが出力されます。

Adding new event to the event store event={"Description":"ASG Lifecycle Termination event received. Instance will be interrupted at 2020-11-09 05:53:39 +0000 UTC \n","Drained":false,"EndTime":"0001-01-01T00:00:00Z","EventID":"asg-lifecycle-term-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","InstanceID":"i-xxxxxxxxxxx","Kind":"SQS_TERMINATE","NodeName":"ip-xx-xx-xx-xx.ap-northeast-1.compute.internal","StartTime":"2020-11-09T05:53:39Z","State":""}
Cordoning the node
Draining the node
evicting pod "sample-xxxxxxxxxx-xxxxx"
evicting pod "sample-xxxxxxxxxx-yyyyy"

停止のイベントを受け取った後、Nodeをスケジューリング対象から除外し、Node上のPodが退避されていることが確認できます(Daemonsetで管理されているPodが稼働している場合はWARNINGが出力されます)。

処理が成功すると以下のログが確認できます。

Completed ASG Lifecycle Hook (queue-name) for instance i-xxxxxxxxxxx

以上のように、NTHの新機能を使うことでASGのインスタンス停止を検知し、Podが退避されていることが確認できました。

まとめ

この記事ではAWS Node Termination Handlerの新機能について紹介しました。 新機能により、スポットインスタンスとASG、両方の停止イベントを検知し、安全にPodが停止・退避できるようになりました。

冒頭でも記載の通り、この新機能は現時点でプレビュー版ですが、活発に開発が行われているリポジトリなので、正式リリースされる日も遠くないでしょう。

参考