Calico と MetalLB を BGP モードで共存させる環境を作ってみた【Kubernetes】
Kubernetes 環境で Calico と MetalLB を BGP モードで共存させる場合、1つの Kubernetes ノードから、外部のルーターに対して2つの BGP セッションを張る必要があります。BGP ではノード間で1つのセッションしか張ることができないため、1つの K8s ノードと1つのルーターの間で Calico と MetalLB の2つのセッションを張ることができません。
これに対するワークアラウンドが公式ドキュメントで紹介されています。この中で、VRF を分ける方法を試してみました。
構成
ルーターとして CSR1000v を用意し、VRFを分けてルートリークさせています。
ルーターの設定
CSR1000v は AS 65000 とし、vrf k8s-metallb と vrf k8s-calico を作成し、Calico と MetalLB の BGP Speaker と BGP セッションを貼っています。K8s ノードはデフォルトゲートウェイを設定してあるので、K8sノードに対して余計なルートを広告しないようにしています。
ip vrf k8s-calico rd 65000:2 route-target export 65000:2 route-target import 65000:2 route-target import 65000:1 ! ip vrf k8s-metallb rd 65000:1 route-target export 65000:1 route-target import 65000:2 route-target import 65000:1 ! interface Loopback0 ip address 1.1.1.0 255.255.255.255 ! interface Loopback2 ip vrf forwarding k8s-calico ip address 1.1.1.2 255.255.255.255 ! interface GigabitEthernet2 ip vrf forwarding k8s-metallb ip address 10.0.0.254 255.255.255.0 ! interface GigabitEthernet3 ip vrf forwarding k8s-calico ip address 10.0.1.254 255.255.255.0 ! router bgp 65000 bgp router-id 1.0.0.0 bgp log-neighbor-changes ! address-family ipv4 vrf k8s-calico bgp router-id 1.0.0.2 redistribute connected neighbor 10.0.0.1 remote-as 65002 neighbor 10.0.0.1 ebgp-multihop 32 neighbor 10.0.0.1 update-source Loopback2 neighbor 10.0.0.1 activate neighbor 10.0.0.1 prefix-list deny-all-routes out neighbor 10.0.0.11 remote-as 65002 neighbor 10.0.0.11 ebgp-multihop 32 neighbor 10.0.0.11 update-source Loopback2 neighbor 10.0.0.11 activate neighbor 10.0.0.11 prefix-list deny-all-routes out neighbor 10.0.0.12 remote-as 65002 neighbor 10.0.0.12 ebgp-multihop 32 neighbor 10.0.0.12 update-source Loopback2 neighbor 10.0.0.12 activate neighbor 10.0.0.12 prefix-list deny-all-routes out neighbor 10.0.0.13 remote-as 65002 neighbor 10.0.0.13 ebgp-multihop 32 neighbor 10.0.0.13 update-source Loopback2 neighbor 10.0.0.13 activate neighbor 10.0.0.13 prefix-list deny-all-routes out exit-address-family ! address-family ipv4 vrf k8s-metallb bgp router-id 1.0.0.1 redistribute connected neighbor 10.0.0.1 remote-as 65001 neighbor 10.0.0.1 update-source GigabitEthernet2 neighbor 10.0.0.1 activate neighbor 10.0.0.1 prefix-list deny-all-routes out neighbor 10.0.0.11 remote-as 65001 neighbor 10.0.0.11 update-source GigabitEthernet2 neighbor 10.0.0.11 activate neighbor 10.0.0.11 prefix-list deny-all-routes out neighbor 10.0.0.12 remote-as 65001 neighbor 10.0.0.12 update-source GigabitEthernet2 neighbor 10.0.0.12 activate neighbor 10.0.0.12 prefix-list deny-all-routes out neighbor 10.0.0.13 remote-as 65001 neighbor 10.0.0.13 update-source GigabitEthernet2 neighbor 10.0.0.13 activate neighbor 10.0.0.13 prefix-list deny-all-routes out maximum-paths 4 exit-address-family ! ip prefix-list deny-all-routes seq 5 deny 0.0.0.0/0 le 32
Calico の設定
Calico の AS Number とピア(CSR1000v)の設定をします。CSR1000v の vrf k8s-calico に割り当てている Loopback 2 をピアとして設定しています。
[root@k8s-master1 ~]# cat calico-bgp.yaml apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: creationTimestamp: null name: default spec: asNumber: 65002 logSeverityScreen: Info nodeToNodeMeshEnabled: true [root@k8s-master1 ~]# [root@k8s-master1 ~]# calicoctl create -f calico-bgp.yaml Successfully created 1 'BGPConfiguration' resource(s) [root@k8s-master1 ~]# [root@k8s-master1 ~]# cat calico-peer.yaml apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: creationTimestamp: null name: bgppeer-csr1000v spec: asNumber: 65000 peerIP: 1.1.1.2 [root@k8s-master1 ~]# [root@k8s-master1 ~]# calicoctl create -f calico-peer.yaml Successfully created 1 'BGPPeer' resource(s) [root@k8s-master1 ~]#
MetalLB のデプロイと設定
MetalLB を K8s にデプロイします。
[root@k8s-master1 ~]# kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.8.1/mani fests/metallb.yaml namespace/metallb-system unchanged podsecuritypolicy.policy/speaker unchanged serviceaccount/controller unchanged serviceaccount/speaker unchanged clusterrole.rbac.authorization.k8s.io/metallb-system:controller unchanged clusterrole.rbac.authorization.k8s.io/metallb-system:speaker unchanged role.rbac.authorization.k8s.io/config-watcher unchanged clusterrolebinding.rbac.authorization.k8s.io/metallb-system:controller unchanged clusterrolebinding.rbac.authorization.k8s.io/metallb-system:speaker unchanged rolebinding.rbac.authorization.k8s.io/config-watcher unchanged daemonset.apps/speaker unchanged deployment.apps/controller unchanged [root@k8s-master1 ~]#
MetalLB の AS Number とピアの設定を行います。CSR1000v の vrf k8s-metallb に割り当てている Gi 2 をピアとして設定しています。
[root@k8s-master1 ~]# cat metallb-config.yaml apiVersion: v1 kind: ConfigMap metadata: namespace: metallb-system name: config data: config: | peers: - peer-address: 10.0.0.254 peer-asn: 65000 my-asn: 65001 address-pools: - name: default protocol: bgp addresses: - 10.100.0.0/24 [root@k8s-master1 ~]# kubectl apply -f metallb-config.yaml configmap/config created [root@k8s-master1 ~]#
動作確認
MetalLB と CSR1000v の BGP の状態
CSR1000v の vrf k8s-metallb の状態を確認してみます。各 k8s ノードと BGP ネイバーを確立していることがわかります。
csr1000v#show bgp vpnv4 unicast vrf k8s-metallb summary BGP router identifier 1.0.0.1, local AS number 65000 BGP table version is 3193, main routing table version 3193 7 network entries using 1792 bytes of memory 7 path entries using 952 bytes of memory 4/3 BGP path/bestpath attribute entries using 1184 bytes of memory 1 BGP AS-PATH entries using 24 bytes of memory 2 BGP extended community entries using 48 bytes of memory 0 BGP route-map cache entries using 0 bytes of memory 0 BGP filter-list cache entries using 0 bytes of memory BGP using 4000 total bytes of memory BGP activity 158/144 prefixes, 1611/1585 paths, scan interval 60 secs Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd 10.0.0.1 4 65001 5512 6058 3193 0 0 1d21h 0 10.0.0.11 4 65001 5344 5839 3193 0 0 1d20h 0 10.0.0.12 4 65001 5344 5833 3193 0 0 1d20h 0 10.0.0.13 4 65001 5511 6072 3193 0 0 1d21h 0 csr1000v#
Calico と CSR1000v の BGP の状態
Calico で定義しているIP pool から払い出されている IP Block は以下のように確認できます。
[root@k8s-master1 ~]# calicoctl ipam show --show-blocks +----------+--------------------+-----------+------------+--------------+ | GROUPING | CIDR | IPS TOTAL | IPS IN USE | IPS FREE | +----------+--------------------+-----------+------------+--------------+ | IP Pool | 192.168.0.0/16 | 65536 | 8 (0%) | 65528 (100%) | | Block | 192.168.116.0/26 | 64 | 1 (2%) | 63 (98%) | | Block | 192.168.160.0/26 | 64 | 1 (2%) | 63 (98%) | | Block | 192.168.165.192/26 | 64 | 4 (6%) | 60 (94%) | | Block | 192.168.195.128/26 | 64 | 2 (3%) | 62 (97%) | +----------+--------------------+-----------+------------+--------------+ [root@k8s-master1 ~]#
Calico から払い出されている IP pool の範囲が CSR1000v に BGP で広告されてきていることがわかります。
csr1000v#show bgp vpnv4 unicast vrf k8s-calico Network Next Hop Metric LocPrf Weight Path Route Distinguisher: 65000:2 (default for vrf k8s-calico) VRF Router ID 1.0.0.2 *> 1.1.1.2/32 0.0.0.0 0 32768 ? *> 10.0.0.0/24 0.0.0.0 0 32768 ? *> 10.0.1.0/24 0.0.0.0 0 32768 ? *m 192.168.116.0/26 10.0.0.13 0 65002 i *m 10.0.0.11 0 65002 i *m 10.0.0.12 0 65002 i *> 10.0.0.1 0 65002 i *m 192.168.160.0/26 10.0.0.13 0 65002 i *m 10.0.0.1 0 65002 i *m 10.0.0.12 0 65002 i *> 10.0.0.11 0 65002 i *m 192.168.165.192/26 10.0.0.13 0 65002 i *m 10.0.0.11 0 65002 i *m 10.0.0.1 0 65002 i *> 10.0.0.12 0 65002 i *m 192.168.195.128/26 10.0.0.13 0 65002 i *m 10.0.0.11 0 65002 i *m 10.0.0.12 0 65002 i *> 10.0.0.1 0 65002 i csr1000v#
nginx + LoadBalancer (MetalLB) を起動
nginx のコンテナ 3 つと、MetalLB を使った LB Service を起動させて nginx へのアクセスに対してロードバランシングさせるようにします。
[root@k8s-master1 ~]# cat nginx.yaml apiVersion: apps/v1beta2 kind: Deployment metadata: name: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx 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 [root@k8s-master1 ~]# kubectl apply -f nginx.yaml deployment.apps/nginx created service/nginx created [root@k8s-master1 ~]#
Pod と Service が作成され、nginx が紐付いている Service に 10.100.0.0 が割当られていることがわかります。
[root@k8s-master1 ~]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-5b688b67d-7tv9n 1/1 Running 0 8m6s 192.168.195.141 k8s-worker2.mylab178.tech <none> <none> nginx-5b688b67d-jphvf 1/1 Running 0 8m6s 192.168.160.9 k8s-worker1.mylab178.tech <none> <none> nginx-5b688b67d-lp5pq 1/1 Running 0 8m6s 192.168.165.209 k8s-worker3.mylab178.tech <none> <none> [root@k8s-master1 ~]# [root@k8s-master1 ~]# kubectl get services NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d23h nginx LoadBalancer 10.111.193.73 10.100.0.0 80:30225/TCP 8m10s [root@k8s-master1 ~]#
ロードバランスの動作を確認するために、各 Pod(nginx) の index.html に Pod 名が表示されるようにしておきます。
[root@k8s-master1 ~]# for PODNAME in `kubectl get pods -l app=nginx -o jsonpath='{.items[*].metadata.name}' `; do > kubectl exec -it ${PODNAME} -- cp /etc/hostname /usr/share/nginx/html/index.html; > done [root@k8s-master1 ~]#
CSR1000v に Service の External IP、10.100.0.0 が広告されてきていることがわかります。
csr1000v#show ip route vrf k8s-metallb 1.0.0.0/32 is subnetted, 2 subnets C 1.1.1.1 is directly connected, Loopback1 B 1.1.1.2 is directly connected, 01:20:31, Loopback2 10.0.0.0/8 is variably subnetted, 5 subnets, 2 masks C 10.0.0.0/24 is directly connected, GigabitEthernet2 L 10.0.0.254/32 is directly connected, GigabitEthernet2 B 10.0.1.0/24 is directly connected, 01:20:31, GigabitEthernet3 L 10.0.1.254/32 is directly connected, GigabitEthernet3 B 10.100.0.0/32 [20/0] via 10.0.0.13, 00:09:14 [20/0] via 10.0.0.12, 00:09:14 [20/0] via 10.0.0.11, 00:09:14 [20/0] via 10.0.0.1, 00:09:14 192.168.116.0/26 is subnetted, 1 subnets B 192.168.116.0 [20/0] via 10.0.0.1 (k8s-calico), 00:45:42 192.168.160.0/26 is subnetted, 1 subnets B 192.168.160.0 [20/0] via 10.0.0.11 (k8s-calico), 00:43:47 192.168.165.0/26 is subnetted, 1 subnets B 192.168.165.192 [20/0] via 10.0.0.13 (k8s-calico), 00:42:16 192.168.195.0/26 is subnetted, 1 subnets B 192.168.195.128 [20/0] via 10.0.0.12 (k8s-calico), 00:43:35 csr1000v#show bgp vpnv4 unicast vrf k8s-metallb Network Next Hop Metric LocPrf Weight Path Route Distinguisher: 65000:1 (default for vrf k8s-metallb) VRF Router ID 1.0.0.1 *> 1.1.1.1/32 0.0.0.0 0 32768 ? *> 1.1.1.2/32 0.0.0.0 0 32768 ? *> 10.0.0.0/24 0.0.0.0 0 32768 ? *> 10.0.1.0/24 0.0.0.0 0 32768 ? *m 10.100.0.0/32 10.0.0.13 0 65001 ? *m 10.0.0.12 0 65001 ? *m 10.0.0.1 0 65001 ? *> 10.0.0.11 0 65001 ? *> 192.168.116.0/26 10.0.0.1 0 65002 i *> 192.168.160.0/26 10.0.0.11 0 65002 i *> 192.168.165.192/26 10.0.0.13 0 65002 i *> 192.168.195.128/26 10.0.0.12 0 65002 i csr1000v#
External IP への接続
Client から External IP に対して http アクセスをしてみます。各 Pod にロードバランスされていることが確認できます。
user1@WSL:~$ for i in `seq 10` ; do curl -s 10.100.0.0 ; done nginx-5b688b67d-jphvf nginx-5b688b67d-7tv9n nginx-5b688b67d-jphvf nginx-5b688b67d-lp5pq nginx-5b688b67d-7tv9n nginx-5b688b67d-jphvf nginx-5b688b67d-lp5pq nginx-5b688b67d-7tv9n nginx-5b688b67d-lp5pq nginx-5b688b67d-7tv9n user1@WSL:~$
Pod IP への接続
続いて Client から Pod IP に対して hpttp アクセスしてみます。
user1@WSL:~$ curl 192.168.195.141 nginx-5b688b67d-7tv9n user1@WSL:~$ curl 192.168.195.141 nginx-5b688b67d-7tv9n user1@WSL:~$ curl 192.168.195.141 nginx-5b688b67d-7tv9n user1@WSL:~$ curl 192.168.160.9 nginx-5b688b67d-jphvf user1@WSL:~$ curl 192.168.160.9 nginx-5b688b67d-jphvf user1@WSL:~$ curl 192.168.160.9 nginx-5b688b67d-jphvf user1@WSL:~$ curl 192.168.165.209 nginx-5b688b67d-lp5pq user1@WSL:~$ curl 192.168.165.209 nginx-5b688b67d-lp5pq user1@WSL:~$ curl 192.168.165.209 nginx-5b688b67d-lp5pq user1@WSL:~$
Pod IP に対して外部から直接アクセス出来ることを確認できました。