深入探索Kubernetes中的Mirror Pod删除过程及其对Static Pod 的影响

这也是一篇研究pod移除流程的文章,这个文章专注mirror pod的移除。mirror pod这个名词可能很陌生,中文直译就是镜像pod,它是pod里的一种类型。

那我们先介绍pod的分类,pod来源分为file、http、apiserver。其中来自apiserver中的pod称为普通pod,其他来源的pod称为static pod(使用kubeadm安装集群的控制平面就是用static pod运行的)。为了管理pod方便,kubelet会在apiserver上为static pod生成对应的pod,这类型pod称为mirror pod,即可以理解是这个pod的替身(基本上跟static pod一模一样,只有uid不一样和annotations里多了"kubernetes.io/config.mirror")。

那么删除mirror pod会发生什么?对节点上static pod有没有影响,会不会移除节点上的static pod?

pod移除流程系列文章

本文使用kubernete版本是1.23和日志级别为4。

kubelet的static pod的机制生成的pod name为配置里的pod name加上node节点名。在这个例子中,定义的pod name为nginx-static-pod,而最终生成的pod名字为nginx-static-pod-10.11.251.2。

下面是kubelet上的static pod配置

text

apiVersion: v1
kind: Pod
metadata:
  name: nginx-static-pod
  namespace: default
spec:
  containers:
  - name: nginx-container
    image: nginx:latest
    ports:
    - containerPort: 80

对应的mirror pod

yaml

apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubernetes.io/config.hash: a8712c005851ee6b29cff91b9ab4b9c6
    kubernetes.io/config.mirror: a8712c005851ee6b29cff91b9ab4b9c6
    kubernetes.io/config.seen: "2023-11-23T14:05:21.400551188+08:00"
    kubernetes.io/config.source: file
  creationTimestamp: "2023-11-23T06:05:21Z"
  name: nginx-static-pod-10.11.251.2
  namespace: default
  ownerReferences:
  - apiVersion: v1
    controller: true
    kind: Node
    name: 10.11.251.2
    uid: d2e3ebf6-2a70-434f-9142-0f74a1dd16bc
  resourceVersion: "292107092"
  uid: 1facfed2-ba67-40f8-9982-ab86a5e7fd33
spec:
  containers:
  - image: nginx:latest
    imagePullPolicy: Always
    name: nginx-container
    ports:
    - containerPort: 80
      protocol: TCP
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
  nodeName: 10.11.251.2
  preemptionPolicy: PreemptLowerPriority
  priority: 0
  restartPolicy: Always
  schedulerName: default-scheduler
  securityContext: {}
  terminationGracePeriodSeconds: 30
  tolerations:
  - effect: NoExecute
    operator: Exists
status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: "2023-11-23T06:05:21Z"
    status: "True"
    type: Initialized
  - lastProbeTime: null
    lastTransitionTime: "2023-11-23T06:05:29Z"
    status: "True"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: "2023-11-23T06:05:29Z"
    status: "True"
    type: ContainersReady
  - lastProbeTime: null
    lastTransitionTime: "2023-11-23T06:05:21Z"
    status: "True"
    type: PodScheduled
  containerStatuses:
  - containerID: docker://b6ca55d329230c8f5776eb1160fe161d6fefa01f2b31e55dbb820add90aadccc
    image: nginx:latest
    imageID: docker-pullable://nginx@sha256:10d1f5b58f74683ad34eb29287e07dab1e90f10af243f151bb50aa5dbb4d62ee
    lastState: {}
    name: nginx-container
    ready: true
    restartCount: 0
    started: true
    state:
      running:
        startedAt: "2023-11-23T06:05:29Z"
  hostIP: 10.11.251.2
  phase: Running
  podIP: 10.26.124.96
  podIPs:
  - ip: 10.26.124.96
  qosClass: BestEffort
  startTime: "2023-11-23T06:05:21Z"

我们直接做个实验,使用kubectl删除mirror pod,然后watch pod变化

完整的kubelet日志和watch记录在 The log for mirror pod in kubelet

mirror pod的uid为1facfed2-ba67-40f8-9982-ab86a5e7fd33

yaml

# kubectl get pod nginx-static-pod-10.11.251.2 -o yaml -w
apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubernetes.io/config.hash: a8712c005851ee6b29cff91b9ab4b9c6
    kubernetes.io/config.mirror: a8712c005851ee6b29cff91b9ab4b9c6
    kubernetes.io/config.seen: "2023-11-23T14:05:21.400551188+08:00"
    kubernetes.io/config.source: file
  creationTimestamp: "2023-11-23T06:05:21Z"
  name: nginx-static-pod-10.11.251.2
  namespace: default
  ownerReferences:
  - apiVersion: v1
    controller: true
    kind: Node
    name: 10.11.251.2
    uid: d2e3ebf6-2a70-434f-9142-0f74a1dd16bc
  resourceVersion: "292107092"
  uid: 1facfed2-ba67-40f8-9982-ab86a5e7fd33

pod被删除,deletionTimestamp字段有值,且deletionGracePeriodSeconds为30

yaml

apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubernetes.io/config.hash: a8712c005851ee6b29cff91b9ab4b9c6
    kubernetes.io/config.mirror: a8712c005851ee6b29cff91b9ab4b9c6
    kubernetes.io/config.seen: "2023-11-23T14:05:21.400551188+08:00"
    kubernetes.io/config.source: file
  creationTimestamp: "2023-11-23T06:05:21Z"
  deletionGracePeriodSeconds: 30
  deletionTimestamp: "2023-11-23T06:11:45Z"
  name: nginx-static-pod-10.11.251.2
  namespace: default
  ownerReferences:
  - apiVersion: v1
    controller: true
    kind: Node
    name: 10.11.251.2
    uid: d2e3ebf6-2a70-434f-9142-0f74a1dd16bc
  resourceVersion: "292110805"
  uid: 1facfed2-ba67-40f8-9982-ab86a5e7fd33

再次被删除,deletionGracePeriodSeconds设置为0

yaml

apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubernetes.io/config.hash: a8712c005851ee6b29cff91b9ab4b9c6
    kubernetes.io/config.mirror: a8712c005851ee6b29cff91b9ab4b9c6
    kubernetes.io/config.seen: "2023-11-23T14:05:21.400551188+08:00"
    kubernetes.io/config.source: file
  creationTimestamp: "2023-11-23T06:05:21Z"
  deletionGracePeriodSeconds: 0
  deletionTimestamp: "2023-11-23T06:11:15Z"
  name: nginx-static-pod-10.11.251.2
  namespace: default
  ownerReferences:
  - apiVersion: v1
    controller: true
    kind: Node
    name: 10.11.251.2
    uid: d2e3ebf6-2a70-434f-9142-0f74a1dd16bc
  resourceVersion: "292110806"
  uid: 1facfed2-ba67-40f8-9982-ab86a5e7fd33

mirror pod从apiserver中移除(这里只有resourceVersion发生了变化)

yaml

apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubernetes.io/config.hash: a8712c005851ee6b29cff91b9ab4b9c6
    kubernetes.io/config.mirror: a8712c005851ee6b29cff91b9ab4b9c6
    kubernetes.io/config.seen: "2023-11-23T14:05:21.400551188+08:00"
    kubernetes.io/config.source: file
  creationTimestamp: "2023-11-23T06:05:21Z"
  deletionGracePeriodSeconds: 0
  deletionTimestamp: "2023-11-23T06:11:15Z"
  name: nginx-static-pod-10.11.251.2
  namespace: default
  ownerReferences:
  - apiVersion: v1
    controller: true
    kind: Node
    name: 10.11.251.2
    uid: d2e3ebf6-2a70-434f-9142-0f74a1dd16bc
  resourceVersion: "292110807"
  uid: 1facfed2-ba67-40f8-9982-ab86a5e7fd33

新的mirror pod创建,uid为7bdace8a-8cc5-4cb4-886b-f3961f07d3b8(pod的uid发生变化,status里只有phase和qosClass字段)

yaml

apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubernetes.io/config.hash: a8712c005851ee6b29cff91b9ab4b9c6
    kubernetes.io/config.mirror: a8712c005851ee6b29cff91b9ab4b9c6
    kubernetes.io/config.seen: "2023-11-23T14:05:21.400551188+08:00"
    kubernetes.io/config.source: file
  creationTimestamp: "2023-11-23T06:11:15Z"
  name: nginx-static-pod-10.11.251.2
  namespace: default
  ownerReferences:
  - apiVersion: v1
    controller: true
    kind: Node
    name: 10.11.251.2
    uid: d2e3ebf6-2a70-434f-9142-0f74a1dd16bc
  resourceVersion: "292110808"
  uid: 7bdace8a-8cc5-4cb4-886b-f3961f07d3b8
  ...
status:
  phase: Pending
  qosClass: BestEffort

mirror pod的status更新

yaml

status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: "2023-11-23T06:05:21Z"
    status: "True"
    type: Initialized
  - lastProbeTime: null
    lastTransitionTime: "2023-11-23T06:05:29Z"
    status: "True"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: "2023-11-23T06:05:29Z"
    status: "True"
    type: ContainersReady
  - lastProbeTime: null
    lastTransitionTime: "2023-11-23T06:05:21Z"
    status: "True"
    type: PodScheduled
  containerStatuses:
  - containerID: docker://b6ca55d329230c8f5776eb1160fe161d6fefa01f2b31e55dbb820add90aadccc
    image: nginx:latest
    imageID: docker-pullable://nginx@sha256:10d1f5b58f74683ad34eb29287e07dab1e90f10af243f151bb50aa5dbb4d62ee
    lastState: {}
    name: nginx-container
    ready: true
    restartCount: 0
    started: true
    state:
      running:
        startedAt: "2023-11-23T06:05:29Z"
  hostIP: 10.11.251.2
  phase: Running
  podIP: 10.26.124.96
  podIPs:
  - ip: 10.26.124.96
  qosClass: BestEffort
  startTime: "2023-11-23T06:05:21Z"

mirror pod被删除

text

I1123 14:11:15.664065  315900 config.go:278] "Setting pods for source" source="api"
I1123 14:11:15.664544  315900 kubelet.go:2130] "SyncLoop DELETE" source="api" pods=[default/nginx-static-pod-10.11.251.2]

podWorker执行syncPod(由于在podWorker中使用static pod来判断状态,而static pod未被删除,所以不执行syncTerminatingPod)

text

I1123 14:11:15.664591  315900 pod_workers.go:888] "Processing pod event" pod="default/nginx-static-pod-10.11.251.2" podUID=a8712c005851ee6b29cff91b9ab4b9c6 updateType=0
I1123 14:11:15.664610  315900 kubelet.go:1554] "syncPod enter" 

在syncPod中执行再次删除mirror pod

text

I1123 14:11:15.664893  315900 kubelet.go:1724] "Trying to delete pod" pod="default/nginx-static-pod-10.11.251.2" podUID=1facfed2-ba67-40f8-9982-ab86a5e7fd33
I1123 14:11:15.664900  315900 mirror_client.go:130] "Deleting a mirror pod" pod="default/nginx-static-pod-10.11.251.2" podUID=1facfed2-ba67-40f8-9982-ab86a5e7fd33

感知到pod再次删除,随后从apiserver中移除

text

I1123 14:11:15.671615  315900 config.go:278] "Setting pods for source" source="api"
I1123 14:11:15.671989  315900 kubelet.go:2130] "SyncLoop DELETE" source="api" pods=[default/nginx-static-pod-10.11.251.2]
I1123 14:11:15.674620  315900 config.go:278] "Setting pods for source" source="api"
I1123 14:11:15.675067  315900 kubelet.go:2124] "SyncLoop REMOVE" source="api" pods=[default/nginx-static-pod-10.11.251.2]

删除完成并创建新的mirror pod

text

I1123 14:11:15.675108  315900 kubelet.go:1729] "Deleted mirror pod because it is outdated" pod="default/nginx-static-pod-10.11.251.2"
I1123 14:11:15.675121  315900 kubelet.go:1740] "Creating a mirror pod for static pod" pod="default/nginx-static-pod-10.11.251.2"
I1123 14:11:15.683985  315900 config.go:383] "Receiving a new pod" 

syncPod执行完成

text

I1123 14:11:15.684056  315900 kubelet.go:1556] "syncPod exit" pod="default/nginx-static-pod-10.11.251.2" podUID=a8712c005851ee6b29cff91b9ab4b9c6 isTerminal=false
I1123 14:11:15.684085  315900 pod_workers.go:988] "Processing pod event done" pod="default/nginx-static-pod-10.11.251.2" podUID=a8712c005851ee6b29cff91b9ab4b9c6 updateType=0

podWorker继续执行syncPod(这个是之前sync REMOVE触发)

text

I1123 14:11:15.684093  315900 pod_workers.go:888] "Processing pod event" pod="default/nginx-static-pod-10.11.251.2" podUID=a8712c005851ee6b29cff91b9ab4b9c6 updateType=0
I1123 14:11:16.053579  315900 kubelet.go:1554] "syncPod enter" pod="default/nginx-static-pod-10.11.251.2" podUID=a8712c005851ee6b29cff91b9ab4b9c6

感知到新创建的mirror pod

text

I1123 14:11:15.684281  315900 kubelet.go:2114] "SyncLoop ADD" source="api" pods=[default/nginx-static-pod-10.11.251.2]

重新执行删除mirror pod,syncPod执行完成,podWorker执行完成

text

I1123 14:11:16.053912  315900 mirror_client.go:130] "Deleting a mirror pod" pod="default/nginx-static-pod-10.11.251.2" podUID=1facfed2-ba67-40f8-9982-ab86a5e7fd33
I1123 14:11:16.058466  315900 kubelet.go:1556] "syncPod exit" pod="default/nginx-static-pod-10.11.251.2" podUID=a8712c005851ee6b29cff91b9ab4b9c6 isTerminal=false
I1123 14:11:16.058484  315900 pod_workers.go:988] "Processing pod event done" pod="default/nginx-static-pod-10.11.251.2" podUID=a8712c005851ee6b29cff91b9ab4b9c6 updateType=0 

podWorker继续执行syncPod(SyncLoop ADD触发的),然后执行完成

text

I1123 14:11:16.058491  315900 pod_workers.go:888] "Processing pod event" pod="default/nginx-static-pod-10.11.251.2" podUID=a8712c005851ee6b29cff91b9ab4b9c6 updateType=0
I1123 14:11:17.057755  315900 kubelet.go:1554] "syncPod enter" pod="default/nginx-static-pod-10.11.251.2" podUID=a8712c005851ee6b29cff91b9ab4b9c6
I1123 14:11:17.058187  315900 kubelet.go:1556] "syncPod exit" pod="default/nginx-static-pod-10.11.251.2" podUID=a8712c005851ee6b29cff91b9ab4b9c6 isTerminal=false
I1123 14:11:17.058208  315900 pod_workers.go:988] "Processing pod event done" pod="default/nginx-static-pod-10.11.251.2" podUID=a8712c005851ee6b29cff91b9ab4b9c6 updateType=0

mirror pod status更新(这个是由statusManager里的syncBatch触发更新)

text

I1123 14:11:21.465237  315900 kubelet.go:2127] "SyncLoop RECONCILE" source="api" pods=[default/nginx-static-pod-10.11.251.2]

感知到mirror pod status更新

text

I1123 14:11:21.465237  315900 kubelet.go:2127] "SyncLoop RECONCILE" source="api" pods=[default/nginx-static-pod-10.11.251.2]

kubelet-mirror-pod-delete

根据上面的现象删除mirror pod并不会移除static pod,而且kubelet还会重新创建一个新的mirror pod,创建出来的mirror pod并没有status condition和container status,最后kubelet进行mirror pod的status更新。实际上对mirror pod的操作并不会影响static pod,因为static pod只在kubelet内部可见,外部可以通过apiserver上的mirror pod或kubelet的HTTP server上的/runningpods/接口进行查询static pod状态。

从源码方面分析,每次syncLoop触发podWorker都执行syncPod。

在syncPod里处理static pod逻辑:

  • 如果该pod是static pod且mirror pod存在,检测这个mirror pod是否是这个static pod的合法镜像pod。如果不是合法mirror pod,则把这个mirror pod删除。
  • 如果这个mirror pod已经被删除,则执行最后删除动作(设置deletionGracePeriodSeconds为0)。
  • 如果static pod没有mirror pod,则创建mirror pod。

代码在 pkg/kubelet/kubelet.go#L1761-L1798

go

	// Create Mirror Pod for Static Pod if it doesn't already exist
	// 如果是static pod,则检查是否存在mirror pod,当mirror pod不存在时候进行创建
	if kubetypes.IsStaticPod(pod) {
		deleted := false
		// 存在mirror pod
		if mirrorPod != nil {
			// mirrorPod被删除或mirrorPod不是pod的mirror pod
			if mirrorPod.DeletionTimestamp != nil || !kl.podManager.IsMirrorPodOf(mirrorPod, pod) {
				// The mirror pod is semantically different from the static pod. Remove
				// it. The mirror pod will get recreated later.
				klog.InfoS("Trying to delete pod", "pod", klog.KObj(pod), "podUID", mirrorPod.ObjectMeta.UID)
				// pod.Name + "_" + pod.Namespace
				podFullName := kubecontainer.GetPodFullName(pod)
				var err error
				// 调用api删除mirror pod(从podFullName解析出name和namespace)
				deleted, err = kl.podManager.DeleteMirrorPod(podFullName, &mirrorPod.ObjectMeta.UID)
				if deleted {
					klog.InfoS("Deleted mirror pod because it is outdated", "pod", klog.KObj(mirrorPod))
				} else if err != nil {
					klog.ErrorS(err, "Failed deleting mirror pod", "pod", klog.KObj(mirrorPod))
				}
			}
		}
		// static pod没有mirror pod或mirror pod被删除,则在获取node未发生错误且node未被删除条件下,创建mirror pod
		if mirrorPod == nil || deleted {
			node, err := kl.GetNode()
			// 当获取node发生错误或node被删除,则无需创建mirror pod
			if err != nil || node.DeletionTimestamp != nil {
				klog.V(4).InfoS("No need to create a mirror pod, since node has been removed from the cluster", "node", klog.KRef("", string(kl.nodeName)))
			} else {
				klog.V(4).InfoS("Creating a mirror pod for static pod", "pod", klog.KObj(pod))
				// node没有被删除,则创建static pod的mirror pod
				if err := kl.podManager.CreateMirrorPod(pod); err != nil {
					klog.ErrorS(err, "Failed creating a mirror pod for", "pod", klog.KObj(pod))
				}
			}
		}
	}

相关内容