Kubernetes with CALICOでコントロールノードとルータを兼用させる

以前Kubernetes with CALICOでコントロールノードとルータを兼用させたいといっていて、当時は結局兼用できずに仮想的に別サーバに分けることで解決したわけだが、先日再挑戦して無事できたので設定について晒す。

初めに

本記事の目的

本記事で扱うもの

本記事で扱わないもの

構成

Nodeバージョン一覧

$ kubectl get node -o wide
NAME         STATUS   ROLES           AGE    VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                           KERNEL-VERSION          CONTAINER-RUNTIME
router       Ready    control-plane   21d    v1.28.8   10.0.0.1        <none>        Debian GNU/Linux 12 (bookworm)     6.1.0-18-amd64          containerd://1.6.20
worker1      Ready    <none>          206d   v1.28.8   10.0.0.2        <none>        Debian GNU/Linux 12 (bookworm)     6.1.0-18-arm64          containerd://1.6.20
worker2      Ready    <none>          206d   v1.28.8   10.0.0.3        <none>        Debian GNU/Linux 12 (bookworm)     6.1.0-18-arm64          containerd://1.6.20

ネットワーク図

Internet                                                           
─┬────────────────────┬──────────────────────────┬─────────┬─        
 │                    │                          │         │         
┌┴────────────────┐ ┌─┴───────────────────┐    ┌─┴─────┐ ┌─┴─────┐ 
│Rental ONU/Router│ │Jump Server          │    │Client1│ │Client2│ 
└──────────────┬──┘ └─┬──────────────┬────┘    └────┬──┘ └┬──────┘ 
               │      │              │              │     │        
Home Network   │      │ Router VPN   │ Client VPN   │     │        
192.168.0.0/24 │      │ 10.1.0.0/24  │ 10.1.1.0/24  │     │        
───────────────┼───  ─┴────────┬─── ─┴──────────────┴─────┴────────
               │               │                                   
┌──────────────┼───────────────┼──────────────────────────┐        
│ Home build   │               │                          │        
│ Router     ┌─┴────────────┐ ┌┴──────────┐ ┌───────────┐ │        
│            │wan0          │ │wg0        │ │lan0       │ │        
│            │192.168.0.2/24│ │10.1.0.2/24│ │10.0.0.1/et│ │        
│            └──────────────┘ └───────────┘ └────┬──────┘ │        
│                                                │        │        
└────────────────────────────────────────────────┼────────┘        
                                                 │                 
Clusteer Network                                 │                 
10.0.0.0/24                                      │                 
───┬────────────┬────────────────────────────────┴─────────        
   │            │                                                  
┌──┴─────┐    ┌─┴──────┐                                           
│Worker1 │    │Worker 2│                                           
└────────┘    └────────┘                                           

以下ノードやネットワークの解説

レンタルルータ

自宅ネットワーク

クラスタネットワーク

自作ルータ(ルータ兼管理ノード)

自宅ノード(ワーカーノード)

中継サーバ

BGP

中継サーバとBGPでルートの共有をし、中継サーバから自宅サーバへアクセスできるようにしていた。 今回、中継サーバの構成はそのままに、自作ルータ側をCalicoを使ってクラスタネットワークのアドレス(10.0.0.0/24)を共有する

BGP Configuration

apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:                                        
  name: default
spec:
  bindMode: None
  communities:
    - name: bgp-large-community
      value: 64512:000
  prefixAdvertisements:
    - cidr: 10.0.0.0/24
      communities:
        - bgp-large-community

クラスタネットワークネットワーク10.0.0.0/24をPrefixAdvertisementsに設定することで、中継サーバにこれを広告している。

BGP Filter

クラスタネットワーク10.0.0.0/24をエクスポート、中継ネットワーク10.1.0.0/16をインポートするようBGPフィルターを設定する。

apiVersion: projectcalico.org/v3
kind: BGPFilter [130]
metadata:n: projectcalico.org/v3
  name: bgp-filter
spec:
  exportV4:
    - action: Accept
      matchOperator: In
      cidr: 10.0.0.0/24
  importV4:
    - action: Accept
      matchOperator: In
      cidr: 10.1.0.0/16

BGP Peer

中継サーバとVPN上で隣接するノードは自作ルータのみなので、自作ルータが中継サーバとピアを組むよう設定する

apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
  name: router
spec:
  peerIP: 10.1.0.1
  asNumber: 65048
  node: router
  filters:
    - bgp-filter
  sourceAddress: None
  keepOriginalNextHop: true

NAT

Host Endpoints

どうもCalicoではフォワーディングをホワイトリスト形式で行っているらしく、インターフェース間での転送にはそれぞれのインターフェースを設定する必要がある模様。

apiVersion: projectcalico.org/v3
kind: HostEndpoint
metadata:
  name: router-wan
spec:
  interfaceName: wan0
  node: router
  expectedIPs:
    - 192.168.0.2
---
apiVersion: projectcalico.org/v3
kind: HostEndpoint
metadata:
  name: router-vpn
  labels: defaultgateway
spec:
  interfaceName: wg0
  node: router
  expectedIPs:
    - 10.1.0.2
---
apiVersion: projectcalico.org/v3
kind: HostEndpoint
metadata:
  name: router-cluster
  labels: defaultgateway
spec:
  interfaceName: lan0
  node: router
  expectedIPs:
    - 10.0.0.1

IP Pool

IP Poolはdisabledにすることで、kubernetes内部で使わないIPアドレスを宣言できる。ここでnatOutgoing: trueとすることで、クラスタネットワークから外部へのNATを有効にできる。

apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  name: border-ipv4-ippool
spec:
  cidr: 10.0.0.0/24
  disabled: true
  natOutgoing: true

GlobalNetworkPolicy

クラスター全体のネットワークポリシー

本来ならファイアーウォールとして余計な通信を除くよう設定すべきだが、今回はとりあえず通信させたいのでIngressもEgressもすべて許可している。 注意点として、DNSやDHCPといったルータとしてのサービスもクラスター内に含まれる都合上、許可する必要がある。とくにApiserverのエンドポイントをドメインにしている状態でDNSをシャットダウンすると、kubectlでドメインが見つからず詰むので要注意。

apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  name: border-ipv4-ippool
spec:
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: allow-forward
spec:
  applyOnForward: true
  types:
    - Ingress
    - Egress
  ingress:
    - action: Allow
  egress:
    - action: Allow

結果

中継サーバでクラスタネットワークへのルートが確認できる。

$ ip route | grep "10.0.0.0/24"
10.0.0.0/24 nhid 56 via 10.1.0.1 dev wg0 proto bgp metric 20 

ワーカーノードが自作ルータ経由で外部にアクセスできることが確認できる。

$ traceroute google.com
traceroute to google.com (172.217.26.238), 30 hops max, 60 byte packets
 1  10.0.0.1 (10.0.0.1)  0.288 ms  0.162 ms  0.118 ms
 2  * * *
 3  * * *
 4  * * *
 5  * * *
 6  * * *
 7  bom05s09-in-f14.1e100.net (172.217.26.238)  40.357 ms 108.170.242.129 (108.170.242.129)  40.114 ms nrt12s51-in-f14.1e100.net (172.217.26.238)  40.437 ms

  1. kubernetes導入前はfrrで行っていた。 ↩︎