詳解 Kubernetes Pod imagePullPolicy

詳解 Kubernetes Pod imagePullPolicy

問題背景

Kubernetes 管理下的容器會(huì)在什么情況下對(duì)容器鏡像重新拉取?

概念理解

官方文檔:https://v1-12.docs.kubernetes.io/docs/concepts/containers/images/#updating-images

對(duì)一個(gè) Pod 來說振惰,spec.containers.imagePullPolicy字段用于管理容器鏡像的拉取策略把将,可選項(xiàng)為IfNotPresentAlways拱雏。

默認(rèn)的 imagePullPolicy 為IfNotPresent贪惹,image配置如image: nginx:1.12.5滴劲,當(dāng)宿主存在該鏡像時(shí)攻晒,kubelet 會(huì)自動(dòng)跳過鏡像拉取的步驟;

如果希望每次容器啟動(dòng)時(shí)都從鏡像庫拉取鏡像班挖,可以通過以下方式中的任一一種來配置:

  1. 將 imagePullPolicy 設(shè)為 Always;
  2. 不配置 imagePullPolicy, 將容器鏡像的版本號(hào)設(shè)置為:latest鲁捏,即 images: nginx:latest
  3. 不配置 imagePullPolicy萧芙,也不配置容器版本號(hào)给梅,如images: nginx

實(shí)驗(yàn)測試

測試準(zhǔn)備

nginx-deployment.yaml 配置 container 對(duì)應(yīng)的 imagePullPolicy: Always

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

測試方法

  1. 創(chuàng)建好對(duì)應(yīng)的 Deployment, 觀察某個(gè) Pod 的事件
  2. 通過某一種手段,不對(duì) Pod 進(jìn)行操作双揪,只是使得 Pod 里的 Container 退出(模擬 OOM場景)动羽;
  3. 觀測 Pod 元數(shù)據(jù)改變情況,觀測 docker image 拉取情況渔期。

測試過程

root@kmaster135:/home/chenjiaxi01/yaml/controllers# kubectl get pods -o wide| grep nginx
nginx-deployment-57f495d87b-k6g48   1/1     Running     2          173m   10.244.2.158   dnode137   <none>
nginx-deployment-57f495d87b-rcvlg   1/1     Running     0          173m   10.244.2.156   dnode137   <none>
nginx-deployment-57f495d87b-tnmrq   1/1     Running     0          173m   10.244.1.141   dnode136   <none>

定位到宿主上:

root@dnode137:~# docker ps | grep nginx-deployment-57f495d87b-rcvlg
9947530bc668        nginx@sha256:e8ab8d42e0c34c104ac60b43ba60b19af08e19a0e6d50396bdfd4cef0347ba83   "nginx -g 'daemon ..."   2 hours ago         Up 2 hours                              k8s_nginx_nginx-deployment-57f495d87b-rcvlg_default_40cc107c-ee33-11e9-8ba3-000c290b4cc5_0
6436b335a958        k8s.gcr.io/pause:3.1                                                            "/pause"                 2 hours ago         Up 2 hours                              k8s_POD_nginx-deployment-57f495d87b-rcvlg_default_40cc107c-ee33-11e9-8ba3-000c290b4cc5_0

停掉 Nginx 容器 9947530bc668:

root@dnode137:~# docker stop 9947530bc668
9947530bc668

觀察 Pod 情況:

root@kmaster135:/home/chenjiaxi01/yaml/controllers# kubectl describe pods nginx-deployment-57f495d87b-rcvlg
Name:               nginx-deployment-57f495d87b-rcvlg
Namespace:          default
Priority:           0
PriorityClassName:  <none>
Node:               dnode137/192.168.77.137
Start Time:         Sun, 13 Oct 2019 20:32:30 -0700
Labels:             app=nginx
                    pod-template-hash=57f495d87b
Annotations:        <none>
Status:             Running
IP:                 10.244.2.156
Controlled By:      ReplicaSet/nginx-deployment-57f495d87b
Containers:
  nginx:
    Container ID:   docker://29a227a4831cce1f02bd83512dfcc377f703f5663ed5e6409a3db2f0884ba374
    Image:          nginx:1.15.4
    Image ID:       docker-pullable://nginx@sha256:e8ab8d42e0c34c104ac60b43ba60b19af08e19a0e6d50396bdfd4cef0347ba83
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Sun, 13 Oct 2019 23:27:29 -0700
    Last State:     Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Sun, 13 Oct 2019 20:32:46 -0700
      Finished:     Sun, 13 Oct 2019 23:27:23 -0700
    Ready:          True
    Restart Count:  1
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-zh48z (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
...
Events:
  Type    Reason   Age                 From               Message
  ----    ------   ----                ----               -------
  Normal  Pulling  32s (x2 over 175m)  kubelet, dnode137  pulling image "nginx:1.15.4"
  Normal  Pulled   27s (x2 over 175m)  kubelet, dnode137  Successfully pulled image "nginx:1.15.4"
  Normal  Created  27s (x2 over 175m)  kubelet, dnode137  Created container
  Normal  Started  27s (x2 over 175m)  kubelet, dnode137  Started container

測試結(jié)論

  1. Pod Start Time 保持不變运吓,所以可以認(rèn)為是 Pod 重啟,而不是刪掉 Pod 重建擎场;
  2. Node 不會(huì)發(fā)生變化羽德,也就是不會(huì)重新調(diào)度;
  3. IP 是否會(huì)發(fā)生變化迅办?停掉 App Container 的時(shí)候沒有發(fā)生變化宅静,如果停掉的是 Infra Container 呢?猜想應(yīng)該是有可能變化的站欺,因?yàn)槭侵亟?Sandbox 時(shí)由 CNI 負(fù)責(zé)的姨夹;
  4. Containers 字段里由于容器發(fā)生了重建纤垂,所以都會(huì)有變化;可以看到 State里有本次容器啟動(dòng)的情況磷账,Last State保留了上一次容器運(yùn)行的基本情況峭沦;
  5. Containers Restart Count 從 0 變?yōu)?1,表示容器的重啟次數(shù)逃糟,這個(gè)數(shù)值也會(huì)體現(xiàn)在 kubectl get pod 里吼鱼;
  6. 從 Events 中可以看到,kubelet, dnode137 pulling image "nginx:1.15.4" 重新拉取了容器鏡像用于拉起新容器绰咽;如果是默認(rèn)的配置imagePullPolicy: IfNotPresent則該信息為Container image "nginx:1.15.4" already present on machine菇肃,如果 image 已經(jīng)存在的話。

源碼分析

在 kublet 的代碼里取募,在主循環(huán)SyncPod有相應(yīng)處理邏輯負(fù)責(zé) image 進(jìn)行拉取:

  1. func SyncPod():pkg/kubelet/kuberuntime/kuberuntime_manager.go:578
// SyncPod syncs the running pod into the desired pod by executing following steps:
//
//  1. Compute sandbox and container changes.
//  2. Kill pod sandbox if necessary.
//  3. Kill any containers that should not be running.
//  4. Create sandbox if necessary.
//  5. Create init containers.
//  6. Create normal containers.
func (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, _ v1.PodStatus, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, backOff *flowcontrol.Backoff) (result kubecontainer.PodSyncResult) {
...
        glog.V(4).Infof("Creating init container %+v in pod %v", container, format.Pod(pod))
        if msg, err := m.startContainer(podSandboxID, podSandboxConfig, container, pod, podStatus, pullSecrets, podIP, kubecontainer.ContainerTypeInit); err != nil {
            startContainerResult.Fail(err, msg)
            utilruntime.HandleError(fmt.Errorf("init container start failed: %v: %s", err, msg))
            return
        }
...
}
  1. func StartContainer: pkg/kubelet/kuberuntime/kuberuntime_container.go:89
// startContainer starts a container and returns a message indicates why it is failed on error.
// It starts the container through the following steps:
// * pull the image
// * create the container
// * start the container
// * run the post start lifecycle hooks (if applicable)
func (m *kubeGenericRuntimeManager) startContainer(podSandboxID string, podSandboxConfig *runtimeapi.PodSandboxConfig, container *v1.Container, pod *v1.Pod, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, podIP string, containerType kubecontainer.ContainerType) (string, error) {
    // Step 1: pull the image.
    imageRef, msg, err := m.imagePuller.EnsureImageExists(pod, container, pullSecrets)
    if err != nil {
        m.recordContainerEvent(pod, container, "", v1.EventTypeWarning, events.FailedToCreateContainer, "Error: %v", grpc.ErrorDesc(err))
        return msg, err
    }
    ...
}
  1. type ImageManager interface:pkg/kubelet/images/types.go:50
// ImageManager provides an interface to manage the lifecycle of images.
// Implementations of this interface are expected to deal with pulling (downloading),
// managing, and deleting container images.
// Implementations are expected to abstract the underlying runtimes.
// Implementations are expected to be thread safe.
type ImageManager interface {
    // EnsureImageExists ensures that image specified in `container` exists.
    EnsureImageExists(pod *v1.Pod, container *v1.Container, pullSecrets []v1.Secret) (string, string, error)

    // TODO(ronl): consolidating image managing and deleting operation in this interface
}
  1. func EnsureImageExist:pkg/kubelet/images/image_manager.go:86
// EnsureImageExists pulls the image for the specified pod and container, and returns
// (imageRef, error message, error).
func (m *imageManager) EnsureImageExists(pod *v1.Pod, container *v1.Container, pullSecrets []v1.Secret) (string, string, error) {
    ...
    present := imageRef != ""
    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, glog.Info)
            return imageRef, "", nil
        } else {
            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, glog.Warning)
            return "", msg, ErrImageNeverPull
        }
    }
    ...
}
  1. func shouldPullImage:pkg/kubelet/images/image_manager.go:62
// shouldPullImage returns whether we should pull an image according to
// the presence and pull policy of the image.
func shouldPullImage(container *v1.Container, imagePresent bool) bool {
    if container.ImagePullPolicy == v1.PullNever {
        return false
    }

    if container.ImagePullPolicy == v1.PullAlways ||
        (container.ImagePullPolicy == v1.PullIfNotPresent && (!imagePresent)) {
        return true
    }

    return false
}

新的問題: 當(dāng)容器配置形如image: nginx:latest時(shí)琐谤,imagePullPolicy默認(rèn)配置為Always,管理默認(rèn)配置的代碼在哪里玩敏?

pkg/apis/core/v1/defaults.go:77:

func SetDefaults_Container(obj *v1.Container) {
    if obj.ImagePullPolicy == "" {
        // Ignore error and assume it has been validated elsewhere
        _, tag, _, _ := parsers.ParseImageName(obj.Image)

        // Check image tag
        if tag == "latest" {
            obj.ImagePullPolicy = v1.PullAlways
        } else {
            obj.ImagePullPolicy = v1.PullIfNotPresent
        }
    }
    if obj.TerminationMessagePath == "" {
        obj.TerminationMessagePath = v1.TerminationMessagePathDefault
    }
    if obj.TerminationMessagePolicy == "" {
        obj.TerminationMessagePolicy = v1.TerminationMessageReadFile
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末斗忌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子旺聚,更是在濱河造成了極大的恐慌织阳,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件翻屈,死亡現(xiàn)場離奇詭異陈哑,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)伸眶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刽宪,“玉大人厘贼,你說我怎么就攤上這事∈ブ簦” “怎么了嘴秸?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長庇谆。 經(jīng)常有香客問我岳掐,道長,這世上最難降的妖魔是什么饭耳? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任串述,我火速辦了婚禮,結(jié)果婚禮上寞肖,老公的妹妹穿的比我還像新娘纲酗。我一直安慰自己衰腌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布觅赊。 她就那樣靜靜地躺著右蕊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吮螺。 梳的紋絲不亂的頭發(fā)上饶囚,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音鸠补,去河邊找鬼坯约。 笑死,一個(gè)胖子當(dāng)著我的面吹牛莫鸭,可吹牛的內(nèi)容都是我干的闹丐。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼被因,長吁一口氣:“原來是場噩夢啊……” “哼卿拴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起梨与,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤堕花,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后粥鞋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缘挽,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年呻粹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了壕曼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡等浊,死狀恐怖腮郊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情筹燕,我是刑警寧澤轧飞,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站撒踪,受9級(jí)特大地震影響过咬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜制妄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一掸绞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧忍捡,春花似錦集漾、人聲如沸切黔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纬霞。三九已至,卻和暖如春驱显,著一層夾襖步出監(jiān)牢的瞬間诗芜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來泰國打工埃疫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留伏恐,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓栓霜,卻偏偏與公主長得像翠桦,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子胳蛮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容