前回の記事で書いた「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にする、みたいな。↩
