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


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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM