目次
はじめに
今年も、アドベントカレンダー担当としてブログを執筆する牛山です。
Amazon EKS Fargateを使用したアベイラビリティーゾーン(AZ)間でPodが分散配置されない事象に出くわしましたので解決する手法の内1つをご紹介します。
前提条件
Podが分散配置されない場合いくつかの原因が考えられます。
- 利用可能なプライベートサブネットがFargateプロファイルに2個以上設定されていない。
- Podの制約が正しくない。
Podを分散配置する場合次の手法が考えられます。
- Fargateプロファイルを複数作成し、それぞれにAZ別サブネットを分けて設定し、AZ別のFargateプロファイルをマニフェストから指定し配置する。
- topologySpreadConstraints制約を適用し、1つのFargateプロファイルに関連付けられているAZ間で分散配置する。
- nodeSelectorを使用し、AZを明示的に指定することで分散配置する。
- 起動時にどのPodsがAWS Fargateを使用するのかを定義する - Amazon EKS
Amazon EKS と Fargate は、Fargate プロファイルで定義されている各サブネットに Pods を分散させます。ただし、分散が偏ってしまう場合があります。均等な分散が必要な場合は、2 つの Fargate プロファイルを使用してください。2 つのレプリカをデプロイし、ダウンタイムを発生させたくないシナリオでは、均等な分散が重要になります。各プロファイルにはサブネットを 1 つだけ含めることをお勧めします。
今回のシナリオではFargateプロファイルに複数サブネットを設定している状態でPodsが分散されない場合を前提とします。
事象確認
どのような状態になっているかを事前に確認します。
Fargateプロファイル確認
Fargateプロファイルに複数サブネットが割当たっているか確認します。
aws eks describe-fargate-profile --region={リージョンを指定} --cluster-name {リージョンに存在するEKSクラスター名を指定} --fargate-profile-name {EKSクラスターに紐付いているFargateプロファイル名を指定} | jq .fargateProfile.subnets
[
  "subnet-086148daf8b59d473",
  "subnet-0afacdbe9b987b738"
]- 複数サブネットがFargateプロファイルにあたっているか確認するコマンド例です。
- コマンド例:aws eks describe-fargate-profile --region=us-east-1 --cluster-name mysystem-primary-cluster --fargate-profile-name mysystem-primary-fargate-profile-1 | jq .fargateProfile.subnets
複数サブネットが割当たっていることを確認できます。
各サブネットはAZ別に設定しており、プライベートサブネットからNatゲートウェイを介してインターネットへアクセスできるようにしています。
Podの配置状態確認
今回は、ロードバランサーを管理するコントローラー「AWS Load Balancer Controller」を例にします。
- AWS Load Balancer Controllerのデプロイは次のリンク手順にしたがっています。
手順に従い、AWS Load Balancer Controllerをデプロイした後の確認内容となります。
次のコマンドを使用してPodの配置状態を確認すると「IP」項目が、 10.0.3.99 と 10.0.3.39 になっています。
Fargateプロファイルに割当たっているサブネットは、AZ us-east-1aが「10.0.3.0/24」の範囲、AZ us-east-1cが「10.0.4.0/24」の範囲になっているので同一AZ内に配置されてしまっていることが分かります。
kubectl get po --all-namespaces -o wide
NAMESPACE     NAME                                            READY   STATUS    RESTARTS   AGE     IP           NODE                                 NOMINATED NODE   READINESS GATES
kube-system   aws-load-balancer-controller-84c8688dd-kpb8q     1/1     Running   0          87s     10.0.3.99    fargate-ip-10-0-3-99.ec2.internal    <none>           <none>
kube-system   aws-load-balancer-controller-84c8688dd-lxjc2     1/1     Running   0          87s     10.0.3.39    fargate-ip-10-0-3-39.ec2.internal    <none>           <none>→aws-load-balancer-controllerが同一AZ内に配置されていることが確認できます。
対処
前置き
同一AZに配置されているPodをAZ us-east-1aが「10.0.3.0/24」の範囲とAZ us-east-1cが「10.0.4.0/24」の範囲でそれぞれ1個ずつ配置されるよう変更します。
冒頭の1、2で紹介している方法は採用せず3の方法で分散させます。
理由としては次の通りです。
- 1の方法は、Fargateプロファイルを複数作成し、各AZを割り当てる方法になりますが、AZ分のFargateプロファイルが必要となりプロファイル名によってはAZの区別がつかないため採用しません。
- 2の方法は、topologySpreadConstraints制約によってAZ負荷分散する方法になりますが、EKS Fargateでは未対応なようですので採用しません。
3番目の方法「nodeSelectorを使用し、AZを明示的に指定することで分散配置」で対応します。
deploymentマニフェストを変更
複数AZ配置されるようにするため、deploymentマニフェストを変更します。
kubectl get deployment aws-load-balancer-controller -n kube-system -o yaml > aws-load-balancer-controller.yaml.org
 →変更前に現在のdeploymentマニフェストをバックアップします。
cp -p aws-load-balancer-controller.yaml.org aws-load-balancer-controller.yaml
 →違う名前でコピーします。
- Fargateでは、AZ負荷分散が保証されないため、AZを直接指定する
grep -q "nodeSelector:" aws-load-balancer-controller.yaml && sed -i_nodeSelector_us-east-1a '/nodeSelector:/a\
        topology.kubernetes.io/zone: us-east-1a' aws-load-balancer-controller.yaml || sed -i_nodeSelector_us-east-1a '/template:/,/spec:/ {/spec:/a\
      nodeSelector:\
        topology.kubernetes.io/zone: us-east-1a
}' aws-load-balancer-controller.yamlnodeSelectorがすでに存在する場合はその下に topology.kubernetes.io/zone: us-east-1a を追加し存在しない場合は template: spec: の下に挿入するワンライナーコマンドです。
us-east-1aは環境に合わせて変更します。
deployment名変更
deployment名をAZ名別にしなければいけないため変更します。
sed -i_deployment '/^metadata:/,/^  name:/s/^  name: aws-load-balancer-controller$/  name: aws-load-balancer-controller-us-east-1a/' aws-load-balancer-controller.yaml
 →metadata: -> name: 項目を変更します。
同じような箇所が複数見つかってしまうため完全一致で置き替えをするようにしています。
us-east-1aは環境に合わせて変更します。
レプリカ数変更
deploymentには複数のFargateを保持する仕組みがあり、今回はAZ別にdeploymentを定義するため、AZ毎に1つ保持する設定とするためレプリカ数を1に変更します。
sed -i_replicas 's/^\(\s*replicas:\s*\).*/\11/' aws-load-balancer-controller.yaml
us-east-1aのAZに1つFargateを保持するようになります。
us-east-1aは環境に合わせて読み替えてください。
ここまでの変更差分確認
diffコマンドで次のように「neme:、replicas、topology.kubernetes.io/zone: us-east-1a」の変更差分がでることを確認します。
diff -u aws-load-balancer-controller.yaml.org aws-load-balancer-controller.yaml
--- aws-load-balancer-controller.yaml.org       2024-11-26 15:48:35.526225300 +0900
+++ aws-load-balancer-controller.yaml   2024-11-26 15:55:10.081667800 +0900
@@ -13,13 +13,13 @@
     app.kubernetes.io/name: aws-load-balancer-controller
     app.kubernetes.io/version: v2.10.1
     helm.sh/chart: aws-load-balancer-controller-1.10.1
-  name: aws-load-balancer-controller
+  name: aws-load-balancer-controller-us-east-1a
   namespace: kube-system
   resourceVersion: "54470"
   uid: b87a8f35-784e-4517-83db-54f32f135b76
 spec:
   progressDeadlineSeconds: 600
-  replicas: 2
+  replicas: 1
   revisionHistoryLimit: 10
   selector:
     matchLabels:
@@ -40,6 +40,8 @@
         app.kubernetes.io/instance: aws-load-balancer-controller
         app.kubernetes.io/name: aws-load-balancer-controller
     spec:
+      nodeSelector:
+        topology.kubernetes.io/zone: us-east-1a
       affinity:
         podAntiAffinity:
           preferredDuringSchedulingIgnoredDuringExecution:
@@ -131,5 +133,5 @@
     type: Progressing
   observedGeneration: 1
   readyReplicas: 2
-  replicas: 2
+  replicas: 1
   updatedReplicas: 2cp -p aws-load-balancer-controller.yaml aws-load-balancer-controller-us-east-1a.yaml
 →ここまでの内容で、us-east-1a用のdeploymentができたのでバックアップします。
デプロイメント削除
適用前に既存デプロイメントの削除をおこないます。
デプロイメントを削除すると対象のFargateも消え、サービスに影響が出ますので注意してください。
kubectl delete -f aws-load-balancer-controller.yaml.org
deployment.apps "aws-load-balancer-controller" deleted変更前のorgファイルを指定し削除します。
適用
削除が完了しましたので、us-east-1a用に作成したdeploymentを適用します。
kubectl apply -f aws-load-balancer-controller.yaml
deployment.apps/aws-load-balancer-controller-us-east-1a createdaws-load-balancer-controller-us-east-1aというデプロイメント名で次のようになります。
kubectl get deployments.apps --all-namespaces
NAMESPACE     NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
kube-system   aws-load-balancer-controller-us-east-1a   1/1     1            1           2m18sFargateがデプロイされているAZを確認すると「AZ us-east-1a「10.0.3.0/24」の範囲」になっていることが次のコマンドより分かります。
kubectl get po --all-namespaces -o wide
NAMESPACE     NAME                                                       READY   STATUS    RESTARTS   AGE     IP           NODE                                 NOMINATED NODE   READINESS GATES
kube-system   aws-load-balancer-controller-us-east-1a-668bd7f9cd-qwtrd   1/1     Running   0          3m2s    10.0.3.186   fargate-ip-10-0-3-186.ec2.internal   <none>           <none>us-east-1c用のdeployment作成
- us-east-1cにデプロイするdeployment作成
sed -i 's/us-east-1a/us-east-1c/g' aws-load-balancer-controller.yaml
 →前段階行程でus-east-1aにした箇所をus-east-1cに変更する。
- us-east-1c用のdeploymentをバックアップ
cp -p aws-load-balancer-controller.yaml aws-load-balancer-controller-us-east-1c.yaml
 →変更後のファイルをバックアップする。
差分確認
正しく置き換わっているかdiffコマンドで確認します。
diff -u aws-load-balancer-controller-us-east-1a.yaml aws-load-balancer-controller.yaml
--- aws-load-balancer-controller-us-east-1c.yaml   2024-11-26 15:55:10.081667800 +0900
+++ aws-load-balancer-controller.yaml   2024-11-26 16:05:34.973583300 +0900
@@ -13,7 +13,7 @@
     app.kubernetes.io/name: aws-load-balancer-controller
     app.kubernetes.io/version: v2.10.1
     helm.sh/chart: aws-load-balancer-controller-1.10.1
-  name: aws-load-balancer-controller-us-east-1a
+  name: aws-load-balancer-controller-us-east-1c
   namespace: kube-system
   resourceVersion: "54470"
   uid: b87a8f35-784e-4517-83db-54f32f135b76
@@ -41,7 +41,7 @@
         app.kubernetes.io/name: aws-load-balancer-controller
     spec:
       nodeSelector:
-        topology.kubernetes.io/zone: us-east-1a
+        topology.kubernetes.io/zone: us-east-1c
       affinity:
         podAntiAffinity:
           preferredDuringSchedulingIgnoredDuringExecution:→「us-east-1a」から「us-east-1c」の変更差分のみ出ることを確認する。
適用
us-east-1c用のdeploymentができましたので適用します。
kubectl apply -f aws-load-balancer-controller.yaml
deployment.apps/aws-load-balancer-controller-us-east-1c created適用後のdeploymentを確認すると、us-east-1aとus-east-1cでAZ別のdeploymentが確認できます。
kubectl get deployments.apps --all-namespaces
NAMESPACE     NAME                                      READY   UP-TO-DATE   AVAILABLE   AGE
kube-system   aws-load-balancer-controller-us-east-1a   1/1     1            1           11m
kube-system   aws-load-balancer-controller-us-east-1c   1/1     1            1           95sFargateデプロイAZを確認するとそれぞれ、第3オクテットのIPが異なり、AZ別指定で分散されていることが確認できます。
kubectl get po --all-namespaces -o wide
NAMESPACE     NAME                                                       READY   STATUS    RESTARTS   AGE     IP           NODE                                 NOMINATED NODE   READINESS GATES
kube-system   aws-load-balancer-controller-us-east-1a-668bd7f9cd-qwtrd   1/1     Running   0          12m     10.0.3.186   fargate-ip-10-0-3-186.ec2.internal   <none>           <none>
kube-system   aws-load-balancer-controller-us-east-1c-7c56c959f6-c7nhh   1/1     Running   0          2m29s   10.0.4.234   fargate-ip-10-0-4-234.ec2.internal   <none>           <none>「10.0.3.186」、「10.0.4.234」になり、第三オクテットが異なることの確認できます。
AZ us-east-1aが「10.0.3.0/24」の範囲、AZ us-east-1cが「10.0.4.0/24」の範囲です。
まとめ
今回のような設定を施さないとFargateは3~5回くらいの頻度で1AZに寄ったり2AZに分散されたりするため分散を保証したい場合、少々管理が煩雑になってしまいます。
自動でAZ負荷分散される機能アップデートが来ることを願うばかりです。
補足
kubectl rollout restart -n kube-system deployment aws-load-balancer-controller-us-east-1a aws-load-balancer-controller-us-east-1c
 →こちらのコマンドでFargateを再起動し次のコマンドと組み合わせることで、AZ保証されることの確認が可能です。
上記、コマンドを複数回おこないそれぞれのdeployment名AZとIPの範囲が対象AZ範囲になっていることが確認できます。
kubectl get po --all-namespaces -o wide
 →Fargate再起動後にこちらのコマンドを実施しAZ保証されることを確認できます。

プロフィール
AWSの設計・構築をメインにおこなっています。
運用・保守をおこなう部署におりましたが、最近、アーキテクト課に異動しました。
日々精進しております。
LINK
クラウドベリージャム:プロフィールページ

