2053 kubelet

kubelet 运行在每个 worker 节点上,接收 kube-apiserver 发送的请求,管理 Pod 容器,执行交互式命令,如 exec、run、logs 等。

kubelet 启动时自动向 kube-apiserver 注册节点信息

为确保安全,部署时关闭了 kubelet 的非安全 http 端口,对请求进行认证和授权,拒绝未授权的访问(如 apiserver、heapster 的请求)。

部署策略:

  • 动态创建 bootstrap token,而不是在 apiserver 中静态配置;

  • 使用 TLS bootstrap 机制自动生成 client 和 server 证书,过期后自动轮转;

  • 在 KubeletConfiguration 类型的 yaml 文件配置主要参数;

  • 关闭只读端口,在安全端口 10250 接收 https 请求,对请求进行认证和授权,拒绝匿名访问和非授权访问;

  • 使用 kubeconfig 访问 apiserver 的安全端口;

部署软件规划:

IP

部署软件包

10.40.61.116

kubelet

10.40.58.153

kubelet

10.40.58.154

kubelet

01.创建 Bootstrapping Token

Token可以是任意的包含128 bit的字符串,可以使用安全的随机数发生器生成。

export BOOTSTRAP_TOKEN=$(head -c 16 /dev/urandom | od -An -t x | tr -d ' ')

cat > bootstrap-token.csv <<EOF
${BOOTSTRAP_TOKEN},kubelet-bootstrap,10001,"system:kubelet-bootstrap"
EOF

注意:在进行后续操作前请检查 bootstrap-token.csv 文件,确认其中的 ${BOOTSTRAP_TOKEN} 环境变量已经被真实的值替换。

kube-apiserver启动时需要使用--token-auth-file=bootstrap-token.csv

如果后续重新生成了 BOOTSTRAP_TOKEN,则需要:

  • 更新 bootstrap-token.csv 文件,分发到所有机器 (master 和 node)的 /etc/kubernetes/ 目录下,分发到node节点上非必需;

  • 重新生成 bootstrap.kubeconfig 文件,分发到所有 node 机器的 /etc/kubernetes/ 目录下;

  • 重启 kube-apiserver 和 kubelet 进程;

  • 重新 approve kubelet 的 csr 请求;

02.创建 bootstrap kubeconfig 文件

执行下面的命令时需要先安装kubectl命令,请参考安装kubectl命令行工具。

  • --embed-certs 为 true 时表示将 certificate-authority 证书写入到生成的 bootstrap.kubeconfig 文件中;

  • 设置客户端认证参数时没有指定秘钥和证书,后续由 kube-apiserver 自动生成;

      export KUBE_APISERVER="https://apiserver-p001.svc.gxd88.cn:6443"
  • 设置集群参数

      kubectl config set-cluster kubernetes \
        --certificate-authority=ca.pem \
        --embed-certs=true \
        --server=${KUBE_APISERVER} \
        --kubeconfig=bootstrap.kubeconfig
  • 设置客户端认证参数

      kubectl config set-credentials kubelet-bootstrap \
        --token=${BOOTSTRAP_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
  • 分发 kubeconfig 文件

将两个 kubeconfig 文件分发到所有 Node 机器的 /srv/kubernetes/kubeconfig 目录

cp bootstrap.kubeconfig  /srv/kubernetes/kubeconfig

03.维护配置文件

新版本的kubelet很多参数已经废弃只能通过创建单独的配置文件方式维护, 启动程序时使用--config

根据主机的实际情况替换addresshealthzBindAddress

cat > /srv/kubernetes/conf/kubelet-config.yaml <<EOF
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
address: "10.40.61.116"
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: "/srv/kubernetes/pki/ca.pem"
authorization:
  mode: Webhook
registryPullQPS: 0
registryBurst: 20
eventRecordQPS: 0
eventBurst: 20
enableDebuggingHandlers: true
enableContentionProfiling: true
healthzPort: 10248
healthzBindAddress: "10.40.61.116"
clusterDomain: "cluster.local"
clusterDNS:
  - "10.244.0.10"
nodeStatusUpdateFrequency: 10s
nodeStatusReportFrequency: 1m
imageMinimumGCAge: 2m
imageGCHighThresholdPercent: 85
imageGCLowThresholdPercent: 80
volumeStatsAggPeriod: 1m
kubeletCgroups: ""
systemCgroups: ""
cgroupRoot: ""
cgroupsPerQOS: true
cgroupDriver: cgroupfs
runtimeRequestTimeout: 10m
hairpinMode: promiscuous-bridge
maxPods: 220
podCIDR: "10.243.0.0/16"
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

authentication.anonymous.enabled: 设置为 false,不允许匿名访问 10250 端口; authentication.x509.clientCAFile: 指定签名客户端证书的 CA 证书,开启 HTTPs 证书认证; authentication.webhook.enabled=true: 开启 HTTPs bearer token 认证。

04.使用systemd管理kubelet

根据实际情况替换--hostname-override, 并且kube-proxy配置的过程中也需要指定该参数

cat > /etc/systemd/system/kubelet.service <<EOF
[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=docker.service
Requires=docker.service

[Service]
ExecStart=/srv/kubernetes/bin/kubelet \\
  --bootstrap-kubeconfig=/srv/kubernetes/kubeconfig/bootstrap.kubeconfig  \\
  --cert-dir=/srv/kubernetes/pki \\
  --kubeconfig=/srv/kubernetes/kubeconfig/kubelet.kubeconfig \\
  --config=/srv/kubernetes/conf/kubelet-config.yaml \\
  --hostname-override=py-modelo2o08cn-p005.pek3.example.net \\
  --pod-infra-container-image=wcr.example.net/wekube/acs/pause-amd64:3.1 \\
  --image-pull-progress-deadline=15m \\
  --logtostderr=true \\
  --v=2 \\
  --log-dir=/srv/kubernetes/log 
Restart=always
RestartSec=5
StartLimitInterval=0

[Install]
WantedBy=multi-user.target
EOF
  • 如果设置了 --hostname-override 选项,则 kube-proxy 也需要设置该选项,否则会出现找不到 Node 的情况;

  • --bootstrap-kubeconfig:指向 bootstrap kubeconfig 文件,kubelet 使用该文件中的用户名和 token 向 kube-apiserver 发送 TLS Bootstrapping 请求;

  • K8S approve kubelet 的 csr 请求后,在 --cert-dir 目录创建证书和私钥文件,然后写入 --kubeconfig 文件;

注意:kube-controller-manager 需要配置 --cluster-signing-cert-file--cluster-signing-key-file 参数,才会为 TLS Bootstrap 创建证书和私钥。

05.启动/停止 kubelet

sudo /bin/systemctl daemon-reload
sudo /bin/systemctl enable kubelet.service

kube-schedulercan be started and stopped as follows:

sudo systemctl start kubelet.service
sudo systemctl stop  kubelet.service

06.验证

0601.授权

kubelet 首次启动时向 kube-apiserver 发送证书签名请求,必须通过后 kubernetes 系统才会将该 Node 加入到集群。

启动时会查找--kubeletconfig参数对应的文件是否存在,如果不存在则使用 --bootstrap-kubeconfig 指定的 kubeconfig 文件向 kube-apiserver 发送证书签名请求 (CSR)。

kube-apiserver 收到 CSR 请求后,对其中的 Token 进行认证,认证通过后将请求的 user 设置为 kubelet-bootstrap:,user 设置为 user=kubelet-bootstrap,这一过程称为 Bootstrap Token Auth。

kubelet 创建 CSR 请求后,下一步需要创建被 approve,有两种方式:

  • kube-controller-manager 自动 aprrove;

  • 手动使用命令 kubectl certificate approve;

CSR 被 approve 后,kubelet 向 kube-controller-manager 请求创建 client 证书,kube-controller-manager 中的 csr approving controller 使用 SubjectAccessReview API 来检查 kubelet 请求(对应的 User 是 kubelet-bootstrap)是否具有相应的权限。

默认情况下,这个 user 没有创建 CSR 的权限,因此 kubelet 会启动失败,可通过如下方式创建一个 clusterrolebinding,将 user kubelet-bootstrap 和 clusterrole system:node-bootstrapper 绑定。

kubectl create clusterrolebinding kubelet-bootstrap \
  --clusterrole=system:node-bootstrapper \
  --user=kubelet-bootstrap

注意: --user=kubelet-bootstrap 是在 创建 TLS Bootstrapping Token 章节中 bootstrap-token.csv 文件中指定的用户名,bootstrap-token.csv 同时也写入了 bootstrap.kubeconfig 文件

0602.查看kubelet服务

$ systemctl status kubelet|grep active
   Active: active (running) since 四 2020-04-16 16:25:45 CST; 1 day 22h ago

0603.查看端口

netstat -lnpt|grep kubelet
tcp        0      0 10.40.61.116:10248      0.0.0.0:*               LISTEN      27496/kubelet
tcp        0      0 10.40.61.116:10250      0.0.0.0:*               LISTEN      27496/kubelet
tcp        0      0 127.0.0.1:37325         0.0.0.0:*               LISTEN      27496/kubelet
  • 10248: healthz http 服务;

  • 10250: https 服务,访问该端口时需要认证和授权(即使访问 /healthz 也需要);

  • 未开启只读端口 10255;

07.允许approve CSR 请求,生成 kubelet client 证书

如果觉的手动允许approve麻烦可以直接配置自动approve

0701.手动允许approve CSR

查看未授权的 CSR 请求:

$ kubectl  get csr
NAME        AGE   SIGNERNAME                      REQUESTOR                                          CONDITION
csr-fsk6w   21s   kubernetes.io/kubelet-serving   system:node:py-modelo2o08cn-p005.pek3.example.net   Pending

通过 CSR 请求:

$ kubectl certificate approve csr-fsk6w
certificatesigningrequest "csr-2b308" approved

$ kubectl get nodes
NAME                                   STATUS   ROLES    AGE   VERSION
py-modelo2o08cn-p005.pek3.example.net   Ready    <none>   74m   v1.18.1

自动生成了 kubelet kubeconfig 文件和公私钥:

$ ls  -l /srv/kubernetes/kubeconfig/kubelet.kubeconfig
-rw------- 1 root root 2326 4月  15 12:23 kubeconfig/kubelet.kubeconfig

$ ls -al /srv/kubernetes/pki/kubelet*
-rw------- 1 root root 1265 4月  15 12:23 /srv/kubernetes/pki/kubelet-client-2020-04-15-12-23-43.pem
lrwxrwxrwx 1 root root   58 4月  15 12:23 /srv/kubernetes/pki/kubelet-client-current.pem -> /srv/kubernetes/pki/kubelet-client-2020-04-15-12-23-43.pem
-rw-r--r-- 1 root root 2355 4月  14 15:38 /srv/kubernetes/pki/kubelet.crt
-rw------- 1 root root 1675 4月  14 15:38 /srv/kubernetes/pki/kubelet.key
-rw------- 1 root root 1342 4月  15 13:37 /srv/kubernetes/pki/kubelet-server-2020-04-15-13-37-56.pem
lrwxrwxrwx 1 root root   58 4月  15 13:37 /srv/kubernetes/pki/kubelet-server-current.pem -> /srv/kubernetes/pki/kubelet-server-2020-04-15-13-37-56.pem

0702.自动approve

创建三个 ClusterRoleBinding,分别用于自动 approve client、renew client 证书。

根据官方文档介绍 renew server证书基于安全的考虑要自己实现controller

Note: The CSR approving controllers implemented in core Kubernetes do not approve node serving certificates for security reasons. To use RotateKubeletServerCertificate operators need to run a custom approving controller, or manually approve the serving certificate requests.

$ cat > csr-crb.yaml <<EOF
# Approve all CSRs for the group "system:bootstrappers"
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: auto-approve-csrs-for-group
subjects:
- kind: Group
  name: system:kubelet-bootstrap
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: system:certificates.k8s.io:certificatesigningrequests:nodeclient
  apiGroup: rbac.authorization.k8s.io

---
# Approve renewal CSRs for the group "system:nodes"
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: auto-approve-renewals-for-nodes
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
EOF

auto-approve-csrs-for-group:自动 approve node 的第一次 CSR; 注意第一次 CSR 时,请求的 Group 为 system:kubelet-bootstrap;

auto-approve-renewals-for-nodes:自动 approve node 后续过期的 client 证书,自动生成的证书 Group 为 system:nodes;

创建资源:

kubectl apply -f csr-crb.yaml

08.授予 kube-apiserver 访问 kubelet API 的权限

因为kubelet启动时已经关闭了anonymous认证, 指定了authentication.x509.clientCAFile证书认证, 并且kube-apiserver中使用--kubelet-client-certificate和--kubelet-client-key标志启动apiserver, 所以需要进行对应的授权

kube-apiserver 的启动参数如下

/srv/kubernetes/bin/kube-apiserver \
  --kubelet-https=true \
  --kubelet-client-certificate=/srv/kubernetes/pki/apiserver.pem \
  --kubelet-client-key=/srv/kubernetes/pki/apiserver-key.pem

apiserver.pem 中的CN为kubernetes,所以需要对该用户授权访问kubelet资源的授权

  • verb=*, resource=nodes, subresource=proxy

  • verb=*, resource=nodes, subresource=stats

  • verb=*, resource=nodes, subresource=log

  • verb=*, resource=nodes, subresource=spec

  • verb=*, resource=nodes, subresource=metrics

授权指令

$ kubectl create clusterrolebinding kube-apiserver:kubelet-apis --clusterrole=system:kubelet-api-admin --user kubernetes

如果不授权会报如下错误

 kubectl  logs kubernetes-dashboard-7c4995887f-zt4rp -n kube-system
Error from server (Forbidden): Forbidden (user=kubernetes, verb=get, resource=nodes, subresource=proxy) ( pods/log kubernetes-dashboard-7c4995887f-zt4rp)

更多细节请看https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-authentication-authorization/#kubelet-authentication

09.控制证书轮转时间

由于kubelet的证书是kube-controller-manager负责签发的, 在kube-controller-manager启动参数中添加如下配置:

/srv/kubernetes/bin/kube-controller-manager \ 
    --experimental-cluster-signing-duration=24h0m0s

默认时间为8760h0m0s也就是1年

09.Q&A

Q: 遇到了一个大坑, 我查看了大量的bootstrap TLS 相关的文档, 文档上大部分的都是说使用bootstrap kubeconfig自动的签名node节点的证书,又根据文档查看说如果不指定--kubeconfig会自动使用bootstrap kubeconfig的APIserver地址, 但是一直报错

server.go:560] standalone mode, no API client

A: 启动的时候要指定--bootstrap-kubeconfig, 也要指定--kubeconfig, 这里的--kubeconfig是指证书生成以后自动生成的kubelet.kubeconfig的目录

Q:

 failed to run Kubelet: cannot create certificate signing request: certificatesigningrequests.certificates.k8s.io is forbidden: User "kubelet-bootstrap" cannot create resource "certificatesigningrequests" in API group "certificates.k8s.io" at the cluster scope

A: kubelet 启动时向 kube-apiserver 发送 TLS bootstrapping 请求,需要先将 bootstrap token 文件中的 kubelet-bootstrap 用户赋予 system:node-bootstrapper cluster 角色(role), 然后 kubelet 才能有权限创建认证请求(certificate signing requests):

kubectl create clusterrolebinding kubelet-bootstrap \
  --clusterrole=system:node-bootstrapper \
  --user=kubelet-bootstrap

Q: 因为我没有安装cni-plugin, 所以我没有指定cni相关的配置, worker几点一直是NotReady的状态, 但是查看官方文档说kubelet存在默认的docker网络插件, 我使用的就是docker, 那么问题出现在那里呢?

kubelet: E0415 12:24:19.385879   22377 kubelet.go:2187] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
kubelet: W0415 12:24:23.101522   22377 cni.go:237] Unable to update cni config: no networks found in /srv/kubernetes/cni/net.d

A: 经过万能的--help查看, 他是这么说的

--network-plugin string <Warning: Alpha feature> The name of the network plugin to be invoked for various events in kubelet/pod lifecycle. This docker-specific flag only works when container-runtime is set to docker.

但是又看--help人家说container-runtime默认就是docker, 情急之下删除了所有cni相关的配置, 发现居然起来了, 那看来就是这样了

Q: 查看kubelet日志报如下的错误

kubelet: E0512 13:20:08.674812   17205 controller.go:178] failed to update node lease, error: Operation cannot be fulfilled on leases.coordination.k8s.io "py-modelo2o08cn-p005.pek3.wecash.net": the object has been modified; please apply your changes to the latest version and try again

A: 报错的主要原因是--hostname-override=py-modelo2o08cn-p005.pek3.wecash.net 三个节点配置的相同, 需要清理rm -rf kubelet-*证书文件和rm -rf kubelet.kubeconfig文件

最后更新于