首先,什么是kubelet bootstrap?在安装 k8s worker node 时,基本上 worker 的初始状态仅仅是安装了 docker 和 kubelet,worker 需要一种机制跟 master 通信。但网络通信的基本假设是通信双方谁也不信任谁。所以,kubelet bootstrap要以自动化的方式解决如下几个问题:

  • 在只知道 api server IP 地址的情况下,worker node 如何获取 api server 的 CA 证书?
  • 如何让 api server 信任 worker?因为在 api server 信任 worker 之前,worker 没有途径拿到自己的证书,有点鸡生蛋蛋生鸡的感觉

本文的实验目的就是将一个新的 worker node(主机名为 test-node) 添加到已有的 k8s 集群中。

其实,使用 kubeadm 工具完成这个工作就是简单的一行命令,kubeadm 会自动完成很多工作。但为了更好的了解kubelet bootstrap的流程,本文不考虑使用 kubeadm

什么是 bootstrap token

要让 api server 信任 worker,worker 得需要先过 master 认证鉴权这一关。k8s 以插件化的方式支持很多种认证方式,比如 token 文件,x509证书,service account 等等,其中就包含一个名为“Bootstrap Token Authentication”的认证方式,基本上 k8s 的默认安装就默认支持这种认证方式。这种方式最初就是设计用来给添加 worker node 用的。使用 Bootstrap Token Authentication 时,只需告诉 kubelet 一个特殊的 token,kubelet 自然就能通过 api server 的认证。所以,首先得保证 k8s 中定义了这个 token:

  1. cat <<EOF | kubectl apply -f -
  2. apiVersion: v1
  3. kind: Secret
  4. metadata:
  5. name: bootstrap-token-abcdef
  6. namespace: kube-system
  7. type: bootstrap.kubernetes.io/token
  8. stringData:
  9. description: "The bootstrap token for testing."
  10. token-id: abcdef
  11. token-secret: 0123456789abcdef
  12. expiration: 2019-09-16T00:00:00Z
  13. usage-bootstrap-authentication: "true"
  14. usage-bootstrap-signing: "true"
  15. auth-extra-groups: system:bootstrappers:test-nodes
  16. EOF

在上面的 token 定义中,需要注意:

  • token 的 name 必须是 bootstrap-token-<token-id> 的格式
  • token 的 type 必须是 bootstrap.kubernetes.io/token
  • token 的 token-id 和 token-secret 分别是6位和16位数字和字母的组合
  • auth-extra-groups 定义了 token 代表的用户所属的额外的 group,而默认 group 名为 system:bootstrappers
  • 这种类型 token 代表的用户名为 system:bootstrap:<token-id>,在本文中就是 system:bootstrap:abcdef

如何生成 kubelet 证书

有了 token,kubelet 就能通过 api server 的认证,连接建立,至此是不是万事大吉了。NO!

也许你已经注意到了,上面的 token 定义有 expiration 字段,表示这个 token 是有有效期的,过了有效期,token 失效,kubelet 就无法使用 token 跟 api server 通信,所以这个 token 只能作为 kubelet 初始化时跟 api server 的临时通信,而并非持久方案。

kubelet 最终还是需要使用证书跟 api server 通信,证书从何而来?如果让 k8s 的安装人员为每一个 worker node 生成并维护一份证书,工作量太大太繁琐,这也是设计 bootstrap token 要解决的问题之一,即:kubelet 使用低权限的 bootstrap token 跟 api server 建立连接后,要能够自动向 api server 申请自己的证书,并且 api server 要能够自动审批证书。

是的,很多人都会想到,k8s 支持 cert sign API。在 k8s 的证书认证中,要么由管理员手动为用户生成证书,要么是使用更为服务化的方式,由需要证书的用户自己向 k8s 申请,k8s 管理员只需在后台审批即可。但手动的为每一个用户审批,并没有带来很大的便利性,所以 k8s 还支持自动审批,只不过目前自动审批仅仅针对 kubelet。下面我们就模拟一个用户手动发送 csr,让 k8s 自动审批。

首先生成一个名为 vip 的用户证书,该证书需要管理员审批,下面的操作都是以 k8s admin 用户身份执行:

  1. name=vip
  2. group=newlands
  3. cat <<EOF | cfssl genkey - | cfssljson -bare $name
  4. {
  5. "hosts": [],
  6. "CN": "$name",
  7. "key": {
  8. "algo": "ecdsa",
  9. "size": 256
  10. },
  11. "names": [
  12. {
  13. "C": "NZ",
  14. "ST": "Wellington",
  15. "L": "Wellington",
  16. "O": "$group",
  17. "OU": "Test"
  18. }
  19. ]
  20. }
  21. EOF
  22. # 创建 csr
  23. cat <<EOF | kubectl apply -f -
  24. apiVersion: certificates.k8s.io/v1beta1
  25. kind: CertificateSigningRequest
  26. metadata:
  27. name: $name
  28. spec:
  29. groups:
  30. - $group
  31. request: $(cat ${name}.csr | base64 | tr -d '\n')
  32. usages:
  33. - key encipherment
  34. - digital signature
  35. - client auth
  36. EOF
  37. # 管理员手动审批
  38. kubectl get csr $name -o json | jq -r '.status.certificate' | base64 -d > $name.crt

我们的目的是让 vip 用户发起的 csr 能够被自动审批。所以首先得允许 vip 用户访问 csr api,使用 kubeadm 安装 k8s 时已经默认创建了该 clusterrole,我们要做的就是给 vip 用户赋予这个 clusterrole:

  1. $ kubectl describe clusterrole system:node-bootstrapper
  2. Name: system:node-bootstrapper
  3. Labels: kubernetes.io/bootstrapping=rbac-defaults
  4. Annotations: rbac.authorization.kubernetes.io/autoupdate=true
  5. PolicyRule:
  6. Resources Non-Resource URLs Resource Names Verbs
  7. --------- ----------------- -------------- -----
  8. certificatesigningrequests.certificates.k8s.io [] [] [create get list watch]
  9. root@lingxiantest-k8s-master [~]
  10. $ kubectl create clusterrolebinding csr-vip --clusterrole system:node-bootstrapper --user vip
  11. clusterrolebinding.rbac.authorization.k8s.io/csr-vip created

其次,我们需要给 vip 用户另一个特殊的 clusterrole,这个 clusterrole 在使用 kubeadm 安装 k8s 时也已经被自动创建:

  1. $ kubectl describe clusterrole system:certificates.k8s.io:certificatesigningrequests:nodeclient
  2. Name: system:certificates.k8s.io:certificatesigningrequests:nodeclient
  3. Labels: kubernetes.io/bootstrapping=rbac-defaults
  4. Annotations: rbac.authorization.kubernetes.io/autoupdate=true
  5. PolicyRule:
  6. Resources Non-Resource URLs Resource Names Verbs
  7. --------- ----------------- -------------- -----
  8. certificatesigningrequests.certificates.k8s.io/nodeclient [] [] [create]
  9. $ kubectl create clusterrolebinding \
  10. nodeclient-vip \
  11. --clusterrole system:certificates.k8s.io:certificatesigningrequests:nodeclient \
  12. --user vip

然后我们以 vip 用户身份执行如下命令(与上文类似),注意因为自动审批目前只针对 kubelet,所以 vip 申请的 csr 用户名必须是 system:node:<name> 的形式,group 必须是 system:nodes,并且 usages 也必须是命令中所示:

  1. name=system:node:test-node
  2. group=system:nodes
  3. cat <<EOF | cfssl genkey - | cfssljson -bare $name
  4. {
  5. "hosts": [],
  6. "CN": "$name",
  7. "key": {
  8. "algo": "ecdsa",
  9. "size": 256
  10. },
  11. "names": [
  12. {
  13. "C": "NZ",
  14. "ST": "Wellington",
  15. "L": "Wellington",
  16. "O": "$group",
  17. "OU": "Test"
  18. }
  19. ]
  20. }
  21. EOF
  22. cat <<EOF | kubectl apply -f -
  23. apiVersion: certificates.k8s.io/v1beta1
  24. kind: CertificateSigningRequest
  25. metadata:
  26. name: $name
  27. spec:
  28. groups:
  29. - $group
  30. request: $(cat ${name}.csr | base64 | tr -d '\n')
  31. usages:
  32. - key encipherment
  33. - digital signature
  34. - client auth
  35. EOF

然后查看 csr,可以看到 csr 的状态已经是 Approved,Issued,实验结束:

  1. $ kubectl get csr
  2. NAME AGE REQUESTOR CONDITION
  3. system:node:test-node 3s vip Approved,Issued

由上述实验可以得知,为了让 bootstrap token 所代表的用户(username:system:bootstrap:<token-id>,group:system:bootstrappers)申请的 csr 能够被自动审批,必须要给该用户或组赋予两个 clusterrole:certificatesigningrequests.certificates.k8s.io/nodeclient 和 system:node-bootstrapper

  1. kubectl create clusterrolebinding nodeclient-test-node \
  2. --clusterrole system:certificates.k8s.io:certificatesigningrequests:nodeclient \
  3. --user system:bootstrap:abcdef
  4. kubectl create clusterrolebinding csr-test-node \
  5. --clusterrole system:node-bootstrapper \
  6. --user system:bootstrap:abcdef

bootstrap token 后处理

通过上述讲解我们知道,bootstrap token 是 kubelet 引导的关键,如果其他人知道了 bootstrap token 那就意味着可以访问 k8s 的某些资源,所以要么给 bootstrap token 设定一个很短的有效期,要么 kubelet 引导结束后就手动删除,防止 bootstrap token 被再次使用。

kubelet 客户端证书 renewal

现在,kubelet 有了自己跟 api server 通信的证书,剩下的一个任务就是处理证书过期的问题。既然证书的生成都是自动化的,如果证书的 renew 需要手动那就太 low 了,所以跟生成证书的原理相似,k8s 也提供一个 clusterrole 可以赋予 kubelet 用户或组让 renew 证书的请求也能够被自动审批:

  1. $ kubectl describe clusterrole system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
  2. Name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
  3. Labels: kubernetes.io/bootstrapping=rbac-defaults
  4. Annotations: rbac.authorization.kubernetes.io/autoupdate=true
  5. PolicyRule:
  6. Resources Non-Resource URLs Resource Names Verbs
  7. --------- ----------------- -------------- -----
  8. certificatesigningrequests.certificates.k8s.io/selfnodeclient [] [] [create]

但这个 clusterrole 可不是赋予 bootstrap token 所代表的用户了,而是使用 bootstrap token 所获取到的证书所代表的用户。在 k8s 中,其用户名是 system:node:<node-name> 的形式,group 是system:nodes,所以就需要:

  1. kubectl create clusterrolebinding nodeclient-cert-renewal \
  2. --clusterrole system:certificates.k8s.io:certificatesigningrequests:selfnodeclient \
  3. --user system:node:test-node

在执行上面的命令前,你可以先检查一下相应的 clusterrolebinding 是否已经创建了:

  1. kubectl get clusterrolebindings -o json | jq -r '.items[] | select(.subjects // [] | .[] | [.kind,.name] == ["Group","system:nodes"]) | .metadata.name'

如果是使用 kubeadm 安装的 k8s cluster,你会看到上面的命令返回 kubeadm:node-autoapprove-certificate-rotation,继续查看这个 clusterrolebinding,你会发现正是我们想要的:

  1. $ kubectl describe clusterrolebinding kubeadm:node-autoapprove-certificate-rotation
  2. Name: kubeadm:node-autoapprove-certificate-rotation
  3. Labels: <none>
  4. Annotations: <none>
  5. Role:
  6. Kind: ClusterRole
  7. Name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
  8. Subjects:
  9. Kind Name Namespace
  10. ---- ---- ---------
  11. Group system:nodes

引导 kubelet

有了上述背景知识,在一个新的 worker node 上配置 kubelet service 就比较简单了。先总结一下我们都创建了什么东西:

  1. 创建了 bootstrap token
  2. 给 bootstrap token 代表的用户 system:bootstrap:abcdef 赋予 clusterole certificatesigningrequests.certificates.k8s.io/nodeclient 和 system:node-bootstrapper,让该用户可以访问 csr API 以及自动审批其创建的 csr
  3. 给新的 work node 代表的用户 system:node:test-node 赋予 clusterrole system:certificates.k8s.io:certificatesigningrequests:selfnodeclient,让它发送的证书 renew 的请求能被自动审批

接下来,需要生成 bootstrap 配置文件,这里假设我们已经知道 api server 的地址以及 ca 证书(关于如何获取这些信息后续会补充),你可以在 master node 执行如下命令生成 bootstrap-kubeconfig 文件并拷贝到 test-node 上:

  1. kubectl config set-cluster k8s --server https://10.0.0.11:6443 --certificate-authority=/etc/kubernetes/pki/ca.crt --embed-certs=true --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig
  2. kubectl config set-credentials test-node --token=abcdef.0123456789abcdef --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig
  3. kubectl config set-context test-node-bootstrap --cluster k8s --user test-node --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig
  4. kubectl config use-context test-node-bootstrap --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig

配置 kubelet systemd 配置文件,需要指定 --kubeconfig 以存放 bootstrap token 获取的证书信息,kubelet 会动态创建该文件,而证书文件本身默认存放在 /var/lib/kubelet/pki 目录下,可以指定 --cert-dir 自定义证书路径:

  1. cat <<EOF > /etc/systemd/system/kubelet.service
  2. [Unit]
  3. Description=Kubernetes Kubelet
  4. Documentation=https://github.com/kubernetes/kubernetes
  5. After=docker.service
  6. Requires=docker.service
  7. [Service]
  8. ExecStart=/usr/bin/kubelet \\
  9. --bootstrap-kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig \\
  10. --kubeconfig=/var/lib/kubelet/kubeconfig \\
  11. --network-plugin=cni \\
  12. --rotate-certificates=true \\
  13. --register-node=true \\
  14. --v=2
  15. Restart=on-failure
  16. RestartSec=5
  17. [Install]
  18. WantedBy=multi-user.target
  19. EOF

重启 kubelet 服务:

  1. systemctl daemon-reload; systemctl restart kubelet

待 kubelet 服务启动后,你可以在 worker node 上 /var/lib/kubelet/pki 目录下看到生成的证书,并且 kubelet 自动生成了 /var/lib/kubelet/kubeconfig 文件。

  1. $ ll /var/lib/kubelet/pki
  2. total 12
  3. -rw------- 1 root root 1114 Sep 17 13:38 kubelet-client-2018-09-17-13-38-33.pem
  4. lrwxrwxrwx 1 root root 59 Sep 17 13:38 kubelet-client-current.pem -> /var/lib/kubelet/pki/kubelet-client-2018-09-17-13-38-33.pem
  5. -rw-r--r-- 1 root root 2193 Sep 15 08:48 kubelet.crt
  6. -rw------- 1 root root 1675 Sep 15 08:48 kubelet.key
  7. $ ll /var/lib/kubelet/kubeconfig
  8. -rw------- 1 root root 1850 Sep 17 13:38 /var/lib/kubelet/kubeconfig

查看证书信息,仅关注 CN 和 O 字段信息,与之前的讲解一致:

  1. $ openssl x509 -noout -text -in /var/lib/kubelet/pki/kubelet-client-current.pem
  2. Subject: O=system:nodes, CN=system:node:test-node

使用 kubectl 查看 node 信息可以看到该 node 状态已经 ready:

  1. $ kubectl get node
  2. NAME STATUS ROLES AGE VERSION
  3. lingxiantest-k8s-master Ready master 6d v1.11.1
  4. lingxiantest-k8s-node1 Ready <none> 6d v1.11.1
  5. test-node Ready <none> 3m v1.11.1

总结

总结一下 kubelet 的 bootstrap 流程:

如果使用 kubeadm

上面的所有步骤都是假设 kubeadm 不可用,但如果是你自己的环境,那么使用 kubeadm 引导一个新的 kubelet 节点是最简单的,因为 kubeadm 会自动帮你干很多事儿。

  1. 在 master node 生成一个新的 token

    因为 bootstrap token 有效期默认是24小时,所以当你考虑向 cluster 中新增 node 时,原来创建的 token 可能早已过期,你需要创建一个新的 bootstrap token。

    1. $ kubeadm token list
    2. TOKEN TTL EXPIRES USAGES DESCRIPTION EXTRA GROUPS
    3. ebpxef.6059low577z0imyv <invalid> 2018-09-30T05:45:38Z authentication,signing <none> system:bootstrappers:kubeadm:default-node-token
    4. $ kubeadm token create
    5. mfa708.ppxrbz1g945jj2un
  2. 在新 worker node 上:

    1. kubeadm join --discovery-token-unsafe-skip-ca-verification --token mfa708.ppxrbz1g945jj2un 10.0.0.18:6443

    这里我为了省事儿,没有去验证 master 的 CA 公钥。命令执行后,你就会看到一个新的 node 已经加入 k8s cluster,是不是很简单?

Kubelet bootstrap 流程的更多相关文章

  1. Kubelet bootstrap认证配置步骤

    kubelet 授权 kube-apiserver 的一些操作 exec run logs 等 RBAC 只需创建一次就可以 kubectl create clusterrolebinding kub ...

  2. k8s TLS bootstrap解析-k8s TLS bootstrap流程分析

    当k8s集群开启了TLS认证后,每个节点的kubelet组件都要使用由kube-apiserver的CA签发的有效证书才能与kube-apiserver通信:当节点非常多的时候,为每个节点都单独签署证 ...

  3. 二进制K8S集群使用Bootstrap Token 方式增加Node

    TLS Bootstraping:在kubernetes集群中,Node上组件kebelet和kube-proxy都需要与kube-apiserver进行通信,为了增加传输安全性,采用https方式, ...

  4. bootstrap之Click大事

    上一篇文章中谈到了bootstrap流程,本文开始把目光bootstrap它可以接受指令(从源代码视图的透视.因为appium该项目现在还处于不断更新,因此,一些指令已经实现.也许未来会实现一些.从视 ...

  5. kubelet组件部署

    目录 前言 创建 kubelet bootstrap kubeconfig 文件 查看kubeadm为各个节点创建的token 查看各 token 关联的 Secret 创建和分发kubelet参数配 ...

  6. kubelet源码分析——kubelet简介与启动

    kubelet是k8s集群中一个组件,其作为一个agent的角色分布在各个节点上,无论是master还是worker,功能繁多,逻辑复杂.主要功能有 节点状态同步:kublet给api-server同 ...

  7. suse 12 二进制部署 Kubernetets 1.19.7 - 第09章 - 部署kubelet组件

    文章目录 1.9.部署kubelet 1.9.0.创建kubelet bootstrap kubeconfig文件 1.9.1.创建kubelet配置文件 1.9.2.配置kubelet为system ...

  8. CentOS7上手动部署入门级kubernetes

    前言 翻看了很多的kubernetes的安装教程,也反复做了一些实验,深感教程之复杂,所以决定写一个极简版本的安装教程,目标在于用尽可能少的参数启动服务,并且剖析各组件关系,然后再在此基础上逐步添加参 ...

  9. Kubernetes集群搭建之Master配置篇

    本次系列使用的所需部署包版本都使用的目前最新的或最新稳定版,安装包地址请到公众号内回复[K8s实战]获取 今天终于到正题了~~ 生成kubernets证书与私钥 1. 制作kubernetes ca证 ...

随机推荐

  1. 在myeclipse中maven项目关于ssh整合时通过pom.xml导入依赖是pom.xml头部会报错

    错误如下 ArtifactTransferException: Failure to transfer org.springframework:spring-jdbc:jar:3.0.5.RELEAS ...

  2. HBase和Phoneix使用示例

    HBase操作 基本操作 创建表 Examples: hbase> create 't1', {NAME => 'f1', VERSIONS => 5} hbase> crea ...

  3. 关于delete和delete[]的区别

    在C++动态内存分配中我们常用到new和delete两种操作,new用来申请内存,delete用来释放内存.那么问题来了,我们应该用delete来释放内存还是用delete[]来释放内存呢? 为了得到 ...

  4. 本文档教授大家在yii2.0里实现文件上传 首先我们来实现单文件上传

    第一步  首先建立一个关于上传的model层  如果你有已经建好的可以使用表单小部件的model层 也可以直接用这个.在这里我们新建一个新的model层 在model层新建文件  Upload.php ...

  5. MySQL 5.7以上 root用户默认密码问题【转】

    https://www.yanning.wang/archives/379.html 废话少说一句话系列: CentOS系统用yum安装MySQL的朋友,请使用 grep "temporar ...

  6. 嵌入式Linux内核tasklet机制(附实测代码)

    Linux 中断编程分为中断顶半部,中断底半部 中断顶半部: 做紧急,耗时短的事情,同时还启动中断底半部. 中断底半部: 做耗时的事件,这个事件在执行过程可以被中断. 中断底半部实现方法: taskl ...

  7. websocket 11

    1. websocket 回顾: - 什么是轮训? - 通过定时器让程序每隔n秒执行一次操作. - 什么是长轮训? - 浏览器向后端发起请求,后端会将请求 hang 住,最多hang 30s. 如果一 ...

  8. Docker入门与实战讲解

    转载自:http://blog.csdn.net/relax_hb/article/details/69668815 简述 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包 ...

  9. ajax请求完成执行的操作

    var createAjax = $("#createId").ajax(function(){ //ajax操作 }); $.when(createAjax).done(func ...

  10. ubuntu12.04 64bit libncurses5-dev和libncurses5-dev:i386共存性问题讨论

    ubuntu12.04 64bit 编译kernel(或者make menuconfig)源码时出现如下错误: HOSTLD scripts/kconfig/mconf scripts/kconfig ...