1. 程式人生 > >Kubernetes 開船記-腳踏兩隻船:用 master 伺服器映象克隆出新叢集

Kubernetes 開船記-腳踏兩隻船:用 master 伺服器映象克隆出新叢集

自從2020年2月23日 [園子全站登船](https://www.cnblogs.com/cmt/p/12353192.html) 之後,我們一邊感嘆“不上船不知道,一上船嚇一跳” —— kubernetes 比 docker swarm 強大太多,一邊有一個杞人憂天的擔憂 —— 假如整個 kubernetes 叢集宕機怎麼辦? 隨著在船上的日子越來越長,隨著對 kubernetes 越來越依賴,我們的杞人憂天也越來越難以揮去...。終於有一天,一個貶義的俗語讓我們豁然開朗 —— “腳踏兩隻船”,如果只有1個叢集,kubernetes 再怎麼工業級標準,也無法讓我們高枕無憂,唯有2個叢集。於是,我們找到了自己的解憂之道 —— 再開一艘船。 再開一艘船的前提條件是再造一艘船,而造船的最佳方式顯然是從現有的這艘船克隆出一艘新船。對應到我們的 kubernetes 叢集是用阿里雲 ecs 伺服器自己搭建的場景,最佳方式就是用已有叢集 master 伺服器的阿里雲 ecs 映象建立新叢集。 帶著這個美好想法,我們開始動手造船 —— 克隆新 kubernetes 叢集,但很快就遇到了殘酷的現實。k8s天不怕地不怕,就怕名兒換(換IP地址或者主機名),而通過映象建立的 master 伺服器使用的是不同IP地址與主機名,雖然不改主機名不會給新叢集帶來問題,但是對命名控們來說這是無法接受的,於是修改新 master 的IP地址與主機名成為克隆的2個挑戰。 經過努力,我們終於戰勝了這2個挑戰,成功克隆出了新叢集,今天通過這篇博文分享一下主要操作步驟。 ### 背景資訊 * 已有叢集 master 主機名是 `k8s-master0`,IP地址是 `10.0.1.81` * 新叢集 master 主機名是 `kube-master0`,IP地址是 `10.0.9.171` * 已有叢集 kubernetes 版本是 1.17.0,新叢集 kubernetes 版本是 1.20.2 * master 伺服器作業系統是 ubuntu 18.04 ### 準備工作 * 已有叢集 master 伺服器 k8s-master0 打快照,建立映象,用映象建立新伺服器 kube-master0 ### 修改IP地址 從 10.0.1.81 改為 10.0.9.171 1)將 /etc/kubernetes 目錄中與IP地址關聯的配置替換為新IP地址 涉及的配置檔案 ```text /etc/kubernetes/kubelet.conf /etc/kubernetes/manifests/etcd.yaml /etc/kubernetes/manifests/kube-apiserver.yaml ``` 通過下面的命令快速完成修改 ```sh oldip=10.0.1.81 newip=10.0.9.171 cd /etc/kubernetes find . -type f | xargs sed -i "s/$oldip/$newip/" ``` 2)給 etcd 啟動命令新增引數 開啟 `/etc/kubernetes/manifests/etcd.yaml`,給`command` 新增 ```text --initial-cluster-state=new --force-new-cluster ``` 注:不太確定該步驟是否必需,當時第一次修改IP之後叢集總是無法正常執行,加了上面的引數才解決,叢集正常執行才能進行第4步的操作。 3)通過 iptables 將舊 IP 地址對映到新 IP 地址 ```sh iptables -t nat -A OUTPUT -d 10.0.1.81 -j DNAT --to-destination 10.0.9.171 ``` 4)修改叢集中與舊IP地址相關的配置 通過下面的命令重啟叢集使之前的修改生效,恢復叢集的基本執行,可以執行 kubectl 命令 ```sh systemctl daemon-reload && systemctl restart kubelet && systemctl restart docker ``` 替換 kubeadm-config ConfigMap 中的舊IP地址配置 ```sh kubectl -n kube-system edit cm kubeadm-config %s/10.0.1.81/10.0.9.171 ``` 5)重新生成 etcd-server 證書(這個證書與IP地址關聯) ```text cd /etc/kubernetes/pki/etcd rm server.crt server.key kubeadm init phase certs etcd-server ``` 6)更新當前使用者的 .kube/config ```sh cp -i /etc/kubernetes/admin.conf $HOME/.kube/config ``` 7)去掉在第2步給 etcd 啟動命令新增的引數 ```sh # --initial-cluster-state=new # --force-new-cluster ``` 8)重啟 kubelet 與 docker 服務使修改生效 ```sh systemctl daemon-reload && systemctl restart kubelet && systemctl restart docker ``` 9)新叢集恢復正常執行 ```text NAME STATUS ROLES AGE VERSION k8s-master0 Ready master 376d v1.17.0 ``` 注:這時 master 的主機名還沒修改 ### 升級 kubernetes 版本 這與克隆新叢集沒有關係,是我們在克隆過程中順便升級,詳見 [Kubernetes 升級過程記錄:從 1.17.0 升級至最新版 1.20.2](https://www.cnblogs.com/dudu/p/14274601.html) ### 修改主機名 從 k8s-master0 改為 kube-master0 1)將宿主機 hostname 修改為 kube-master0 ```sh hostnamectl set-hostname kube-master0 ``` 2)替換 /etc/kubernetes/manifests 中與主機名相關的配置 ```sh oldhost=k8s-master0 newhost=kube-master0 cd /etc/kubernetes/manifests find . -type f | xargs sed -i "s/$oldhost/$newhost/" ``` 3)匯出叢集中 k8s-master0 的 node 配置檔案 ```sh kubectl get node k8s-master0 -o yaml > kube-master0.yml ``` 4)將配置檔案中的 k8s-master0 替換為 kube-master0 ```sh sed -i "s/k8s-master0/kube-master0/" kube-master0.yml ``` 5)通過 etcdctl 命令從 etcd 資料庫中刪除 /registry/minions/k8s-master0 ```sh docker exec -it $(docker ps -f name=etcd_etcd -q) /bin/sh etcdctl --endpoints 127.0.0.1:2379 --cacert /etc/kubernetes/pki/etcd/ca.crt --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key /registry/minions/k8s-master0 ``` 執行上面的刪除命令後,k8s-master0 就會從 kubectl get nodes 的輸出列表中消失。 6)用之前匯出並修改的 node 配置檔案部署 kube-master0 ```sh kubectl apply -f kube-master0.yml ``` 部署後 kube-master0 出現中 kubectl get nodes 的輸出列表中,但處於 NotReady 狀態 ```text $ kubectl get nodes NAME STATUS ROLES AGE VERSION kube-master0 NotReady control-plane,master 21h v1.20.2 ``` 在這個地方折騰了不少時間,其實問題很簡單,kubelet 使用的證書是與主機名繫結的,修改主機名後證書失效了。 7)重新生成 kubelet 使用的證書 檢視 /etc/kubernetes/kubelet.conf ```conf users: - name: default-auth user: client-certificate: /var/lib/kubelet/pki/kubelet-client-current.pem client-key: /var/lib/kubelet/pki/kubelet-client-current.pem ``` 用 openssl 命令檢視證書繫結的 common name (CN) ```sh $ openssl x509 -noout -subject -in kubelet-client-current.pem subject=O = system:nodes, CN = system:node:k8s-master0 ``` 證書繫結的是舊主機名,需要針對新主機名重新生成證書 ```sh kubeadm init phase kubeconfig kubelet ``` 執行上面的命令重新生成證書後,/etc/kubernetes/kubelet.conf 中 users 部分變成下面的內容: ```conf users: - name: system:node:kube-master0 user: client-certificate-data: ***... client-key-data: ***... ``` 重啟 kubelet ```sh systemctl restart kubelet ``` kubelet 重啟後,kube-master0 就進入了 Ready 狀態 ```sh $ kubectl get nodes NAME STATUS ROLES AGE VERSION kube-master0 Ready control-plane,master 18h v1.20.2 ``` 到此,修改IP地址與主機名已成功完成。 ### 新船啟航 生成 node 加入叢集的命令 ```sh $ kubeadm token create --print-join-command ``` 通過生成的 join 命令加入新的 node ``` kubeadm join k8s-api:6443 --token ***** --discovery-token-ca-cert-hash ***** ``` 刪除所有舊的 NotReady 狀態的 node ```sh kubectl delete node $(kubectl get nodes | grep NotReady | cut -d " " -f1) ``` 克隆出的新船啟航! ```text NAME STATUS ROLES AGE VERSION kube-master0 Ready control-plane,master 21h v1.20.2 kube-node1 Ready 7d17h v1.20.2 kube-node2 Ready 6d16h v1.20.2 kube-node3 Ready 5d19h v1.20.2 ``` 參考資料: * [Changing master IP address](https://github.com/kubernetes/kubeadm/issues/338) * [Kubernetes from scratch: Certificates](https://medium.com/@oleg.pershin/kubernetes-from-scratch-certificates-53a1a16b5f03)