掘金 后端 ( ) • 2024-03-29 14:49
  1. 判断network plugin是否就绪,如果没有就绪,则只有使用hostnework的pod可以进行下面的步骤

    // 
    if err := kl.runtimeState.networkErrors(); err != nil && !kubecontainer.IsHostNetworkPod(pod) {
    kl.recorder.Eventf(pod, v1.EventTypeWarning, events.NetworkNotReady, "%s: %v", NetworkNotReadyErrorMsg, err)
    return false, fmt.Errorf("%s: %v", NetworkNotReadyErrorMsg, err)
    }
    
  2. 将pod使用secret和configmap添加到 secret管理器和configmap管理器,让 kubelet的 secret管理器和configmap管理器知道哪些pod在使用改secret或者configmap

    // 确保kubelet知道pod使用的secret和configmap
    if !kl.podWorkers.IsPodTerminationRequested(pod.UID) {
    if kl.secretManager != nil {
       kl.secretManager.RegisterPod(pod)
    }
    if kl.configMapManager != nil {
       kl.configMapManager.RegisterPod(pod)
    }
    }
    
  3. 更新或者创建cgroup

    if err := kl.containerManager.UpdateQOSCgroups(); err != nil {
    klog.V(2).InfoS("Failed to update QoS cgroups while syncing pod", "pod", klog.KObj(pod), "err", err)
    }
    // //EnsureExists将pod作为参数,并确保
    ////如果启用了qos cgroup层次结构标志,则存在pod cgroup。
    ////如果pod cgroup还不存在,这个方法会创建它。
    if err := pcm.EnsureExists(pod); err != nil {
      kl.recorder.Eventf(pod, v1.EventTypeWarning, events.FailedToCreatePodContainer, "unable to ensure pod container exists: %v", err)
      return false, fmt.Errorf("failed to ensure that the pod: %v cgroups exist and are correctly applied: %v", pod.UID, err)
    }
    
  4. 为pod创建数据目录

    // 创建数据目录,pod所在在的路径,包括volunes,plugins
    if err := kl.makePodDataDirs(pod); err != nil {
    kl.recorder.Eventf(pod, v1.EventTypeWarning, events.FailedToMakePodDataDirectories, "error making pod data directories: %v", err)
    klog.ErrorS(err, "Unable to make pod data directories for pod", "pod", klog.KObj(pod))
    return false, err
    }
    
  5. 判断volume是否挂载,等待挂载: volume管理器会持续监听状态,将pv挂载到pod对于卷类型的目录【目录默认是/var/lib/kubelet/pods//{podID}/volumes/{kubernetes.io~卷类型}/{pv名称}】

    // 等待全部的volumes挂载到pod
    if err := kl.volumeManager.WaitForAttachAndMount(pod); err != nil {
      kl.recorder.Eventf(pod, v1.EventTypeWarning, events.FailedMountVolume, "Unable to attach or mount volumes: %v", err)
      klog.ErrorS(err, "Unable to attach or mount volumes for pod; skipping pod", "pod", klog.KObj(pod))
      return false, err
    }
    
  6. 调用 cri接口创建沙箱

    这里区分k8s版本: 如果是1.24之前

    cri由docker-shim实现,docker-shim会调用docker

    1、拉取pause镜像

    image := defaultSandboxImage
    podSandboxImage := ds.podSandboxImage
    if len(podSandboxImage) != 0 {
       image = podSandboxImage
    }
    // 检查镜像是否存在,不存在则拉取
    if err := ensureSandboxImageExists(ds.client, image); err != nil {
       return nil, err
    }
    

    2、创建沙箱【就是容器pause】

    createResp, err := ds.client.CreateContainer(*createConfig)
    

    3、创建沙箱检查点

    if err = ds.checkpointManager.CreateCheckpoint(createResp.ID, constructPodSandboxCheckpoint(config)); 
    

    4、开始沙箱【pause这个容器】

    err = ds.client.StartContainer(createResp.ID)
    

    5、设置网络: 调用cni接口设置沙箱网络

    err = ds.network.SetUpPod(config.GetMetadata().Namespace, config.GetMetadata().Name, cID, config.Annotations, networkOptions)
    

    1.24及之后

    【假设是containerd】,则在containerd端实现的cri接口内部还会调用 cni接口配置网络

    1、拉取镜像

    image, err := c.ensureImageExists(ctx, c.config.SandboxImage, config)
    

    2、创建容器

    container, err := c.client.NewContainer(ctx, id, opts...)
    if err != nil {
        return nil, fmt.Errorf("failed to create containerd container: %w", err)
    }
    // 创建沙箱运行的rootDir
    sandboxRootDir := c.getSandboxRootDir(id)
    // 准备沙箱运行的必要文件,只有linux有实际实现: 有resolv.conf,hosts,/dev/shm
    if err = c.setupSandboxFiles(id, config);
    ​
    // 更新沙箱的网络命名空间路径
    c.updateNetNamespacePath(spec, sandbox.NetNSPath)
    if err := container.Update(ctx,...),
                            
    ​
    ​
    

    3、调用cni接口设置网络

    // 调用cni设置网络
    if err := c.setupPodNetwork(ctx, &sandbox); err != nil {
                return nil, fmt.Errorf("failed to setup network for sandbox %q: %w", id, err)
    }
    
    // 创建一个沙盒,实际就是pause容器,后续可以将其他的容器加入这个沙盒
    podSandboxID, msg, err = m.createPodSandbox(pod, podContainerChanges.Attempt)
    ​
    // 
    
  7. 开始临时容器【临时容器的的使用需要k8s集群开启临时容器的功能特性】

    if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
       for _, idx := range podContainerChanges.EphemeralContainersToStart {
          start("ephemeral container", metrics.EphemeralContainer, ephemeralContainerStartSpec(&pod.Spec.EphemeralContainers[idx]))
       }
    }
    
  8. 启动初始化容器。需要等待上一个初始化容器执行结束

    // 启动初始化容器
    if container := podContainerChanges.NextInitContainerToStart; container != nil {
       // Start the next init container.
       if err := start("init container", metrics.InitContainer, containerStartSpec(container)); err != nil {
          return
       }
    ​
       // Successfully started the container; clear the entry in the failure
       klog.V(4).InfoS("Completed init container for pod", "containerName", container.Name, "pod", klog.KObj(pod))
    }
    
  9. 启动业务容器

    // 启动业务容器
    for _, idx := range podContainerChanges.ContainersToStart {
       start("container", metrics.Container, containerStartSpec(&pod.Spec.Containers[idx]))
    }