写点什么

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

作者:良凯尔
  • 2022 年 3 月 29 日
  • 本文字数:5884 字

    阅读完需:约 19 分钟

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/pkiapiserver.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/kubernetesadmin.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/manifestskube-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/manifestsetcd.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: v1data:  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: Secretmetadata:  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-infoapiVersion: v1data:  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: nullkind: ConfigMapmetadata:
复制代码


(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: v1clusters:- cluster:    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0F...    server: https://192.168.1.10:6443  name: test-clustercontexts:- context:    cluster: test-cluster    user: system:node:test-cluster-master-1  name: system:node:test-cluster-master-1current-context: system:node:test-cluster-master-1kind: Configpreferences: {}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.confapiVersion: v1clusters:- cluster:    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0F...    server: https://192.168.1.10:6443  name: default-clustercontexts:- context:    cluster: default-cluster    namespace: default    user: default-auth  name: default-contextcurrent-context: default-contextkind: Configpreferences: {}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 流程结束。



发布于: 刚刚阅读数: 2
用户头像

良凯尔

关注

热爱的力量 2020.01.10 加入

kubernetes开发者

评论

发布
暂无评论
kubeadm工作原理-kubeadm init原理分析-kubeadm join原理分析_容器_良凯尔_InfoQ写作平台