2026年6月4日

2026年6月4日

MetalLBでAuto Scalingと連携する方法|HPA・ノード増減に追従させる設定と注意点

MetalLB自体には台数を増減する機能はありませんが、Podの自動スケール(HPA)やノードの自動追加と組み合わせると、増えたPodやノードへ通信を振り分ける入口として働きます。ポイントは、L2モードでは入口が1台のノードに集中するため、スケールアウトの効果を入口まで広げたい場合はBGPモードを選ぶこと、そして externalTrafficPolicy と広告するノードの範囲を意図して決めることです。この記事では、MetalLBとAuto Scalingの関係、モード別の挙動、設定例、増減時のつまずきと確認方法を説明します。

MetalLBとAuto Scalingの役割分担

オンプレミスや自前のVPSで動かすKubernetesには、クラウドのようなロードバランサーがありません。MetalLBは、type: LoadBalancer のServiceに外部IPアドレスを割り当て、そのアドレスへの通信をクラスタのノードに届ける役割を持ちます。

Auto Scalingに関わる仕組みは、主に次の3つです。

  • HPA(Horizontal Pod Autoscaler): CPU使用率などに応じてPodの数を増減する
  • ノードの自動増減: Cluster AutoscalerやCluster APIなどでノードを追加・削除する(ベアメタルでは、ノードを自動で用意できる基盤がある場合に限られます)
  • MetalLB: Serviceの外部IPを、どのノードから広告するかを管理する

HPAでPodが増えると、ServiceのエンドポイントにそのPodが自動で加わり、kube-proxy(またはCNIの代替実装)が振り分けます。MetalLBの設定を変える必要はありません。ノードが増えた場合も、MetalLBのspeakerはDaemonSetとして動くため、新しいノードにも自動で配置されます。つまり「連携」の多くは自動で起きますが、その結果どこに負荷が集まるかはモードと設定で大きく変わります。

L2モードとBGPモードでの違い

L2モード

L2モードでは、Serviceごとに選ばれた1台のノードが、外部IPへのARP(IPv6ではNDP)に応答します。外部からの通信はすべてそのノードに入り、そこからPodへ転送されます。

Podをいくら増やしても、入口のノードの帯域や処理能力は1台分のままです。小〜中規模のWordPressなど、入口の帯域が問題にならない用途では十分ですが、通信量そのものが増えてスケールしたい場合はボトルネックになります。担当ノードが落ちると別のノードが引き継ぎますが、切り替わるまでの数秒間は通信が途切れることがあります。

BGPモード

BGPモードでは、各ノードのspeakerがルーターとBGPで接続し、外部IPへの経路を広告します。ルーターがECMP(等コストマルチパス)に対応していれば、通信は複数のノードに分散されます。ノードが増えると新しいノードも経路を広告し、入口の数も増えます。

ただし、ノードが追加・削除されるとルーター側のハッシュ計算が変わり、既存の接続が別のノードに振り直されて切れることがあります。長時間の接続(WebSocketや大きなファイルのダウンロード)が多いサービスでは、この影響を考慮します。

externalTrafficPolicyの選び方

Serviceの externalTrafficPolicy は、スケール時の挙動に直接影響します。

  • Cluster(既定): どのノードに届いた通信も、クラスタ内のいずれかのPodへ転送します。分散は均等になりやすい一方、ノード間の転送が増え、送信元IPがノードのものに置き換わります
  • Local: 通信を受けたノード上のPodにだけ転送します。送信元IPが保たれ、余分な転送もありません。MetalLBは、準備ができたPodがあるノードからだけ広告します

Local では、HPAでPodが増えたノードが広告に加わり、Podがいなくなったノードは広告から外れます。Podの配置が偏ると負荷も偏るため、Podをノードに分散させる設定(topologySpreadConstraints など)と組み合わせます。WordPressのログやセキュリティ系のプラグインで閲覧者のIPアドレスを使いたい場合は、Local にするか、前段のIngressコントローラーで実IPを引き継ぐ設定が必要です。

設定例

MetalLB v0.13以降は、設定をカスタムリソース(CRD)で書きます。まずIPアドレスの範囲を定義します。HPAでPodが増えてもServiceの数は変わらないため、アドレスはServiceの数だけあれば足ります。

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: public-pool
  namespace: metallb-system
spec:
  addresses:
    - 192.0.2.240-192.0.2.250

BGPモードでは、ピア(ルーター)と広告の設定を追加します。nodeSelectors を使うと、広告するノードを絞れます。自動で増減するノードに入口を任せたくない場合は、入口専用のラベルを付けたノードだけを指定します。

apiVersion: metallb.io/v1beta2
kind: BGPPeer
metadata:
  name: upstream-router
  namespace: metallb-system
spec:
  myASN: 64500
  peerASN: 64501
  peerAddress: 192.0.2.1
---
apiVersion: metallb.io/v1beta1
kind: BGPAdvertisement
metadata:
  name: public-adv
  namespace: metallb-system
spec:
  ipAddressPools:
    - public-pool
  nodeSelectors:
    - matchLabels:
        node-role.example.com/ingress: "true"

ASN(64500など)とアドレスは例です。ルーター側の設定と合わせて書き換えてください。最後に、ServiceとHPAを用意します。HPAがCPU使用率を見るには、metrics-serverなどのメトリクス基盤と、Podの resources.requests の指定が必要です。

apiVersion: v1
kind: Service
metadata:
  name: wordpress
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  selector:
    app: wordpress
  ports:
    - port: 80
      targetPort: 80
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: wordpress
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: wordpress
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70

実際の構成では、MetalLBのIPをIngressコントローラーのServiceに割り当て、その背後のWordPressをHPAで増減させる形が一般的です。WordPressを複数Podで動かす場合は、アップロードファイルを共有ストレージやオブジェクトストレージに置き、セッションやキャッシュがPodごとに分かれないようにしておく必要があります。

スケール時につまずきやすい点と確認方法

設定変更は、ステージング用のクラスタで試してから本番に入れます。うまく振り分けられないときは、次の順に確認します。

  1. 外部IPが割り当てられているか: kubectl get svc で EXTERNAL-IP が <pending> のままなら、IPアドレスプールが無い・使い切った・Serviceの指定と合っていない、のいずれかです
  2. どのノードが広告しているか: kubectl describe svc wordpress のイベントに、MetalLBが割り当てやノードの情報を出します。speakerのログも確認します
  3. Podが準備完了になっているか: Local では、readinessProbeに合格したPodがあるノードだけが広告します。HPAで増えた直後のPodが準備中だと、そのノードには通信が来ません
  4. ノード削除時の切断: ノードを削除する前に kubectl drain でPodを退避させると、Podの移動と広告の取り下げが順に進み、切断を減らせます
kubectl get ipaddresspools -n metallb-system
kubectl get pods -n metallb-system -o wide
# speaker のログ(ラベルはインストール方法によって異なります)
kubectl logs -n metallb-system -l component=speaker --tail=50

CRDの apiVersion や設定項目はバージョンで変わることがあるため、使っているMetalLBのバージョンの公式ドキュメントと照らし合わせてください(2026年10月現在)。

まとめ

  • MetalLBは台数を増減しないが、HPAで増えたPodや追加されたノードには自動で追従する
  • L2モードは入口が1台に集中する。入口ごとスケールさせたいならECMP対応ルーターとBGPモード
  • externalTrafficPolicy: Local は送信元IPを保てるが、Podの偏りがそのまま負荷の偏りになる
  • 広告するノードは nodeSelectors で意図して決め、ノード削除前はdrainする
  • WordPressを複数Podにするなら、共有ストレージとキャッシュ・セッションの扱いも合わせて整える

お気軽にご相談ください

お見積りへ お問い合わせへ
▲