高可用性アーキテクチャでは、ワークロードを複数のゾーンに均等に分散させることが重要です。本トピックでは、Container Service for Kubernetes (ACK) クラスターにおいて、ack-autoscaling-placeholder コンポーネントを使用して、ゾーンを意識した高速なエラスティックスケーリングを実現する方法について説明します。これにより、スケールアウト時にノードが適切なゾーンに同時に追加されます。
前提条件
スケールアウト対象の各ゾーンに、少なくとも 1 つの vSwitch を作成してください。詳細については、「vSwitch の作成と管理」をご参照ください。vSwitch を作成後、ノードプールの作成と管理時に選択できます。
仕組み
課題
単一のスケーリンググループ内で複数ゾーンの vSwitch を使用してノードプールを構成すると、Cluster Autoscaler はどのゾーンに新しいノードが必要かを特定できません。その結果、スケールアウトされたインスタンスが特定のゾーンに集中し、他のゾーンには分散されない可能性があります。これではマルチゾーンデプロイメントの目的が達成されません。
ソリューション
ACK では、ack-autoscaling-placeholder コンポーネントを使用してこの課題を解決します。このコンポーネントはリソース冗長性を利用して、マルチゾーンのエラスティックスケーリングを、複数のノードプールに対する同時指向性スケーリングに変換します。詳細については、「ack-autoscaling-placeholder を使用して Pod を数秒でスケーリングする」をご参照ください。このメカニズムは以下の 3 段階で動作します。
ゾーンごとにノードプールを作成し、ゾーンラベルを設定します。 各ノードプールには、所属ゾーンを識別するラベルが割り当てられます。
nodeSelector を使用してプレースホルダー Pod をデプロイします。
ack-autoscaling-placeholderコンポーネントは、ゾーンラベルに基づいて各ゾーンにプレースホルダー Pod をスケジュールします。これらのプレースホルダー Pod は、アプリケーション Pod よりも低い重みを持つ PriorityClass を使用します。アプリケーション Pod がプレースホルダー Pod をプリエンプト(横取り)します。 アプリケーション Pod が Pending 状態になると、既存ノード上の低優先度のプレースホルダー Pod を置き換えます。これにより、置き換えられたプレースホルダー Pod 自体が Pending 状態になります。プレースホルダー Pod は antiAffinity ではなく nodeSelector ベースのスケジューリングを使用しているため、Cluster Autoscaler は各 Pending プレースホルダー Pod が必要とする正確なゾーンを特定し、対応するノードプールに対して同時にスケールアウトをトリガーできます。
スケーリングフロー
次の図は、このアーキテクチャを使用して 2 つのゾーンで同時にスケーリングを行う仕組みを示しています。
ack-autoscaling-placeholderコンポーネントが各ゾーンにプレースホルダー Pod を作成します。プレースホルダー Pod は、実際のアプリケーション Pod よりもスケジューリング優先度が低くなっています。アプリケーション Pod が Pending 状態になると、すぐにプレースホルダー Pod をプリエンプトし、各ゾーンの既存ノード上にデプロイされます。プリエンプトされたプレースホルダー Pod はその後、Pending 状態になります。
プレースホルダー Pod が nodeSelector を使用してスケジュールされているため、Cluster Autoscaler は対応するゾーンに対して同時にスケールアウトを実行できます。
ステップ 1:ゾーンごとのノードプールの作成とカスタムノードラベルの設定
ACK コンソールにログインします。左側のナビゲーションウィンドウで、クラスター をクリックします。
クラスター ページで、管理対象のクラスターを検索し、その名前をクリックします。左側のナビゲーションウィンドウで、ノード > ノードプール を選択します。
ノードプールの作成 をクリックし、画面の指示に従ってノードプールを構成します。この例では、ゾーン I でオートスケーリングを有効にした auto-zone-I という名前のノードプールを作成します。以下の表は重要なパラメーターのみを示しています。詳細については、「ノードプールの作成と管理」をご参照ください。ノードプールリストで auto-zone-I ノードプールのステータスが アクティブ と表示されたら、ノードプールの作成は完了です。
パラメーター 説明 ノードプール名 auto-zone-I スケーリングモード 自動 を選択してオートスケーリングを有効にします。 vSwitch ゾーン I の vSwitch を選択します。 ノードラベル キー を available_zone、値 をiに設定します。上記の手順を繰り返して、オートスケーリングが必要な各ゾーンに対してオートスケーリングを有効にしたノードプールを作成します。

検証:ノードプールリストで、ゾーンごとのノードプールがすべて アクティブ ステータスであり、ゾーンラベルが正しく適用されていることを確認します。
ステップ 2:ack-autoscaling-placeholder のデプロイとプレースホルダーデプロイメントの構成
コンポーネントのデプロイ
ACK コンソールの左側ナビゲーションウィンドウで、マーケットプレイス > マーケットプレイス を選択します。
ack-autoscaling-placeholder を検索してクリックします。ack-autoscaling-placeholder ページで、デプロイ をクリックします。
クラスター ドロップダウンリストからクラスターを、名前空間 ドロップダウンリストから名前空間を選択し、次へ をクリックします。チャートバージョン ドロップダウンリストからチャートバージョンを選択し、パラメーター を構成して、OK をクリックします。コンポーネントのデプロイ後、左側ナビゲーションウィンドウで アプリケーション > Helm を選択すると、アプリケーションが デプロイ済み 状態であることを確認できます。
プレースホルダーデプロイメントを使用した Helm リリースの更新
詳細ページの左側ナビゲーションウィンドウで、アプリケーション > Helm を選択します。
Helm ページで、ack-autoscaling-placeholder-default の 操作 列にある 更新 をクリックします。
リリースの更新 パネルで、以下の例に基づいて YAML ファイルを更新し、OK をクリックします。ゾーンごとにプレースホルダーをデプロイし、ゾーンごとにプレースホルダーデプロイメントを定義します。この例では、ゾーン I、K、H にプレースホルダーデプロイメントを作成します。
deploymentsリスト内の各エントリは同じ構造に従います。以下に示す最初のエントリには完全な注釈が付与されていますが、以降のエントリはnameおよびnodeSelectorの値のみが異なります。以下の表は、各ゾーンで変更が必要なフィールドをまとめたものです。更新が成功すると、各ゾーン用のプレースホルダーデプロイメントが作成されます。フィールド ゾーン I ゾーン K ゾーン H nameack-place-holder-Iack-place-holder-Kack-place-holder-HnodeSelector{"avaliable_zone":i}{"avaliable_zone":k}{"avaliable_zone":h}deployments: # --- Zone I placeholder --- - affinity: {} annotations: {} containers: - image: registry-vpc.cn-beijing.aliyuncs.com/acs/pause:3.1 imagePullPolicy: IfNotPresent name: placeholder resources: requests: cpu: 3500m # CPU request for each placeholder pod. memory: 6 # Memory request for each placeholder pod. imagePullSecrets: {} labels: {} name: ack-place-holder-I # Deployment name. Use a unique suffix per zone. nodeSelector: {"avaliable_zone":i} # Must match the node label key and value from Step 1. replicaCount: 10 # Number of placeholder pods per zone. tolerations: [] # --- Zone K placeholder (same structure, different zone) --- - affinity: {} annotations: {} containers: - image: registry-vpc.cn-beijing.aliyuncs.com/acs/pause:3.1 imagePullPolicy: IfNotPresent name: placeholder resources: requests: cpu: 3500m memory: 6 imagePullSecrets: {} labels: {} name: ack-place-holder-K nodeSelector: {"avaliable_zone":k} replicaCount: 10 tolerations: [] # --- Zone H placeholder (same structure, different zone) --- - affinity: {} annotations: {} containers: - image: registry-vpc.cn-beijing.aliyuncs.com/acs/pause:3.1 imagePullPolicy: IfNotPresent name: placeholder resources: requests: cpu: 3500m memory: 6 imagePullSecrets: {} labels: {} name: ack-place-holder-H nodeSelector: {"avaliable_zone":h} replicaCount: 10 tolerations: [] fullnameOverride: "" nameOverride: "" podSecurityContext: {} priorityClassDefault: enabled: true name: default-priority-class value: -1
検証:アプリケーション > Helm を選択し、ack-autoscaling-placeholder-default が更新済みの構成で デプロイ済み 状態であることを確認します。
ステップ 3:ワークロード用の PriorityClass の作成
ワークロードの PriorityClass は、プレースホルダーの PriorityClass (-1) よりも高い値を持つ必要があります。これにより、アプリケーション Pod がプレースホルダー Pod をプリエンプトできます。以下の 2 つのオプションがあります。
オプション A:名前付き PriorityClass —
priorityClassNameを介して特定のワークロードに明示的に割り当てます。オプション B:グローバル PriorityClass — PriorityClass を指定しないすべての Pod に自動的に適用されます。
オプション A:名前付き PriorityClass
priorityClass.yaml という名前のファイルを作成し、以下の内容をコピーします。
apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: high-priority value: 1000000 # The priority value. Must be higher than the default priority value (-1) of the placeholder pods created in Step 2. globalDefault: false description: "This priority class should be used for XYZ service pods only."
オプション B:グローバル PriorityClass
各 Pod ごとに個別の PriorityClass が必要ない場合は、グローバル PriorityClass をデフォルトとして構成できます。これが有効になると、PriorityClass を指定していない Pod は自動的にこの優先度値を採用し、プリエンプションが自動的に有効になります。
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: global-high-priority
value: 1 # The priority value. Must be higher than the default priority value (-1) of the placeholder pods created in Step 2.
globalDefault: true
description: "This priority class should be used for XYZ service pods only."PriorityClass の適用
PriorityClass を作成します。期待される出力:
kubectl apply -f priorityClass.yamlpriorityclass.scheduling.k8s.io/high-priority created
検証:kubectl get priorityclassを実行し、high-priority(またはglobal-high-priority)が正しい優先度値とともにリストに表示されることを確認します。
ステップ 4:ワークロードの作成
この例ではゾーン I を使用します。
workload.yaml という名前のファイルを作成し、以下の内容をコピーします。
apiVersion: apps/v1 kind: Deployment metadata: name: placeholder-test labels: app: nginx spec: replicas: 1 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: nodeSelector: # Rules used to select nodes. avaliable_zone: "i" priorityClassName: high-priority # The PriorityClass configured in Step 3. Optional if global configuration is enabled. containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 resources: requests: cpu: 3 # The resource request of the workload. memory: 5ワークロードをデプロイします。期待される出力:デプロイ後、ワークロード > Pod ページで、ワークロードの PriorityClass がプレースホルダー Pod の PriorityClass よりも高いことを確認できます。プレースホルダー Pod はスケールアウトされたノード上で実行され、Cluster Autoscaler による同時スケーリングをトリガーして、次のワークロードスケーリングイベントに備えます。ノード > ノード を選択すると、ノードページでワークロード Pod が以前プレースホルダー Pod をホストしていたノード上で実行されていることを確認できます。
kubectl apply -f workload.yamldeployment.apps/placeholder-test created

検証:ワークロード > Pod ページで、ワークロード Pod が Running 状態であり、置き換えられたプレースホルダー Pod が正しいゾーンでノードのスケールアウトをトリガーしたことを確認します。