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

結論、何ができて何ができなかったか

経緯

かれこれ5年ほど自宅サーバを運用している。もっぱらファイルサーバ用途だったのだが、素人の場当たり的な運用によりしばしば設定ミスによる障害が発生し、可用性はかなり低かった。

可用性の低さ自体は自分専用なので許容していたのだが、とはいえ高いに越したことはない。

ということで、学習も兼ねて、自作ルータ2台のマルチホーミング1でゲートウェイを、cephでストレージを、kubernetesでサービスをそれぞれ冗長化するという計画をここ1年ほどちまちま進めていた。

で、自作ルータもcephクラスタの構築も済み、満をじしてkubernetesを導入しようとしたところ、色々うまく行かなかったので、それについて書く。

やりたいこと

起きたトラブル(本記事で扱うこと)

本記事で扱わないこと

構成

なお、IPアドレスやホスト名は実際のものからわかりやすいものに変えてある。

$ kubectl get node -o wide               
NAME        STATUS   ROLES           AGE    VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                           KERNEL-VERSION           CONTAINER-RUNTIME
aiserver    Ready    control-plane   5d3h   v1.28.1   10.10.0.1       <none>        Fedora Linux 38 (Server Edition)   6.4.12-200.fc38.x86_64   cri-o://1.26.1
router1     Ready    control-plane   5d3h   v1.28.0   10.10.0.2       <none>        Debian GNU/Linux 12 (bookworm)     6.1.0-11-amd64           containerd://1.6.20
router2     Ready    control-plane   5d3h   v1.28.1   10.10.0.3       <none>        Debian GNU/Linux 12 (bookworm)     6.1.0-11-amd64           containerd://1.6.20
worker1     Ready    <none>          5d1h   v1.28.1   10.10.0.4       <none>        Debian GNU/Linux 12 (bookworm)     6.1.0-11-arm64           containerd://1.6.20
worker2     Ready    <none>          5d1h   v1.28.1   10.10.0.5       <none>        Debian GNU/Linux 12 (bookworm)     6.1.0-11-arm64           containerd://1.6.20
worker3     Ready    <none>          29h    v1.28.1   10.10.0.6       <none>        Debian GNU/Linux 12 (bookworm)     6.1.0-10-arm64           containerd://1.6.20

ルータ2台

AI用サーバ1台

ワーカーノード3台

Internal IPがクラスター外のものが振られる

参考: kubelet - How to change the internal IP of Kubernetes worker nodes? - Stack Overflow ホスト側でkubeletの設定をすることで指定が可能。

Debianの場合

KUBELET_EXTRA_ARGS='--node-ip 10.10.0.1'

Fedoraの場合は/etc/sysconfig/kubeletに置く。

CalicoのIPの指定

参考: Configure IP autodetection | Calico Documentation

CalicoのIPはデフォルトでは上述のInternalIPとなっていないので、InternalIPを参照するようcustom-resources.yamlに指定する必要がある。

apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  # Configures Calico networking.
  calicoNetwork:
    # Note: The ipPools section cannot be modified post-install.
    ipPools:
    - blockSize: 26
      cidr: 10.20.0.0/16
      encapsulation: VXLANCrossSubnet
      natOutgoing: Enabled
      nodeSelector: all()
+   nodeAddressAutodetectionV4:
+     kubernetes: NodeInternalIP

#<後略>

CalicoとFRRのBGPの併用

それぞれデフォルトでは0.0.0.0をリッスンしているようなので、それぞれ別のIPに紐づける必要がある。

FRR

bgpd=yes
#<略>
- bgpd_options=" -A 127.0.0.1"
+ bgpd_options=" -A 127.0.0.1 -l 10.30.0.1/24"
#<略>

参考: BGP — FRR latest documentation

CALICO

先ほどのInternalIPの設定はBGPのPEERの設定らしく、Listenする自分のIPはデフォルトではbindMode: Noneとなっており、0.0.0.0をリッスンしている模様。

BGP configuration | Calico Documentation

apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
  name: default
spec:
  bindMode: NodeIP

設定を読み込んで、ノードを作り直す

$ kubectl apply -f bgp-config
$ kubectl delete pod --all -n calico-system

これでInternal Node IPのみをリッスンするようになるはず

ファイヤーウォールとCalicoの衝突

動的なFirewalldを停止し、

$ sudo systemctl disable --now firewalld

nftablesで最低限のNATのみ静的に設定する

flush ruleset

table bridge bridge_filter {
    chain input {
        type filter hook input priority 0;
    }
    chain forward {
        type filter hook forward priority 0;
    }
    chain output {
        type filter hook output priority 0;
    }
}

table inet filter {
    chain input {
        type filter hook input priority 0;
    }
    chain forward {
        type filter hook forward priority 0;
    }
    chain output {
        type filter hook output priority 0;
    }
}
table ip nat {
    chain prerouting {
        type nat hook prerouting priority 0;
    }
    chain postrouting {
        type nat hook postrouting priority 0;
        oifname "wan0" iifname {"lan0"} masquerade
    }
}
$ sudo systemctl enable --now nftables

ここまでやった後のpodの状況が以下

$ kubectl get pod --all-namespaces        
NAMESPACE          NAME                                         READY   STATUS    RESTARTS        AGE
calico-apiserver   calico-apiserver-7b4bf88c5b-449ll            1/1     Running   0               107m
calico-apiserver   calico-apiserver-7b4bf88c5b-wtf4s            1/1     Running   1 (34m ago)     107m
calico-system      calico-kube-controllers-5f94576855-gzjzm     1/1     Running   0               42m
calico-system      calico-node-7thnm                            1/1     Running   0               42m
calico-system      calico-node-l4fs5                            1/1     Running   0               42m
calico-system      calico-node-n2dlk                            1/1     Running   0               42m
calico-system      calico-node-pqd98                            1/1     Running   0               42m
calico-system      calico-node-t966m                            1/1     Running   1 (34m ago)     42m
calico-system      calico-node-xtmr2                            1/1     Running   0               42m
calico-system      calico-typha-847799db4-hg8x7                 1/1     Running   2 (33m ago)     42m
calico-system      calico-typha-847799db4-rw7rt                 1/1     Running   0               42m
calico-system      calico-typha-847799db4-zcrth                 1/1     Running   0               42m
calico-system      csi-node-driver-8swwz                        2/2     Running   0               42m
calico-system      csi-node-driver-b92vj                        2/2     Running   0               42m
calico-system      csi-node-driver-cg4dm                        2/2     Running   0               42m
calico-system      csi-node-driver-jnzlr                        2/2     Running   0               42m
calico-system      csi-node-driver-vr5ck                        2/2     Running   2 (34m ago)     42m
calico-system      csi-node-driver-wggsc                        2/2     Running   0               42m
kube-system        coredns-5dd5756b68-6lnlt                     1/1     Running   0               29h
kube-system        coredns-5dd5756b68-cfsnj                     1/1     Running   0               29h
kube-system        etcd-hedgehog                                1/1     Running   3 (34m ago)     29h
kube-system        etcd-rabbit                                  1/1     Running   4 (3h1m ago)    29h
kube-system        etcd-wolf                                    1/1     Running   18              29h
kube-system        kube-apiserver-hedgehog                      1/1     Running   11 (34m ago)    29h
kube-system        kube-apiserver-rabbit                        1/1     Running   11 (3h1m ago)   29h
kube-system        kube-apiserver-wolf                          1/1     Running   18              29h
kube-system        kube-controller-manager-hedgehog             1/1     Running   4 (34m ago)     29h
kube-system        kube-controller-manager-rabbit               1/1     Running   5 (3h1m ago)    29h
kube-system        kube-controller-manager-wolf                 1/1     Running   3               29h
kube-system        kube-proxy-9b2tr                             1/1     Running   0               29h
kube-system        kube-proxy-hdzp8                             1/1     Running   1 (3h1m ago)    29h
kube-system        kube-proxy-j2xfb                             1/1     Running   1 (34m ago)     29h
kube-system        kube-proxy-n2fdc                             1/1     Running   14 (27h ago)    29h
kube-system        kube-proxy-pmk2q                             1/1     Running   0               29h
kube-system        kube-proxy-zwbj8                             1/1     Running   6 (27h ago)     28h
kube-system        kube-scheduler-hedgehog                      1/1     Running   10 (34m ago)    29h
kube-system        kube-scheduler-rabbit                        1/1     Running   6 (3h1m ago)    29h
kube-system        kube-scheduler-wolf                          1/1     Running   7               29h
tigera-operator    tigera-operator-7c8ff84d4f-bhq5h             1/1     Running   2 (33m ago)     29h

これで解決、かと思いきや…

ノードの仮想サーバへの移行

実際に運用していると何かおかしい。

結論から言うと、natを有するルータでcalicoがうまく動いていなかった。このエラーではcalicoのコンテナがしておらず、上記のようにrunningステータスとして一見正常に動いているように見え、その上ai用サーバのコントロールノードが正常だったため、一見するとちゃんと機能して言うように見えた。が、実際はルータ圏コントロールノードとワーカーノードが正常に通信出来ておらず、断続的に通信エラーが発生していた模様。

この段階でルータとCalicoの兼用は諦め、ルータ内にコントロールノード用の仮想サーバを建て、クラスターのネットワークにブリッジ接続することにした。

最初からそうしておけばよかった。

その後

Calico側でNATを設定することができるらしいことを知った。正直NATの問題が解消したところで他のトラブルがないとも限らないので、仮想サーバ化の方が無難だとは思うが。
そのうち仮想化による不都合が発生したら試すかもしれない。
参考: Configure outgoing NAT | Calico Documentation


  1. ここでいう擬似マルチホーミングは、あくまでも自宅クラスタのゲートウェイがマルチなのであって、インターネットへの経路自体は1つのまま。障害の原因が基本的に人為的なミスのため、ほとんど触らないレンタルルータの可用性については不満がないのと単純に2回線契約する金がもったいないので。 ↩︎

  2. 具体的には現在Lightsail上で動かしているアクセス解析ソフトのplausibleや、将来的に公開予定のmastodon互換のakkomaおひとり様インスタンスを自宅サーバに統合する予定。 ↩︎

  3. ルータやAIサーバがosdを兼用しているのは、単純にGPUやNICの増築のため(SBCではなく)自作PC互換のハードウェアが都合が良かったため。とは言えコスト的にはなるべく安価なSBCを使い、自作PCは最小限にしたい。ということで、現在のルータ2台AI用サーバ1台の計3台体制に落ち着いた。3台というと、クラスタの最小限の数としてもちょうどいいため、コントロールノードなどもやらせている。将来的にはOrange Pi Plusのような、NIC二つにSSDも詰めるSBCをOSD兼ワーカーノードとして追加するかもしれない ↩︎

  4. Ubuntuではないのは、過去に2度LTSでないUbuntuでトラブルを経験し、以来LTSでないUbuntuを信用していないため。LTS使うならDebianでいいし。 ↩︎