在 Hetzner Cloud 上架設 k3s
原文由 Ellie Huxtable 于 發布,訂閱此部落格
最近我為了 Atuin 架設了一個 HA 的 k3s 叢集!
我採用的是 HA etcd,這表示我們需要執行奇數個「server」節點,而且顯然不能只有一個。所以最少就是 3 個。
所有的伺服器都設在私有網路中、在各自的子網路裡,而且都是 ARM 的主機。防火牆則設定為幾乎阻擋所有的連入流量,以及大部分的連出流量。
老實說我很驚訝這件事竟然這麼簡單。我可能在不知情的情況下搞砸了什麼,但幾年前我用 kubeadm 的經驗可沒這麼愉快。k3s 萬歲!
參考資料
我看了不少資料。
這篇教學是個不錯的起點: https://community.hetzner.com/tutorials/k3s-glusterfs-loadbalancer
不過 k3s 的官方文件真的寫得很好,我主要就是看那些: https://docs.k3s.io/
我沒有使用那些(可能很不錯的)自動化 Hetzner k3s 建置工具。我想確保自己真的搞懂整個過程,而且過去我也曾在實體機器上跑過幾次 Kubernetes。只是當時用的是 kubeadm,不是 k3s。
伺服器
首先是第一台 server!要注意的是,k3s 把執行 control plane 的節點稱為「server」,其他節點則稱為「agent」。預設情況下,它會允許 master 節點也排程一般的工作負載,對我的使用情境來說應該沒問題。
我把 cloud controller 停用了,因為我們會安裝 Hetzner 專用的版本,同時也停用了 local storage,因為我打算使用 Longhorn。記得要選一組夠安全的 token!
另外,因為我在 Hetzner 上啟用了私有網路,而且希望叢集也使用它,所以我把 flannel 指向了私有網路的網卡介面。
curl -sfL https://get.k3s.io | sh -s - server \
--cluster-init \
--disable-cloud-controller \
--disable local-storage \
--node-name="$(hostname -f)" \
--flannel-iface=enp7s0 \
--kubelet-arg="cloud-provider=external" \
--secrets-encryption \
--disable=traefik \
--token=CHANGE ME後續的機器執行的指令非常類似
curl -sfL https://get.k3s.io | sh -s - server \
--server SERVER ADDRESS \
--disable-cloud-controller \
--disable local-storage \
--node-name="$(hostname -f)" \
--flannel-iface=enp7s0 \
--kubelet-arg="cloud-provider=external" \
--secrets-encryption \
--disable=traefik \
--token=CHANGE ME請注意:
- 確認網路介面是否正確
- 以安全的方式產生 token
- 確認是否仍需要 cloud-provider=external。在 Kubernetes v1.29+ 之後,可能就不需要了。
- 只有第一台 server 的建置需要加上「cluster init」。之後你就已經有叢集了——不需要再 init!
第一次我一路把 Hetzner Cloud Controller 都設好了,才在他們的文件中看到,要求每個節點都要加上
--kubelet-arg="cloud-provider=external"這個參數。大多數的參數,你只要重新執行安裝程式,它就會調整設定並重啟節點。但唯獨這個參數,如果你漏掉了,就得把整個叢集重建。HCCM 只會替一開始就正確設定的節點加上標籤,沒有標籤的節點就無法跟你的 LB 正確搭配運作。
補充一點背景。Kubernetes 有一大堆 CCM(cloud controller manager),基本上就是讓 k8s 能跟雲端供應商好好整合。為了要安裝外部的 CCM,我們目前需要設定上述那個參數。不過,這個參數已經被標記為棄用好一段時間了。原本預計要在 v1.24 移除,但到現在都還沒發生。
就我的理解,目前 kubelet 內建了一些 CCM,所以這個參數才能讓你使用非內建的 CCM。未來的規劃是不再內建 CCM,到時候這個參數就會變得多餘(所以才會被棄用)
有興趣深入了解的話可以看看這個 issue: https://github.com/kubernetes/kubernetes/issues/110018
根據連結的 PR,這個變更可能會在 v1.29 中加入。所以如果你跑的是 v1.29 以上的版本,或許就不需要 cloud provider 這個參數了!
到這個階段,你可以在
kubectl get nodes任何一台已設定好的機器上執行,會得到像這樣的結果:
NAME STATUS ROLES AGE VERSION
server-1 Ready control-plane,etcd,master 4m4s v1.27.6+k3s1
server-2 Ready control-plane,etcd,master 47s v1.27.6+k3s1
server-3 Ready control-plane,etcd,master 19s v1.27.6+k3s1也可以用下面的指令來檢查 secret encryption 的狀態:
k3s secrets-encrypt statusEncryption Status: Enabled
Current Rotation Stage: start
Server Encryption Hashes: All hashes match
Active Key Type Name
------ -------- ----
* AES-CBC aescbckey太棒了!
存取
在做更多設定之前,我想先從我的筆電設定好 kubectl 的存取。直接在節點本機上執行指令感覺不太對勁。
等設定全部完成後,我會再架設 Tailscale(或可能是 innernet)來做存取,但現在就先用 ssh port forward。你可以透過以下方式取得 kubeconfig:
cat /etc/rancher/k3s/k3s.yaml在其中一台節點上執行。
只要執行
ssh -L 6443:localhost:6443 root@a server ip就可以從本機裝置使用 kubectl 了。不過還是建議之後換成更穩固一點的作法喔 😊
Hetzner Cloud Controller Manager
試著快速唸三次看看。總之,hccm 會把我們的叢集跟 Hetzner Cloud API 整合起來,這表示我們可以(以下偷自 README):
- 將伺服器類型加入到
node.kubernetes.io/instance-type標籤中,設定外部的 IPv4 與 IPv6 位址,並將已從 Hetzner Cloud 刪除的節點也從 Kubernetes 中刪除。 - 透過在節點上設定
topology.kubernetes.io/region與topology.kubernetes.io/zone標籤,讓 Kubernetes 感知伺服器所在的故障域。 - 讓你的 Pod 流量可以使用 Hetzner Cloud 私有網路。
- 讓你可以在 Kubernetes Service 中使用 Hetzner Cloud Load Balancer。
Hetzner 的部落格文章建議直接套用一個 manifest,但 hccm 的官方文件則是推薦用 Helm chart。我是用啟用私有網路的方式來設定的(老實說我不懂為什麼會想在公開網路上跑這個,或許還是別這麼做吧?)
helm repo add hcloud https://charts.hetzner.cloud
helm repo update hcloud接著你需要建立一個 k8s secret,裡面包含 Hetzner Cloud API token 和網路名稱(這也是為什麼我想確保 secret 有做靜態加密的原因之一)
kubectl -n kube-system create secret generic hcloud --from-literal=token=SOME SECRET --from-literal=network=NETWORK NAMEhelm install hccm hcloud/hcloud-cloud-controller-manager -n kube-system --set networking.enabled=true --set networking.clusterCIDR=10.42.0.0/16請留意 clusterCIDR 的設定。如果你沒有改過 k3s 預設值,用 10.42.0.0/16 就可以了。
kubectl logs -n kube-system deployment/hcloud-cloud-controller-manager現在應該會看到一些輸出,接著
kubectl describe node agent-1應該會顯示一些額外的資訊標註:
node.kubernetes.io/instance-type=cax21
topology.kubernetes.io/region=fsn1
topology.kubernetes.io/zone=fsn1-dc14Agents
k3s 預設的行為其實會把所有的 deployment 都排程到節點上,所以如果你可以接受這個行為,可能就不會需要太多這類節點
設定方式跟伺服器非常類似!只是設定少一點。你同樣會需要之前的那組 token
curl -sfL https://get.k3s.io | sh -s - agent \
--server SERVER ADDRESS \
--node-name="$(hostname -f)" \
--flannel-iface=enp7s0 \
--kubelet-arg="cloud-provider=external" \
--token=CHANGE ME你可以依需求架設任意數量的這類節點
kubectl get nodesNAME STATUS ROLES AGE VERSION
agent-1 Ready <none> 9s v1.27.6+k3s1
server-1 Ready control-plane,etcd,master 100m v1.27.6+k3s1
server-2 Ready control-plane,etcd,master 96m v1.27.6+k3s1
server-3 Ready control-plane,etcd,master 96m v1.27.6+k3s1使用 Traefik 的 Ingress
稍早我把節點設定成停用 traefik。但現在我又要把它裝回來?
基本上,k3s 預設的 Traefik 會使用它自己的 load balancer。這其實沒什麼問題,但我想要確保我的 LB 是由 hccm 來管理。這樣我就能擁有一個真正的雲端 LB,而且所有的目標都會由叢集自動管理。
我花了一點時間才把它弄好(我在下面會詳細說明我犯的那些設定錯誤)
helm repo add traefik https://traefik.github.io/charts
helm repo update接著我建立了 traefik.values.yaml。我不會在這裡貼上全部內容——通常我是先取得預設值、存成檔案,再依需求修改。你最好也照自己想要的方式來設定!
不過我要說,你 service 上的這些 annotation 非常重要:
service:
enabled: true
## -- Single service is using `MixedProtocolLBService` feature gate.
## -- When set to false, it will create two Service, one for TCP and one for UDP.
single: true
type: LoadBalancer
# -- Additional annotations applied to both TCP and UDP services (e.g. for cloud provider specific config)
annotations:
load-balancer.hetzner.cloud/location: fsn1
load-balancer.hetzner.cloud/name: lb
load-balancer.hetzner.cloud/use-private-ip: "true"首先我們設定 lb 的位置。給它取個名字。然後告訴它要使用私有 IP。我一開始沒有加這個選項,結果 load balancer 完全跑不起來!所有的目標都是不健康的狀態。
預設情況下,hccm 只會把公開 IP 加到 LB 上。我的防火牆把它擋掉了(不允許公開流量直接連到節點),所以什麼都無法正常路由。改了這個設定後,一切就正常了 😇
我也使用 Cloudflare 來終止 SSL。我之後可能會再架設 cert-manager,但我正努力讓我的叢集盡可能保持無狀態。而且在邊緣終止 SSL 也很方便、輕鬆。
下一步
到這個階段,我已經可以部署無狀態的服務,並透過 Cloudflare 把 DNS 指過去了!
雖然現在運作得還不錯,但接下來還有很多想做的事
- 使用 Longhorn 架設儲存,目前我們還沒有儲存
- 建置監控
- 用 Cloud init 自動化 agent 的建置
- 簡易的 VPN 存取
- 進一步的安全性強化
隨機一篇部落格
留言
登入後參與討論