GrabはKubernetes環境にあるKafkaのセットアップをアップデートし、耐障害性を向上させ、予期せぬKafkaブローカーの終了に備えて人による介入を完全に排除した。初期設計の欠点に対処するため、チームはAWS Node Termination Handler (NTH)と統合し、ターゲットグループのマッピングにLoad Balancer Controllerを使用し、ストレージをELBボリュームに切り替えた。
Grabは、Cobanリアルタイムデータプラットフォームの一部として、2年前からStrimziを使用したApache KafkaonKubernetes(EKS)を実運用している。チームは以前、現在CNCFのインキュベート・プロジェクトとなっているStrimziを活用し、すべてのサーバーとサーバー、クライアントとサーバーの統合に実績のある認証、認可、機密性のメカニズムを適用することで、Kafkaクラスタのセキュリティを強化した。
初期のセットアップはうまくいっていたが、EKSノードがメンテナンスやインフラの問題でAWSによって予期せず停止された場合は別であった。この場合、ブローカーが潔く降格されないため、Kafkaクライアントが突然エラーに直面することになる。さらに悪いことに、影響を受けたブローカーインスタンスは、新たにプロビジョニングされたEKSワーカーノード上で再起動できなかった。その結果、Cobanのエンジニアが介入しなければ、Kafkaクラスタは3つのブローカーノードのうち2つしか利用できないデグレード状態で稼働することになった。
開発者はAWS Node Termination Handler (NTH)を活用し、ワーカーノードをドレインすることでKafkaクライアントへの中断を最小限に抑え、SIGTERMシグナルでKafkaプロセスのグレースフル・シャットダウンをトリガーした。チームは、アベイラビリティゾーン(AZ)とオートスケーリンググループ(ASG)に関連するものを含む、より広範なイベントセットを捕捉するため、インスタンスメタデータサービス(IMDS)モードではなく、キュープロセッサモードを使用することを選択した。
AWS Node Termination Handler (Queue Processor) Used To Support Graceful Kafka Shutdown(出典:Grab Engineering Blog)
チームは、AWSロードバランサーコントローラー(LBC)を使用して、ネットワークロードバランサー(NLB)ターゲットグループを動的にマッピングすることで、ワーカーノード終了時のネットワーク接続の切断の問題を解決した。エンジニアは、ヘルスチェックの頻度を増やし、NLBにPod Readiness Gateを設定することで、NLBが各ターゲットグループを"healthy"(正常である)とマークするのに時間がかかりすぎるという問題に対処しなければならなかった。
チームが乗り越えなければならなかった最後の大きなハードルは、新しくプロビジョニングされたKafkaワーカーノードが正しく起動し、データストレージボリュームにアクセスできるようにすることだった。エンジニアたちは、NVMeインスタンス・ストレージ・ボリュームではなく、Elastic Block Storage(EBS)ボリュームを使用した。ESBを使用することで、低コスト、インスタンス仕様からボリュームサイズを切り離す、同期時間の短縮、スナップショットバックアップ、ダウンタイムなしで実行できる容量増加など、多くのメリットが得られる。さらに、EC2のインスタンスタイプを、ストレージに最適化されたものから、汎用またはメモリに最適化されたものに変更した。
KubernetesとStrimziへの追加設定により、新しいクラスタ用にEBSボリュームを自動的に作成し、Kafkaポッドが異なるワーカーノードに再配置されるたびに、EC2インスタンス間でボリュームをアタッチ/デタッチできるようになった。
すべての機能強化の後、EC2インスタンスのリタイアメントや、すべてのワーカーノードをローテーションする必要があるオペレーションは、人の手を借りずに実行できるようになった。チームはさらなる改善を計画しており、NTHのWebhookを使用して新しいインスタンスをプロアクティブにスピンアップしたり、NTHによって開始されたアクションを通知するSlack通知を送信したりするほか、Kubernetes Cluster Autoscalerを置き換えるKarpenterを展開する予定だ。