負荷分散

負荷対策 #

nodeSelector #

NodeはPodが配置されるVMや物理マシンですが、 配置されるPodの処理内容によって使用されるリソースが大きく変わることがあります。 具体的にはIngress Gatewayはクラスター内外のトラフィックを集中的に処理することが事前にわかっています。 また、Ingress Gatewayがなければアプリケーションにアクセスできないため、必ずリソースが枯渇しない状態にする必要があります。 そのため、Gatewayとしての役割以外を持つPodと別のNodeに配置されるようにすることで、Podが安定して稼働できるリソースの確保を実現します。

KubernetesはNodeに対してラベルを付与し、PodにnodeSelectorを付与することで指定のNodeに対してPodを配置することができます。 Nodeに付与されているlabelは以下のコマンドで確認することができます。

$ kubectl get nodes --show-labels
# 簡単のため表示を省略
gateway01   Ready    <none>        1d    v1.21.12   node-role=gateway
gateway02   Ready    <none>        1d    v1.21.12   node-role=gateway
worker01    Ready    <none>        1d    v1.21.12   node-role=worker
worker02    Ready    <none>        1d    v1.21.12   node-role=worker

この場合、workerに対してPodを配置したい場合は次のように記述することができます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  namespace: demo
spec:
  template:
    # 中略
    spec:
      nodeSelector:
        node-role: worker

PodAntiAffinity #

Podのスケジューリングはデフォルトのまま使用すると、Nodeに対する配置は明示的にコントロールされません。 つまり、あるアプリケーションを搭載したPodがNodeに偏らないようにしたいが、偏ってしまう(逆も然り)など発生します。 特にBFFサーバーはステートレスなサーバーであるため、分散配置されている方が望ましいでしょう。

KubernetesではpodAffinity(podを条件に応じて集約)またはpodAntiAffinity(podを条件に応じて分散)を指定することでPodのスケジューリングをコントロールすることができます。 例えば、「app.kubernetes.io/name=myappというラベルを持つPodが、なるべく同じNodeに配置されない」スケジューリング設定は次のように表現できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: demo
  labels:
    app: myapp
    app.kubernetes.io/name: myapp
spec:
  template:
    spec:
      affinity:
        # ポッド間のスケジューリングルール
        # 以下の条件に合致する場所に配置しないポリシー
        podAntiAffinity: # 逆は podAntiAffinity
          # 優先的に考慮されるスケジューリングポリシー
          preferredDuringSchedulingIgnoredDuringExecution:
            - # 優先度の重み付け(1 - 100の間で定義)
              # Nodeごとにweightを加算しScoreを算出し、
              # 最も高いスコアを獲得したNodeに対してPodが配置される
              weight: 1
              # app.kubernetes.io/name = "myapp" のラベルを持つPod
              podAffinityTerm:
                # Nodeをフィルタリングするためのキー。
                # この空間内のNodeに対するPod間のSelectorでAffinityのScoreが計算される
                # kubernetes.io/hostnameは各Nodeに付与される識別子として利用できる
                # (Nodeのフィルタリング条件として偏りがないキー)
                topologyKey: kubernetes.io/hostname
                labelSelector:
                  # app.kubernetes.io/name = "myapp" にマッチするPodを集計対象とする
                  matchExpressions:
                    - key: app.kubernetes.io/name
                      operator: In
                      values:
                        - myapp

preferredDuringSchedulingIgnoredDuringExecutionpodAffinityTermで指定されたlabelSelectorに該当するPodをtopologyKeyごとにweightを加算してスコアを算出します。 podAntiAffinityで利用されるスコアになるため、スコアが高くなるほどPodはスコアの高いNodeに対してなるべく配置されないようになります。 (podAntiAffinityはスコアの符号がマイナスで、podAffinityはスコアの符号がプラスと思えばわかりやすい。)

また、ここではlabelSelectorで使うkeytopologyKeyはWell-Known Labelsを利用しています。