[超重要]如何進行自建Kubernetes叢集核心憑證年度更新
最近讀 infoQ 翻譯的一篇Kubernetes 开源 9 年,但我们已经有了 8 年的踩坑血泪史(原文:Lessons From Our 8 Years Of Kubernetes In Production — Two Major Cluster Crashes, Ditching Self-Managed, Cutting Cluster Costs, Tooling, And More),看得心驚膽跳,其中 Kubernetes 憑證沒換把 Kubernetes 叢集搞掛這件事,我已經不只一次在文章裡看到。一般的教學文件會教你如何架 Kubernetes 叢集,教你 Pod、Service、Deployment、Network等等物件的基礎,但很少好好跟你談談憑證這件事。但如果你跟我一樣走自架 Kubernetes 叢集的路,這件事就無比重要,它關係到你的 Kubernetes 叢集的持續運作。這篇我們來解一下 Kubernetes 叢集憑證。
- 當我們利用
kubeadm
架設 Kubernetes 叢集時,預設會產生一張有效期一年的憑證。也就是說,預設 365 天之後,憑證會過期。 - 憑證會用於各項 Kubernetes 叢集核心服務之間。憑證過期,代表全部核心服務無法正常運作。也就是為何,一張憑證就能讓整個Kubernetes 叢集 Crash。
我們要了解 Kubernetes 叢集憑證可以從 kubeadm certs
下手:
$ sudo kubeadm certs check-expiration
[check-expiration] Reading configuration from the cluster...
[check-expiration] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
CERTIFICATE EXPIRES RESIDUAL TIME CERTIFICATE AUTHORITY EXTERNALLY MANAGED
admin.conf Feb 23, 2025 09:46 UTC 354d ca no
apiserver Feb 23, 2025 09:46 UTC 354d ca no
apiserver-etcd-client Feb 23, 2025 09:46 UTC 354d etcd-ca no
apiserver-kubelet-client Feb 23, 2025 09:46 UTC 354d ca no
controller-manager.conf Feb 23, 2025 09:46 UTC 354d ca no
etcd-healthcheck-client Feb 23, 2025 09:46 UTC 354d etcd-ca no
etcd-peer Feb 23, 2025 09:46 UTC 354d etcd-ca no
etcd-server Feb 23, 2025 09:46 UTC 354d etcd-ca no
front-proxy-client Feb 23, 2025 09:46 UTC 354d front-proxy-ca no
scheduler.conf Feb 23, 2025 09:46 UTC 354d ca no
CERTIFICATE AUTHORITY EXPIRES RESIDUAL TIME EXTERNALLY MANAGED
ca Feb 21, 2034 09:46 UTC 9y no
etcd-ca Feb 21, 2034 09:46 UTC 9y no
front-proxy-ca Feb 21, 2034 09:46 UTC 9y no
可以明確看到現有憑證會影響那些服務,EXPIRES
能知道過期日是那天,記得釘到行事曆上。
官方提供「主動」與「被動」更新的方式。
主動更新 Kubernetes 叢集憑證
就是定期更新你的 Control Plane 核心版本,例如 v1.29.0 升級為 v1.29.1。基本上,目前每季都會有小版號會推出,更新之後會自動更新憑證。當然前提是,你的更新間隔必須少於一年。
被動更新 Kubernetes 叢集憑證
就是上面說的「行事曆」,每年特定日期進行手動更新。Kubernetes 叢集更新憑證的難度很低,唯一缺點就是怕忘記沒做。
$ sudo kubeadm certs renew all
[renew] Reading configuration from the cluster...
[renew] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
certificate embedded in the kubeconfig file for the admin to use and for kubeadm itself renewed
certificate for serving the Kubernetes API renewed
certificate the apiserver uses to access etcd renewed
certificate for the API server to connect to kubelet renewed
certificate embedded in the kubeconfig file for the controller manager to use renewed
certificate for liveness probes to healthcheck etcd renewed
certificate for etcd nodes to communicate with each other renewed
certificate for serving etcd renewed
certificate for the front proxy client renewed
certificate embedded in the kubeconfig file for the scheduler manager to use renewed
Done renewing certificates. You must restart the kube-apiserver, kube-controller-manager, kube-scheduler and etcd, so that they can use the new certificates.
更新指令設計的很輕巧,一行,沒了,並且訊息也非常好心提示你 kube-apiserver
、kube-controller-manager
、kube-scheduler
、etcd
這幾個核心服務必須重啟,它們才會套用新憑證。可以發現,這幾個都是 Kubernetes 叢集的 Static Pod。
$ ls /etc/kubernetes/manifests/
etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
Static Pod 也是 Pod,其實並沒有 restart
這種選項,可以暴力的用 kubectl delete
去刪除 Pod,來去觸發重新建立 Pod 的動作,來達到 restart
這個目的。
$ kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-5dd5756b68-mfgsn 1/1 Running 8 (3h1m ago) 10d
coredns-5dd5756b68-zcqj4 1/1 Running 8 (3h1m ago) 10d
etcd-k8s-master 1/1 Running 8 (3h1m ago) 10d
kube-apiserver-k8s-master 1/1 Running 8 (3h1m ago) 10d
kube-controller-manager-k8s-master 1/1 Running 8 (3h1m ago) 10d
kube-proxy-65954 1/1 Running 6 (3h1m ago) 10d
kube-proxy-g9nfh 1/1 Running 8 (3h1m ago) 10d
kube-proxy-hf56r 1/1 Running 6 (3h1m ago) 10d
kube-scheduler-k8s-master 1/1 Running 8 (3h1m ago) 10d
但懂 Static Pod 運作機制的讀者會知道,當 Yaml 不存在 /etc/kubernetes/manifests/
目錄下時,Static Pod 會被刪除,當 Yaml 建立在 /etc/kubernetes/manifests/
目錄下時,Static Pod 會被建立。我們應該學習優雅的方式。
mkdir -p /tmp/manifests
sudo mv /etc/kubernetes/manifests/*yaml /tmp/manifests
# 查詢 containerd 裡的容器狀態
sudo crictl ps
# 約過 20-30 秒後
sudo cp /tmp/manifests/*.yaml /etc/kubernetes/manifests
在我們將 Yaml 移出 /etc/kubernetes/manifests/
目錄後,因為核心服務的 Pod 被刪除,因此 kubectl
也會無法使用,因此可以暫時改用 crictl
來查詢容器狀態,以確定 Static Pod 已被刪除。
例如,我們在移出 Yaml 與移入 Yaml 前後進行查詢 containerd 的容器。
# 移出
$ sudo crictl ps
CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID POD
b19db98978e8a ead0a4a53df89 3 hours ago Running coredns 8 c0bcac8a2acef coredns-5dd5756b68-zcqj4
8bdfc01810f35 ead0a4a53df89 3 hours ago Running coredns 8 41b5ae3144211 coredns-5dd5756b68-mfgsn
b4ebf0346af23 f9c73fde068fd 3 hours ago Running kube-flannel 8 933d15c9c2c3f kube-flannel-ds-j22v5
889e4a5ed20ff 123aa721f941b 3 hours ago Running kube-proxy 8 e078feb5da8cc kube-proxy-g9nfh
# 移入
$ sudo crictl ps
CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID POD
49ba95ea22f7e 309c26d006295 1 second ago Running kube-scheduler 0 e10e9cf8a00de kube-scheduler-k8s-master
0b23f9a006398 4d9d9de55f196 1 second ago Running kube-controller-manager 0 3387d4f99f1be kube-controller-manager-k8s-master
1f2983169aea9 73deb9a3f7025 1 second ago Running etcd 0 dfb1848f8312d etcd-k8s-master
a36b0f2949fba eeb80ea665767 1 second ago Running kube-apiserver 0 491bcf049e723 kube-apiserver-k8s-master
b19db98978e8a ead0a4a53df89 3 hours ago Running coredns 8 c0bcac8a2acef coredns-5dd5756b68-zcqj4
8bdfc01810f35 ead0a4a53df89 3 hours ago Running coredns 8 41b5ae3144211 coredns-5dd5756b68-mfgsn
b4ebf0346af23 f9c73fde068fd 3 hours ago Running kube-flannel 8 933d15c9c2c3f kube-flannel-ds-j22v5
889e4a5ed20ff 123aa721f941b 3 hours ago Running kube-proxy 8 e078feb5da8cc kube-proxy-g9nfh
你可能會說,重開機就好了。這是對的,但這就少了練功的機會,而且一般來說,Control Plane 應該不是想重開機就能重開機的。
特別注意:執行此步驟執行時要非常小心,最好是安排在維護時間進行,因為等於這段時間你整個 Control Plane 是處於無法提供服務的狀態。尤其是單節點 Control Plane。
$ sudo kubeadm certs check-expiration
[check-expiration] Reading configuration from the cluster...
[check-expiration] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
CERTIFICATE EXPIRES RESIDUAL TIME CERTIFICATE AUTHORITY EXTERNALLY MANAGED
admin.conf Mar 06, 2025 06:10 UTC 364d ca no
apiserver Mar 06, 2025 06:10 UTC 364d ca no
apiserver-etcd-client Mar 06, 2025 06:10 UTC 364d etcd-ca no
apiserver-kubelet-client Mar 06, 2025 06:10 UTC 364d ca no
controller-manager.conf Mar 06, 2025 06:10 UTC 364d ca no
etcd-healthcheck-client Mar 06, 2025 06:10 UTC 364d etcd-ca no
etcd-peer Mar 06, 2025 06:10 UTC 364d etcd-ca no
etcd-server Mar 06, 2025 06:10 UTC 364d etcd-ca no
front-proxy-client Mar 06, 2025 06:10 UTC 364d front-proxy-ca no
scheduler.conf Mar 06, 2025 06:10 UTC 364d ca no
CERTIFICATE AUTHORITY EXPIRES RESIDUAL TIME EXTERNALLY MANAGED
ca Feb 21, 2034 09:46 UTC 9y no
etcd-ca Feb 21, 2034 09:46 UTC 9y no
front-proxy-ca Feb 21, 2034 09:46 UTC 9y no
重新確認憑證狀態會發現 RESIDUAL
被重設為 364d 的滿血狀態。
另外官網文件提到,最後請重新執行以下指令,將更新後的憑證重新套用一下,這樣 User 執行 kubectl
才不會有問題。 :
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
我個人在 kubeadm v1.28.0
這版實驗的結果,沒有更新 .kube/config
也能正常執行 kubectl
。
$ ll .kube/
-rw------- 1 kkbruce kkbruce 5645 Mar 3 14:15 config
$ ll /etc/kubernetes/
-rw------- 1 root root 5649 Mar 6 06:10 admin.conf
這點有點奇怪,可以看到 admin.conf
是 Mar 6 有被更新過。User 本身的 config
還是 Mar 3 的版本。我有進一步去比對兩個檔案的內容,Hash 確實不一樣。為避免麻煩,我最後還是乖乖執行,我就沒有往下去追為什麼舊版 config
的內容還能正常執行 kubectl
了。
自動更新 Kubernetes 叢集憑證指令碼
這裡我們想做兩件事:
- 可以將前面執行步驟收集到的指令整理成指令碼加以利用。
- 透過排程的方式,每 1 個月執行檢查一次。(再次提醒,重啟核心服務會造成 Control Plane 服務中斷哦。)
自動化排程很方便,但就持續服務的角度來看,會造成服務會中斷就很麻煩,請讀者自行考慮。
echo "[Task 1] Run certs renew all"
sudo kubeadm certs renew all
echo "[Task 2] Stop static pods"
sudo mkdir -p /tmp/manifests
sudo mv /etc/kubernetes/manifests/*yaml /tmp/manifests
echo "[Task 3] Wait for static pods closed (watch kube-apiserver container)..."
while true; do
numLines=$(sudo crictl ps | grep "kube-apiserver" | wc -l)
if [ "$numLines" = "0" ]; then
break
fi
sleep 1
done
echo "===== static pod has been removed! ====="
# start static pod
echo "[Task 4] Start static pod"
sudo cp /tmp/manifests/*.yaml /etc/kubernetes/manifests
# montior static pod
echo "Wait for static pods start(watch kube-apiserver container)..."
while true; do
numLines=$(sudo crictl ps | grep "kube-apiserver" | wc -l)
if [ "$numLines" != "0" ]; then
break
fi
sleep 1
done
echo "===== static pod has started! ====="
# update kubeconfig
echo "[Task 5] update kubeconfig"
sudo cp -f /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# display certs
echo "[Task 6] Run certs check-expiration"
sudo kubeadm certs check-expiration
有了 Shell Bash 指令碼,Linux 排程有很多選擇,Crontab 或 Systemd timer 就看讀者個人選擇吧。
參考資料:
沒有留言:
張貼留言
感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。