まえがき
前回の記事で作成した3ノードのK8sクラスタをより実運用に向けとするため、 MetalLB を導入し、負荷分散と外部アクセス環境の構築を行う
構築の手順、構築してみて解ったことなどをまとめる
MetalLBには Layer 2 mode
と BGP mode
の2つのモードがあるが、 BGP mode で構築を行う
※BGP mode を使用するには、上流にBGPを喋れるルータが必要
何番煎じかはわからないが、MetalLB, BGP の仕組みも交えて解説をする
検証に使用する3台の物理マシン(白いタワーPC)
MetalLB (BGP mode) の仕組み
1. 前提条件 - 構成
前回クラスタ構築完了時点では、K8sノードはClientと同じセグメントに属していたが、新たにVLANを切り、別のネットワークセグメントへと切り出し、図の構成となった
2. 経路情報の広告
MetalLB (BGP mode) では、BGP (Border Gateway Protocol) を使用して、metallb:controller
が払い出す ExternalIP セグメントを metallb:speaker
が BGP Router へ広告することで動的なルーティングを実現するミドルウェアである
BGPをルーティングに使用することで特別な機器を必要とせず、L3のみでロードバランサのような振る舞いをさせることができる
経路広告の流れは以下の通りである
- controller が Service へ ExternalIP の払い出しを行う
- controller が speaker へ 払い出しを通知
- speaker が 上流の BGP Router へ 経路情報を広告
3. next hop による負荷分散
経路情報が広告され、ExternalIP への接続要求があるとルータは BGP table (next hop) に記載されたノードへパケットを通す
論理的には青い点線で描かれた、 ipvs
へとセッションが張られる
ipvs は自ノードもしくは他ノードで Listen している pod へパケットを転送する
2段構えのロードバランサとなっているが、この仕組みがあることで、 ノードレベルでの負荷分散とノードと BGP 間の経路情報を単純化している
ルータは next hup に指定された対向へパケットを通すだけなので、接続を受けたノードに pod が居ない可能性は十分に考えられる。そこで、 ipvs が接続を受け、クラスタ内の適切な pod へパケットを転送することで、pod レベルの負荷分散と障害耐性を実現する
クライアントからの通信の流れは以下の通りである
- クライアントが service (
172.16.x.x
) へ接続要求 - BGP ルータが next hop に指示されたIPノードへパケットを通す
- ipvs がセッションを受け、いずれかのノードで動く pod へ転送
- 元の経路をたどり、 pod がクライアントへ接続を返す
MetalLB 導入
MicroK8sの場合、 いずれかのノードで add-on を有効にするだけで使用可能な状態となる
$ sudo microk8s.enable metallb
設定を行う
ルータ側設定
ルータには Sophos XG Firewall を使用している
VyOS を使用した構築例1や EdgerouterX を使用した構築例2 なども参考とした

ルータID にルータのIPアドレス、ローカルAS には 64512 - 65534
3 から採番し入力 64512
とした。
ネイバーには対向(ノード)のIPアドレスとルータ同様、AS番号の採番を行うが ルータから1インクリメントした 64513
とした。
K8s側設定
MetalLB 公式ドキュメントの例をベースに、設定ファイルを作成した。
最小構成としては、以下の内容で機能する
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
peers:
- peer-address: 10.227.0.1
peer-asn: 64512
my-asn: 64513
address-pools:
- name: default
protocol: bgp
addresses:
- 172.16.192.0/20
適用は kubectl apply
で行う
$ kubectl apply -f metallb-config.yaml
yaml の内容について
-
data.config.peers.peer-address
は 対向(上流ルータ)のIPアドレス -
data.config.peers.peer-asn
は 対向(上流ルータ)のAS番号 -
data.config.peers.my-asn
は 自分(ノード)に割り当てるAS番号 -
data.config.address-pools.protocol
は ロードバランシングに使用するプロトコル"layer2"
または"bgp"
を指定 -
data.config.address-pools.address
は service に割り当てる IPアドレスのプール範囲指定
動作確認
BGP セッションの確認
XG Firewall に限らず、ルータにはBGPの状態確認ができるコマンドがある
設定が正常に完了すると以下のような出力がされる
BGP router identifier 10.227.0.1, local AS number 64512
RIB entries 7, using 448 bytes of memory
Peers 3, using 7452 bytes of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
192.168.11.11 4 64513 7352 7365 0 0 0 2d13h04m 3
192.168.11.12 4 64513 6145 6174 0 0 0 16:34:39 3
192.168.11.13 4 64513 7352 7368 0 0 0 2d13h04m 3
Total number of neighbors 3
Up/Down
や never
や down
、 MsgRcvd
, MsgSent
が 0
となっている場合は、BGPセッションに失敗している
AS番号やIPアドレスの確認、ゾーンポリシーやファイアウォールなどの確認を行う
小生もルータ側のゾーンポリシー設定で、 dynamic routing を Allow にしておらず小一時間ハマった。
(ノード側のサブネットも間違っていた…)
確認用サービス (nginx) を立ち上げる
唐突だが、metallb:speaker の経路広告がルータの BGP Table に反映されるか、Clientからの疎通確認を行うために、nginx のサービスを立ち上げる
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: LoadBalancer
spec.replicas
は適宜書き換える
適用は kubectl apply
で行う
$ kubectl apply -f nginx.yaml
service と pods の状態は以下のコマンドで確認できる
$ kubectl get svc,po
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.152.183.1 <none> 443/TCP 3d13h
service/nginx LoadBalancer 10.152.183.53 172.16.192.1 80:31778/TCP 3d11h
NAME READY STATUS RESTARTS AGE
pod/nginx-8c9df995d-j7w8t 1/1 Running 4 3d11h
pod/nginx-8c9df995d-d9djn 1/1 Running 4 3d11h
pod/nginx-8c9df995d-fwvbd 1/1 Running 6 3d11h
ExternalIP には 172.16.192.1
が割り当てられ、80/tcp
で Listen している
pods も replicas
に設定した3つが立ち上がっている
BGP table (next hop) の確認
service/nginx
に ExternalIP が割り当てられる頃には、ルータのBGPテーブルに経路情報が反映されているはず
ルータによって確認方法や出力は異なるが、 172.16.192.1/32
に対し、Next Hop
が割り当てられていることが確認できる
kubectl get svc
で ExternalIP が割り当てられているにも関わらず、BGPテーブルへの反映が行われていない場合は、speaker pod の状態確認や、そもそも BGP セッション がコケている可能性が高いので確認して欲しい
BGP table version is 0, local router ID is 10.227.0.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,
r RIB-failure, S Stale, R Removed
Origin codes: i - IGP, e - EGP, ? - incomplete
Network Next Hop Metric LocPrf Weight Path
*> 10.227.0.0/20 0.0.0.0 0 32768 i
* 172.16.192.1/32 192.168.11.12 0 64513 ?
* 192.168.11.11 0 64513 ?
*> 192.168.11.13 0 64513 ?
Total number of prefixes 4
クライアントからの疎通確認
ブラウザから http://172.16.192.1/
へアクセスして、おなじみの Welcome to nginx! が表示されれば動作確認は完了となる
アクセスできない場合は、ルータ, ノードへ ping が通るかを確認し、 traceroute
で経路を追ってどこに原因があるかを探ると良い
まとめ
目的と手段が入れ替わっていそうな気がするが、ロードバランサが利用可能となり Kubernetes を使用した Homebridge の実運用へ一歩前進した。BGP とはなんぞや状態だったが、実際に構築して触れてみることで概要だけでも理解することが出来た。
次回は、PersistentVolume の構築を予定しているが、NFSマウントするだけな気もするので、Ceph を使用した、分散ストレージ環境の構築と合わせて記事を公開する予定(予定)
その頃には脱ESXiと脱XG Firewall計画が進んでるはず。
ちゃんと理解している方が読むと、ちゃんと理解していないのがバレそうな記事だが、お気づきの点があればコメントで指摘お願いします。
(開発エンジニアなので細かい部分は目を瞑って欲しい…。)
参考資料
- MetalLB, bare metal load-balancer for Kubernetes
- KubernetesロードバランサーのMetalLBを導入した話(Necoプロジェクト体験入部)
- BGP とは | 「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典