はじめに
コンテナを更新するための仕組みについて、コンテナ名とタグの関係を説明し、コンテナ名とコンテナのハッシュ値でコンテナを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として組み込んで自動的にアップデートする方法について記載します。
