1.9、部署kubelet
- kubelet运行在每个node节点上,接收kube-apiserver发送的请求,管理Pod容器,执行交互命令
- kubelet启动时自动向kube-apiserver注册节点信息,内置的cAdivsor统计和监控节点的资源使用资源情况
- 为确保安全,部署时关闭了kubelet的非安全http端口,对请求进行认证和授权,拒绝未授权的访问
1.9.0、创建kubelet bootstrap kubeconfig文件
#!/usr/bin/env bash source /opt/k8s/bin/k8s-env.sh for node_name in ${NODE_NAMES[@]} do printf "\e[1;34m${node_name}\e[0m\n" # 创建 token export BOOTSTRAP_TOKEN=$(kubeadm token create \ --description kubelet-bootstrap-token \ --groups system:bootstrappers:${node_name} \ --kubeconfig ~/.kube/config) # 设置集群参数 kubectl config set-cluster kubernetes \ --certificate-authority=/etc/kubernetes/cert/ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=/opt/k8s/ssl/kubelet-bootstrap-${node_name}.kubeconfig # 设置客户端认证参数 kubectl config set-credentials kubelet-bootstrap \ --token=${BOOTSTRAP_TOKEN} \ --kubeconfig=/opt/k8s/ssl/kubelet-bootstrap-${node_name}.kubeconfig # 设置上下文参数 kubectl config set-context default \ --cluster=kubernetes \ --user=kubelet-bootstrap \ --kubeconfig=/opt/k8s/ssl/kubelet-bootstrap-${node_name}.kubeconfig # 设置默认上下文 kubectl config use-context default --kubeconfig=/opt/k8s/ssl/kubelet-bootstrap-${node_name}.kubeconfig done
- 向kubeconfig写入的是token,bootstrap结束后kube-controller-manager为kubelet创建client和server证书
"查看kubeadm为各个节点创建的token" k8s-01:~ # kubeadm token list --kubeconfig ~/.kube/config TOKEN TTL EXPIRES USAGES DESCRIPTION EXTRA GROUPS 5750z9.ycsk3jxiahgz1gkn 23h 2021-02-14T00:55:41+08:00 authentication,signing kubelet-bootstrap-token system:bootstrappers:k8s-05 f4scbn.lev5uqmokwai5k0e 23h 2021-02-14T00:55:40+08:00 authentication,signing kubelet-bootstrap-token system:bootstrappers:k8s-02 kjfsng.qmjesofryg97c80q 23h 2021-02-14T00:55:41+08:00 authentication,signing kubelet-bootstrap-token system:bootstrappers:k8s-04 nseipt.09jaep1j8qnoqn1a 23h 2021-02-14T00:55:40+08:00 authentication,signing kubelet-bootstrap-token system:bootstrappers:k8s-01 zlal1h.856gawjgom560fys 23h 2021-02-14T00:55:40+08:00 authentication,signing kubelet-bootstrap-token system:bootstrappers:k8s-03
- token有效期为1天,超期后将不能被用来bootstrap kubelet,且会被kube-controller-manager的token cleaner清理
kube-apiserver接收kubelet的bootstrap token后,将请求的user设置为system:bootstrap; group设置为system:bootstrappers,后续将为这个group设置ClusterRoleBinding
1.9.1、创建kubelet配置文件
k8s-01:~ # cd /opt/k8s/conf/ k8s-01:/opt/k8s/conf # source /opt/k8s/bin/k8s-env.sh k8s-01:/opt/k8s/conf # cat > kubelet-config.yaml.template <<EOF kind: KubeletConfiguration apiVersion: kubelet.config.k8s.io/v1beta1 address: "##NODE_IP##" staticPodPath: "" syncFrequency: 1m fileCheckFrequency: 20s httpCheckFrequency: 20s staticPodURL: "" port: 10250 readOnlyPort: 0 rotateCertificates: true serverTLSBootstrap: true authentication: anonymous: enabled: false webhook: enabled: true x509: clientCAFile: "/etc/kubernetes/cert/ca.pem" authorization: mode: Webhook registryPullQPS: 0 registryBurst: 20 eventRecordQPS: 0 eventBurst: 20 enableDebuggingHandlers: true enableContentionProfiling: true healthzPort: 10248 healthzBindAddress: "##NODE_IP##" clusterDomain: "${CLUSTER_DNS_DOMAIN}" clusterDNS: - "${CLUSTER_DNS_SVC_IP}" nodeStatusUpdateFrequency: 10s nodeStatusReportFrequency: 1m imageMinimumGCAge: 2m imageGCHighThresholdPercent: 85 imageGCLowThresholdPercent: 80 volumeStatsAggPeriod: 1m kubeletCgroups: "" systemCgroups: "" cgroupRoot: "" cgroupsPerQOS: true cgroupDriver: systemd runtimeRequestTimeout: 10m hairpinMode: promiscuous-bridge maxPods: 220 podCIDR: "${CLUSTER_CIDR}" podPidsLimit: -1 resolvConf: /etc/resolv.conf maxOpenFiles: 1000000 kubeAPIQPS: 1000 kubeAPIBurst: 2000 serializeImagePulls: false evictionHard: memory.available: "100Mi" nodefs.available: "10%" nodefs.inodesFree: "5%" imagefs.available: "15%" evictionSoft: {} enableControllerAttachDetach: true failSwapOn: true containerLogMaxSize: 20Mi containerLogMaxFiles: 10 systemReserved: {} kubeReserved: {} systemReservedCgroup: "" kubeReservedCgroup: "" enforceNodeAllocatable: ["pods"] EOF
1.9.2、配置kubelet为systemctl启动
k8s-01:~ # cd /opt/k8s/conf/ k8s-01:/opt/k8s/conf # source /opt/k8s/bin/k8s-env.sh k8s-01:/opt/k8s/conf # cat > kubelet.service.template <<EOF [Unit] Description=Kubernetes Kubelet Documentation=https://github.com/GoogleCloudPlatform/kubernetes After=docker.service Requires=docker.service [Service] WorkingDirectory=${K8S_DIR}/kubelet ExecStart=/opt/k8s/bin/kubelet \\ --v=2 \\ --hostname-override=##NODE_IP## \\ --bootstrap-kubeconfig=/etc/kubernetes/cert/kubelet-bootstrap.kubeconfig \\ --cert-dir=/etc/kubernetes/cert \\ --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \\ --config=/etc/kubernetes/kubelet-config.yaml \\ --logtostderr=true \\ --pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.2 \\ --image-pull-progress-deadline=15m \\ --cni-conf-dir=/etc/cni/net.d \\ --root-dir=${K8S_DIR}/kubelet Restart=always RestartSec=5 StartLimitInterval=0 [Install] WantedBy=multi-user.target EOF
- –bootstrap-kubeconfig:指向 bootstrap kubeconfig 文件,kubelet 使用该文件中的用户名和 token 向 kube-apiserver 发送 TLS Bootstrapping 请求
- K8S approve kubelet 的 csr 请求后,在 --cert-dir 目录创建证书和私钥文件,然后写入 --kubeconfig 文件
kubelet
设置了--hostname-override
选项,kube-proxy
也需要设置该选项
,否则会出现找不到 Node
的情况;
1.9.3、拉取kubelet依赖的pause镜像
k8s-01:~ # docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.2 k8s-01:~ # cd /opt/k8s/packages/ k8s-01:/opt/k8s/packages # docker save $(docker images | grep -v REPOSITORY | awk 'BEGIN{OFS=":";ORS=" "}{print $1,$2}') -o pause.tar "将镜像保存到本地,分发到其他节点"
1.9.4、分发kubelet证书和文件到其他节点
#!/usr/bin/env bash source /opt/k8s/bin/k8s-env.sh for (( i=0; i < 5; i++ )) do sed -e "s/##NODE_IP##/${NODE_IPS[i]}/" /opt/k8s/conf/kubelet.service.template > \ /opt/k8s/conf/kubelet-${NODE_IPS[i]}.service sed -e "s/##NODE_IP##/${NODE_IPS[i]}/" /opt/k8s/conf/kubelet-config.yaml.template > \ /opt/k8s/conf/kubelet-config-${NODE_IPS[i]}.yaml.template done for node_name in ${NODE_NAMES[@]} do printf "\e[1;34m${node_name}\e[0m\n" scp /opt/k8s/ssl/kubelet-bootstrap-${node_name}.kubeconfig \ ${node_name}:/etc/kubernetes/cert/kubelet-bootstrap.kubeconfig done for host in ${NODE_IPS[@]} do printf "\e[1;34m${host}\e[0m\n" scp /opt/k8s/conf/kubelet-${host}.service ${host}:/etc/systemd/system/kubelet.service scp /opt/k8s/conf/kubelet-config-${host}.yaml.template ${host}:/etc/kubernetes/kubelet-config.yaml scp /opt/k8s/packages/pause.tar ${host}:/opt/k8s/ ssh root@${host} "docker load -i /opt/k8s/pause.tar" done
1.9.5、授权kubelet-bootstrap用户组允许请求证书
k8s-01:~ # kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --group=system:bootstrappers
- 不创建的话,kubelet会启动失败
1.9.6、启动kubelet服务
#!/usr/bin/env bash source /opt/k8s/bin/k8s-env.sh for host in ${NODE_IPS[@]} do printf "\e[1;34m${host}\e[0m\n" ssh root@${host} "mkdir -p ${K8S_DIR}/kubelet/kubelet-plugins/volume/exec/" ssh root@${host} "systemctl daemon-reload && \ systemctl enable kubelet --now && \ systemctl status kubelet | grep Active" done
kubelet 启动后使用 --bootstrap-kubeconfig 向 kube-apiserver 发送 CSR 请求,当这个CSR 被 approve 后,kube-controller-manager 为 kubelet 创建 TLS 客户端证书、私钥和 --kubeletconfig 文件
- 注意:
kube-controller-manager
需要配置--cluster-signing-cert-file
和--cluster-signing-key-file
参数,才会为TLS Bootstrap 创建证书和私钥
1.9.7、自动approve CSR请求
- 创建三个
ClusterRoleBinding
,分别用于自动approve client
、renew client
、renew server
证书
k8s-01:~ # cd /opt/k8s/conf/ k8s-01:/opt/k8s/conf # cat > csr-crb.yaml <<EOF # Approve all CSRs for the group "system:bootstrappers" kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: auto-approve-csrs-for-group subjects: - kind: Group name: system:bootstrappers apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: system:certificates.k8s.io:certificatesigningrequests:nodeclient apiGroup: rbac.authorization.k8s.io --- # To let a node of the group "system:nodes" renew its own credentials kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: node-client-cert-renewal subjects: - kind: Group name: system:nodes apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient apiGroup: rbac.authorization.k8s.io --- # A ClusterRole which instructs the CSR approver to approve a node requesting a # serving cert matching its client cert. kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: approve-node-server-renewal-csr rules: - apiGroups: ["certificates.k8s.io"] resources: ["certificatesigningrequests/selfnodeserver"] verbs: ["create"] --- # To let a node of the group "system:nodes" renew its own server credentials kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: node-server-cert-renewal subjects: - kind: Group name: system:nodes apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: approve-node-server-renewal-csr apiGroup: rbac.authorization.k8s.io EOF k8s-01:/opt/k8s/conf # kubectl apply -f csr-crb.yaml
auto-approve-csrs-for-group
自动approve node的第一次CSR,注意第一次CSR时,请求的Group为system:bootstrappersnode-client-cert-renewal
自动approve node后续过期的client证书,自动生成的证书Group为system:nodes
node-server-cert-renewal
自动approve node后续过期的server证书,自动生成的证书Group
1.9.8、查看节点是否都为ready
k8s-01:~ # kubectl get node NAME STATUS ROLES AGE VERSION 192.168.72.39 Ready <none> 20s v1.19.7 192.168.72.40 Ready <none> 19s v1.19.7 192.168.72.41 Ready <none> 18s v1.19.7 192.168.72.42 Ready <none> 18s v1.19.7 192.168.72.43 Ready <none> 17s v1.19.7
1.9.9、手动approve server cert csr
- 基于安全考虑,CSR approving controllers不会自动approve kubelet server证书签名请求,需要手动approve
k8s-01:~ # kubectl get csr | grep Pending | awk '{print $1}' | xargs kubectl certificate approve
1.9.10、bear token认证和授权
- 创建一个ServiceAccount,将它和ClusterRole system:kubelet-api-admin绑定,从而具有调用kubelet API的权限
k8s-01:~ # kubectl create sa kubelet-api-test k8s-01:~ # kubectl create clusterrolebinding kubelet-api-test --clusterrole=system:kubelet-api-admin --serviceaccount=default:kubelet-api-test