クラウドでの運用かデータセンターでの運用に関係なく、ワークロードの適切なサイジングと、コスト抑制のための最適化の機会を見つけることは、大きな課題です。オブザーバビリティを通して、コストの把握、最適化の可能性が最も高いワークロードの決定、さらに新しい最適化を見つけるために試行錯誤ができるため、これらの課題を達成する上で不可欠な要素です。ビジネス、ワークロードや機能は、提供するサービスごとに異なり、またアーキテクチャーやビジネスニーズに基づいた要件がある場合がほとんどです。そのため、オブザーバビリティが不可欠です。

このブログでは、クラウド料金の削減を目標に、Kubernetesインフラストラクチャを最適化する機会を見つけるための重要な戦略について検討します。 

意見ではなくデータから開始

New Relicでは、独自のオブザーバビリティプラットフォームを活用して、さまざまなサービスやリソースのコスト情報をレポートすることができます。この情報をチームに提供し、意見ではなくデータを活用して最適化の機会を見つけ、コストを削減できるようにします。 

当社のサービスの大半はKubernetes上で実行されているため、チームがコスト調査で使用する重要な監視ソースの1つはKubernetesイベントです。Kubernetesインテグレーションをインストールすれば、これらのイベントの収集を簡単に開始できます。このイングレーションにより、コンテナ オーケストレーション レイヤーが計装され、各イベントの複数メトリクスを使用してイベントをレポートします。便利なユースケースとして、Kubernetes並行ポッド オートスケーラー データを含む、K8sHpaSampleイベントが挙げられます。当社はこのデータを社内で使い、いくつかのコスト最適化の方法を見つけて実装しました。 

クラウド料金の削減にも役立つ、これらの最適化についていくつかご紹介します:

  • 適切なサイジング
  • 自動スケーリング
  • x86とARMインスタンスンスの比較
  • スポットインスタンス

適切なサイジング 

仮想マシンの時代になり、一部のチームは過大なリソースを活用するようになりました。Kubernetesベースのコンテナコンピューティングが普及している昨今、この問題はさらに深刻になっています。チームがワークロードの実際の需要を大幅に上回る、非常に高いリクエストのリソースを設定するのはよくあることです。これにより、時間単位で支払われる可能性のあるクラウドインスタンスが無駄になり、その結果コストが高くなります。

コンピューティングパワーをより効果的に活用し、コストを抑えるには、Kubernetesインテグレーションにすでに含まれている、組み込みのKubernetesダッシュボードとアラートを利用することをご検討ください。次の画像は、このインテグレーションパックに含まれるダッシュボードの例で、Kubernetesクラスタの大まかなビューを提供します。

最初のステップとして、K8sContainerSampleイベントの以下のメトリクスを確認して、ポッドとノードのCPUとメモリの使用量を監視する必要があります。これには、以下のメトリクスが含まれます:

  • cpuUsedCores
  • cpuRequestedCores
  • CPULimitCores
  • memoryWorkingSetBytes 
  • memoryRequestedBytes

これらのメトリクスをNRQLクエリで組み合わせ、コンテナのCPUとメモリの要求が、サービスの安定性を維持するために実際に必要なものと一致しているかを確認します。サービスが定常状態以上のバーストを必要とする場合、コンテナ要求を低く抑えながら、コンテナのCPU制限をより高く設定することを検討してください。このNRQLクエリを使用すると、特定のコンテナについて、CPU使用率対要求CPUが実際にどうなっているかを確認することができます:

Select average(cpuUsedCores)/max(cpuRequestedCores) from K8sContainerSample TIMESERIES FACET podname WHERE namespace = ‘your_namespace’ WHERE (containerName = ‘your_container’ AND clustername = ‘your_cluster’) limit max

テスト的に実行した結果、あるサービスの使用量に大きなばらつきがあることがわかった場合は、次のステップに進み、自動スケーリングによって最適なリソース割り当てができるかどうかを判断できます。

自動スケーリング

適切なサイジングは、コンピューティング経費を制御するための第一歩であるべきです。しかし、企業の規模やシナリオによってはそれが困難な場合もあります。次のステップでは、Kubernetesで利用可能な自動スケーリング機能を活用します。自動スケーリングは、指定されたポリシーに基づいて最適なパフォーマンスとリソース効率を確保するために、クラスタ内のノード数だけでなく、観測されたリソース利用に基づいて実行中のポッド数を動的に変更します。以下の3つのオプションを検討します::

  • Horizontal Pod Autoscaling (HPA):サービス内のポッド数を動的に調整する機能を提供
  • Cluster Autoscaler:ノードの利用率と保留中のポッドに基づいて、クラスタのワーカーノード数を調整
  • Karpenter:クラスタオートスケーラーに代わるオープンソースの代替手段

当社のコンピューティングフリートは、主にKubernetesクラスタ上で稼働しており、目的別に特定のインスタンスタイプを基盤とするさまざまなノードプールで構成されています。その中には、(その上で実行されるワークロードの特性から)特定の社内チームが所有するものもあれば、複数のチームで共有するものもあります。どちらのケースでも、ワークロードの特性に応じてより適したインスタンスタイプを選択するために、各チームで独自のオートスケール実験を行うことを奨励しています。実験を行う際には、HPA(並列ポッドオートスケーラー)とクラスタオートスケーラー(ノードグループにノードを追加する場合)の動作を確認する必要があります。このクエリと、上記のリソース使用量のセクションにあるNRQLクエリを使って、ワークポッド数をモニターし、HPAの動作を評価します。

SELECT max(podsAvailable) FROM K8sDeploymentSample FACET clusterName TIMESERIES LIMIT 100 WHERE (namespace = 'your_namespace') AND (deploymentName = 'your_deploymentName') SINCE 30 minutes AGO

以下のグラフは、各行が特定のクラスタ内のポッドインスタンス数を表しています。ここでは、HPAが適切なサイジングアクションを実行し、サービスのインスタンスを自動的に減少させるという仮説が確認できました:

 

さらに、InfrastructureEventデータを使用して、クラスタオートスケーラーによる決定に基づいてノードを追加または削除したときに、クラスタがどのように動作するかを確認するNRQLクエリも紹介します:

FROM InfrastructureEvent select uniqueCount(event.metadata.uid)where event.reason IN ('RegisteredNode', 'RemovingNode') facet event.reason TIMESERIES MAX since 1 day ago limit max

 

この観察とHPAアクションを組み合わせると、全体的な自動スケーリングの決定と、クラスタオートスケーラーがワークロードの急増にどれだけ迅速に対応できるかを把握できます。

最後に、クラスタオートスケーラーは広く使用されているツールですが、ワークロードのスケジューリングに関しては限界があり、ビンパッキングの問題を効率的な方法で解決することができません。ビンパッキングでは、無駄なスペースを最小限に抑える方法で、サイズの異なるアイテムを一定数のビンやコンテナに詰め込む必要があります。可能な限り少ない数のビンを使用する効率的な配置を見つけることが目標です。 

ここでは、クラスタの動的サイズ変更を可能にし、さらにコスト効率に優れた機能を追加する別のオプションを紹介します。

クラスタオートスケーラーに代わるもの:Karpenter 

クラスタオートスケーラーは、クラスタの各ノードプールのワーカー数を動的にサイズ変更するための、Kubernetesの「事実上の」標準です。この制限は、特定のインスタンスタイプ(とハードウェア特性)に固定されたままなので、クラスタオートスケーラーアルゴリズムは正しく計算を行うことができます。しかし、1時間ごとにデータを評価し、コスト的に最も有利なインスタンスタイプを特定し、自動対応する異なるインスタンスタイプのワークロードを統合できることを想像してみてください。オープンソースプロジェクトのKarpenterは、クラスタオートスケーラーに取って代わり、コスト最適化の機会を提供します。スケジュールされていないポッドの集約リソース要求を監視し、ノードの起動と終了(ポッド退避イベントの開始)を決定して、スケジューリングのレイテンシとインフラストラクチャのコストを最小限に抑えます。さらに、より費用対効果の高い割り当てシナリオがあるときはいつでもノードを統合するため、ワークロードに合わせて「カスタマイズされた」インスタンスタイプをデプロイすることで、ビンパッキングの問題にも取り組みます。

1年以上前から我々はKarpenterを利用しており、ワークロードを割り当てる際にKarpenterがどのようなパフォーマンスを発揮しているかを観察するために、K8sNodeSampleイベントによって公開されるメトリクスを活用しています。ノードプールを比較するには、このNRQLクエリを使って(WHERE句を使うことにより)フィルタリングし、興味のあるものを含めることができます:

FROM K8sNodeSample SELECT (average(cpuRequestedCores) * cardinality(nodeName))/ (average(allocatableCpuCores) * cardinality(nodeName))

Karpenterでバックアップされたプールと、非Karpenterプール(Cluster APIで管理され、クラスタオートスケーラーが有効)のビンパッキング有効率の経時的な分析を図に示します。ビンのパッキング効率はKarpenterプールの方が高く(約84 %)、その結果、リソースをより効率的に配分していることがわかります: 

自動スケーリングツールとしてKarpenterを導入する際には、Kubernetesのイベントインテグレーションからアクセスできる「Evicted Pods」と「Nodes Consolidationイベント」を確認することを検討してください。以下のようなNRQLクエリを使って、これらのイベントを確認し、Karpenterが過去1時間でどのように実行されたかを観察できます:

FROM InfrastructureEvent SELECT uniqueCount(event.involvedObject.name)AS 'Pods Evicted' WHERE event.source.component = 'karpenter' WHERE event.reason IN ('Evict', 'Evicted') AND event.message = 'Evicted pod' LIMIT MAX COMPARE WITH 24 hour ago

FROM InfrastructureEvent SELECT uniqueCount(event.involvedObject.name)as 'Node Consolidation Events' WHERE event.source.component = 'karpenter' WHERE event.reason IN ('ConsolidateTerminateNode', 'ConsolidateLaunchNode', 'DeprovisioningTerminating') LIMIT MAX COMPARE WITH 24 hour ago

出力は次のようになります:

このデータに基づいて、Karpenterの動作がサービスの信頼性にどのような影響を及ぼしているかを評価し(多くのポッドが退避させられているため)、Karpenterの設定を調整してノード統合イベントに関するKarpenterの積極性を整理する必要があります。

クラスタでKarpenterを使用して、特定のインスタンスタイプを使用するだけで、コストを15 %以上削減することができました。Karpenterが管理するノードプールのインスタンスタイプ(「混合インスタンスプール」と呼ばれる)を追加するにつれて、この割合は増加していくと予想されます。

x86とARMアーキテクチャーの比較

大きなコスト節約に繋がるもうひとつの最適化の手段は、さまざまなプロセッサを評価することです。多くの人がx86で稼働している一方で、当社は特定のワークロードにARMを活用することでコスト削減を実現しています。ARMプロセッサは現在、ワットあたりのパフォーマンスでIntelより優位に立っており、そのため特定の計算タスクに対してよりエネルギー効率が高くなります。その結果、1時間当たりのインスタンスコストが下がり、コスト削減と二酸化炭素排出量の削減が可能になります。

コスト削減の可能性は、ワークロードの特性に大きく依存します。そのため、まず適切な潜在的ワークロードを見極めることが重要です。これは、次のメトリクスを確認することで実現できます。

  • CPU使用率:CPU使用率が高いということは、ARMに変換するのに適したサービスである可能性が高い
  • ポッド数:ポッド数が安定しているということは、変換するのに適したサービスである可能性が高い 
  • サービス固有のメトリクス:例えば、httpサービスの場合、インスタンスタイプを変えながら、1分あたりのリクエスト数(多いほど良い)がサービスにどのような影響を与えるかを確認する

以下の例は、異なるCPU特性を持つ2つの異なるクラスタで実行された、サービスのCPU使用率を示しています。20、40、60人のユーザーに対して負荷テストを繰り返した結果、ARMインスタンスがこの特定のワークロードに適していないことが確認されました。

ワークロードが適合するかどうかを判断するために、ARMインスタンスンス上で実行されているサービスにパフォーマンス低下が発生していないことを確認し、コストを比較します。同様のパフォーマンス結果を得るために必要なx86ノードとARMインスタンスンスの数(前のセクションのNRQLクエリを使用可)と、各オプションのコストを計算する必要があります。 

※Easter egg:ビジネスがAWSで運用され、規制遵守のためにデータの暗号化が必要な場合、Graviton 3インスタンス(AWSが提供するARMインスタンス)とAmazon Corretto Crypt Providerライブラリを組み合わせることで、そのようなワークロードにARMインスタンスを使用するための最適なパフォーマンスが提供されます。

スポットインスタンス

最後に提案する最適化は、スポットインスタンスを活用することです。スポットインスタンスは、余分な計算容量を使用する計算インスタンス(クラウドプロバイダーの用語によってはEC2または仮想マシン)であるため、価格が低くなる傾向があります。利用率の低い計算インスタンスを大幅な割引価格でクラウドプロバイダーにリクエストすることは可能ですが、その代償として、予告なしに中断されるリスクがあります。そうなった場合、ワークロードを空いているインスタンスに移行するまでに少なくとも2分かかります。スポット価格とは、スポットインスタンスの1時間あたりのコストであり、各クラウドプロバイダーは、各アベイラビリティゾーンやリージョンにおいて、インスタンスタイプごとにスポット価格を決定します。この価格は、長期的なスポットインスタンスの需給と供給に基づいて、徐々に変更されます。そのため、キャパシティが空いていれば、スポットインスタンスはいつでも実行されるということを考慮するのが非常に重要です。

これらの結果、ワークロードが変動しやすく、アプリケーションの実行時間やアプリケーションの中断に柔軟に対応できる場合、コスト効率の高い選択肢となります。優れたワークロードの例をいくつか挙げます。

  • 「ホットスペア」インスタンス。この例として、CI/CDエージェントなどのワークロードが挙げられます。
  • 中断しやすいOLTP(オンライントランザクション処理)のワークロードも、スポットインスタンスの有力な候補になる可能性があります。
  • タイムシフト可能なワークロード(バッチ処理、cronスタイルの実行)。

スポットインスタンスの動作を理解するために、以下を分析します。

  • K8sNodeSampleのデータに基づく、スポットインスタンスの使用状況に関する全体的なデータ
  • Kubernetesノードイベント、InfrastructureEvent データに基づくサービス中断とノード終了ハンドラーのイベントを追跡するのに便利です

すでに述べたように、各スポットのインスタンスタイプがクラウドプロバイダーによってどのように割り込まれるのか、またアベイラビリティゾーン間に大きな差異があるのかを観察することが非常に重要です。

これらを実現するために役立つNRQLクエリには、以下のようなものがあります:

  • インスタンスタイプごとの中断:

FROM InfrastructureEvent select uniqueCount(event.metadata.uid)where event.source.component = 'aws-node-termination-handler' and event.reason in ('RebalanceRecommendation', 'ScheduledEvent', 'ASGLifecycle', 'SpotInterruption', 'StateChange', 'UnknownInterruption') facet event.reason,`event.metadata.annotations.instance-type`timeseries max limit max since 1 day ago

  • AZごとの中断:

FROM InfrastructureEvent select uniqueCount(event.metadata.uid)where event.source.component = 'aws-node-termination-handler' and event.reason in ('RebalanceRecommendation', 'ScheduledEvent', 'ASGLifecycle', 'SpotInterruption', 'StateChange', 'UnknownInterruption') facet event.reason,`event.metadata.annotations.availability-zone`timeseries max limit max since 1 day ago

まとめ

費用対効果の実現は、クラウドで事業を展開する企業にとって最優先事項のひとつです。この投稿では、適切なサイジング、自動スケーリング、スポットインスタンスやARMプロセッサの活用など、さまざまな方法とアプローチを検討しました。その中には非常に困難なものもありますが、New Relicではこれらのテクニックを応用して、計算コストを削減しました。 

リソースを動的に調整し、過剰なプロビジョニングを防ぎ、Karpenterのような革新的なソリューションを採用することで、企業はパフォーマンスと信頼性を向上させるだけでなく、クラウド運用における大幅かつ持続可能なコスト削減を実現できます。

さらにオブザーバビリティの実践は、継続的な監視と微調整を可能にし、リソースの有効活用が保証されます。これらの対策を組み合わせることで、アプリケーションパフォーマンスと信頼性が向上するだけでなく、合理的でコスト効率の高いクラウドインフラストラクチャを実現し、リソースの使用量を実際の要件に合わせることで、クラウド全体の支出を削減できます。