创建一个 k8s pod,背后究竟发生了什么
本例使用 kubectl 创建、运行了一个 nginx pod:
(注:环境为 Kubernetes 1.19.16)
kubectl run nginx --image=nginx --restart=Never
通过 tcpdump 抓包、分析,得到的交互流程如下图所示:
注解:
【1~4】为 k8s 启动时就发生的步骤
【1】scheduler 启动时向 apiserver 请求监听所有pod的列表,使用了参数 fieldSelectorstatus.phase!=Succeeded,status.phase!=Failed
,表明对 phase 为 Succeeded 和 Failed 的 pod 不感兴趣,排除在监听对象之外。事实上 scheduler 对 phase 为 Pending 的 pod 更感兴趣,因为刚创建的 pod 状态为 Pending,而 scheduler 的主要职责就是把 Pending 状态的 pod 调度到某个节点上,由该节点上的 kubelet 把 pod 运行起来。
【2】apiserver 返回的报文如下:
HTTP/1.1 200 OK
...
Transfer-Encoding: chunked
头部 Transfer-Encoding: chunked
表示 response 的 body 是以 chunk 分块的形式推送给 scheduler,当前没有 chunk。换句话说,当 scheduler 请求监听的pod列表有变动时,apiserver 会把相应的pod信息推送给 scheduler,scheduler 只需保持连接、耐心等待就行。
【3】kubelet 启动时 apiserver 请求监听所有pod的列表,使用了参数 fieldSelectorfieldSelector=spec.nodeName=node-1
,表明只对 spec.nodeName 设置为 node-1 的 pod 感兴趣。新创建的 pod 不会设置 spec.nodeName 字段,经由 scheduler 调度处理后,pod 的 spec.nodeName 字段会被设置为某个节点的名字,表明应由该节点运行 pod。本例中 kubelet 正是运行在节点 node-1 上,这就是使用参数 fieldSelectorfieldSelector=spec.nodeName=node-1
的意义,因为 kubelet 只对调度到 node-1 上的 pod 感兴趣。
【4】返回的报文和【2】类似。
【5】对应用户执行的kubectl run nginx --image=nginx --restart=Never
命令,kubectl 请求 apiserver 创建一个 pod 对象,请求报文 body 如下:
{
"kind": "Pod",
"apiVersion": "v1",
"metadata": {
"name": "nginx",
"creationTimestamp": null
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx"
}
],
"restartPolicy": "Never"
},
"status": {}
}
【6】由于【1】scheduler 启动时监听 pod 列表,然后新创建的 pod 满足 scheduler 监听条件,所以这个pod的信息以 chunk 的形式推送到了 scheduler,对应【2】。
【7】pod 的资源创建后,apiserver 便立即返回了,无需等待 pod 运行。
【8】由于在【6】接收到新的 pod 的信息,scheduler 经过一些决策,决定将 pod 调度到 node-1,因此向 apiserver 发送对应的 pod binding 请求,在请求的body中指定了 node-1。
【9】apiserver 收到【8】的请求后,将 pod 的 spec.nodeName 字段设置为 node-1,这刚好满足了【3】kubelet 感兴趣的 pod 条件,所以更新后的pod的信息以 chunk 的形式推送到了 kubelet,对应【4】。
【10】apiserver 返回 pod binding 请求成功。
【11】由于在【9】接收到新的 pod 的信息,kubelet 将 pod 拉起运行,同时将 pod 的最新信息——包括 pod ip ,node 信息,运行状态等同步更新到 apiserver 。
【12】apiserver 返回 pod 信息更新成功。自此流程结束。