Kubelet Kubernetes 的初始化系统(init system)
• 从不同源获取 Pod 清单,并按需求启停 Pod 的核心组件:
• Pod 清单可从本地文件目录,给定的 HTTPServer 或 KubeAPIServer 等源头获取
• Kubelet 将运行时,网络和存储抽象成了 CRI(容器运行时接口,通过调用docker接口将容器启动起来),CNI(帮你setup网络),CSI(通过调用存储接口来创建volume,然后跟这个节点发生绑定关系,attach上来,然后mount上你的volume)。(k8s为了实现标准化都将上面这些功能抽象为一个一个的接口)
• 负责汇报当前节点的资源信息和健康状态;
可以看到kubelet支持的功能是非常多的,这里面其中最重要的是podworker,其功能就是处理当前节点被调度pod的声明周期的,也就是说你建了一个pod,然后调度器将这个pod调度到某个节点上面了,那么kubelet就需要去工作了,它的工作就是启动这个pod,用容器运行时去启动,也就是将进程拉起来,放到某个namespace下面去,通过cgroup对资源做限制,通过网络插件将网络配置好。
kubelet还可以去扫描本地的某个目录来加载pod,这样就将这些静态的pod都拉起来了。通过这种方式kubelet就可以将static pod控制平面所有组件拉起来。所以这些就不像二进制搭建的k8s,平面组件都是由systemd去管理的,通过kubelet拉起来就好了,这样的好处就是简化了集群的部署同时实现了高可用。
可以看到这些控制平面核心组件都可以是pod方式进行管理,而二进制部署的都是以systemd方式去管理的。
kube-system kube-apiserver-master 1/1 Running 77 154d
kube-system kube-controller-manager-master 1/1 Running 35 57d
kube-system kube-scheduler-master 1/1 Running 69 104d
kubelet没有出现在上面以pod的方式去运行,kubeadm去部署的kubelet是被systemd去管理起来的。
[root@master kubelet]# vim config.yaml
staticPodPath: /etc/kubernetes/manifests
这个叫做静态pod路径,除了可以去watch apiserver还可以扫描本地路径来加载pod,这样就将这些pod都给拉起来了。
[root@master manifests]# ls
kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
通过static pod就可以将kubernetes控制面组件全部拉起来,这样就无需去配置systemd里面去管理了,直接用kubelet拉起来就行了。这样就简化了集群的部署,并且依赖于kublet来保证控制平面的高可用。
[root@node1 ~]# netstat -tpln | grep 10250
tcp6 0 0 :::10250 :::* LISTEN 508/kubelet
[root@master ~]# kubectl describe node node1
Unschedulable: false
System Info:
Machine ID: 326eca80bafb4c4cb9dba7d4914d5205
System UUID: 05054D56-48C8-570C-70AB-2248EC6190C1
Boot ID: 4e687aaf-6d4c-4e64-af78-38432d70aad3
Kernel Version: 3.10.0-693.el7.x86_64
OS Image: CentOS Linux 7 (Core)
Operating System: linux
Architecture: amd64
Container Runtime Version: docker://20.10.12
Kubelet Version: v1.18.8
Kube-Proxy Version: v1.18.8
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
Resource Requests Limits
-------- -------- ------
cpu 350m (21%) 0 (0%)
memory 70Mi (5%) 170Mi (12%)
ephemeral-storage 0 (0%) 0 (0%)
hugepages-1Gi 0 (0%) 0 (0%)
hugepages-2Mi 0 (0%) 0 (0%)
Lease:
HolderIdentity: node1
AcquireTime:
RenewTime: Sat, 12 Mar 2022 21:37:25 +0800
Non-terminated Pods: (3 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits AGE
--------- ---- ------------ ---------- --------------- ------------- ---
kube-system calico-node-mzmvx 250m (15%) 0 (0%) 0 (0%) 0 (0%) 17d
kube-system kube-proxy-dm8b4 0 (0%) 0 (0%) 0 (0%) 0 (0%) 17d
kube-system nodelocaldns-rnvph 100m (6%) 0 (0%) 70Mi (5%) 170Mi (12%) 17d
特别需要注意的是 Kubernetes 1.11+ 版本以后,kubelet 就移除了 10255 端口, metrics 接口又回到了 10250 端口中。
- job_name: 'kubernetes-kubelet'
kubernetes_sd_configs:
- role: node
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
insecure_skip_verify: true
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
节点管理主要是节点自注册和节点状态更新:
Kubelet 以 PodSpec 的方式工作。PodSpec 是描述一个 Pod 的 YAML 或 JSON 对象。 kubelet 采用一组通过各种机制提供的 PodSpecs(主要通过 apiserver),并确保这些 PodSpecs 中描述的 Pod 正常健康运行。
向 Kubelet 提供节点上需要运行的 Pod 清单的方法:
Kubelet 通过 API Server Client(Kubelet 启动时创建)使用 Watch 加 List 的方式监听 "/registry/nodes/$ 当前节点名" 和 “/registry/pods” 目录,将获取的信息同步到本地缓存中。
Kubelet 监听 etcd,所有针对 Pod 的操作都将会被 Kubelet 监听到。如果发现有新的绑定到本节点的 Pod,则按照 Pod 清单的要求创建该 Pod。
如果发现本地的 Pod 被修改,则 Kubelet 会做出相应的修改,比如删除 Pod 中某个容器时,则通过 Docker Client 删除该容器。 如果发现删除本节点的 Pod,则删除相应的 Pod,并通过 Docker Client 删除 Pod 中的容器。
Kubelet 读取监听到的信息,如果是创建和修改 Pod 任务,则执行如下处理:
所有以非 API Server 方式创建的 Pod 都叫 Static Pod。Kubelet 将 Static Pod 的状态汇报给 API Server,API Server 为该 Static Pod 创建一个 Mirror Pod 和其相匹配。Mirror Pod 的状态将真实反映 Static Pod 的状态。当 Static Pod 被删除时,与之相对应的 Mirror Pod 也会被删除。
[root@master ~]# ls /etc/kubernetes/manifests/
kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
Pod 通过两类探针检查容器的健康状态:
Kubelet 定期调用容器中的 LivenessProbe 探针来诊断容器的健康状况。LivenessProbe 包含如下三种实现方式:
LivenessProbe 和 ReadinessProbe 探针包含在 Pod 定义的 spec.containers.{某个容器} 中。
cAdvisor 是一个开源的分析容器资源使用率和性能特性的代理工具,集成到 Kubelet中,当Kubelet启动时会同时启动cAdvisor,且一个cAdvisor只监控一个Node节点的信息。cAdvisor 自动查找所有在其所在节点上的容器,自动采集 CPU、内存、文件系统和网络使用的统计信息。cAdvisor 通过它所在节点机的 Root 容器,采集并分析该节点机的全面使用情况。
cAdvisor 通过其所在节点机的 4194 端口暴露一个简单的 UI。
Kubelet 会监控资源的使用情况,并使用驱逐机制防止计算和存储资源耗尽。在驱逐时,Kubelet 将 Pod 的所有容器停止,并将 PodPhase 设置为 Failed。
Kubelet 定期(housekeeping-interval
)检查系统的资源是否达到了预先配置的驱逐阈值,包括
Eviction Signal | Condition | Description |
---|---|---|
memory.available |
MemoryPressue | memory.available := node.status.capacity[memory] - node.stats.memory.workingSet (计算方法参考这里) |
nodefs.available |
DiskPressure | nodefs.available := node.stats.fs.available (Kubelet Volume以及日志等) |
nodefs.inodesFree |
DiskPressure | nodefs.inodesFree := node.stats.fs.inodesFree |
imagefs.available |
DiskPressure | imagefs.available := node.stats.runtime.imagefs.available (镜像以及容器可写层等) |
imagefs.inodesFree |
DiskPressure | imagefs.inodesFree := node.stats.runtime.imagefs.inodesFree |
这些驱逐阈值可以使用百分比,也可以使用绝对值,如
--eviction-hard=memory.available<500Mi,nodefs.available<1Gi,imagefs.available<100Gi
--eviction-minimum-reclaim="memory.available=0Mi,nodefs.available=500Mi,imagefs.available=2Gi"`
--system-reserved=memory=1.5Gi
这些驱逐信号可以分为软驱逐和硬驱逐
驱逐动作包括回收节点资源和驱逐用户 Pod 两种:
除了驱逐之外,Kubelet 还支持一系列的容器和镜像垃圾回收选项,它们未来将会被驱逐替代:
垃圾回收参数 | 驱逐参数 | 解释 |
---|---|---|
--image-gc-high-threshold |
--eviction-hard 或 --eviction-soft |
现存的驱逐回收信号可以触发镜像垃圾回收 |
--image-gc-low-threshold |
--eviction-minimum-reclaim |
驱逐回收实现相同行为 |
--minimum-image-ttl-duration |
由于驱逐不包括TTL配置,所以它还会继续支持 | |
--maximum-dead-containers |
一旦旧日志存储在容器上下文之外,就会被弃用 | |
--maximum-dead-containers-per-container |
一旦旧日志存储在容器上下文之外,就会被弃用 | |
--minimum-container-ttl-duration |
一旦旧日志存储在容器上下文之外,就会被弃用 | |
--low-diskspace-threshold-mb |
--eviction-hard or eviction-soft |
驱逐回收将磁盘阈值泛化到其他资源 |
--outofdisk-transition-frequency |
--eviction-pressure-transition-period |
驱逐回收将磁盘压力转换到其他资源 |
容器运行时(Container Runtime)是 Kubernetes 最重要的组件之一,负责真正管理镜像和容器的生命周期。Kubelet 通过 容器运行时接口(Container Runtime Interface,CRI) 与容器运行时交互,以管理镜像和容器。
Container Runtime Interface(CRI)是 Kubernetes v1.5 引入的容器运行时接口,它将 Kubelet 与容器运行时解耦,将原来完全面向 Pod 级别的内部接口拆分成面向 Sandbox 和 Container 的 gRPC 接口,并将镜像管理和容器管理分离到不同的服务。
CRI 最早从从 1.4 版就开始设计讨论和开发,在 v1.5 中发布第一个测试版。在 v1.6 时已经有了很多外部容器运行时,如 frakti 和 cri-o 等。v1.7 中又新增了 cri-containerd 支持用 Containerd 来管理容器。
CRI 基于 gRPC 定义了 RuntimeService 和 ImageService 等两个 gRPC 服务,分别用于容器运行时和镜像的管理。其定义在
Kubelet 作为 CRI 的客户端,而容器运行时则需要实现 CRI 的服务端(即 gRPC server,通常称为 CRI shim)。容器运行时在启动 gRPC server 时需要监听在本地的 Unix Socket (Windows 使用 tcp 格式)。
目前基于 CRI 容器引擎已经比较丰富了,包括
/usr/bin/kubelet \
--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf \
--kubeconfig=/etc/kubernetes/kubelet.conf \
--pod-manifest-path=/etc/kubernetes/manifests \
--allow-privileged=true \
--network-plugin=cni \
--cni-conf-dir=/etc/cni/net.d \
--cni-bin-dir=/opt/cni/bin \
--cluster-dns=10.96.0.10 \
--cluster-domain=cluster.local \
--authorization-mode=Webhook \
--client-ca-file=/etc/kubernetes/pki/ca.crt \
--cadvisor-port=0 \
--rotate-certificates=true \
--cert-dir=/var/lib/kubelet/pki
如下 kubelet 内部组件结构图所示,Kubelet 由许多内部组件构成
通过 Kubelet 的 10255 端口可以查询 Node 的汇总指标。有两种访问方式
http://:10255/stats/summary
kubectl proxy
来访问,比如kubectl proxy&
curl http://localhost:8001/api/v1/proxy/nodes/:10255/stats/summary