はじめに
以下の記事の続き。「はじめてのデプロイ」ではあくまでデプロイすることに主眼を置いていたため、外部からのアクセスは NodePort を利用し、特定Worker ノードの持つアドレスへ Port 番号を使ってアクセスする、かなり強引なやり方だった。前段にMetal LBを置いて80/TCPとして通信を終端してそこから各Workerノードに通信を振り分ける仕組みを作ってみる。
Metal LB 3つの実装
2025年5月現在、これまでの実装のほか、Metal LBは BGP部分にFRRを利用する FRR-Modeと、FRR-K8s Modeが存在する。このうち、FRR-K8s Modeはまだ experimental とのこと。まずは標準実装での検証を行う。
Metal LB のインストール
当然ながら公式サイトの記述に沿って構築する。作業はすべて Master ノードで実施する。
インストールは Helm Chartを利用する方法と Manifest を利用する方法あるが、まずは Manifest でのインストールを試みる。
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml
IPVSモード利用時の注意点
kube-proxyのIPVS (IP Virtual Server)モードとして利用している場合、Strict ARPをtrueにする必要があるとのこと。
今回試した環境ではデフォルトのiptables モード。iptables/IPVSどちらも、kube-proxyのTraffic 分散方式で、IPVS のほうがよりスケーラブル。今回の検証では、まずはデフォルトの iptables モードで進める。
正常性確認
Metal LBは名前空間 metallb-system にインストールされ、controllerとspeaker podがそれぞれ存在する。
ubuntu@k8s-master:~$ kubectl -n metallb-system get pod
NAME READY STATUS RESTARTS AGE
controller-bb5f47665-jxcj2 1/1 Running 0 2m22s
speaker-g6mmh 1/1 Running 0 2m22s
speaker-kzhd6 1/1 Running 0 2m22s
speaker-qppgk 1/1 Running 0 2m22s
ubuntu@k8s-master:~$ kubectl -n metallb-system get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
metallb-webhook-service ClusterIP 172.16.25.63 <none> 443/TCP 2m26s
Controller ...IPアドレスのアサインを行うコントローラ
Speaker ... デーモンセット。ユーザが選択した Protocol を使用して外部と接続する。
L2モードでの MetalLB
- Metal LB でのIPアドレス Pool を指定(
metallb-l2-IPAddressPool.yaml
) - Metal LB での広告設定(
metallb-l2-Advertisement.yaml
) - Metal LB を利用する Deployment 例(
nginx-metallb-l2-deployment.yaml
) - Metal LB を利用する Service設定例(
nginx-metallb-l2-service.yaml
)
Metal LB でのIPアドレス Pool を指定
Pool アドレスレンジを指定する。Worker ノードが所属するセグメント(192.168.11.0/24)末尾で11アドレスを確保し、これをPoolアドレスとした。MetalLBを利用する場合、namespaceは基本的にmetallb-system
上に作る模様。Pool アドレスに名前(first-Pool
)を付け、これをL2Advertisement 側から呼び出す。
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: first-pool
namespace: metallb-system
spec:
addresses:
- 192.168.11.240-192.168.11.250
2022年7月にリリースされた 0.13.2から Config方法が変更された(ConfigMap ⇒ CRD)。少し古めのサイトだと Config Map での実装例となっているため注意(ハマった)。
Metal LB での広告設定
metallb-l2-IPAddressPool.yaml
で定義したIPアドレスPoolをここのSpec で呼び出す。
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: example
namespace: metallb-system
spec:
ipAddressPools:
- first-pool
Metal LB を利用する Deployment 例
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-metallb-l2
spec:
replicas: 2
selector:
matchLabels:
app: nginx-metallb-l2
template:
metadata:
labels:
app: nginx-metallb-l2
spec:
containers:
- name: nginx
image: nginx:stable
command: ["/bin/sh", "-c", "/mnt/start.sh"]
ports:
- containerPort: 80
volumeMounts:
- mountPath: /mnt
name: nfs-volume
volumes:
- name: nfs-volume
persistentVolumeClaim:
claimName: nfs-dynamic-pvc
Metal LB を利用する Service設定例
apiVersion: v1
kind: Service
metadata:
name: nginx-metallb-l2-service
spec:
selector:
app: nginx-metallb-l2
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
設定投入
kubectl apply -f metallb-l2-IPAddressPool.yaml
kubectl apply -f metallb-l2-Advertisement.yaml
kubectl apply -f nginx-metallb-l2-deployment.yaml
kubectl apply -f nginx-metallb-l2-service.yaml
正常性確認
ubuntu@k8s-master:~$ kubectl get IPAddressPool -n metallb-system
NAME AUTO ASSIGN AVOID BUGGY IPS ADDRESSES
first-pool true false ["192.168.11.240-192.168.11.250"]
ubuntu@k8s-master:~$
ubuntu@k8s-master:~$ kubectl get L2Advertisement -n metallb-system
NAME IPADDRESSPOOLS IPADDRESSPOOL SELECTORS INTERFACES
example ["first-pool"]
ubuntu@k8s-master:~$ kubectl get pod -l app=nginx-metallb-l2
NAME READY STATUS RESTARTS AGE
nginx-metallb-l2-d8b48657c-rqqp5 1/1 Running 0 3h16m
nginx-metallb-l2-d8b48657c-tlnq6 1/1 Running 0 3h16m
External-IPの確認
nginx-metallb-l2-service
のEXTERNAL-IP
として、IPアドレスPoolに含まれる192.168.11.240
が払いだされていることが確認できる。
ubuntu@k8s-master:~$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
(snip)
nginx-metallb-l2-service LoadBalancer 172.16.102.250 192.168.11.240 80:32529/TCP 65m
アクセス確認
curl
からExternal IPに指定された192.168.11.240
へアクセスすると、それぞれのPodから応答してくれることが確認できる。NodePort だとかなり限定的だが、ようやっと外部からまともに見えるようになる。
ubuntu@k8s-master:~$ curl http://192.168.11.240
<html><body><h1>Hello from nginx-metallb-l2-d8b48657c-tlnq6</h1></body></html>
ubuntu@k8s-master:~$ curl http://192.168.11.240
<html><body><h1>Hello from nginx-metallb-l2-d8b48657c-rqqp5</h1></body></html>