結論、何ができて何ができなかったか
- ルータ(特にNAT機能)とCALICOは共存できない
今回はルータ内に仮想サーバをたて、そっちをノード化することで対応できた。
NAT機能をCalico側で対応することも可能らしいが、今回は未検証。 - 複数IPを持つことが原因のトラブルは解決可能
ネットワークが2つ推奨なceph osdとの共存は可能と思われる(が、今回の構成では結果としてやっていない)
経緯
かれこれ5年ほど自宅サーバを運用している。もっぱらファイルサーバ用途だったのだが、素人の場当たり的な運用によりしばしば設定ミスによる障害が発生し、可用性はかなり低かった。
可用性の低さ自体は自分専用なので許容していたのだが、とはいえ高いに越したことはない。
ということで、学習も兼ねて、自作ルータ2台のマルチホーミング1でゲートウェイを、cephでストレージを、kubernetesでサービスをそれぞれ冗長化するという計画をここ1年ほどちまちま進めていた。
で、自作ルータもcephクラスタの構築も済み、満をじしてkubernetesを導入しようとしたところ、色々うまく行かなかったので、それについて書く。
やりたいこと
- サービスの冗長化のためkubernetesの導入
- すでに稼働中のcephクラスター(自作ルータ含む)に同居させ、ceph RBDをストレージとして利用する。
- プライベート用のサービスに加え、将来的に公開用のサービス2をネットワーク的に分離するため、cniとしてネットワークポリシーを設定可能なcalicoを使用する。
起きたトラブル(本記事で扱うこと)
- ノードが複数のIPを持っていた場合に、Internal IPとしてクラスタ外のIPが割り振られる
⇒Internal IPの手動指定 - ルータ兼用ノードでBGPサーバとCALICOのポートが衝突する。
=> CALICO側でリッスンするIPを指定したり、ポートを変更する - ルータ兼用ノードでファイヤーウォール,NATとCALICOが共存できない
=> ルータ内に仮想サーバを建て、そちらをノード化する。
本記事で扱わないこと
- Cephクラスタの構築
- 自作ルータの構築
- Calicoの基本設定
- 仮想サーバの構築
構成
なお、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台
- アーキテクチャ: amd64
- OS: Debian Bookworm
- VPNゲートウェイ
- 中継サーバとの経路の冗長化にBGPによる経路交換をしている
- 自宅クラスタ側の経路の冗長化に、ゲートウェイをkeepalivedで切り替えられるようにしている。
- CEPH 管理ノード
- CEPH OSD 3
AI用サーバ1台
- アーキテクチャ: amd64
- OS: Fedora
日進月歩のAI用ということもあり、新しい機能をすぐ使えるようにこいつだけDebianではなくFedoraを採用している。4
幸い今の所cephもkubernetesもfedoraが混じっていること自体は問題にはなっていない。 - 元はゲーミングPCだったが、サーバ趣味がゲームよりも優先された結果今年とうとうヘッドレスになった。
- CEPH 管理ノード
- CEPH OSD
ワーカーノード3台
- アーキテクチャ: arm64
(いずれもRaspberry Pi 4 8GBRAMモデル。) - OS: Debian bookwarm
- 以降とくに今回の記事では触れない
(驚いたことに、アーキテクチャ混在でも問題が起きなかったので)
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つのまま。障害の原因が基本的に人為的なミスのため、ほとんど触らないレンタルルータの可用性については不満がないのと単純に2回線契約する金がもったいないので。 ↩
-
具体的には現在Lightsail上で動かしているアクセス解析ソフトのplausibleや、将来的に公開予定のmastodon互換のakkomaおひとり様インスタンスを自宅サーバに統合する予定。 ↩
-
ルータやAIサーバがosdを兼用しているのは、単純にGPUやNICの増築のため(SBCではなく)自作PC互換のハードウェアが都合が良かったため。とは言えコスト的にはなるべく安価なSBCを使い、自作PCは最小限にしたい。ということで、現在のルータ2台AI用サーバ1台の計3台体制に落ち着いた。3台というと、クラスタの最小限の数としてもちょうどいいため、コントロールノードなどもやらせている。将来的にはOrange Pi Plusのような、NIC二つにSSDも詰めるSBCをOSD兼ワーカーノードとして追加するかもしれない ↩
-
Ubuntuではないのは、過去に2度LTSでないUbuntuでトラブルを経験し、以来LTSでないUbuntuを信用していないため。LTS使うならDebianでいいし。 ↩