天天看點

kubeadm工作原理-kubeadm init原理分析-kubeadm join原理分析

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流程結束。

kubeadm工作原理-kubeadm init原理分析-kubeadm join原理分析

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流程結束。

kubeadm工作原理-kubeadm init原理分析-kubeadm join原理分析

繼續閱讀