hidemium's blog

日々学んだことをアウトプットする。

MetalLBを使ったロードバランサーのインストール

前回の記事では、KubesprayによるKubernetesのインストールを行いました。今回は、MetalLBを使ったロードバランサーのインストールを試してみました。

hidemium.hatenablog.com

構成

MetalLBのインストール

KubesprayでMetalLBのインストールを行うことが可能なため、inventoryのgroup_varsに設定を入れていきます。

L2モードでの動作を行うため、以下のようにARPの設定を有効にします。

$ vi k8s-cluster.yml
# configure arp_ignore and arp_announce to avoid answering ARP queries from kube-ipvs0 interface
# must be set to true for MetalLB, kube-vip(ARP enabled) to work
kube_proxy_strict_arp: true

以下のように、MetalLBを有効にし、MetalLBから払い出すIPアドレス帯を指定します。

$ vi addons.yml
# MetalLB deployment
metallb_enabled: true
metallb_speaker_enabled: "{{ metallb_enabled }}"
metallb_ip_range:
  - "10.0.50.201-10.0.50.254"
# metallb_pool_name: "loadbalanced"
# metallb_auto_assign: true
metallb_avoid_buggy_ips: true

クラスタ作成

masterノードとworkerノードを再作成し、前回と同様に、クラスタを構築します。

$ ansible-playbook -i inventory/mycluster/hosts.yml  --become --become-user=root cluster.yml

MetalLBがインストールされているか確認します。

$ kubectl get all -n metallb-system 
NAME                              READY   STATUS    RESTARTS   AGE
pod/controller-5ffbbd5949-pmxf9   1/1     Running   0          2d14h
pod/speaker-4d4v8                 1/1     Running   0          2d14h
pod/speaker-9f9lh                 1/1     Running   0          2d14h
pod/speaker-h8c97                 1/1     Running   0          2d14h
pod/speaker-jxwgm                 1/1     Running   0          2d14h
pod/speaker-xdbml                 1/1     Running   0          2d14h

NAME                     DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
daemonset.apps/speaker   5         5         5       5            5           kubernetes.io/os=linux   2d14h

NAME                         READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/controller   1/1     1            1           2d14h

NAME                                    DESIRED   CURRENT   READY   AGE
replicaset.apps/controller-5ffbbd5949   1         1         1       2d14h

MetalLBの動作確認

以下のように、nginxをデプロイします。

$ kubectl create deployment nginx --image=nginx

デプロイ後の状態は以下の通りです。

$ kubectl get pod,svc
NAME                        READY   STATUS    RESTARTS   AGE
pod/nginx-76d6c9b8c-4cqrc   1/1     Running   0          17s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.233.0.1   <none>        443/TCP   4h15

デプロイしたnginxをserviceとして公開します。この時、 typeにLoadBalancerを指定します。

$ kubectl expose deployment nginx --port 80 --type LoadBalancer

nginxがLoadBalancerで公開され、EXTERNAL-IPにMetalLBで設定したIPアドレスが付与されていることが分かります。

$ kubectl get pod,svc
NAME                        READY   STATUS    RESTARTS   AGE
pod/nginx-76d6c9b8c-4cqrc   1/1     Running   0          43s

NAME                 TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/kubernetes   ClusterIP      10.233.0.1      <none>        443/TCP        4h16m
service/nginx        LoadBalancer   10.233.51.192   10.0.50.201   80:31592/TCP   2s

nginxに対してEXTERNAL-IPでアクセスできるか確認します。

$ INGRESS_EXTERNAL_IP=`kubectl get svc nginx -o jsonpath='{.status.loadBalancer.ingress[0].ip}'`
$ curl $INGRESS_EXTERNAL_IP
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

MetalLBのL2モード(ARP)の動作

MetalLB には、ControllerとSpeakerの 2 つのコンポーネントが含まれています。Controllerはmasterノードにデプロイされ、SpeakerはDaemonsetモードでworkerノードの各ノードにデプロイされます。

Controllerは、事前に定義したIPプールからIPアドレスの割り当てを管理し、Speakerは、選択したモード(L2モード(ARP/NDP)、BGP)に従ってPodへの通信ができるようにします。

今回利用したL2モード(ARP)では、1 つのノードがサービスをローカル ネットワークに通知する役割を担います。ノードを確認すると、ネットワーク インターフェイスに複数の IP アドレスが割り当てられているように見えます。

ARP要求が来た場合、SpeakerがARPのリプライを返します。

また、サービス IP のすべてのトラフィックが 1 つのノードに送信されます。そこから kube-proxyを経由してトラフィックをすべてのサービスのポッドに分散します。

ARP要求とARPリプライの挙動について確認してみます。

EXTERNAL-IPに対して、arpingを実行します。

$ sudo arping -I ens192 10.0.50.201

ARP応答を行うノードについて以下のコマンドで確認します。

$ kubectl describe service nginx
Events:
  Type    Reason        Age                From                Message
  ----    ------        ----               ----                -------
  Normal  IPAllocated   37m                metallb-controller  Assigned IP ["10.0.50.201"]
  Normal  nodeAssigned  33m (x2 over 37m)  metallb-speaker     announcing from node "kube-worker01"

ARP応答を行うノードで、tcpdumpを行い、ARP要求とARP応答が行われていることを確認します。

$ sudo tcpdump -n -i ens192 arp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on ens192, link-type EN10MB (Ethernet), snapshot length 262144 bytes
22:33:19.405011 ARP, Request who-has 10.0.50.201 tell 10.0.50.163, length 46
22:33:19.405155 ARP, Reply 10.0.50.201 is-at 00:50:56:88:f9:61, length 46
22:33:20.406228 ARP, Request who-has 10.0.50.201 tell 10.0.50.163, length 46
22:33:20.406378 ARP, Reply 10.0.50.201 is-at 00:50:56:88:f9:61, length 46
22:33:21.406975 ARP, Request who-has 10.0.50.201 tell 10.0.50.163, length 46
22:33:21.407122 ARP, Reply 10.0.50.201 is-at 00:50:56:88:f9:61, length 46

リソースの削除

動作確認ができたので、リソースを削除しておきます。

kubectl delete deployment nginx
kubectl delete svc nginx

参考