概要
kubelet 是运行在每个节点上的主要的“节点代理”,每个节点都会启动 kubelet进程,用来处理 Master 节点 Scheduler 下发到本节点的任务,按照 PodSpec 描述来管理Pod 和其中的容器(PodSpec 是用来描述一个 pod 的 YAML 或者 JSON 对象)。
kubelet 通过各种机制(主要通过 apiserver )获取一组 PodSpec 并保证在这些 PodSpec 中描述的容器健康运行。
kubelet 的主要功能
1、kubelet 默认监听四个端口,分别为 10250 、10255、10248、4194。
LISTEN 0 128 *:10250 *:* users:(("kubelet",pid=48500,fd=28)) LISTEN 0 128 *:10255 *:* users:(("kubelet",pid=48500,fd=26)) LISTEN 0 128 *:4194 *:* users:(("kubelet",pid=48500,fd=13)) LISTEN 0 128 127.0.0.1:10248 *:* users:(("kubelet",pid=48500,fd=23))
- 10250(kubelet API):kubelet server 与 apiserver 通信的端口,定期请求 apiserver 获取自己所应当处理的任务,通过该端口可以访问获取 node 资源以及状态。
- 10248(健康检查端口):通过访问该端口可以判断 kubelet 是否正常工作, 通过 kubelet 的启动参数
--healthz-port
和--healthz-bind-address
来指定监听的地址和端口。
$ curl http://127.0.0.1:10248/healthz ok
- 4194(cAdvisor 监听):kublet 通过该端口可以获取到该节点的环境信息以及 node 上运行的容器状态等内容,访问 http://localhost:4194 可以看到 cAdvisor 的管理界面,通过 kubelet 的启动参数
--cadvisor-port
可以指定启动的端口。
$ curl http://127.0.0.1:4194/metrics
- 10255 (readonly API):提供了 pod 和 node 的信息,接口以只读形式暴露出去,访问该端口不需要认证和鉴权。
// 获取 pod 的接口,与 apiserver 的 // http://127.0.0.1:8080/api/v1/pods?fieldSelector=spec.nodeName= 接口类似 $ curl http://127.0.0.1:10255/pods // 节点信息接口,提供磁盘、网络、CPU、内存等信息 $ curl http://127.0.0.1:10255/spec/
2、kubelet 主要功能:
- pod 管理:kubelet 定期从所监听的数据源获取节点上 pod/container 的期望状态(运行什么容器、运行的副本数量、网络或者存储如何配置等等),并调用对应的容器平台接口达到这个状态。
- 容器健康检查:kubelet 创建了容器之后还要查看容器是否正常运行,如果容器运行出错,就要根据 pod 设置的重启策略进行处理。
- 容器监控:kubelet 会监控所在节点的资源使用情况,并定时向 master 报告,资源使用数据都是通过 cAdvisor 获取的。知道整个集群所有节点的资源情况,对于 pod 的调度和正常运行至关重要。
工作
可以把 Kubelet 当成一种特殊的 Controller,它每隔 20 秒(可以自定义)向 kube-apiserver 通过 NodeName 获取自身 Node 上所要运行的 Pod 清单。一旦获取到了这个清单,它就会通过与自己的内部缓存进行比较来检测新增加的 Pod,如果有差异,就开始同步 Pod 列表。我们来详细分析一下同步过程:
- 如果 Pod 正在创建, Kubelet 就会记录一些在 Prometheus 中用于追踪 Pod 启动延时的指标。
- 然后生成一个 PodStatus 对象,它表示 Pod 当前阶段的状态。Pod 的状态(Phase) 是 Pod 在其生命周期中的最精简的概要,包括 Pending,Running,Succeeded,Failed 和 Unkown 这几个值。状态的产生过程非常过程,所以很有必要深入了解一下背后的原理:
首先串行执行一系列 Pod 同步处理器(PodSyncHandlers),每个处理器检查检查 Pod 是否应该运行在该节点上。当所有的处理器都认为该 Pod 不应该运行在该节点上,则 Pod 的 Phase 值就会变成 PodFailed,并且将该 Pod 从该节点上驱逐出去。例如当你创建一个 Job 时,如果 Pod 失败重试的时间超过了 spec.activeDeadlineSeconds 设置的值,就会将 Pod 从该节点驱逐出去。 - 接下来,Pod 的 Phase 值由 init 容器 和应用容器的状态共同来决定。因为目前容器还没有启动,容器被视为处于等待阶段,如果 Pod 中至少有一个容器处于等待阶段,则其 Phase 值为 Pending。
- 最后,Pod 的 Condition 字段由 Pod 内所有容器的状态决定。现在我们的容器还没有被容器运行时创建,所以 PodReady 的状态被设置为 False。可以通过 kubectl 查看:$ kubectl get <PODNAME> -o go-template='{{range .status.conditions}}{{if eq .type "Ready"}}{{.status}}{{end}}{{end}}'
- 然后生成一个 PodStatus 对象,它表示 Pod 当前阶段的状态。Pod 的状态(Phase) 是 Pod 在其生命周期中的最精简的概要,包括 Pending,Running,Succeeded,Failed 和 Unkown 这几个值。状态的产生过程非常过程,所以很有必要深入了解一下背后的原理:
- 生成 PodStatus 之后(Pod 中的 status 字段),Kubelet 就会将它发送到 Pod 的状态管理器,该管理器的任务是通过 apiserver 异步更新 etcd 中的记录。
- 接下来运行一系列准入处理器来确保该 Pod 是否具有相应的权限(包括强制执行 AppArmor 配置文件和 NO_NEW_PRIVS),被准入控制器拒绝的 Pod 将一直保持 Pending 状态。
- 如果 Kubelet 启动时指定了 cgroups-per-qos 参数,Kubelet 就会为该 Pod 创建 cgroup 并进行相应的资源限制。这是为了更方便地对 Pod 进行服务质量(QoS)管理。
- 然后为 Pod 创建相应的目录,包括 Pod 的目录(/var/run/kubelet/pods/<podID>),该 Pod 的卷目录(<podDir>/volumes)和该 Pod 的插件目录(<podDir>/plugins)。
- 卷管理器会挂载 Spec.Volumes 中定义的相关数据卷,然后等待是否挂载成功。根据挂载卷类型的不同,某些 Pod 可能需要等待更长的时间(比如 NFS 卷)。
- 从 apiserver 中检索 Spec.ImagePullSecrets 中定义的所有 Secret,然后将其注入到容器中。
- 最后通过容器运行时接口(Container Runtime Interface(CRI))开始启动容器(下面会详细描述)。