【Openshift】ImagePullPolicyについて
概要
- ImagePullPolicyは
deployment.yml
などに定義される、コンテナのデプロイに関する設定です。 - ImagePullPolicyの挙動について、実際に動かしながら学んでいきたいと思います。
ImagePullPolicyとは
ImagePullPolicyには3種類があります。
imagePullPolicy |
説明 |
---|---|
Always | 常にイメージをコンテナレジストリからPull |
IfNotPresent | ノード上にイメージが存在しない場合のみPull |
Never | 常にイメージをコンテナレジストリからPullしない |
ImagePullPolicyの設定がない場合、イメージのタグがlatestの場合、Always
、それ以外の場合IfNotPresent
になります。
検証環境
- 検証はOpenShift Local(CRC)を使います。
- OpenShift Localの構築については下記記事を参照ください。
https://zenn.dev/ramenpanda/articles/a7792869aaf8a3
$ crc version
CRC version: 2.49.0+e843be
OpenShift version: 4.18.2
コンテナレジストリとImageStreamについて
コンテナレジストリとは、コンテナイメージの保管場所です。
ImageStreamとは、コンテナレジストリに含まれたコンテナイメージの参照を抽象化したものです。
ImageStreamでは、コンテナはタグによって管理されています。
基本的にOpenShiftではコンテナレジストリを直接操作するのではなく、ImageStreamを通じてコンテナイメージを操作するため、今回の検証でもImageStreamを通じてコンテナレジストリのイメージを操作します。
試しにImageStreamを取得してみます。
eap74-openjdk11-openshift-rhel8
というイメージがコンテナレジストリにある状態です。
NAME IMAGE REFERENCE UPDATED
eap74-openjdk11-openshift-rhel8:7.4.21-2.1743444175 registry.redhat.io/jboss-eap-7/eap74-openjdk11-openshift-rhel8@sha256:9e0df1a68c1941db963d028e262664c0dff08eac16b3f0b6d9d019e3db4ef9c4 41 hours ago
ノードについて
ノードとは、OpenShiftクラスタ内でコンテナ化されたアプリケーションを実行するサーバーや仮想マシンを指します。
ノードは3種類ありますが、Podを稼働させるノードは「ワーカーノード」なのでImagePullPolicy
のIfnotPresent
で記載されている「ノード」はワーカーノードのことだと考えられます。
kubeadminでログインしてノードを調べてみると、以下のようになりました。
$ oc get nodes
NAME STATUS ROLES AGE VERSION
crc Ready control-plane,master,worker 43d v1.31.6
CRCは開発向けの軽量クラスタなので、control-plane, master, workerが一つのノードになっていことが分かります。
ノード上のイメージを確認するために、oc debugをすると、対象ノード(crc)のコンテナランタイム環境にアクセスできました。
$ oc debug node/crc
If you don't see a command prompt, try pressing enter.
sh-5.1#
さらにここ上で、imagesを取得します。
# chroot /host crictl images
image-registry.openshift-image-registry.svc:5000/sample/sample-image v1 dcc0a20c03b65 1.09GB
これは、私が作成したコンテナなのですが、タグv1のコンテナがノード上に存在しています。
ここまでで、ImagePullPolicy
の挙動を左右する、コンテナレジストリ上のイメージ、ノード上のイメージを操作する方法を確認できました。
検証方法
- ここから、
ImagePullPolicy
の挙動を確認していきたいと思います。 - 検証にあたって、
Always
とIfNotPresent
の違いを見ていこうと思います。(Never
はAlways
の反対なので、Always
を理解できればNever
も分かるだろうということで割愛)
定義に基づきフローチャートを作成してみました。
これを表にまとめると以下のようになります。
※定義上、Always
の「ノードにイメージが存在」列は不要なのですが、Always
とIfnotPresent
の違いを確認するためにあえて含めています。
No. | ImagePullPolicy | ノードにイメージが存在 | レジストリにイメージが存在 | 想定結果 |
---|---|---|---|---|
1 | IfNotPresent | × | × | エラー |
2 | IfNotPresent | × | 〇 | レジストリ |
3 | IfNotPresent | 〇 | × | キャッシュ |
4 | IfNotPresent | 〇 | 〇 | キャッシュ |
5 | Always | × | × | エラー |
6 | Always | × | 〇 | レジストリ |
7 | Always | 〇 | × | エラー |
8 | Always | 〇 | 〇 | レジストリ |
確認の際は、実際にwarファイルを作成し、JBossコンテナにデプロイします。
- 以下のようなdeployment.ymlを用意し、
oc apply
することでPodを起動します。
containers:
- image: ~ custom-image-java:v1
imagePullPolicy: <IfNotPresent> or <Always>
Pod起動の詳細は以下の記事を参照ください。
ノード無、レジストリ無(No.1,5)
準備
- ノード上のコンテナは削除
sh-5.1# chroot /host crictl rmi dcc0a20c03b65
Deleted: image-registry.openshift-image-registry.svc:5000/sample/custom-image-java:v1
- コンテナレジストリ上にイメージは無し。
$ oc get is
No resources found in a namespace.
結果
- IfNotPresent
$ oc get pod
NAME READY STATUS RESTARTS AGE
custom-image-java-5f4d6597d7-glwp9 0/1 ImagePullBackOff 0 3s
→ イメージが取得できないエラー
- Always
$ oc get pod
NAME READY STATUS RESTARTS AGE
custom-image-java-5f4d6597d7-gq7zg 0/1 ImagePullBackOff 0 5s
→ イメージが取得できないエラー
→No.1,5とも予想通り。
ノード無、レジストリ有(No.2,6)
準備
-
ノード上のコンテナは先ほど削除したため無し。
※No.2の確認後、ノードにイメージが復活するので削除してからNo.6を実施しました -
コンテナレジストリ上にイメージを格納。
NAME IMAGE REFERENCE UPDATED
custom-image-java:v1 image-registry.openshift-image-registry.svc:5000/sample/custom-image-java@sha256:e96359da793defdfda9ad7b0e19593270bd0c2a49204745822d4bda7718e3995 4 seconds ago
結果
- IfNotPresent
$ oc get pod
NAME READY STATUS RESTARTS AGE
custom-image-java-65f5bfc8d9-bvwgt 1/1 Running 0 4s
→ 正常に起動。動いているのはレジストリのイメージ。
- Always
oc get pod
NAME READY STATUS RESTARTS AGE
custom-image-java-5f4d6597d7-ktnk8 1/1 Running 0 1s
→ 正常に起動。動いているのはレジストリのイメージ。
→No.2,6とも予想通り。
ノード有、レジストリ無(No.3,7)
準備
-
ノード上にはNo.6であげたイメージが残存
-
レジストリのイメージは削除
oc delete istag custom-image-java:v1
imagestreamtag.image.openshift.io "custom-image-java:v1" deleted
結果
- IfNotPresent
$ oc get pod
NAME READY STATUS RESTARTS AGE
custom-image-java-65f5bfc8d9-8v5jh 1/1 Running 0 13s
→ 正常に起動。動いているのはノード上のイメージ。
- Always
$ oc get pod
NAME READY STATUS RESTARTS AGE
custom-image-java-5f4d6597d7-sf9cw 0/1 ImagePullBackOff 0 4s
→ コンテナレジストリを見に行くも、イメージがないためエラー。
→No.3,7とも予想通り。
ノード有、レジストリ有(No.4,8)
準備
-
前提
ノードにもレジストリにもコンテナがある場合、どっちのコンテナが起動しているか分からなくなるため、イメージに乗せるwarファイル名で識別することにします。 -
ノード
-
node.war
をJBossイメージに含めたアプリケーションコンテナを作成し、それをコンテナレジストリに格納します。
oc start-build custom-image-java --from-dir=node.war --follow -n sample
- deployment.ymlのIfNotPresentをAlwaysにし、Podを起動することで、ノードにイメージをキャッシュします。
- コンテナレジストリに登録したイメージは消しておきます。
- レジストリ
registory.war
をJBossイメージに含めたアプリケーションコンテナを作成し、それをコンテナレジストリに格納します。
結果
- IfnotPresent
$ oc get pod
NAME READY STATUS RESTARTS AGE
custom-image-java-7b4df8f765-52lx5 1/1 Running 0 56s
コンテナは起動しましたので、JBossログからどちらのwarのコンテナがデプロイされたかを確認します。
# oc logs custom-image-java-7b4df8f765-52lx5
[org.jboss.as.server.deployment] (MSC service thread 1-3) WFLYSRV0027: Starting deployment of "node.war" (runtime-name: "node.war")
→ Node上のコンテナ(キャッシュ)がデプロイされていることがわかります。
- Always
同様の手順で確認すると、JBossログより、レジストリのコンテナがデプロイされていました。
Starting deployment of "registory.war" (runtime-name: "registory.war")
→No.4,8とも予想通り。
まとめ
- 以上の検証結果より、
ImagePullPolicy
が``Alwaysと
IfnotPresent`のとき、「検証方法」の表の通りとなることが分かりました。
所感
-
IfNotPresent
のNo4(レジストリに新しいコンテナを登録しても、ノードのキャッシュが残っている場合古いコンテナがデプロイされてしまう)が怖いなと思いました。 - ただ、恐らく業務でOpenShiftを扱う際は
kubeadmin
のような広い権限は与えられていないと思うので、ノード上のキャッシュを消すことは難しいかもしれません。 - そうなると、過去使ったタグでレジストリにPushするのは避けた方が良さそうです。(当たり前かもしれませんが)
- 過去使ったタグでレジストリにPushされる可能性が0でない場合は、Alwaysにしておけば安全な気がします。
Discussion