はじめに
コンテナを更新するための仕組みについて、コンテナ名とタグの関係を説明し、コンテナ名とコンテナのハッシュ値でコンテナをKubernetesでデプロイする方法の説明します。
また、実行しているコンテナのハッシュ値やコンテナレジストリ(Quay.io)のAPIを使用してハッシュ値を取得する方法を説明します。
最後に、コンテナ名とコンテナのハッシュ値でアップデートの仕組みを考える方法を説明します。
実行コンテナのアップデートについて
コンテナをKubernetesで実行させるには、コンテナの名前とタグをPodリソースに記載してKubernetesリソースに適応することでコンテナが実行します。通常でコンテナの運用でアップデートを考える場合は、タグでバージョン管理することでa version、b versionとコンテナのバージョン管理することができます。
図1 バージョン別コンテナの実行
タグによるコンテナ切り替えの図を図1に記載しました。その図について簡単に説明しますと、a version実行させたときにコンテナレジストリから、aというコンテナをpullしてローカルに保存れ、それを使って実行します。aからbに切り替えた時に、新規にbというコンテナをローカルにダウンロードしてaのコンテナを残したままbが起動します。もし、既存コンテナがある状態で、bからaに切り替えた場合は通常の動作は、図2のように、ローカルのaというコンテナを使用して実行されます。
図2 バージョン別コンテナの実行(既存コンテナ有り時)
余談となりますが、図2の動作はimagePullPolicyとパラメータがIfNotPresentの時の動作です。特に設定しない場合は、コンテナのタグによって表1の通りの動作をします。
表1 コンテナタグ別のPod起動時の動作
コンテナタグ | パラメータ指定なし時のパラメータ | 動作 |
latest | Always | 常にpullしてから立ち上げ |
latest以外 | IfNotPresent | ローカルに一致するコンテナがないならpull |
もし、タグaのコンテナを更新し、a`コンテナがコンテナレジストリ登録されたあとで、bコンテナから、aコンテナに起動を切り替えると図3のように、ローカルに保存された古いコンテナ実行されます。
図3 コンテナbからコンテナaに切り替えた時の動作図
そこで、重要になるののがコンテナのハッシュ値となります。図4のように、コンテナのないような異なるならば、各コンテナごとにSHA-256の値が異なります
図4 SHA-256値を指定してコンテナの実行
定期的に、実行中のコンテナハッシュ値と、コンテナレジストリ内のコンテナハッシュ値を定期的に監視して、異なることを発揮できれば新しいバージョンの検出することが可能であります。また、以下ののようにKubernetesリソースパラメータを変更することで、ハッシュ値を指定してコンテナを起動させることが可能になります。
image: version@sha256:値
以降に、実行中のコンテナハッシュ値確認方法や、コンテナレジストリのコンテナハッシュ値の確認方法について説明します。
Podリソースについて
以下のようなDeploymentリソースをOpenShiftで適用してコンテナを立ち上げます。
apiVersion: apps/v1 kind: Deployment metadata: name: nginx annotations: image.openshift.io/triggers: |- [ { "from": { "kind": "ImageStreamTag", "name": "openshift/hello-openshift:latest" }, "fieldPath": "spec.template.spec.containers[0].image" } ] spec: selector: matchLabels: app: nginx replicas: 1 template: metadata: labels: app: nginx spec: containers: - name: nginx image: quay.io/k_nakajima/nginx-sample:latest ports: - containerPort: 8080
DeploymentリソースをKubernetesに適用すると最終的にPodリソースが作成されます。そのPodのリソースを確認すると以下の通り、実行コンテナのSHA-256を得ます。
$ oc get pod -o yaml apiVersion: v1 items: - apiVersion: v1 kind: Pod metadata: annotations: k8s.v1.cni.cncf.io/network-status: |- [{ "name": "openshift-sdn", "interface": "eth0", "ips": [ "10.217.0.246" ], "default": true, "dns": {} }] k8s.v1.cni.cncf.io/networks-status: |- [{ "name": "openshift-sdn", "interface": "eth0", "ips": [ "10.217.0.246" ], "default": true, "dns": {} }] openshift.io/scc: restricted creationTimestamp: "2021-10-07T03:05:47Z" generateName: nginx-54589dd565- labels: app: nginx pod-template-hash: 54589dd565 managedFields: - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:metadata: f:generateName: {} f:labels: .: {} f:app: {} f:pod-template-hash: {} f:ownerReferences: .: {} k:{"uid":"b165968b-3191-4e72-a839-0c673691f234"}: .: {} f:apiVersion: {} f:blockOwnerDeletion: {} f:controller: {} f:kind: {} f:name: {} f:uid: {} f:spec: f:containers: k:{"name":"nginx"}: .: {} f:image: {} f:imagePullPolicy: {} f:name: {} f:ports: .: {} k:{"containerPort":8080,"protocol":"TCP"}: .: {} f:containerPort: {} f:protocol: {} f:resources: {} f:terminationMessagePath: {} f:terminationMessagePolicy: {} f:dnsPolicy: {} f:enableServiceLinks: {} f:restartPolicy: {} f:schedulerName: {} f:securityContext: .: {} f:fsGroup: {} f:seLinuxOptions: f:level: {} f:terminationGracePeriodSeconds: {} manager: kube-controller-manager operation: Update time: "2021-10-07T03:05:47Z" - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:metadata: f:annotations: f:k8s.v1.cni.cncf.io/network-status: {} f:k8s.v1.cni.cncf.io/networks-status: {} manager: multus operation: Update time: "2021-10-07T03:05:51Z" - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:status: f:conditions: k:{"type":"ContainersReady"}: .: {} f:lastProbeTime: {} f:lastTransitionTime: {} f:status: {} f:type: {} k:{"type":"Initialized"}: .: {} f:lastProbeTime: {} f:lastTransitionTime: {} f:status: {} f:type: {} k:{"type":"Ready"}: .: {} f:lastProbeTime: {} f:lastTransitionTime: {} f:status: {} f:type: {} f:containerStatuses: {} f:hostIP: {} f:phase: {} f:podIP: {} f:podIPs: .: {} k:{"ip":"10.217.0.246"}: .: {} f:ip: {} f:startTime: {} manager: kubelet operation: Update time: "2021-10-07T03:06:04Z" name: nginx-54589dd565-v9xm9 namespace: test-wp ownerReferences: - apiVersion: apps/v1 blockOwnerDeletion: true controller: true kind: ReplicaSet name: nginx-54589dd565 uid: b165968b-3191-4e72-a839-0c673691f234 resourceVersion: "4937689" uid: 1313f0e1-7f23-487f-84f5-63de3cedef72 spec: containers: - image: quay.io/k_nakajima/nginx-sample:latest imagePullPolicy: Always name: nginx ports: - containerPort: 8080 protocol: TCP resources: {} securityContext: capabilities: drop: - KILL - MKNOD - SETGID - SETUID runAsUser: 1000620000 terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount name: kube-api-access-kg7xb readOnly: true dnsPolicy: ClusterFirst enableServiceLinks: true imagePullSecrets: - name: default-dockercfg-46zdg nodeName: crc-5dd5m-master-0 preemptionPolicy: PreemptLowerPriority priority: 0 restartPolicy: Always schedulerName: default-scheduler securityContext: fsGroup: 1000620000 seLinuxOptions: level: s0:c25,c10 serviceAccount: default serviceAccountName: default terminationGracePeriodSeconds: 30 tolerations: - effect: NoExecute key: node.kubernetes.io/not-ready operator: Exists tolerationSeconds: 300 - effect: NoExecute key: node.kubernetes.io/unreachable operator: Exists tolerationSeconds: 300 volumes: - name: kube-api-access-kg7xb projected: defaultMode: 420 sources: - serviceAccountToken: expirationSeconds: 3607 path: token - configMap: items: - key: ca.crt path: ca.crt name: kube-root-ca.crt - downwardAPI: items: - fieldRef: apiVersion: v1 fieldPath: metadata.namespace path: namespace - configMap: items: - key: service-ca.crt path: service-ca.crt name: openshift-service-ca.crt status: conditions: - lastProbeTime: null lastTransitionTime: "2021-10-07T03:05:47Z" status: "True" type: Initialized - lastProbeTime: null lastTransitionTime: "2021-10-07T03:06:04Z" status: "True" type: Ready - lastProbeTime: null lastTransitionTime: "2021-10-07T03:06:04Z" status: "True" type: ContainersReady - lastProbeTime: null lastTransitionTime: "2021-10-07T03:05:47Z" status: "True" type: PodScheduled containerStatuses: - containerID: cri-o://8838c47b8a26622f77a969fb2380d99700da8c6a007d817b2b36993517419988 image: quay.io/k_nakajima/nginx-sample:latest imageID: quay.io/k_nakajima/nginx-sample@sha256:0f152bc1261aea012bbf89977a821f1b319b9dc9feb7f4ba54f08f617b2937ec lastState: {} name: nginx ready: true restartCount: 0 started: true state: running: startedAt: "2021-10-07T03:06:04Z" hostIP: 192.168.126.11 phase: Running podIP: 10.217.0.246 podIPs: - ip: 10.217.0.246 qosClass: BestEffort startTime: "2021-10-07T03:05:47Z" kind: List metadata: resourceVersion: "" selfLink: ""
以上の値から、.status.containerStatuses[].imageIDの値を確認すると「quay.io/k_nakajima/nginx-sample@sha256:0f152bc1261aea012bbf89977a821f1b319b9dc9feb7f4ba54f08f617b2937ec」のように実行中のコンテナのIDを確認しました。
レジストリコンテナについて
quay.ioのコンテナレジストリから、コンテナ情報をしたい場合は以下の通りに入力することで確認します。
curl https://quay.io/api/v1/repository/<user name>/<contena name>
実際に実行してみますと以下の通りになります。
$ curl https://quay.io/api/v1/repository/k_nakajima/nginx-sample {"trust_enabled": false, "description": "", "tags": {"latest": {"image_id": "0d04b0dd8f324b53c10e2e746d6e87ffe2c2b1cf48e4291e9dd4a1aec40f6389", "last_modified": "Thu, 07 Oct 2021 03:04:01 -0000", "name": "latest", "manifest_digest": "sha256:0f152bc1261aea012bbf89977a821f1b319b9dc9feb7f4ba54f08f617b2937ec", "size": 171490154}}, "tag_expiration_s": 1209600, "is_public": true, "is_starred": false, "is_free_account": true, "kind": "image", "name": "nginx-sample", "namespace": "k_nakajima", "is_organization": false, "state": "NORMAL", "can_write": false, "status_token": "", "can_admin": false}
上記のJSONの結果から、「k_nakajima/nginx-sample:latest」のSHA256の値は0f152bc1261aea012bbf89977a821f1b319b9dc9feb7f4ba54f08f617b2937ec」
コンテナIDで起動
今まで、コンテナイメージの指定を「quay.io/k_nakajima/nginx-sample:latest」にしていましたが「quay.io/k_nakajima/nginx-sample@sha256:0f152bc1261aea012bbf89977a821f1b319b9dc9feb7f4ba54f08f617b2937ec」というように、Deploymentリソース内の.spec.template.spec.containers[0].imageの値を変更すると新しくPodが起動して、ハッシュ値指定のコンテナが立ち上がり、以下のコマンドでImageIDを確認すると同じ値が確認することができます。
$ oc get pod -o jsonpath={..status.containerStatuses[*].imageID} quay.io/k_nakajima/nginx-sample@sha256:0f152bc1261aea012bbf89977a821f1b319b9dc9feb7f4ba54f08f617b2937ec
まとめ
今回は、同じ名前、同じタグのコンテナをアップデートした時のアップデートのやり方について説明しました。同じ名前、同じタグでも、コンテナのハッシュ値を実行中のコンテナとコンテナレジストリのコンテナで見比べることで、アップデートしていないか確認することがわかることが理解できたと思います。(仕組み図は図5を参照)
図5 コンテナのアップデート処理について
次回は、図5の仕組みをOperatorとして組み込んで自動的にアップデートする方法について記載します。