クラスタ オートスケーラーがスケールダウンしない問題のトラブルシューティング


このページでは、クラスタ オートスケーラーが Google Kubernetes Engine(GKE)ノードをスケールダウンできない問題を診断して解決する方法について説明します。

このページは、アプリやサービスで発生した予期しない状況やネガティブな状況を解決したいアプリ デベロッパー、プロダクトやサービスの提供の中断を防ぎたいプラットフォーム管理者およびオペレーターを対象としています。

クラスタ オートスケーラーがノードをスケールダウンするタイミングを理解する

トラブルシューティングの手順に進む前に、クラスタ オートスケーラーがノードのスケールダウンを試みるタイミングを理解しておくと役に立ちます。スケールダウンの必要がない場合、クラスタ オートスケーラーはスケールダウンしません。

クラスタ オートスケーラーは使用率係数を計算し、ノードが十分に使用されておらず、スケールダウンの対象となるかどうかを判断します。使用率係数は、ノード上の Pod によってリクエストされた vCPU とメモリの量を、ノードで割り当て可能な vCPU とメモリの量で除算して計算されます。

クラスタ オートスケーラーは 10 秒ごとにノードの使用率係数をチェックし、必要なしきい値を下回っていないかどうかを確認します。balanced 自動スケーリング プロファイルを使用している場合、使用率係数のしきい値は 0.5 です。optimize-utilization プロファイルを使用している場合、使用率係数は異なります。使用率係数が vCPU とメモリの両方で必要なしきい値を下回ると、クラスタ オートスケーラーはノードが十分に使用されていないと見なします。

ノードが十分に使用されていない場合、クラスタ オートスケーラーはノードを削除対象としてマークし、次の 10 分間、ノードをモニタリングして、使用率係数が必要なしきい値を下回っていることを確認します。10 分経過してもノードが十分に使用されていない場合、クラスタ オートスケーラーはノードを削除します。

例: 使用率係数の計算

クラスタ オートスケーラーが有効になっているクラスタがあり、balanced 自動スケーリング プロファイルを使用しています。このクラスタのノードは、4 つの vCPU と 16 GB のメモリを提供する e2-standard-4 マシンタイプでプロビジョニングされています。このノードの Pod は 0.5 vCPU と 10 GB のメモリをリクエストするため、クラスタ オートスケーラーは次のように使用率係数を計算します。

  • vCPU 使用率係数: 0.5 vCPU / 4 vCPU = 0.125
  • メモリ使用率係数: 10 GB / 16 GB = 0.625

このシナリオでは、メモリ使用率係数(0.625)がしきい値(0.5)を超えているため、クラスタ オートスケーラーは、このノードが十分に使用されていないとみなしません。vCPU の使用率は低くても、メモリ使用量が多いため、スケールダウンを行わず、Pod のワークロードに十分なリソースを確保します。

問題の原因が制限事項であるかどうかを確認する

クラスタの使用率が 10 分以上低く、スケールダウンしていない場合は、問題がクラスタ オートスケーラーの制限によるものではないことを確認します。

エラーを確認する

多くの場合、問題が制限によるものでなければ、エラー メッセージから原因を診断できます。

通知でエラーを確認する

問題が発生して 72 時間以内であれば、Google Cloud コンソールでエラーに関する通知を確認します。これらの通知では、クラスタ オートスケーラーがスケールダウンしなかった理由について貴重な分析情報が提供されます。また、エラーを解決する方法や、関連するログを表示して詳細を調査する方法に関するアドバイスも提供されます。

Google Cloud コンソールで通知を表示するには、次の操作を行います。

  1. Google Cloud コンソールで、[Kubernetes クラスタ] ページに移動します。

    Kubernetes クラスタに移動

  2. [通知] 列を確認します。次の通知は、スケールダウンの問題に関連しています。

    • Can't scale down nodes
    • Scale down blocked by pod
  3. 関連する通知をクリックすると、問題の原因とその解決に推奨される対応方法の詳細が表示されます。

  4. 省略可: このイベントのログを表示するには、[ログ] をクリックします。この操作を行うと、ログ エクスプローラが開き、スケーリング イベントの詳細な調査に役立つクエリが自動的に設定されます。スケールダウン イベントの仕組みの詳細については、クラスタ オートスケーラー イベントを表示するをご覧ください。

通知に表示されたアドバイスを試しても問題が解決しない場合は、エラー メッセージの表を参照してください。

イベントでエラーを確認する

検出された問題が 72 時間以上前に発生している場合は、Cloud Logging でイベントを表示します。多くの場合、発生したエラーはイベントに記録されます。

Google Cloud コンソールでクラスタ オートスケーラーのログを表示するには、次の操作を行います。

  1. Google Cloud コンソールで、[Kubernetes クラスタ] ページに移動します。

    Kubernetes クラスタに移動

  2. 調査するクラスタの名前を選択して、[クラスタの詳細] ページを表示します。

  3. [クラスタの詳細] ページで、[ログ] タブをクリックします。

  4. [ログ] タブで、[オートスケーラー ログ] タブをクリックしてログを表示します。

  5. 省略可: 高度なフィルタを適用して結果を絞り込むには、ページの右側にある矢印のボタンをクリックして、ログ エクスプローラでログを表示します。

スケールダウン イベントの詳細については、クラスタ オートスケーラー イベントを表示するをご覧ください。Cloud Logging の使用方法の例については、次のトラブルシューティングの例をご覧ください。

例: 72 時間以上経過した問題のトラブルシューティング

次の例は、クラスタがスケールダウンされない問題を調査して解決する方法を示しています。

シナリオ:

1 週間前、GKE Enterprise ダッシュボードで、クラスタが CPU とメモリの 10% しか使用していないことに気付きました。使用率が低いにもかかわらず、クラスタ オートスケーラーが想定どおりにノードを削除しませんでした。ダッシュボードを見ると、問題は解決したようですが、再発を防ぐために何が起きたのかを調べることにします。

調査:

  1. 問題が発生してから 72 時間以上経過しているため、通知メッセージを確認するのではなく、Cloud Logging を使用して問題を調査します。
  2. Cloud Logging で、イベントのエラーを表示するの説明に従って、クラスタ オートスケーラー イベントのロギングの詳細を確認します。
  3. 調査対象のクラスタに属するノードを含む scaleDown イベントnodesToBeRemoved フィールドで検索します。特定の JSON フィールド値によるフィルタリングなど、ログエントリをフィルタリングできます。詳細については、高度なログクエリをご覧ください。
  4. scaleDown イベントは見つかりませんでした。ただし、scaleDown イベントが見つかった場合は、関連する eventId を含む eventResult イベントを検索できます。errorMsg フィールドでエラーを検索できます。
  5. 調査を続けるため、nodes フィールドに調査対象のノードを含む noScaleDown イベントを検索します。

    ノードがスケールダウンされない理由を含む noScaleDown イベントが見つかりました。メッセージ ID は "no.scale.down.node.pod.not.backed.by.controller" で、1 つのパラメータ "test-single-pod" が指定されています。

解決策:

エラー メッセージの表を調べたところ、このメッセージは、Pod がコントローラによってサポートされていないため、スケールダウンをブロックしていることがわかりました。Pod に "cluster-autoscaler.kubernetes.io/safe-to-evict": "true" アノテーションを追加することが解決策の一つであることがわかりました。test-single-pod を調査すると、同僚がアノテーションを追加し、アノテーションを適用した後、クラスタ オートスケーラーがクラスタを正しくスケールダウンしたことがわかりました。問題が再発しないように、安全に追加できるすべての Pod にアノテーションを追加することにしました。

スケールダウン エラーを解決する

エラーを特定したら、次の表を使用して、エラーの原因と解決方法を確認します。

ScaleDown エラー

scaleDown イベントのエラーイベント メッセージは、対応する eventResult イベントの resultInfo.results[].errorMsg フィールドにあります。

イベント メッセージ 詳細 パラメータ 緩和策
"scale.down.error.failed.to.mark.to.be.deleted" ノードを削除対象としてマークできませんでした。 失敗したノード名。 このメッセージは一時的なものです。 問題が解決しない場合は、Cloud カスタマーケアにお問い合わせのうえ、さらに調査を依頼してください。
"scale.down.error.failed.to.evict.pods" 一部の Pod をノードから強制排除できなかったため、クラスタ オートスケーラーはスケールダウンできません。 失敗したノード名。 Pod の PodDisruptionBudget を確認し、許容可能な場合は、ルールでアプリケーション レプリカの強制排除が許可されていることを確認します。詳細については、Kubernetes ドキュメントでアプリケーションの停止予算の指定をご覧ください。
"scale.down.error.failed.to.delete.node.min.size.reached" クラスタがすでに最小サイズになっているため、ノードを削除できず、クラスタ オートスケーラーはスケールダウンできません。 失敗したノード名。 ノードプールの自動スケーリングに設定された最小値を確認し、必要に応じて設定を調整します。詳細については、エラー: Nodes in the cluster have reached minimum size をご覧ください。

noScaleDown イベントが発生する理由

noScaleDown イベントは、クラスタ オートスケーラーによる削除がブロックされているノードがある場合に生成されます。noScaleDown イベントはベスト エフォート型であり、考えられるすべてのケースに対応しているわけではありません。

NoScaleDown の最上位の理由

noScaleDown イベントの最上位の理由メッセージが noDecisionStatus.noScaleDown.reason フィールドに表示されます。このメッセージには、クラスタ オートスケーラーがクラスタをスケールダウンできない最上位の理由が含まれています。

イベント メッセージ 詳細 緩和策
"no.scale.down.in.backoff" スケールダウンがバックオフ期間中(一時的にブロック中)であるため、クラスタ オートスケーラーはスケールダウンできません。

このメッセージは一時的なもので、直近でスケールアップ イベントがあった場合に発生する可能性があります。

メッセージが続く場合は、Cloud カスタマーケアにお問い合わせください。

"no.scale.down.in.progress"

前のスケールダウンがまだ進行中であるため、クラスタ オートスケーラーはスケールダウンできません。

Pod はいずれ削除されるため、このメッセージは一過性のものになるはずです。このメッセージが頻繁に発生する場合は、スケールダウンをブロックしている Pod の終了猶予期間を確認します。解決を早めるため、不要になった Pod を削除することもできます。

NoScaleDown のノードレベルの理由

noScaleDown イベントのノードレベルの理由メッセージが noDecisionStatus.noScaleDown.nodes[].reason field に表示されます。このメッセージには、クラスタ オートスケーラーが特定のノードを削除できない理由が含まれています。

イベント メッセージ 詳細 パラメータ 緩和策
"no.scale.down.node.scale.down.disabled.annotation" ノードに cluster-autoscaler.kubernetes.io/scale-down-disabled: true アノテーションが付いているため、クラスタ オートスケーラーはノードプールからノードを削除できません。 なし クラスタ オートスケーラーは、このアノテーションを持つノードの使用率を考慮せずにスキップします。このメッセージは、ノードの使用率に関係なくログに記録されます。クラスタ オートスケーラーでこれらのノードをスケールダウンする場合は、アノテーションを削除します。
"no.scale.down.node.node.group.min.size.reached"

ノードグループのサイズが最小サイズの上限を超えている場合、クラスタ オートスケーラーはスケールダウンできません。

これは、ノードを削除すると、ノードの自動プロビジョニング設定で定義されているクラスタ全体の最小リソース上限に違反するためです。

なし ノードプールの自動スケーリングに設定された最小値を確認します。クラスタ オートスケーラーでこのノードをスケールダウンする場合は、最小値を調整します。
"no.scale.down.node.minimal.resource.limits.exceeded"

クラスタ オートスケーラーは、クラスタ全体の最小リソース上限に違反するため、ノードをスケールダウンできません。

これらは、ノードの自動プロビジョニングに設定されているリソースの上限です。

なし メモリと vCPU の制限を確認し、クラスタ オートスケーラーでこのノードをスケールダウンする場合は、 制限を下げます
"no.scale.down.node.no.place.to.move.pods" Pod を移動する場所がないため、クラスタ オートスケーラーはスケールダウンできません。 なし Pod の再スケジュールが予想される場合は、使用率の低いノード上の Pod のスケジューリング要件を確認し、クラスタ内の別のノードに移動できるかどうかを判断します。詳細については、エラー: No place to move Pods をご覧ください。
"no.scale.down.node.pod.not.backed.by.controller"

Pod がコントローラによってサポートされていないため、スケールダウンがブロックされています。

具体的には、認識されたコントローラがない Pod が原因で、クラスタ オートスケーラーは使用率の低いノードをスケールダウンできません。使用できるコントローラには、ReplicationController、DaemonSet、Job、StatefulSet、ReplicaSet などがあります。

ブロックしている Pod の名前。 Pod にアノテーション "cluster-autoscaler.kubernetes.io/safe-to-evict": "true" を設定するか、許容されるコントローラを定義します。
"no.scale.down.node.pod.not.safe.to.evict.annotation" ノードの Pod に safe-to-evict=false アノテーションがあります。 ブロックしている Pod の名前。 Pod を安全に強制排除できる場合は、Pod のマニフェストを編集して、アノテーションを "cluster-autoscaler.kubernetes.io/safe-to-evict": "true" に更新します。
"no.scale.down.node.pod.kube.system.unmovable" Pod が DaemonSet 以外で、ミラーリングがなく、kube-system Namespace に PodDisruptionBudget のない Pod であるため、スケールダウンがブロックされています。 ブロックしている Pod の名前。

デフォルトでは、kube-system Namespace 内の Pod はクラスタ オートスケーラーによって削除されません。

この問題を解決するには、kube-system Pod に PodDisruptionBudget を追加するか、ノードプールの taint と toleration を組み合わせて使用し、kube-system Pod をアプリケーション Pod から分離します。詳細については、エラー: kube-system Pod unmoveable をご覧ください。

"no.scale.down.node.pod.not.enough.pdb" Pod に十分な PodDisruptionBudget がないため、スケールダウンがブロックされています。 ブロックしている Pod の名前。 Pod の PodDisruptionBudget を確認し、制限を緩和することを検討します。詳細については、エラー: Not enough PodDisruptionBudget をご覧ください。
"no.scale.down.node.pod.controller.not.found" Pod のコントローラ(Deployment や ReplicaSet など)が見つからないため、スケールダウンがブロックされています。 なし コントローラが削除された後に Pod を動作したままにしているアクションを特定するには、ログを確認します。この問題を解決するには、Pod を手動で削除します。
"no.scale.down.node.pod.unexpected.error" 予期しないエラーが発生したため、Pod のスケールダウンがブロックされています。 なし このエラーの根本原因は不明です。 詳細な調査については、Cloud カスタマーケアにお問い合わせください。

詳細な調査を行う

以降のセクションでは、ログ エクスプローラと gcpdiag を使用してエラーに関する追加の分析情報を取得する方法について説明します。

ログ エクスプローラでエラーを調査する

エラー メッセージをさらに調査する場合は、エラーに固有のログを確認します。

  1. Google Cloud コンソールで、[ログ エクスプローラ] ページに移動します。

    [ログ エクスプローラ] に移動

  2. クエリペインに次のクエリを入力します。

    resource.type="k8s_cluster"
    log_id("container.googleapis.com/cluster-autoscaler-visibility")
    jsonPayload.resultInfo.results.errorMsg.messageId="ERROR_MESSAGE"
    

    ERROR_MESSAGE は、調査するメッセージに置き換えます。例: scale.down.error.failed.to.delete.node.min.size.reached

  3. [クエリを実行] をクリックします。

gcpdiag を使用してエラーをデバッグする

gcpdiag は、Google Cloud テクニカル エンジニアのサポートを受けて作成されたオープンソース ツールです。正式にサポートされている Google Cloud プロダクトではありません。

次のいずれかのエラー メッセージが表示された場合は、gcpdiag を使用して問題のトラブルシューティングを行うことができます。

  • scale.down.error.failed.to.evict.pods
  • no.scale.down.node.node.group.min.size.reached

gcpdiag ツールのフラグの一覧と説明については、gcpdiag の使用手順をご覧ください。

複雑なスケールダウン エラーを解決する

以降のセクションでは、緩和策に複数のステップが含まれるエラーと、クラスタ オートスケーラー イベント メッセージが関連付けられていないエラーを解決する方法について説明します。

エラー: Nodes in the cluster have reached minimum size

次のエラーは、クラスタ内のノード数がすでに最小サイズに達しているため、クラスタ オートスケーラーがノードを削除できなかったことを示します。

通知

クラスタ オートスケーラーの最小リソースの上限に達したため、使用率の低いノードのスケールダウンがブロックされています。

イベント

"scale.down.error.failed.to.delete.node.min.size.reached"

この問題を解決するには、自動スケーリングの最小上限を確認して更新します。

  1. Google Cloud コンソールで、[Kubernetes クラスタ] ページに移動します。

    Kubernetes クラスタに移動

  2. 通知または Cloud Logging で特定されたクラスタの名前をクリックします。

  3. [クラスタの詳細] ページで、[ノード] タブに移動します。

  4. [ノード数] 列の値を確認し、[自動スケーリング] 列に表示されている最小ノード数と比較します。たとえば、[自動スケーリング] 列に 4~6 個のノードが表示され、ノードプール内のノード数が 4 個の場合、ノードプール数はすでに最小サイズに等しいため、クラスタ オートスケーラーはノードをさらにスケールダウンできません。

  5. 構成が正しく、ノード数の値が自動スケーリングに定義された最小値と等しい場合、クラスタ オートスケーラーは意図したとおりに機能しています。ノードの最小数がニーズに対して大きすぎる場合は、ノードをスケールダウンできるように最小サイズを減らす必要があります。

エラー: No place to move Pods

クラスタ オートスケーラーがノードをスケールダウンしようとして、そのノードの Pod を別のノードに移動できないと、次のエラーが発生します。

通知

クラスタ内の別のノードに移動できない Pod があるため、使用率の低いノードのスケールダウンはブロックされます。

イベント

"no.scale.down.node.no.place.to.move.pods"

この Pod の再スケジュールが不要な場合、このメッセージは想定内のものです。変更の必要はありません。Pod の再スケジュールが必要な場合は、Pod のマニフェストの pod.spec block で次の定義を確認します。

  • NodeAffinity: 使用率の低いノード上の Pod のスケジューリング要件を確認します。これらの要件を確認するには、Pod マニフェストで NodeAffinity ルールまたは NodeSelector ルールを探します。Pod に nodeSelector が定義されていて、このセレクタに一致する(他のノードプールの)ノードがクラスタにない場合、クラスタ オートスケーラーは Pod を別のノードに移動できず、使用率の低いノードを削除できなくなります。
  • maxPodConstraint: maxPodConstraint がデフォルトの 110 以外の数値に構成されている場合は、意図した変更かどうかを確認します。この値を下げると、問題が発生する可能性が高くなります。クラスタ内の他のすべてのノードが maxPodConstraint で定義された値にすでに達しており、新しい Pod をスケジュールするスペースがない場合、クラスタ オートスケーラーは Pod を他のノードに再スケジュールできません。maxPodConstraint の値を増やすと、ノードにスケジュールされる Pod の数が増え、クラスタ オートスケーラーが Pod を再スケジュールし、使用率の低いノードをスケールダウンするためのスペースを確保できます。maxPodConstraint を定義する場合は、各ノードに約 10 個のシステム Pod があることに注意してください。
  • hostPort: Pod に hostPort を指定すると、そのノードで実行できる Pod は 1 つだけになります。これにより、ノードの Pod がすでに使用されている場合、Pod を別のノードに移動できないため、クラスタ オートスケーラーがノード数を減らせなくなる可能性があります。これは想定された動作です。

エラー: kube-system Pod unmoveable

システム Pod がスケールダウンを妨げている場合は、次のエラーが発生します。

通知

Pod が DaemonSet 以外で、ミラーリングがなく、kube-system Namespace に PodDisruptionBudget のない Pod であるため、スケールダウンがブロックされています。

イベント

"no.scale.down.node.pod.kube.system.unmovable"

kube-system Namespace 内の Pod はシステム Pod と見なされます。デフォルトでは、クラスタ オートスケーラーは kube-system Namespace 内の Pod を削除しません。

このエラーを解決するには、次のいずれかの解決策を選択します。

  • kube-system Pod に PodDisruptionBudget を追加します。kube-system Pod に PodDisruptionBudget を手動で追加する方法の詳細については、Kubernetes クラスタ オートスケーラーに関するよくある質問をご覧ください。

    PodDisruptionBudget を作成すると、システム ワークロードの可用性に影響し、クラスタのダウンタイムが発生する可能性があります。クラスタ オートスケーラーは、スケールダウン プロセス中に、これらのシステム ワークロードを別のワーカーノードで再スケジュールします。

  • ノードプールの taint と toleration を組み合わせて使用し、kube-system Pod をアプリケーション Pod から分離します。詳細については、GKE でのノードの自動プロビジョニングをご覧ください。

ノードに kube-system Pod があることを確認する

ノードで kube-system Pod が実行されているかどうか不明な場合は、次の手順で確認します。

  1. Google Cloud コンソールの [ログ エクスプローラ] ページに移動します。

    [ログ エクスプローラ] に移動

  2. [クエリビルダー] をクリックします。

  3. 次のクエリを使用して、すべてのネットワーク ポリシーのログレコードを検索します。

    - resource.labels.location="CLUSTER_LOCATION"
    resource.labels.cluster_name="CLUSTER_NAME"
    logName="projects/PROJECT_ID/logs/container.googleapis.com%2Fcluster-autoscaler-visibility"
    jsonPayload.noDecisionStatus.noScaleDown.nodes.node.mig.nodepool="NODE_POOL_NAME"
    

    次のように置き換えます。

    • CLUSTER_LOCATION: クラスタが配置されているリージョン。
    • CLUSTER_NAME: クラスタの名前。
    • PROJECT_ID: クラスタが属するプロジェクトの ID。
    • NODE_POOL_NAME: ノードプールの名前。

    ノードプールで実行されている kube-system Pod がある場合、出力には次が含まれます。

    "no.scale.down.node.pod.kube.system.unmovable"
    

エラー: Not enough PodDisruptionBudget

PodDisruptionBudget がスケールダウンを妨げている場合、次のエラーが発生します。

通知

Pod を削除するために十分な Pod Disruption Budget がないノード上で実行されている Pod があるため、使用率の低いノードのスケールダウンはブロックされます。

イベント

NoScaleDownNodePodNotEnoughPdb: "no.scale.down.node.pod.not.enough.pdb"

PodDisruptionBudget の制限が厳しいかどうかを確認するには、その設定を確認します。

kubectl get pdb --all-namespaces

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

NAMESPACE        NAME    MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
example-app-one  one_pdb       N/A             1                 1               12d
example-app-two  two_pdb       N/A             0                 0               12d

この例では、two_pdb ラベルセレクタに一致する Pod はクラスタ オートスケーラーによって強制排除されません。この PodDisruptionBudget の maxUnavailable: 0 設定では、すべてのレプリカが常に使用可能である必要があります。また、disruptionsAllowed: 0 はこれらの Pod の中断を禁止します。そのため、これらの Pod を実行しているノードをスケールダウンすることはできません。スケールダウンすると中断が発生し、PodDisruptionBudget に違反します。

PodDisruptionBudget が意図したとおりに機能している場合は、これ以上の操作は必要ありません。使用率の低いノードの Pod を移動できるように PodDisruptionBudget を調整する場合は、PodDisruptionBudget のマニフェストを編集します。たとえば、maxUnavailable0 に設定した場合は、クラスタ オートスケーラーがスケールダウンできるように 1 に変更します。

問題: ノードが封鎖ステータスのままで削除されない

Google サービス アカウントに編集者ロールがないため、クラスタ オートスケーラーがノードプールのサイズを減らすことができないと、次のようなエラーが発生します。

Required 'compute.instanceGroups.update' permission for 'INSTANCE_GROUP_NAME'.

この問題の一般的な症状は、クラスタ オートスケーラーがノードプールのサイズを減らそうとしても、ノードのステータスが変更されないことです。

この問題を解決するには、デフォルトのサービス アカウント(PROJECT_NUMBER@cloudservices.gserviceaccount.com)にプロジェクトの編集者ロール(roles/editor)が付与されているかどうかを確認します。サービス アカウントにこのロールがない場合は、追加します。GKE は、このサービス アカウントを使用してプロジェクトのリソースを管理します。方法については、IAM ドキュメントの単一のロールを付与または取り消すをご覧ください。

次のステップ