前回の記事で書いた「LA10超えて限界すぎるのでN100マシン買ってcontrol plane移したい」を実際にやったので続編いきます。
買ったのはBeelink Mini S12 ProのN100/メモリ16G/SSD500Gモデル。購入価格は31800 - 9000 = 22800円でした。
Windowsのライセンスがあれとかいう話がありますが、Ubuntu入れるので特に気にせず買いました。 Ubuntu 22.04 LTSのkernelにはWiFiとBluetoothのドライバがない1ので、このデバイスについての動作確認はWindowsないしUbuntu 23.10以降2等で行う必要があります。
既存のクラスタ、controll planeを移行できないか試行錯誤したけど結局諦めてPVだけ保全しクラスタ爆破し再作成したので、以下に書かれているのはクラスタ作成した手順です。
おうち N100マシンとさくらVPSをつなぐ
繋ぐうえで必要な条件がNetworking (CNI) - Documentationに説明があります。具体的には、以下2つ。
- controller planeとworkerでいくつかのTCP/UDP接続
- node間でpod CIDRとservice CIDRが疎通する
- pod CIDRは
10.244.0.0/16
で、service CIDRは10.96.0.0/12
- service CIDR宛接続は各node上のkube-proxy(が設定するiptablesやipvs)によってpodCIDR宛にDNATされるので、LBがないならpod CIDRだけnode間疎通すれば良い。
- pod CIDRは
以上2つを実現すれば良く、今回はWireGuardを使ってつなぎました。なお、おうちN100マシンはNATの内側にいるので、おうちN100マシンからさくらVPSへ接続を開始します。
# さくらVPS(worker) [Interface] Address = 10.220.0.1 ListenPort = 51100 PrivateKey = さくら秘密鍵 MTU=1460 [Peer] PublicKey = おうち公開鍵 AllowedIps = 10.220.0.2/32, 10.244.0.0/16 PersistentKeepalive = 25
# おうちN100(controller plane + worker) [Interface] Address = 10.220.0.2 PrivateKey = おうち秘密鍵 MTU = 1460 [Peer] PublicKey = さくら公開鍵 AllowedIps = 10.220.0.1/32, 10.244.0.0/16 PersistentKeepalive = 25 Endpoint = (さくらVPS):51100
あとはさくらVPSのiptablesで51100/udpの接続を通す。MTUはデバイス側でちゃんと設定されていれば不要な気もしますが、よしなに。
AllowedIps
の設定で前掲の条件2は満たせます。条件1はこの設定に加えてcontroller planeのインストール時にk0sの設定をいじることで対応します。
クラスタの設営
基本的にはManual (advanced) - Documentationにしたがっていきます。
controller planeの設営
変更点は2. Bootstrap a controller nodeで2つあります。
k0s config create
で生成した初期設定ファイルを書き換える
設定ファイルのspec.api.address
とspec.storage.etcd.peerAddress
を書き換え。
デフォルトだと、ここのIPアドレスは非ローカルなアドレスが入るんですが、spec.api.address
をWireGuardのインタフェイスIPアドレスにしてやります(今回の例では10.220.0.2
)。これによって、workerがWireGuardを経由してcontroll planeへ接続ができるようになります(詳細は後述)。
なお、spec.storage.etcd.peerAddress
についてはこの時点ではいじらなくていいけど、後日etcd増やしたくなったときのためにいじっておきます。
--enable-worker
をつける
sudo k0s install controller
の引数に--enable-worker
をつけると、controller planeかつnodeになります。
これでnodeにはなるんですが、Worker Node Configuration - Documentation > Taintsにあるとおり、nodeにrole.kubernetes.io/master:NoExecut
のtaintがついて3podが置かれません。この挙動はcontrollerを入れる際に--no-taints
をつけることで回避できます。
taintsは後から剥がしてもいいので、どっちでも。
worker の設営
controllerをつくったら、workerをつくります。controller planeで3. Create a join tokenにあるようにjoin tokenをつくってworkerに食わせます。トークンの有効期限はjoinするときだけ気にするようで、参加後はトークンがexpire/invalidateされていても正常にクラスタが動いてます。
ちなみにこのtokenにはcontroller planeの設定ファイルにあるspec.api.address
が接続先として入っています4。前節で書き換え済なので、このトークンを使うとにWireGuardトンネル経由でworkerからcontroller planeに接続します。
ちなみに、WireGuardでVPNを設定する際にさくらVPSのwgk0sデバイスに対し何らiptablesでACCEPTしていないので、worker(さくらVPS)→controller(おうちN100マシン)の片方向にしか接続を開始できません。ですが、Controller-Worker communicationにあるとおり、k0sは最初からKonnectivityが入っていてworkerからcontrollerに対してtcpセッションを発行するのでしっかりつながる。kubectlコマンドも、worker nodeで実行した場合は正常にトンネルを通過し、controller nodeで実行する場合はlocal接続なので無事に繋がります。
前回の記事との差異
externalIPs
をやめた
あるsvc manifestにexternalIPs
を指定すると、ここに書かれたIPアドレスを持たないノードに対し「書かれたexternalIPs
アドレス宛接続をIPVSによって当該svcのportについて書かれたpodへDNAT」する設定が入ります。このIPVSに流すための前段処理5によってWireGuardのパケットもlo
に吸い取られて接続断となります。
kube-proxyをiptablesモードにすれば回避できるような気もしますが、そこまでしてexternalIPs
を使う必要はないので大人しくNodePortを使う。
OpenEBS Local PVをやめた
どうせlocalPVなので、素朴なlocal PVにしました。また、2ノード構成にしたことからnfs PVを導入しています。
既にあるOpenEBS Local PVから、local PVおよびnfs PVに移行することになります。PV移行時にpersistentVolumeReclaimPolicyがRetainでなければPVC消したときにPVが消えたりするので要注意。
使い分けですが、Feed ReaderとしてMinifluxを使っている都合で立ててるPostgreSQLとひたすら溜めてるEEWをlocal PVに置き、それ以外はnode意識せずに済むようnfs PVに置くことにします。
StorageClassですが、volumeBindingMode変えなくてImmediateでもいいかな~ということで端折っています。
k8s local PV
local PVの特徴としてnodeAffinityを書くんですが、ここで使うnodeSelectorTerm.matchFields
はmetadata.name
しかkeyに使えません。APIのNodeSelectorTerm v1 coreには特段の記述がありませんが、実装を見るとmetadata.name
しか使えない。それ以外の項目を使うには、matchExpression
を使う必要があります。
また、PVCはPVを選ぶ際に、容量やアクセスモードなど限られたやつで適当に良さげなのをマッチするので、今回マイグレーションする際には確実にPVとPVCがマッチできないと困る。なので、PVにラベルを張り、PVCにラベルセレクターを書きます。
apiVersion: v1 kind: PersistentVolume metadata: name: local-eewlog labels: target: eewlog spec: capacity: storage: 2Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: local-storage local: path: /var/lib/eewlog/data nodeAffinity: required: nodeSelectorTerms: - matchFields: - key: metadata.name operator: In values: - name-of-sakura-vps - matchExpressions: - key: status.nodeInfo.machineID operator: In values: - hogepoyohogepoyo --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: eewlog-pvc spec: accessModes: - ReadWriteOnce storageClassName: local-storage resources: requests: storage: 2Gi selector: matchLabels: target: eewlog
NFS
インターネット and WireGuard越しなのでlatencyが気になりますが、qperfでlatency測ってみると5ms程度なので、たまにちょっと読み書きする程度なら気にならないはず。
$ qperf -vvu -t 1 -m 1k 10.220.0.2 tcp_lat tcp_lat: latency = 5.75 ms msg_size = 1 KB time = 1 sec timeout = 5 sec
まず素朴にnfsのclientとserverをNetwork File System (NFS) | Ubuntuの説明に従って入れる。
そしてNFS Serverの設定として、/etc/exports
を書く。k8sのnfs PVは指定したnfs targetをpodが走ってるnode上の適当なpathへmountし、当該pathを更にcontainer runtimeによってpod containerにbind mountする形で実装されています6。よって、nfsのアクセス許可ホストはnodeがWireGuard経由で繋いでくる10.220.0.0/24
になります。他のオプションは雰囲気で書き、sudo systemctl reload nfs-kernel-server
などで反映。
/export/nfs 10.220.0.0/24(rw,async,crossmnt,no_root_squash,no_subtree_check)
暫くしてから気づいたけど/export
にするとtab補完で/etc
が一発で出なくなってだるいですね。そのうち変えよう。
あとはPV(とPVC)のYAMLを書くだけ。
apiVersion: v1 kind: PersistentVolume metadata: name: nfs-dgmtx-status labels: target: dgmtx-status spec: capacity: storage: 2Mi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: server: 10.220.0.1 path: /export/nfs/dgmtx-status --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: dgmtx-status-pvc spec: accessModes: - ReadWriteOnce storageClassName: nfs resources: requests: storage: 2Mi selector: matchLabels: target: dgmtx-status
HitchのALPNでHTTP/2を有効化
ことし2月末にingress-nginxが更新されてnginxが1.25系に更新され、h2cがhttp/1.1へfallbackできるようになりました。これに伴いingress-nginxがh2cに対応しました。クラスタを再構築したことでingress-nginxも最新になりました。やったね。
対応としてはHitchのALPNにh2を追加します。追加と言ってもdocker-hitch見るとわかりますが、default valueはh2,http/1.1
なので、deploymentで--alpn-protos=http/1.1
を消すだけ。
ここでのh2通信は対Cloudflareなので、従前よりユーザとCloudflare間はh2だったはずだから見た目は変わんないけど、h2特有のなんかしらが出来たら良いな。
やってみた感想
めっちゃ安定した。現状はcontrollerのtaint剥がしてないのでpodあらかた従前のさくらVPS上workerにいますが、controller/workerどちらもLAは0.1ぐらいをふらふら。やはりメモリが足らなかった。
将来構想として東京にもN100ノード置くことをちょっと考えていますが、WireGuardの設定7さえなんとかすれば良さそう?
- 詳細はMini S12 Pro ubuntu wifi driver - Beelink ForumやAX101 Ubuntu 22.04 or 22.10 driver - Intel Communityを参照。↩
- 8/29にリリースされた24.04.1 LTSにdo-release-upgradeしたところ、Bluetoothデバイスが見えるようになりました(2024/08/31追記)。↩
-
実装を見ると、
--single-node
のときはtaintつかない。↩ -
このtokenはYAMLをzipしたあとbase64したものなので、
cat join.token | base64 -d | gunzip -
あたりで中身を覗くことが出来ます。↩ - IPVSはINPUTチェインを通った直後に処理するので、INPUTチェインまでパケットが流れる状態になってる必要がある。この順序はnetfilter/ipvs/ip_vs_core.cにコメントあり。↩
- 詳細はpodにNFSがmountされるまで #kubernetes #コードリーディング - クリエーションライン株式会社を参照。↩
- さくらVPSをhubにすると100Mbpsでサチるから東西どっちかのおうちノードをhubにする、みたいな。↩