kubelet启动容器之拉取镜像

概览

kubelet通过以下四个步骤,来启动pod容器:

  1. 拉取镜像
  2. 创建容器
  3. 启动容器
  4. 执行容器启动后的钩子

kubelet启动容器的第一步便是拉取镜像,核心源码如下:

启动Pod容器之拉取镜像

kubernetes\pkg\kubelet\kuberuntime\kuberuntime_container.go

func (m *kubeGenericRuntimeManager) startContainer(podSandboxID string, podSandboxConfig *runtimeapi.PodSandboxConfig, spec *startSpec, pod *v1.Pod, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, podIP string, podIPs []string) (string, error) {
    container := spec.container

    // Step 1: pull the image.
    imageRef, msg, err := m.imagePuller.EnsureImageExists(pod, container, pullSecrets, podSandboxConfig)
    if err != nil {
        s, _ := grpcstatus.FromError(err)
        m.recordContainerEvent(pod, container, "", v1.EventTypeWarning, events.FailedToCreateContainer, "Error: %v", s.Message())
        return msg, err
    }
...
}

主要逻辑便是调用EnsureImageExists函数,接下来我们对m.imagePuller.EnsureImageExists()函数深入分析

函数入参及返回值解析

入参解析

  • pod *v1.Pod入参: 当前容器所属pod实例
  • container *v1.Container: 容器实例(包含镜像名称等元数据信息)
  • pullSecrets []v1.Secret: 拉取镜像所需的凭据
  • podSandboxConfig *runtimeapi.PodSandboxConfig: pod沙箱配置实例(包含主机名、dns、日志目录等)

返回值解析

  • 返回值一镜像像的引用(镜像摘要或ID,如harbor.wl.io/library/redis:5.0.12): 返回本地存储中容器的镜像引用(摘要或ID),如果镜像不在本地存储中返回""。
  • 返回值二镜像拉取信息:失败/成功及原因
  • 返回值三异常:拉取镜像过程中产生的异常(如拉取镜像网络不可达)

设置镜像tag默认值

当容器镜像不存在tag时,会设置缺省tag值: latest

func (m *imageManager) EnsureImageExists(pod *v1.Pod, container *v1.Container, pullSecrets []v1.Secret, podSandboxConfig *runtimeapi.PodSandboxConfig) (string, string, error) {
...
    // If the image contains no tag or digest, a default tag should be applied.
    // 为没有摘要或tag的镜像设置默认tag: latest
    image, err := applyDefaultImageTag(container.Image)
    if err != nil {
        msg := fmt.Sprintf("Failed to apply default image tag %q: %v", container.Image, err)
        m.logIt(ref, v1.EventTypeWarning, events.FailedToInspectImage, logPrefix, msg, klog.Warning)
        return "", msg, ErrInvalidImageName
    }
...
}

例如:下面spec.containers[0].image只声明了镜像名称,未声明tag

$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: nginx-with-no-tag
spec:
  containers:
  - name: nginx
    image: nginx
EOF

查看pod事件,发现并没有因为未指定镜像tag而中断

$ kubectl describe pod nginx-with-no-tag
...
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  119s  default-scheduler  Successfully assigned default/nginx-with-no-tag to node69
  Normal  Pulling    118s  kubelet, node69    Pulling image "nginx"
  Normal  Pulled     74s   kubelet, node69    Successfully pulled image "nginx"
  Normal  Created    73s   kubelet, node69    Created container nginx
  Normal  Started    73s   kubelet, node69    Started container nginx

查询本地镜像列表

$ docker images|grep nginx
nginx                                                                                                           latest                         ea335eea17ab   44 hours ago    141MB

拉取镜像

1.判断是否需要拉取镜像

以下情况需要拉取镜像:

  1. 容器的镜像拉取策略为Always
  2. 容器的镜像拉取策略为IfNotPresent,并且本地没有该镜像

如果需要拉取镜像继续后续流程,如果不需要拉取镜像直接返回,并根据以下场景返回对应信息:

  • 容器的镜像拉取策略为Never,并且本地存在该镜像,则返回: 镜像引用信息(即镜像名称,如harbor.wl.io/library/redis:5.0.12
  • 容器的镜像拉取策略为Never,并且本地不存在该镜像,则返回异常: Container image xxx is not present with pull policy of Never

源码实现

func (m *imageManager) EnsureImageExists(pod *v1.Pod, container *v1.Container, pullSecrets []v1.Secret, podSandboxConfig *runtimeapi.PodSandboxConfig) (string, string, error) {
...
    spec := kubecontainer.ImageSpec{Image: image}
    imageRef, err := m.imageService.GetImageRef(spec)
    if err != nil {
        msg := fmt.Sprintf("Failed to inspect image %q: %v", container.Image, err)
        m.logIt(ref, v1.EventTypeWarning, events.FailedToInspectImage, logPrefix, msg, klog.Warning)
        return "", msg, ErrImageInspect
    }

    present := imageRef != ""
    // 当容器的镜像拉取策略为Never时只用本地的镜像而非从镜像库拉取
    if !shouldPullImage(container, present) {
        if present {
            msg := fmt.Sprintf("Container image %q already present on machine", container.Image)
            m.logIt(ref, v1.EventTypeNormal, events.PulledImage, logPrefix, msg, klog.Info)
            return imageRef, "", nil
        }
        msg := fmt.Sprintf("Container image %q is not present with pull policy of Never", container.Image)
        m.logIt(ref, v1.EventTypeWarning, events.ErrImageNeverPullPolicy, logPrefix, msg, klog.Warning)
        return "", msg, ErrImageNeverPull
    }
...
}

2.判断是否超过镜像拉取时间

--image-pull-progress-deadline值,默认一分钟。

若在此截止日期前未进行镜像拉取,则镜像拉取将被取消。这个特定于docker的标志只在容器运行时被设置为docker时有效

func (m *imageManager) EnsureImageExists(pod *v1.Pod, container *v1.Container, pullSecrets []v1.Secret, podSandboxConfig *runtimeapi.PodSandboxConfig) (string, string, error) {
...
    backOffKey := fmt.Sprintf("%s_%s", pod.UID, container.Image)
    if m.backOff.IsInBackOffSinceUpdate(backOffKey, m.backOff.Clock.Now()) {
        msg := fmt.Sprintf("Back-off pulling image %q", container.Image)
        m.logIt(ref, v1.EventTypeNormal, events.BackOffPullImage, logPrefix, msg, klog.Info)
        return "", msg, ErrImagePullBackOff
    }
...
}

3.拉取镜像操作

调用容器运行时拉取镜像,并开启垃圾回收。

func (m *imageManager) EnsureImageExists(pod *v1.Pod, container *v1.Container, pullSecrets []v1.Secret, podSandboxConfig *runtimeapi.PodSandboxConfig) (string, string, error) {
...
    m.logIt(ref, v1.EventTypeNormal, events.PullingImage, logPrefix, fmt.Sprintf("Pulling image %q", container.Image), klog.Info)
    pullChan := make(chan pullResult)
    m.puller.pullImage(spec, pullSecrets, pullChan, podSandboxConfig)
    imagePullResult := <-pullChan
    if imagePullResult.err != nil {
        m.logIt(ref, v1.EventTypeWarning, events.FailedToPullImage, logPrefix, fmt.Sprintf("Failed to pull image %q: %v", container.Image, imagePullResult.err), klog.Warning)
        m.backOff.Next(backOffKey, m.backOff.Clock.Now())
        if imagePullResult.err == ErrRegistryUnavailable {
            msg := fmt.Sprintf("image pull failed for %s because the registry is unavailable.", container.Image)
            return "", msg, imagePullResult.err
        }

        return "", imagePullResult.err.Error(), ErrImagePull
    }
    m.logIt(ref, v1.EventTypeNormal, events.PulledImage, logPrefix, fmt.Sprintf("Successfully pulled image %q", container.Image), klog.Info)
    m.backOff.GC()
}
Copyright © weiliang 2021 all right reserved,powered by Gitbook本书发布时间: 2024-04-22 16:03:41

results matching ""

    No results matching ""