最近在测试 Kubernetes 1.11.2 新版本的相关东西,发现新版本的 Bootstrap Token 功能已经进入 Beta 阶段,索性便尝试了一下;虽说目前是为 kubeadm 设计的,不过手动挡用起来也不错,这里记录一下使用方式
一、环境准备 首先需要有一个运行状态正常的 Master 节点,目前我测试的是版本是 1.11.2,低版本我没测试;其次本文默认 Node 节点 Docker、kubelet 二进制文件、systemd service 配置等都已经处理好,更具体的环境如下:
Master 节点 IP 为 192.168.1.61
,Node 节点 IP 为 192.168.1.64
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 docker1.node ➜ ~ kubectl version Client Version: version.Info{Major:"1" , Minor:"11" , GitVersion:"v1.11.2" , GitCommit:"bb9ffb1654d4a729bb4cec18ff088eacc153c239" , GitTreeState:"clean" , BuildDate:"2018-08-07T23:08:19Z" , GoVersion:"go1.10.3" , Compiler:"gc" , Platform:"linux/amd64" } Server Version: version.Info{Major:"1" , Minor:"11" , GitVersion:"v1.11.2" , GitCommit:"bb9ffb1654d4a729bb4cec18ff088eacc153c239" , GitTreeState:"clean" , BuildDate:"2018-08-07T23:08:19Z" , GoVersion:"go1.10.3" , Compiler:"gc" , Platform:"linux/amd64" } docker1.node ➜ ~ docker info Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 0 Server Version: 18.06.1-ce Storage Driver: overlay2 Backing Filesystem: xfs Supports d_type: true Native Overlay Diff: true Logging Driver: json-file Cgroup Driver: cgroupfs Plugins: Volume: local Network: bridge host macvlan null overlay Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog Swarm: inactive Runtimes: runc Default Runtime: runc Init Binary: docker-init containerd version: 468a545b9edcd5932818eb9de8e72413e616e86e runc version: 69663f0bd4b60df09991c08812a60108003fa340 init version: fec3683 Security Options: apparmor seccomp Profile: default Kernel Version: 4.15.0-33-generic Operating System: Ubuntu 18.04.1 LTS OSType: linux Architecture: x86_64 CPUs: 2 Total Memory: 3.847GiB Name: docker1.node ID: AJOD:RBJZ:YP3G:HCGV:KT4R:D4AF:SBDN:5B76:JM4M:OCJA:YJMJ:OCYQ Docker Root Dir: /data/docker Debug Mode (client): false Debug Mode (server): false Registry: https://index.docker.io/v1/ Labels: Experimental: false Insecure Registries: 127.0.0.0/8 Live Restore Enabled: false
二、TLS Bootstrapping 回顾 在正式进行 TLS Bootstrapping 操作之前,**如果对 TLS Bootstrapping 完全没接触过的请先阅读 Kubernetes TLS bootstrapping 那点事 **;我想这里有必要简单说明下使用 Token 时整个启动引导过程:
在集群内创建特定的 Bootstrap Token Secret
,该 Secret 将替代以前的 token.csv
内置用户声明文件 在集群内创建首次 TLS Bootstrap 申请证书的 ClusterRole、后续 renew Kubelet client/server 的 ClusterRole,以及其相关对应的 ClusterRoleBinding;并绑定到对应的组或用户 调整 Controller Manager 配置,以使其能自动签署相关证书和自动清理过期的 TLS Bootstrapping Token 生成特定的包含 TLS Bootstrapping Token 的 bootstrap.kubeconfig
以供 kubelet 启动时使用 调整 Kubelet 配置,使其首次启动加载 bootstrap.kubeconfig
并使用其中的 TLS Bootstrapping Token 完成首次证书申请 证书被 Controller Manager 签署,成功下发,Kubelet 自动重载完成引导流程 后续 Kubelet 自动 renew 相关证书 可选的: 集群搭建成功后立即清除 Bootstrap Token Secret
,或等待 Controller Manager 待其过期后删除,以防止被恶意利用 三、使用 Bootstrap Token 第二部分算作大纲了,这部分将会按照第二部分的总体流程来走,同时会对一些细节进行详细说明
3.1、创建 Bootstrap Token 既然整个功能都时刻强调这个 Token,那么第一步肯定是生成一个 token,生成方式如下:
1 2 ➜ ~ echo "$(head -c 6 /dev/urandom | md5sum | head -c 6) " ."$(head -c 16 /dev/urandom | md5sum | head -c 16) " 47f392.d22d04e89a65eb22
这个 47f392.d22d04e89a65eb22
就是生成的 Bootstrap Token,保存好 token,因为后续要用;关于这个 token 解释如下:
Token 必须满足 [a-z0-9]{6}\.[a-z0-9]{16}
格式;以 .
分割,前面的部分被称作 Token ID
,Token ID
并不是 “机密信息”,它可以暴露出去;相对的后面的部分称为 Token Secret
,它应该是保密的
本部分官方文档地址 Token Format
3.2、创建 Bootstrap Token Secret 对于 Kubernetes 来说 Bootstrap Token Secret
也仅仅是一个特殊的 Secret
而已;对于这个特殊的 Secret
样例 yaml 配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 apiVersion: v1 kind: Secret metadata: name: bootstrap-token-07401b namespace: kube-system type: bootstrap.kubernetes.io/token stringData: description: "The default bootstrap token generated by 'kubeadm init'." token-id: 47f392 token-secret: d22d04e89a65eb22 expiration: 2018-09-10T00:00:11Z usage-bootstrap-authentication: "true" usage-bootstrap-signing: "true" auth-extra-groups: system:bootstrappers:worker,system:bootstrappers:ingress
需要注意几点:
作为 Bootstrap Token Secret
的 type 必须为 bootstrap.kubernetes.io/token
,name 必须为 bootstrap-token-<token id>
(Token ID 就是上一步创建的 Token 前一部分) usage-bootstrap-authentication
、usage-bootstrap-signing
必须存才且设置为 true
(我个人感觉 usage-bootstrap-signing
可以没有,具体见文章最后部分)expiration
字段是可选的,如果设置则 Secret
到期后将由 Controller Manager 中的 tokencleaner
自动清理auth-extra-groups
也是可选的,令牌的扩展认证组,组必须以 system:bootstrappers:
开头最后使用 kubectl create -f bootstrap.secret.yaml
创建即可
本部分官方文档地址 Bootstrap Token Secret Format
3.3、创建 ClusterRole 和 ClusterRoleBinding 具体都有哪些 ClusterRole
和 ClusterRoleBinding
,以及其作用请参考上一篇的 Kubernetes TLS bootstrapping 那点事 ,不想在这里重复了
在 1.8 以后三个 ClusterRole
中有两个已经有了,我们只需要创建剩下的一个即可:
1 2 3 4 5 6 7 8 9 10 kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: system:certificates.k8s.io:certificatesigningrequests:selfnodeserver rules: - apiGroups: ["certificates.k8s.io" ] resources: ["certificatesigningrequests/selfnodeserver" ] verbs: ["create" ]
然后是三个 ClusterRole
对应的 ClusterRoleBinding
;需要注意的是 在使用 Bootstrap Token
进行引导时,Kubelet 组件使用 Token 发起的请求其用户名为 system:bootstrap:<token id>
,用户组为 system:bootstrappers
;so 我们在创建 ClusterRoleBinding
时要绑定到这个用户或者组上 ;当然我选择懒一点,全部绑定到组上
1 2 3 4 5 6 7 8 9 10 11 kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --group=system:bootstrappers kubectl create clusterrolebinding node-client-auto-approve-csr --clusterrole=system:certificates.k8s.io:certificatesigningrequests:nodeclient --group=system:bootstrappers kubectl create clusterrolebinding node-client-auto-renew-crt --clusterrole=system:certificates.k8s.io:certificatesigningrequests:selfnodeclient --group=system:nodes kubectl create clusterrolebinding node-server-auto-renew-crt --clusterrole=system:certificates.k8s.io:certificatesigningrequests:selfnodeserver --group=system:nodes
关于本部分首次请求用户名变为 system:bootstrap:<token id>
官方文档原文如下:
Tokens authenticate as the username system:bootstrap:and are members of the group system:bootstrappers. Additional groups may be specified in the token’s Secret.
3.4、调整 Controller Manager 根据官方文档描述,Controller Manager 需要启用 tokencleaner
和 bootstrapsigner
(目测这个 bootstrapsigner
实际上并不需要,顺便加着吧),完整配置如下(为什么贴完整配置? 文章凑数啊…):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 KUBE_CONTROLLER_MANAGER_ARGS=" --address=127.0.0.1 \ --bind-address=192.168.1.61 \ --port=10252 \ --secure-port=10258 \ --cluster-name=kubernetes \ --cluster-signing-cert-file=/etc/kubernetes/ssl/k8s-root-ca.pem \ --cluster-signing-key-file=/etc/kubernetes/ssl/k8s-root-ca-key.pem \ --controllers=*,bootstrapsigner,tokencleaner \ --deployment-controller-sync-period=10s \ --experimental-cluster-signing-duration=86700h0m0s \ --enable-garbage-collector=true \ --leader-elect=true \ --master=http://127.0.0.1:8080 \ --node-monitor-grace-period=40s \ --node-monitor-period=5s \ --pod-eviction-timeout=5m0s \ --terminated-pod-gc-threshold=50 \ --root-ca-file=/etc/kubernetes/ssl/k8s-root-ca.pem \ --service-account-private-key-file=/etc/kubernetes/ssl/k8s-root-ca-key.pem \ --feature-gates=RotateKubeletServerCertificate=true"
3.5、生成 bootstrap.kubeconfig 前面所有步骤实际上都是在处理 Api Server、Controller Manager 这一块,为的就是 “老子启动后 TLS Bootstarpping 发证书申请你两个要立马允许,不能拒绝老子”;接下来就是比较重要的 bootstrap.kubeconfig
配置生成,这个 bootstrap.kubeconfig
是最终被 Kubelet 使用的,里面包含了相关的 Token,以帮助 Kubelet 在第一次通讯时能成功沟通 Api Server;生成方式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 kubectl config set-cluster kubernetes \ --certificate-authority=/etc/kubernetes/ssl/k8s-root-ca.pem \ --embed-certs=true \ --server=https://127.0.0.1:6443 \ --kubeconfig=bootstrap.kubeconfig kubectl config set-credentials system:bootstrap:47f392 \ --token=47f392.d22d04e89a65eb22 \ --kubeconfig=bootstrap.kubeconfig kubectl config set-context default \ --cluster=kubernetes \ --user=system:bootstrap:47f392 \ --kubeconfig=bootstrap.kubeconfig kubectl config use-context default --kubeconfig=bootstrap.kubeconfig
3.6、调整 Kubelet Kubelet 启动参数需要做一些相应调整,以使其能正确的使用 Bootstartp Token
,完整配置如下(与使用 token.csv 配置没什么变化,因为主要变更在 bootstrap.kubeconfig 中):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 KUBELET_ARGS=" --address=192.168.1.64 \ --allow-privileged=true \ --alsologtostderr \ --anonymous-auth=true \ --bootstrap-kubeconfig=/etc/kubernetes/bootstrap.kubeconfig \ --cert-dir=/etc/kubernetes/ssl \ --cgroup-driver=cgroupfs \ --cluster-dns=10.254.0.2 \ --cluster-domain=cluster.local. \ --fail-swap-on=false \ --healthz-port=10248 \ --healthz-bind-address=192.168.1.64 \ --feature-gates=RotateKubeletClientCertificate=true,RotateKubeletServerCertificate=true \ --node-labels=node-role.kubernetes.io/k8s-master=true \ --image-gc-high-threshold=70 \ --image-gc-low-threshold=50 \ --kube-reserved=cpu=500m,memory=512Mi,ephemeral-storage=1Gi \ --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \ --system-reserved=cpu=1000m,memory=1024Mi,ephemeral-storage=1Gi \ --serialize-image-pulls=false \ --sync-frequency=30s \ --pod-infra-container-image=k8s.gcr.io/pause:3.1 \ --resolv-conf=/etc/resolv.conf \ --rotate-certificates"
一切准备就绪后,执行 systemctl daemon-reload && systemctl start kubelet
启动即可
四、其他说明 可能有人已经注意到,在官方文档中最后部分有关于 ConfigMap Signing 的相关描述,同时要求了启用 bootstrapsigner
这个 controller,而且在上文创建 Bootstrap Token Secret
中我也说 usage-bootstrap-signing
这个可以不设置;其中官方文档上的描述我们能看到的大致只说了这么两段稍微有点用的话:
In addition to authentication, the tokens can be used to sign a ConfigMap. This is used early in a cluster bootstrap process before the client trusts the API server. The signed ConfigMap can be authenticated by the shared token.
The ConfigMap that is signed is cluster-info in the kube-public namespace. The typical flow is that a client reads this ConfigMap while unauthenticated and ignoring TLS errors. It then validates the payload of the ConfigMap by looking at a signature embedded in the ConfigMap.
从这两段话中我们只能得出两个结论:
Bootstrap Token 能对 ConfigMap 签名 可以签名一个 kube-public
NameSpace 下的名字叫 cluster-info
的 ConfigMap,并且这个 ConfigMap 可以在没进行引导之前强行读取 说实话这两段话搞得我百思不得骑姐其解,最终我在 kubeadm 的相关文档中找到了真正的说明及作用:
在使用 kubeadm init
时创建 cluster-info
这个 ConfigMap,ConfigMap 中包含了集群基本信息 在使用 kubeadm join
时目标节点强行读取 ConfigMap 以得知集群基本信息,然后进行 join
综上所述,我个人认为手动部署下,在仅仅使用 Bootstrap Token 进行 TLS Bootstrapping 时,bootstrapsigner
这个 controller 和 Bootstrap Token Secret
中的 usage-bootstrap-signing
选项是没有必要的,当然我还没测试(胡吹谁不会)…
最后附上 kubeadm
的文档说明: Create the public cluster-info ConfigMap 、Discovery cluster-info