kubeadm工作原理-kubeadm init原理分析-kubeadm join原理分析。kubeadm是社群維護的Kubernetes叢集一鍵部署利器,使用兩條指令即可完成k8s叢集中master節點以及node節點的部署,其底層原理是利用了k8s TLS bootstrap特性。
kubeadm概述
kubeadm是社群維護的Kubernetes叢集一鍵部署利器,使用兩條指令即可完成k8s叢集中master節點以及node節點的部署,其底層原理是利用了k8s TLS bootstrap特性。
kubeadm部署k8s叢集示例
(1)k8s master節點部署:
$ kubeadm init
此外,我們也可以自己編寫yaml檔案來自定義kubeadm的啟動過程和一些元件的啟動參數等等
$ kubeadm init --config xxx.yaml
(2)k8s node節點部署(将一個node節點加入到已有叢集當中):
$ kubeadm join <kube-apiserver的ip + 端口> --token <token>
此外,我們也可以自己編寫yaml檔案來自定義kubeadm的啟動過程和一些元件的啟動參數,包括kube-apiserver的ip與端口、token等
$ kubeadm join --config xxx.yaml
關于自定義yaml檔案以及更多的kubeadm用法請參考:https://kubernetes.io/zh/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/
k8s TLS bootstrap概述
當k8s叢集開啟了TLS認證後,每個節點的kubelet元件都要使用由kube-apiserver的CA簽發的有效證書才能與kube-apiserver通信;當節點非常多的時候,為每個節點都單獨簽署證書是一件非常繁瑣而又耗時的事情。
此時k8s TLS bootstrap功能應運而生。
k8s TLS bootstrap功能就是讓kubelet先使用一個預先商定好的低權限token連接配接到kube-apiserver,向kube-apiserver申請證書,然後kube-controller-manager給kubelet動态簽署證書,後續kubelet都将通過動态簽署的證書與kube-apiserver通信。
關于k8s TLS bootstrap的詳細分析這裡暫時不展開。
kubeadm原了解析
大緻流程
在k8s master中,會先啟動一個kubelet,控制面元件通過kubelet static pod特性啟動,在k8s master控制面元件啟動成功後,其他節點需要加入到k8s叢集時,使用TLS bootstrap來簡化加入的過程,先通過bootstrap-token與kube-apiserver通信,自動從kube-controller-manager處簽發拿到與kube-apiserver通信的證書,然後自動生成與kube-apiserver通信的kubeconfig檔案,後續将使用kubeconfig檔案與kube-apiserver進行通信。
詳細流程解析
1.kubeadm init
kubeadm init的結果是完成一個k8s master節點的部署,包括kube-apiserver、kube-controller-manager、kube-scheduler、etcd等控制面元件以及kubelet資料面元件,即該master節點既是控制面又是資料面,是以master節點上也是可以運作pod的;
以下為kubeadm init的處理流程代碼(基于k8s v1.17.4版本),一共13步:
// cmd/kubeadm/app/cmd/init.go-NewCmdInit()
...
// initialize the workflow runner with the list of phases
initRunner.AppendPhase(phases.NewPreflightPhase()) // 1.環境檢查
initRunner.AppendPhase(phases.NewKubeletStartPhase()) // 2.配置并啟動kubelet
initRunner.AppendPhase(phases.NewCertsPhase()) // 3.證書生成
initRunner.AppendPhase(phases.NewKubeConfigPhase()) // 4.kubeconfig檔案生成
initRunner.AppendPhase(phases.NewControlPlanePhase()) // 5.控制面元件yaml生成
initRunner.AppendPhase(phases.NewEtcdPhase()) // 6.etcd元件yaml生成
initRunner.AppendPhase(phases.NewWaitControlPlanePhase()) // 7.等待控制面元件運作
initRunner.AppendPhase(phases.NewUploadConfigPhase()) // 8.上傳配置
initRunner.AppendPhase(phases.NewUploadCertsPhase()) // 9.上傳CA憑證/私鑰
initRunner.AppendPhase(phases.NewMarkControlPlanePhase()) // 10.master節點打污點
initRunner.AppendPhase(phases.NewBootstrapTokenPhase()) // 11.生成bootstrap token和ca證書configmap
initRunner.AppendPhase(phases.NewKubeletFinalizePhase()) // 12.更換kubelet證書
initRunner.AppendPhase(phases.NewAddonPhase()) // 13.安裝Addon
...
(1)環境檢查。檢查項包括作業系統核心版本、k8s元件暴露服務的指定端口是否被占用、docker是否安裝、iptables指令是否安裝等等,其實這一步還包括了拉取kubeadm部署所需的鏡像;
(2)配置并啟動kubelet。建立kubelet啟動所需的配置檔案,并啟動kubelet,kubeadm使用了systemd的方式部署啟動kubelet;
# systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
Loaded: loaded (/usr/lib/systemd/system/kubelet.service; disabled; vendor preset: enabled)
Drop-In: /usr/lib/systemd/system/kubelet.service.d
└─10-kubeadm.conf
Active: active (running) ...
為什麼master上還需要配置啟動kubelet呢?
因為kubeadm init的時候需要将master控制面元件kube-apiserver、kube-controller-manager、kube-scheduler、etcd以pod的方式運作起來,而現在又沒有在運作的控制面以及kubelet,怎麼辦呢,kubeadm的做法是,給master節點上也安裝啟動kubelet,然後使用kubelet static pod特性将master控制面元件運作起來。
關于static pod,詳細内容可參考:https://kubernetes.io/zh/docs/tasks/configure-pod-container/static-pod/
(3)證書生成。即生成kubernetes對外提供服務所需的各種證書,放到/etc/kubernetes/pki目錄下;
# ls /etc/kubernetes/pki
apiserver.crt apiserver-etcd-client.crt apiserver-etcd-client.key apiserver.key apiserver-kubelet-client.crt apiserver-kubelet-client.key ca.crt ca.key etcd front-proxy-ca.crt front-proxy-ca.key front-proxy-client.crt front-proxy-client.key sa.key sa.pub
(4)kubeconfig配置檔案生成。即生成master節點上kube-controller-manager、kube-scheduler、kubelet元件等通路kube-apiserver的kubeconfig檔案,放到/etc/kubernetes目錄下,檔案包含了apiserver的位址、監聽端口、證書等資訊,使用該kubeconfig檔案即可直接與kube-apiserver通信;
# ls /etc/kubernetes
admin.conf controller-manager.conf kubelet.conf manifests pki scheduler.conf
master上的kubelet啟動後,使用kubeadm生成的kubeconfig與kube-apiserver進行通信,通過證書輪換,向kube-apiserver申請新的證書,由kube-controller-manager簽發證書傳回。
注意:這裡master上的kubelet不會使用TLS bootstrap特性。
(5)控制面元件yaml檔案生成。即kubeadm為4個控制面元件kube-apiserver、kube-controller-manager、kube-scheduler生成pod yaml檔案,放到/etc/kubernetes/manifests目錄下,然後kubelet會根據static pod特性,使用pod的方式将它們部署起來;
# ls /etc/kubernetes/manifests
kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
(6)etcd元件yaml檔案生成。即kubeadm為etcd元件生成pod yaml檔案,放到/etc/kubernetes/manifests目錄下,然後kubelet會根據static pod特性,使用pod的方式将etcd部署起來;
# ls /etc/kubernetes/manifests
etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
(7)等待控制面元件運作。kubeadm會不間斷檢查localhost:6443/healthz這個url,等待master元件完全啟動;
(8)上傳配置。這裡會建立2個configmap有,都建立在kube-system命名空間下,名稱分别是kubeadm-config、kubelet-config-xxx(k8s版本),分别存儲着kubeadm的叢集配置資訊、kubelet的配置資訊;
(9)上傳CA憑證/私鑰。該步驟預設不執行,通過增加
——upload-certs
參數啟用,它會将相關的CA憑證/私鑰加密後作為data,在
kube-system
命名空間下建立名稱為
kubeadm-certs
的secret,給後續的master節點kubeadm join使用,這樣join時可以直接從secret中解密出CA憑證/私鑰,然後簽發其他證書,而無需手工複制相關CA憑證/私鑰;
kubeadm init執行完成後,會輸出一個名稱為certificateKey的值,然後在其他master節點join時,加上
--certificate-key
參數即可。
certificateKey是在添加新的master節點時用來解密
kubeadm-certs
secret中的證書的秘鑰。
kubeadm-certs示例如下,其中的證書和私鑰均已加密,通過certificateKey解密即可使用:
apiVersion: v1
data:
ca.crt: KfdZpEDF1wJfaexXls5...
ca.key: VXfm7luIyM3QT+Rd04+...
etcd-ca.crt: wwSzqCcltkrP26...
etcd-ca.key: gqusZazZLF33Ip...
front-proxy-ca.crt: EmfgKP6...
front-proxy-ca.key: wKMYSrk...
sa.key: pscxeFTGoCFZ6hrE1XK...
sa.pub: keey1WPkWdj2TjEb/oM...
kind: Secret
metadata:
name: kubeadm-certs
namespace: kube-system
ownerReferences:
- apiVersion: v1
blockOwnerDeletion: true
controller: true
kind: Secret
name: bootstrap-token-xxxxxx
...
...
type: Opaque
注意:secret
kubeadm-certs
和解密密鑰certificateKey會在兩個小時後失效。
(10)master節點打污點。将該master節點打上污點,不作為計算節點資料面使用;
(11)生成bootstrap token和ca證書configmap。
kubeadm會為該k8s叢集生成一個bootstrap token并列印出來,後續的node節點通過這個token,通過kubeadm join指令,使用TLS bootstrap特性即可加入到這個k8s叢集中,當然,這裡還包括了為該token建立RBAC的各個對象,賦予該token建立CSR證書簽名請求的權限、自動批複CSR請求的權限、輪換證書請求自動批複的權限等,這裡不展開介紹,後續分析k8s TLS bootstrap原理時再做分析;
kubeadm init執行完成後,也可以通過以下指令擷取token:
# kubeadm token list
kubeadm還會将ca.crt、apiserver url等資訊,儲存到一個configmap當中,給後續加入該k8s叢集的node節點使用,configmap名稱為cluster-info,位于kube-public命名空間下;
# kubectl get configmap -n kube-public -o yaml cluster-info
apiVersion: v1
data:
kubeconfig: |
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0F...
server: https://192.168.1.10:6443
name: ""
contexts: null
current-context: ""
kind: Config
preferences: {}
users: null
kind: ConfigMap
metadata:
(12)更換kubelet證書。前面說過,master上的kubelet啟動後,使用kubeadm生成的kubeconfig與kube-apiserver進行通信,通過證書輪換,向kube-apiserver申請新的證書,由kube-controller-manager簽發證書傳回。而這裡說的更換kubelet證書,其實就是将kubelet與kube-apiserver通信的kubeconfig檔案中的證書替換成由kube-controller-manager簽發傳回的證書,即将kubeconfig檔案中的
client-certificate
與
client-key
的值都替換成
/var/lib/kubelet/pki/kubelet-client-current.pem
;
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0F...
server: https://192.168.1.10:6443
name: test-cluster
contexts:
- context:
cluster: test-cluster
user: system:node:test-cluster-master-1
name: system:node:test-cluster-master-1
current-context: system:node:test-cluster-master-1
kind: Config
preferences: {}
users:
- name: system:node:test-cluster-master-1
user:
client-certificate: /var/lib/kubelet/pki/kubelet-client-current.pem
client-key: /var/lib/kubelet/pki/kubelet-client-current.pem
(13)安裝Addon。安裝coredns與kube-proxy,kubeadm init流程結束。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuUzN5gDM3cjMy0CNwQTMyAjM2EzNyMDMyIDMy0yNwAzM5MjMvw1MwIjMwIzLcdDMwMTOzIzLcd2bsJ2Lc12bj5ycn9Gbi52YuIjMwIzZtl2Lc9CX6MHc0RHaiojIsJye.png)
2.kubeadm join
kubeadm join的結果是完成一個k8s master節點或node節點加入一個已有的k8s叢集。
以下為kubeadm join的處理流程代碼(基于k8s v1.17.4版本),一共5步:
// cmd/kubeadm/app/cmd/join.go-NewCmdInit()
...
joinRunner.AppendPhase(phases.NewPreflightPhase()) // 1.環境檢查
joinRunner.AppendPhase(phases.NewControlPlanePreparePhase()) // 2.控制面準備
joinRunner.AppendPhase(phases.NewCheckEtcdPhase()) // 3.檢查etcd是否健康
joinRunner.AppendPhase(phases.NewKubeletStartPhase()) // 4.啟動kubelet
joinRunner.AppendPhase(phases.NewControlPlaneJoinPhase()) // 5.控制面操作
...
(1)環境檢查。檢查項包括作業系統核心版本、k8s元件暴露服務的指定端口是否被占用、docker是否安裝、iptables指令是否安裝等等,但這裡的環境檢查與kubeadm init時的檢查有點不同,這裡會區分是join的master節點還是node節點,如果是node節點,則僅僅進行node相關的檢查;
另外,這裡還會擷取kube-public命名空間下的configmap對象cluster-info,從中CA、master api等資訊;
(2)控制面準備。如果是node的join,這一步的邏輯不會執行。這裡會從kube-system命名空間中加載名稱為kubeadm-certs的secret對象,然後生成控制面元件kube-apiserver、kube-controller-manager、kube-scheduler所需的證書,最後生成它們的部署yaml,放置到kubelet的static pod目錄下,被kubelet使用static pod特性啟動;
(3)檢查etcd是否健康。
(4)啟動kubelet。根據CA、bootstrap token等資訊生成/etc/kubernetes/bootstrap-kubelet.conf檔案,通過TLS bootstrap機制,kubelet使用bootstrap token來向kube-apiserver申請證書,由kube-controller-manager簽發證書傳回,然後kubelet根據傳回的證書生成kubeconfig檔案并寫入到/etc/kubernetes/kubelet.conf檔案,後續kubelet将會使用該kubeconfig檔案來與kube-apiserver通信;
# cat /etc/kubernetes/kubelet.conf
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0F...
server: https://192.168.1.10:6443
name: default-cluster
contexts:
- context:
cluster: default-cluster
namespace: default
user: default-auth
name: default-context
current-context: default-context
kind: Config
preferences: {}
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
注意:bootstrap-kubelet.conf檔案會在kubelet.conf檔案生成後,被kubeadm删除掉;
(5)控制面操作。如果是node的join,這一步的邏輯不會執行。控制面操作包括生成etcd的static pod yaml、更新kube-system命名空間下的configmap對象kubeadm-config,将該控制節點資訊更新進去、将該master節點打上污點,不作為計算節點資料面使用;至此,kubeadm join流程結束。