Kubernetes源码解析 - Deployment如何筛选哪些Pod归自己管?

2,545 阅读2分钟

Kubernetes集群中,Deployment会动态地管理自己所属的Pod,使之维持在期望的水平。少之则加,多之则减。

那么,Deployment是怎么筛选哪些Pod是自己管理的Pod呢?

这里分两步走:

首先筛选出deployment下的replicaSet,然后筛选出Deployment下面的pod,并且对pod按照replicaSet进行分组:

// List ReplicaSets owned by this Deployment, while reconciling ControllerRef
// through adoption/orphaning.
rsList, err := dc.getReplicaSetsForDeployment(d)
if err != nil {
   return err
}
// List all Pods owned by this Deployment, grouped by their ReplicaSet.
// Current uses of the podMap are:
//
// * check if a Pod is labeled correctly with the pod-template-hash label.
// * check that no old Pods are running in the middle of Recreate Deployments.
podMap, err := dc.getPodMapForDeployment(d, rsList)
if err != nil {
   return err
}

筛选replicaSet

根据Deployment的NameSpace筛选出所有replicaSet,然后在ClaimReplicaSets()中,根据Deployment的label筛选出自己管理的replicaSet。

// getReplicaSetsForDeployment uses ControllerRefManager to reconcile
// ControllerRef by adopting and orphaning.
// It returns the list of ReplicaSets that this Deployment should manage.
func (dc *DeploymentController) getReplicaSetsForDeployment(d *apps.Deployment) ([]*apps.ReplicaSet, error) {
   // List all ReplicaSets to find those we own but that no longer match our
   // selector. They will be orphaned by ClaimReplicaSets().
   rsList, err := dc.rsLister.ReplicaSets(d.Namespace).List(labels.Everything())
   if err != nil {
      return nil, err
   }
   deploymentSelector, err := metav1.LabelSelectorAsSelector(d.Spec.Selector)
   if err != nil {
      return nil, fmt.Errorf("deployment %s/%s has invalid label selector: %v", d.Namespace, d.Name, err)
   }
   // If any adoptions are attempted, we should first recheck for deletion with
   // an uncached quorum read sometime after listing ReplicaSets (see #42639).
   canAdoptFunc := controller.RecheckDeletionTimestamp(func() (metav1.Object, error) {
      fresh, err := dc.client.AppsV1().Deployments(d.Namespace).Get(d.Name, metav1.GetOptions{})
      if err != nil {
         return nil, err
      }
      if fresh.UID != d.UID {
         return nil, fmt.Errorf("original Deployment %v/%v is gone: got uid %v, wanted %v", d.Namespace, d.Name, fresh.UID, d.UID)
      }
      return fresh, nil
   })
   cm := controller.NewReplicaSetControllerRefManager(dc.rsControl, d, deploymentSelector, controllerKind, canAdoptFunc)
   return cm.ClaimReplicaSets(rsList)
}

筛选pod

根据Deployment的NameSpace和selector筛选出Deployment下的所有pod,再按Pod所属的replicaSet对pod进行分组。

// getPodMapForDeployment returns the Pods managed by a Deployment.
//
// It returns a map from ReplicaSet UID to a list of Pods controlled by that RS,
// according to the Pod's ControllerRef.
func (dc *DeploymentController) getPodMapForDeployment(d *apps.Deployment, rsList []*apps.ReplicaSet) (map[types.UID]*v1.PodList, error) {
   // Get all Pods that potentially belong to this Deployment.
   selector, err := metav1.LabelSelectorAsSelector(d.Spec.Selector)
   if err != nil {
      return nil, err
   }
   pods, err := dc.podLister.Pods(d.Namespace).List(selector)
   if err != nil {
      return nil, err
   }
   // Group Pods by their controller (if it's in rsList).
   podMap := make(map[types.UID]*v1.PodList, len(rsList))
   for _, rs := range rsList {
      podMap[rs.UID] = &v1.PodList{}
   }
   for _, pod := range pods {
      // Do not ignore inactive Pods because Recreate Deployments need to verify that no
      // Pods from older versions are running before spinning up new Pods.
      controllerRef := metav1.GetControllerOf(pod)
      if controllerRef == nil {
         continue
      }
      // Only append if we care about this UID.
      if podList, ok := podMap[controllerRef.UID]; ok {
         podList.Items = append(podList.Items, *pod)
      }
   }
   return podMap, nil
}

Deployment的Selector

由于Deployment是按Selector中的条件对Pod的label进行过滤,从而筛选出自己管理的Pod。因此,Selector中的条件必须和template中pod的label一致。

我们来看看Deployment中对Selector的定义:

// Label selector for pods. Existing ReplicaSets whose pods are
// selected by this will be the ones affected by this deployment.
// It must match the pod template's labels.
Selector *metav1.LabelSelector `json:"selector" protobuf:"bytes,2,opt,name=selector"`

使用示例:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

这里,spec.matchLabels与spec.template.metadata.labels是一致的,这样才能正确地筛选到pod。