众所周知 TLS 的作用就是对通讯加密,防止中间人窃听;同时如果证书不信任的话根本就无法与 apiserver建立连接,更不用提有没有权限向 apiserver 请求指定内容。在开启了 TLS 的集群中,每当与集群交互的时候少不了的是身份认证,使用 kubeconfig(即证书) 和 token 两种认证方式是最简单也最通用的认证方式。
当集群开启了 TLS 认证后,每个节点的 kubelet 组件都要使用由 apiserver 使用的 CA 签发的有效证书才能与 apiserver 通讯;此时如果节点多起来,为每个节点单独签署证书将是一件非常繁琐的事情;TLS bootstrapping 功能就是让 kubelet 先使用一个预定的低权限用户连接到 apiserver,然后向 apiserver 申请证书,kubelet 的证书由 apiserver 动态签署;
TLS Bootstrapping ,使用 Token 时整个启动引导过程:
1、在集群内创建特定的 Bootstrap Token Secret ,该 Secret 将替代以前的 token.csv 内置用户声明文件
2、在集群内创建首次 TLS Bootstrap 申请证书的 ClusterRole、后续 renew Kubelet client/server 的 ClusterRole,以及其相关对应的 ClusterRoleBinding;并绑定到对应的组或用户
3、调整 Controller Manager 配置,以使其能自动签署相关证书和自动清理过期的 TLS Bootstrapping Token
4、生成特定的包含 TLS Bootstrapping Token 的 bootstrap.kubeconfig 以供 kubelet 启动时使用
5、调整 Kubelet 配置,使其首次启动加载 bootstrap.kubeconfig 并使用其中的 TLS Bootstrapping Token 完成首次证书申请
6、证书被 Controller Manager 签署,成功下发,Kubelet 自动重载完成引导流程
7、后续 Kubelet 自动 renew 相关证书
kubeadm部署的如下所示:
--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --network-plugin=cni --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.2
启用 TLS Bootstrapping 机制
TLS Bootstraping:Master apiserver启用TLS认证后,Node节点kubelet和kube-proxy要与kube-apiserver进行通信,必须使用CA签发的有效证书才可以,当Node节点很多时,这种客户端证书颁发需要大量工作,同样也会增加集群扩展复杂度。
为了简化流程,Kubernetes引入了TLS bootstraping机制来自动颁发客户端证书,kubelet会以一个低权限用户自动向apiserver申请证书,kubelet的证书由apiserver动态签署。
所以强烈建议在Node上使用这种方式,目前主要用于kubelet,kube-proxy还是由我们统一颁发一个证书。(当机器越来越多的时候,手动的为kubelet颁发证书,还是比较麻烦的,应用这个机制就是为自动的为kubelet颁发证书)
TLS bootstraping 工作流程:
创建上述配置文件中token文件:
cat > /opt/kubernetes/cfg/token.csv << EOF
c47ffb939f5ca36231d9e3121a252940,kubelet-bootstrap,10001,"system:node-bootstrapper"
EOF
格式:token,用户名,UID,用户组 这个是系统自带的用户组,主要就是让node使用strap颁发证书,低权限用户。
node上拿着这个token c47ffb939f5ca36231d9e3121a252940 请求过来,就会以最低权限颁发证书。
token也可自行生成替换:
head -c 16 /dev/urandom | od -An -t x | tr -d ' '
授权kubelet-bootstrap用户允许请求证书
要给这个用户授予权限
[root@k8s-master logs]# kubectl get clusterrole
NAME CREATED AT
system:node-bootstrapper 2022-03-25T12:33:52Z
kubectl create clusterrolebinding kubelet-bootstrap \
--clusterrole=system:node-bootstrapper \
--user=kubelet-bootstrap
部署kubelet
1. 创建配置文件
cat > /opt/kubernetes/cfg/kubelet.conf << EOF
KUBELET_OPTS="--logtostderr=false \\
--v=2 \\
--log-dir=/opt/kubernetes/logs \\
--hostname-override=k8s-master \\
--network-plugin=cni \\
--kubeconfig=/opt/kubernetes/cfg/kubelet.kubeconfig \\
--bootstrap-kubeconfig=/opt/kubernetes/cfg/bootstrap.kubeconfig \\
--config=/opt/kubernetes/cfg/kubelet-config.yml \\
--cert-dir=/opt/kubernetes/ssl \\
--pod-infra-container-image=lizhenliang/pause-amd64:3.0"
EOF
- –hostname-override:显示名称,集群中唯一
- –network-plugin:启用CNI
- –kubeconfig:空路径,会自动生成,后面用于连接apiserver
- –bootstrap-kubeconfig:首次启动向apiserver申请证书
- –config:配置参数文件
- –cert-dir:kubelet证书生成目录
- –pod-infra-container-image:管理Pod网络容器的镜像
这个文件也是生成的,并且将CA证书加载进来,主要用来连接apiserver。
[root@k8s-master cfg]# pwd
/opt/kubernetes/cfg
[root@k8s-master cfg]# ls kubelet.kubeconfig
kubelet.kubeconfig
以kubelet开头的都是自动生成的,主要使用 kubelet-client-current.pem kubelet.crt kubelet.key这三个证书
[root@k8s-master ssl]# ls kubelet*
kubelet-client-2022-03-25-21-23-51.pem kubelet-client-current.pem kubelet.crt kubelet.key
[root@k8s-master ssl]# pwd
/opt/kubernetes/ssl
2. 配置参数文件
cat > /opt/kubernetes/cfg/kubelet-config.yml << EOF
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
address: 0.0.0.0
port: 10250
readOnlyPort: 10255
cgroupDriver: cgroupfs
clusterDNS:
- 10.0.0.2
clusterDomain: cluster.local
failSwapOn: false
authentication:
anonymous:
enabled: false
webhook:
cacheTTL: 2m0s
enabled: true
x509:
clientCAFile: /opt/kubernetes/ssl/ca.pem
authorization:
mode: Webhook
webhook:
cacheAuthorizedTTL: 5m0s
cacheUnauthorizedTTL: 30s
evictionHard:
imagefs.available: 15%
memory.available: 100Mi
nodefs.available: 10%
nodefs.inodesFree: 5%
maxOpenFiles: 1000000
maxPods: 110
EOF
cgroupDriver: cgroupfs 要和docker info里面显示的驱动保持一致。
3. 生成bootstrap.kubeconfig文件(为了解决,当kubelet很多的时候,为了避免手动颁发证书,引入tls机制,能够自动的为将要加入集群的node颁发证书。所有kubelet要链接apiserver都需要证书,要不然使用手签,要不然使用tls,这里使用tls,使用这个文件去连接apiserver,然后颁发证书,加入集群。)
KUBE_APISERVER="https://192.168.159.3:6443" # apiserver IP:PORT
TOKEN="c47ffb939f5ca36231d9e3121a252940" # 与token.csv里保持一致
# 生成 kubelet bootstrap kubeconfig 配置文件
kubectl config set-cluster kubernetes \
--certificate-authority=/opt/kubernetes/ssl/ca.pem \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=bootstrap.kubeconfig
kubectl config set-credentials "kubelet-bootstrap" \
--token=${TOKEN} \
--kubeconfig=bootstrap.kubeconfig
kubectl config set-context default \
--cluster=kubernetes \
--user="kubelet-bootstrap" \
--kubeconfig=bootstrap.kubeconfig
kubectl config use-context default --kubeconfig=bootstrap.kubeconfig
[root@k8s-master cfg]# cat bootstrap.kubeconfig
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR2akNDQXFhZ0F3SUJBZ0lVY0tqQnBLWnBOWDBvd1FMZlZYYUhHMmlaYXJrd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1pURUxNQWtHQTFVRUJoTUNRMDR4RURBT0JnTlZCQWdUQjBKbGFXcHBibWN4RURBT0JnTlZCQWNUQjBKbAphV3BwYm1jeEREQUtCZ05WQkFvVEEyczRjekVQTUEwR0ExVUVDeE1HVTNsemRHVnRNUk13RVFZRFZRUURFd3ByCmRXSmxjbTVsZEdWek1CNFhEVEl5TURNeU5URXhOVGd3TUZvWERUSTNNRE15TkRFeE5UZ3dNRm93WlRFTE1Ba0cKQTFVRUJoTUNRMDR4RURBT0JnTlZCQWdUQjBKbGFXcHBibWN4RURBT0JnTlZCQWNUQjBKbGFXcHBibWN4RERBSwpCZ05WQkFvVEEyczRjekVQTUEwR0ExVUVDeE1HVTNsemRHVnRNUk13RVFZRFZRUURFd3ByZFdKbGNtNWxkR1Z6Ck1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBeUNRUlZTWmhHUnJaNEV6TjZVbGQKNE9nVFRGVDhjNU95MW1ka24wbVgxYmxDdFRGckNBZ0VQOGdyRTZGVWI1T0k5SU1na3JlUVVvSlVoTGtrVVBIaApURnBzcXA0cmgyVUlKVjJFZ3FPKzNpbUc4NWJtdjBPNE5hb3B2cThCQXZVREZKdWcxOHJYK2ZYeEx5VFE5V0tRCkpmUWNhVmQ4V2FYbWFrMHJhU0JEeDhtNUZpdjEvekV1YmlEZmVCUTJ0NFRvQllkd1k0MmpyN1k3VW95ZVExZlgKTndxUUpjTVFsS1h0c0ZwZGE5T2pycE5PcWkrZHBISXJXYkVBZGhYTkNJZy9odzBuNFJ4OTRFSU0yUVhTWUJOZApMZHpqamU4UFZPenAyVXpnYTZJZ294L3JXRVcvVnA5MTM0YlZGcU0veTJjajB6TnZzS2xJUEVnbENMUE92cUh5ClB3SURBUUFCbzJZd1pEQU9CZ05WSFE4QkFmOEVCQU1DQVFZd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFqQWQKQmdOVkhRNEVGZ1FVSjYzTXZVSlNPbzNDendOSWdsa3dIL2ozTnM0d0h3WURWUjBqQkJnd0ZvQVVKNjNNdlVKUwpPbzNDendOSWdsa3dIL2ozTnM0d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFJdG1ha3M4cy9hYnhSc1Z5NHhmClhEVzlaeHNObFp2dzc2YnhxeTYvem56RW5UV0JyMHdpV0tmRXFtbk5xNE8rdnZxT1hVQklSMVBaamVYY3FYNlIKN2dlMWRmRGE3dVNqNFdYU2tONS9xbG5xMkNKVGxteVFKQUdHcTZpdnIzQ2ppQ0FKWlU4Z0FRTkg0Q2hJc0NlcwpXMy9HQjZ4a2xFdkkydm5pS2w4MEFxK0tXcXg2c0xCbmIrTlFiOW1zOStRN250ZDBveFBOK04wcnpOZ0JVUUwvCldsUUw0b1ZLWDN2VElqVzlEVkRrdVh0YjdzbzJkZ2JXM3FxRy9scUhDb1NYZ3c5bjdab2c0MmNqS0VOSUc5R1AKMnlWUmlMUHRjRVlOYStybXJ2SytncTZ0WWc2MTM4OXpIbXBxY0FvQW1kYVZQUTlsQzlSK1IweVNGNkZmenJmaQpJWG89Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
server: https://192.168.111.3:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubelet-bootstrap
name: default
current-context: default
kind: Config
preferences: {}
users:
- name: kubelet-bootstrap
user:
token: c47ffb939f5ca36231d9e3121a252940
users:
- name: kubelet-bootstrap
user:
token: c47ffb939f5ca36231d9e3121a252940
上面这个就是通过kubectl config生成的文件,指定了CA的证书,token要保持一致,因为是用token来请求的。
拷贝到配置文件路径:
cp bootstrap.kubeconfig /opt/kubernetes/cfg
当你启动kubelet,它就会拿着bootstrap.kubeconfig向apiserver发起请求,apiserver会去验证这个token是不是可信任的,是否有权限,ca证书是不是正确的,通过之后才会为其颁发证书,最后kubelet就启动成功了。
4. systemd管理kubelet
cat > /usr/lib/systemd/system/kubelet.service << EOF
[Unit]
Description=Kubernetes Kubelet
After=docker.service
[Service]
EnvironmentFile=/opt/kubernetes/cfg/kubelet.conf
ExecStart=/opt/kubernetes/bin/kubelet \$KUBELET_OPTS
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
5. 启动并设置开机启动
systemctl daemon-reload
systemctl start kubelet
systemctl enable kubelet
6. 批准kubelet证书申请并加入集群
# 查看kubelet证书请求
kubectl get csr
NAME AGE SIGNERNAME REQUESTOR CONDITION
node-csr-uCEGPOIiDdlLODKts8J658HrFq9CZ--K6M4G7bjhk8A 6m3s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending
# 批准申请
kubectl certificate approve node-csr-uCEGPOIiDdlLODKts8J658HrFq9CZ--K6M4G7bjhk8A
# 查看节点
kubectl get node
NAME STATUS ROLES AGE VERSION
k8s-master NotReady <none> 7s v1.18.3
注:由于网络插件还没有部署,节点会没有准备就绪 NotReady
I0327 21:48:51.905063 2254 bootstrap.go:115] Kubeconfig /opt/kubernetes/cfg/kubelet.kubeconfig exists and is valid, skipping bootstrap
I0327 21:48:51.923413 2254 dynamic_cafile_content.go:129] Loaded a new CA Bundle and Verifier for "client-ca-bundle::/opt/kubernetes/ssl/ca.pem"
I0327 21:48:51.924452 2254 manager.go:146] cAdvisor running in container: "/sys/fs/cgroup/cpu,cpuacct/system.slice/kubelet.service"
I0327 21:48:52.007848 2254 fs.go:125] Filesystem UUIDs: map[23612460-2ba4-4f9b-8e6f-74e23f32fd35:/dev/dm-0 a3e099f4-0adb-49e8-9862-98593c721741:/dev/sda1 c7c91808-9ff6-49bd-bcd9-2f77af693db9:/dev/dm-1]
最后文件内容如下
[root@k8s-master cfg]# cat kubelet.kubeconfig
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR2akNDQXFhZ0F3SUJBZ0lVY0tqQnBLWnBOWDBvd1FMZlZYYUhHMmlaYXJrd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1pURUxNQWtHQTFVRUJoTUNRMDR4RURBT0JnTlZCQWdUQjBKbGFXcHBibWN4RURBT0JnTlZCQWNUQjBKbAphV3BwYm1jeEREQUtCZ05WQkFvVEEyczRjekVQTUEwR0ExVUVDeE1HVTNsemRHVnRNUk13RVFZRFZRUURFd3ByCmRXSmxjbTVsZEdWek1CNFhEVEl5TURNeU5URXhOVGd3TUZvWERUSTNNRE15TkRFeE5UZ3dNRm93WlRFTE1Ba0cKQTFVRUJoTUNRMDR4RURBT0JnTlZCQWdUQjBKbGFXcHBibWN4RURBT0JnTlZCQWNUQjBKbGFXcHBibWN4RERBSwpCZ05WQkFvVEEyczRjekVQTUEwR0ExVUVDeE1HVTNsemRHVnRNUk13RVFZRFZRUURFd3ByZFdKbGNtNWxkR1Z6Ck1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBeUNRUlZTWmhHUnJaNEV6TjZVbGQKNE9nVFRGVDhjNU95MW1ka24wbVgxYmxDdFRGckNBZ0VQOGdyRTZGVWI1T0k5SU1na3JlUVVvSlVoTGtrVVBIaApURnBzcXA0cmgyVUlKVjJFZ3FPKzNpbUc4NWJtdjBPNE5hb3B2cThCQXZVREZKdWcxOHJYK2ZYeEx5VFE5V0tRCkpmUWNhVmQ4V2FYbWFrMHJhU0JEeDhtNUZpdjEvekV1YmlEZmVCUTJ0NFRvQllkd1k0MmpyN1k3VW95ZVExZlgKTndxUUpjTVFsS1h0c0ZwZGE5T2pycE5PcWkrZHBISXJXYkVBZGhYTkNJZy9odzBuNFJ4OTRFSU0yUVhTWUJOZApMZHpqamU4UFZPenAyVXpnYTZJZ294L3JXRVcvVnA5MTM0YlZGcU0veTJjajB6TnZzS2xJUEVnbENMUE92cUh5ClB3SURBUUFCbzJZd1pEQU9CZ05WSFE4QkFmOEVCQU1DQVFZd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFqQWQKQmdOVkhRNEVGZ1FVSjYzTXZVSlNPbzNDendOSWdsa3dIL2ozTnM0d0h3WURWUjBqQkJnd0ZvQVVKNjNNdlVKUwpPbzNDendOSWdsa3dIL2ozTnM0d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFJdG1ha3M4cy9hYnhSc1Z5NHhmClhEVzlaeHNObFp2dzc2YnhxeTYvem56RW5UV0JyMHdpV0tmRXFtbk5xNE8rdnZxT1hVQklSMVBaamVYY3FYNlIKN2dlMWRmRGE3dVNqNFdYU2tONS9xbG5xMkNKVGxteVFKQUdHcTZpdnIzQ2ppQ0FKWlU4Z0FRTkg0Q2hJc0NlcwpXMy9HQjZ4a2xFdkkydm5pS2w4MEFxK0tXcXg2c0xCbmIrTlFiOW1zOStRN250ZDBveFBOK04wcnpOZ0JVUUwvCldsUUw0b1ZLWDN2VElqVzlEVkRrdVh0YjdzbzJkZ2JXM3FxRy9scUhDb1NYZ3c5bjdab2c0MmNqS0VOSUc5R1AKMnlWUmlMUHRjRVlOYStybXJ2SytncTZ0WWc2MTM4OXpIbXBxY0FvQW1kYVZQUTlsQzlSK1IweVNGNkZmenJmaQpJWG89Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
server: https://192.168.111.3: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: /opt/kubernetes/ssl/kubelet-client-current.pem
client-key: /opt/kubernetes/ssl/kubelet-client-current.pem
新增加Worker Node
在master节点将Worker Node涉及文件拷贝到新节点
[root@k8s-master ~]# scp -r /opt/kubernetes [email protected]:/opt/
[root@k8s-master ~]# scp -r /usr/lib/systemd/system/{kubelet,kube-proxy}.service [email protected]:/usr/lib/systemd/system
[root@k8s-master ~]# scp -r /opt/cni/ [email protected]:/opt/
在新的worker节点上删除kubelet证书和kubeconfig文件
[root@k8s-node3 ~]#rm -rf /opt/kubernetes/cfg/kubelet.kubeconfig
[root@k8s-node3 ~]#rm -rf /opt/kubernetes/ssl/kubelet*
注:这几个文件是证书申请审批后自动生成的,每个Node不同,必须删除重新生成。
修改主机名
[root@k8s-node3 ~]# vim /opt/kubernetes/cfg/kubelet.conf
--hostname-override=k8s-node1
[root@k8s-node3 ~]# vim /opt/kubernetes/cfg/kube-proxy-config.yml
hostnameOverride: k8s-node3
启动并设置开机启动
systemctl daemon-reload
systemctl start kubelet
systemctl enable kubelet
systemctl start kube-proxy
systemctl enable kube-proxy
在Master上批准新Node kubelet证书申请
kubectl get csr
NAME AGE SIGNERNAME REQUESTOR CONDITION
node-csr-4zTjsaVSrhuyhIGqsefxzVoZDCNKei-aE2jyTP81Uro 89s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending
kubectl certificate approve node-csr-4zTjsaVSrhuyhIGqsefxzVoZDCNKei-aE2jyTP81Uro
Node 的扩容
在实际生产系统中经常会 现服务器容址不足的情况, 这时就需要购买新的服务器, 然后将应用系统进行水平扩展来完成对系统的扩容。
指定为当前 Kubernetes 集群 Maste 的地址, 最后启动这些服务。通过 kubelet 服务默认的自动注册机制,新的 Node 将自动加入现有的 Kubernetes 集群中,如图所示:
Kubernetes Master 在接受新 Node 的注册之后,会自动将其纳入当前集群的调度范围, 之后创建容器时就可以对新的 Node 进行调度了,通过这种机制, Kubernetes 实现了集群中 Node 的扩容。