Amazon Web Services ブログ

Delivery Hero で活躍する Amazon EKS と Spot インスタンス

この記事は Amazon EKS and Spot Instances in action at Delivery Hero (記事公開日 : 2022 年 6 月 3 日) の翻訳です。原文は Delivery Hero のプリンシパルシステムエンジニア Christos Skevis、Delivery Hero のシニアシステムエンジニア Giovanny Salazar、AWS のフレキシブルコンピュート担当シニアスペシャリストソリューションアーキテクト Cristian Măgherușan-Stanciu、そして AWS のコンテナ担当シニアスペシャリストソリューションアーキテクト Sascha Möllering による共著です。

この記事では、Amazon Elastic Compute Cloud (Amazon EC2) Spot で構成されるマネージド型ノードグループを使用して、プロダクショングレードの Amazon Elastic Kubernetes Service (Amazon EKS) クラスターを構成する方法を紹介します。その後、最大級のフードデリバリー企業である Delivery Hero が、Amazon EKS と Amazon EC2 Spot インスタンスを本番環境で使用することで、COVID-19 の流行時にビジネスを大きく成長させつつもコストを抑制してきた方法を紹介します。

Kubernetes は、ここ数年で多くの企業が採用し始めた人気のコンテナフレームワークです。初期の頃は、多くの AWS ユーザーが Kubernetes のインフラストラクチャ全体を管理していたため、運用に多大な労力を要していました。AWS は、お客様からのフィードバックに基づき、AWS 上で Kubernetes をより簡単に実行可能にするいくつかのサービスや機能をリリースしました。この運用作業の大部分を AWS が引き受けることで、お客様はアプリケーションの構築や顧客へのサービス提供に集中できるようになったのです。

2018 年、AWS は Amazon EKS をリリースし、複数のアベイラビリティゾーンにまたがってプロビジョニングされる、フルマネージド型で、安全かつ高可用性のアップストリーム Kubernetes クラスターのコントロールプレーンを提供開始しました。これには、ロギングや Pod レベルでの最小権限アクセスのサポート、その他多くの運用上のベストプラクティスが含まれます。2019 年には、マネージド型ノードグループをリリースしました。これにより、Amazon EKS クラスターに計算能力を提供する基盤となる Amazon EC2 インスタンスも、Amazon EKS がプロビジョニングおよび管理するようになり、Kubernetes バージョンの更新などの運用作業をさらにシンプルにしました。

マネージド型ノードグループのリリース以前は、Amazon EKS コントロールプレーンが作成されると、eksctlAWS CloudFormation、または他のツールを使用して、クラスターの Amazon EC2 インスタンスを作成および管理する必要がありました。マネージド型ノードグループを使用すると、Kubernetes データプレーンも Amazon EKS API を用いてネイティブに管理できます。ノードグループは、Amazon EKS コンソールの主要なコンポーネントであり、クラスターの実行に利用されるインフラストラクチャを一箇所で簡単に管理および可視化可能にします。

続いて 2020 年 12 月には、AWS は Amazon EKS マネージド型ノードグループで Amazon EC2 Spot インスタンスを実行するためのサポートをリリースし、予備の Amazon EC2 キャパシティプールを利用することで Kubernetes ワーカーノードのコストを最大 90% 削減可能にしました。Spot マネージド型ノードグループは、Amazon EKS の Spot ベストプラクティスを遵守するために必要だった作業のほとんどを引き受け、適切に設計されたコンテナアプリケーションに影響を与えることなく、Spot の中断を自動的に処理します。お客様がやるべきことは、複数のインスタンスタイプのリストを提供し、マネージド型ノードグループに設定するインスタンスタイプを多様化して、ノードグループが複数のキャパシティプールを利用できるようにするだけです。これは、すべての Spot インスタンスワークロードにとってとても重要です。

475 種類を超える幅広いインスタンスタイプを考慮すると、これは言うは易く行うは難しでした。そこで 2021 年初頭には、Amazon EC2 Instance Selector と呼ばれるオープンソースライブラリを、eksctl コマンドラインツールに自動的に統合しました。これにより、マネージド型ノードグループ内のインスタンスのサイズを指定するだけで良くなり、やるべき手順がさらにシンプルになりました。最近のバージョンの eksctl を使用してコマンドラインからマネージド型ノードグループを作成する場合、Spot に適した多様なインスタンスが完全に自動で選択されます。指定したサイズに対して、可能な限り多様なファミリー、世代のインスタンスタイプが自動的に選択されるのです。

セルフマネージド型ノードで Spot インスタンスの中断を処理するためには、お客様は AWS Node Termination Handler などの追加の自動化ツールをクラスター上で実行する必要がありました。Amazon EKS マネージド型ノードグループでは、追加のツールなしで自動的に Spot インスタンスの中断を処理します。基盤となる Amazon EC2 Auto Scaling グループは、キャパシティリバランシングに最適化されています。つまり、ノードグループ内の Spot インスタンスの 1 つの中断のリスクが高まり、Amazon EC2 インスタンスのリバランス通知を受け取ると、Amazon EC2 Auto Scaling グループは代替インスタンスを起動しようとします。より多くのインスタンスタイプをマネージド型ノードグループに設定しておくことで、Amazon EC2 Auto Scaling が代替の Spot インスタンスを直ちに起動し、中断を安全に処理できる可能性が高まります。以下の図に示されているように、Amazon EKS は、オンデマンドマネージド型ノードグループと同様に、スケールイン中および停止中のインスタンスから Pod を自動的に drain します。

Amazon EKS マネージド型ノードグループのキャパシティリバランシング

図 1 : Amazon EKS マネージド型ノードグループのキャパシティリバランシング

準備

この記事では、Amazon EKS クラスターを作成および管理するためのコマンドラインインターフェース (CLI) ツールである eksctl を使用して、Amazon EKS クラスターを作成します。このツールは Go で書かれ、AWS CloudFormation を使用しています。また、このツールは Weaveworks によって作成されましたが、コミュニティからの貢献も歓迎しています。

Amazon EKS クラスターを作成する

この章では、eksctl を使用してAmazon EKS クラスターを作成する方法を紹介します。また、Kubernetes Cluster Autoscaler のような中断できないワークロードをホスト可能な、オンデマンドマネージド型ノードグループも含めて説明します。

eksctl create cluster --name simple-cluster \
    --region eu-west-1 --nodegroup-name=ng-on-demand \
    --managed --node-type=t3.small

クラスターの作成に成功すると、オンデマンドノードが作成されていることを確認できます。

kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-192-168-13-24.eu-west-1.compute.internal Ready <none> 13m v1.20.4-eks-6b7464
ip-192-168-53-176.eu-west-1.compute.internal Ready <none> 13m v1.20.4-eks-6b7464
ip-192-168-79-142.eu-west-1.compute.internal Ready <none> 13m v1.20.4-eks-6b7464

Spot インスタンスを追加する

次に、Spot マネージド型ノードグループを作成します。eksctl を使用して、Spot インスタンスで動作する新しいノードを起動し、Amazon EKS クラスターに接続します。新しい Instance Selector との統合により、インスタンスタイプを明示的に指定しなくても、様々なインスタンスタイプに自動的に分散できるようになりました。

eksctl create nodegroup --name=ng-spot \
    --region=eu-west-1 \
    --cluster=simple-cluster \
    --managed --spot \
    --instance-selector-vcpus=2 \
    --instance-selector-memory=4 \
    --instance-selector-cpu-architecture=x86_64

作成した Spot マネージド型ノードグループは、クラスター内の Spot インスタンスの中断回数を減らすことを目標とします。これには、Spot のベストプラクティスに従って、 SpotAllocationStrategy として capacity-optimized 配分戦略を使用することで、利用可能なキャパシティが最も大きい Spot インスタンスプールから起動することが含まれます。オンデマンドと Spot のキャパシティは、別々の Amazon EC2 Auto Scaling グループに分離することが推奨されます。これら 2 つはスケジューリング特性が根本的に異なるため、(訳注 : 1 つの Auto Scaling グループ内で) オンデマンドベースキャパシティを指定する方法よりも望ましいです。Spot インスタンスはいつでも (Amazon EC2 がキャパシティを必要とする時に) 中断する可能性があるため、中断可能なノードを taint し、追い出し (preempt) 動作に対して明示的に Pod の toleration を必要とする方法がよく用いられます。これらの taint はノードのスケジューリング特性の違いに繋がるため、複数の Amazon EC2 Auto Scaling グループに分離する必要があるのです。

また、Spot マネージド型ノードグループは eks.amazonaws.com/capacityType ラベルを作成し、SPOT の値をノードに設定するので、作成した Spot ノードに関するクエリに使用できます。

kubectl get nodes \
    --label-columns=eks.amazonaws.com/capacityType \
    --selector=eks.amazonaws.com/capacityType=SPOT
NAME STATUS ROLES AGE VERSION CAPACITYTYPE
ip-192-168-55-103.eu-west-1.compute.internal Ready <none> 17m v1.20.4-eks-6b7464 SPOT
ip-192-168-94-138.eu-west-1.compute.internal Ready <none> 17m v1.20.4-eks-6b7464 SPOT

このラベルは、どのノードが Spot インスタンスで、どれがオンデマンドインスタンスであるかを識別するために使用できるので、Spot インスタンス上で実行するのに適切なワークロードをスケジューリングできます。2021 年には、Amazon EKS のマネージド型ノードグループで Kubernetes の node taint のサポートもリリースしました。この追加のメカニズムを使用して、Spot キャパシティ上でのアプリケーションのスケジューリングを制御することも可能です。

Karpenter を導入する

標準の Kubernetes Cluster Autoscaler は、マネージド型ノードグループ内のインスタンスのサイズがすべて同じであることを前提とします。これは、Spot 利用者に推奨される、インスタンスタイプの多様化のレベルを低下させます。この多様性の低下を補うには、複数のノードグループを同じクラスターにアタッチする必要がありますが、特に大規模な場合には複雑さが増大します。AWS の新しいオープンソースプロジェクトである Karpenter は、代替となるノードプロビジョニングメカニズムを提供することで、この複雑さを回避します。Kubernetes は、Pod をスケジューリングするために追加のキャパシティを必要とするときには、ノードグループのキャパシティを調整する代わりに、Amazon EC2 インスタンスを直接起動します。Spot インスタンスと併用する場合、Karpenter は幅広いインスタンスタイプで capacity-optimized 配分戦略を使用し、Spot インスタンスの多様性を向上させます。このように、Karpenter は、複数のマネージド型ノードグループを使用する複雑さを回避することで、大幅な多様化をサポートします。Karpenter のハンズオン Deep Dive については、Karpenter ワークショップを参照してください。

後片付け

コストを削減するために、1 つのコマンドで既存のすべてのリソースを簡単に削除できます。

eksctl delete cluster --name simple-cluster --region eu-west-1

Delivery Hero における Spot

Delivery Hero は、4 大陸 40 カ国以上で展開する世界有数のローカルデリバリープラットフォームです。現在、Delivery Hero はフードデリバリーに加え、食料品や日用品を最短 10 から 15 分で顧客に届けることを目指し、次世代の e コマースである “クイックコマース” を開拓しています。同社は、長年にわたり AWS を利用し、クラウド上でワークロードを実行するメリットを体感しています。

過去 2 年間、Delivery Hero は非常に速いペースで成長したため、クラウドコストも同様に増加し、ある分野では技術負債によって急増していました。Delivery Hero の技術部門と AWS のアカウントマネージャーが協力することで、これらのコストを抑制するためのソリューションがいくつも生まれました。最もインパクトのある施策の 1 つは、Amazon EKS プラットフォームに Spot インスタンスを導入したことです。

Delivery Hero は、Terraform でクラウドインフラストラクチャを管理し、これまでにセルフマネージド型ノードを使用してきました。その後、顧客への影響を最小限に抑えるために Amazon EC2 Spot のベストプラクティスに従いつつ、徐々に Spot インスタンスを既存の構成に導入していきました。まず、capacity-optimized 配分戦略を導入し、その後、Delivery Hero の社内の標準と要件をいくつか追加しました。

チームには、主に次のような要件がありました。

  • トラフィックとクラスターが急速に成長する中で、十分な計算能力を確保するために、cluster-overprovisioner を設置する必要があります。
  • まれに Spot インスタンスが利用できない場合、自動的にオンデマンドノードにフォールバックします。
  • 特定のアプリケーションをオンデマンドノードで実行し続けるオプションが必要です。(例 : CoreDNS)
  • クラスターごとに、実行する Spot インスタンスの割合を制御します。
  • 似たサイズの Spot インスタンスを使用した負荷テストを行い、インスタンスタイプを選択します。

Delivery Hero は、Amazon EKS セルフマネージド型ノードの機能とともに、様々な Kubernetes ネイティブのメカニズムを組み合わせました。cluster-overprovisioner、cluster-autoscaler priority expander、カスタマイズされた Amazon EC2 ユーザーデータ、そして複数のインスタンスタイプを持つ複数の Amazon EC2 Auto Scaling グループを、Kubernetes の node taint と組み合わせて使用することで、ビジネス要件を満たすとともに最良のユーザー体験を提供できます。

Delivery Hero の Amazon EKS クラスターにおける Spot インスタンスの実装の詳細は、Delivery Hero のブログでご覧いただけます。また、現在の設定をシンプルにするために、Karpenter の採用も検討中です。

まとめ

この記事では、Amazon EKS の Spot インスタンスのサポートは、よりシンプルで使いやすく、より堅牢な構成に進化しており、最小限の労力で Spot インスタンスを利用できることを紹介しました。また、最近の eksctl との統合により、Spot マネージド型ノードグループを使用した Amazon EKS をより簡単に利用できることを紹介しました。しかし、広く多様化させたい場合には、Kubernetes Cluster Autoscaler の制約により、スケール時に比較的複雑な構成になることがあります。そこで、新しい Karpenter プロジェクトを利用することで、今後このような設定をさらにシンプルにできることを説明しました。

最後に、Delivery Hero が Amazon EKS プラットフォームで Spot インスタンスを使用したコスト抑制に成功し、現在は前述のような簡素化の途中であることを紹介しました。AWS では、多くのユーザーが同様の Spot インスタンスの導入と簡素化の過程を歩むのを見てきており、この分野の動向にも注目しています。