🍜

【Openshift】ImagePullPolicyについて

に公開

概要

  • ImagePullPolicyはdeployment.ymlなどに定義される、コンテナのデプロイに関する設定です。
  • ImagePullPolicyの挙動について、実際に動かしながら学んでいきたいと思います。

ImagePullPolicyとは

ImagePullPolicyには3種類があります。

imagePullPolicy 説明
Always 常にイメージをコンテナレジストリからPull
IfNotPresent ノード上にイメージが存在しない場合のみPull
Never 常にイメージをコンテナレジストリからPullしない

ImagePullPolicyの設定がない場合、イメージのタグがlatestの場合、Always、それ以外の場合IfNotPresentになります。

https://docs.redhat.com/en/documentation/openshift_container_platform/4.8/html/images/managing-images#images-image-pull-policy-overview_image-pull-policy

検証環境

$ 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を稼働させるノードは「ワーカーノード」なのでImagePullPolicyIfnotPresentで記載されている「ノード」はワーカーノードのことだと考えられます。

https://docs.redhat.com/ja/documentation/red_hat_openshift_data_foundation/4.9/html/planning_your_deployment/node-types_rhodf#node-types_rhodf

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の挙動を確認していきたいと思います。
  • 検証にあたって、AlwaysIfNotPresentの違いを見ていこうと思います。(NeverAlwaysの反対なので、Alwaysを理解できればNeverも分かるだろうということで割愛)

定義に基づきフローチャートを作成してみました。

これを表にまとめると以下のようになります。

※定義上、Alwaysの「ノードにイメージが存在」列は不要なのですが、AlwaysIfnotPresentの違いを確認するためにあえて含めています。

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起動の詳細は以下の記事を参照ください。
https://zenn.dev/ramenpanda/articles/8cb9e1589fcdf9

ノード無、レジストリ無(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ファイル名で識別することにします。

  • ノード

  1. node.warをJBossイメージに含めたアプリケーションコンテナを作成し、それをコンテナレジストリに格納します。
oc start-build custom-image-java --from-dir=node.war --follow -n sample
  1. deployment.ymlのIfNotPresentをAlwaysにし、Podを起動することで、ノードにイメージをキャッシュします。
  2. コンテナレジストリに登録したイメージは消しておきます。
  • レジストリ
    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が``AlwaysIfnotPresent`のとき、「検証方法」の表の通りとなることが分かりました。

所感

  • IfNotPresentのNo4(レジストリに新しいコンテナを登録しても、ノードのキャッシュが残っている場合古いコンテナがデプロイされてしまう)が怖いなと思いました。
  • ただ、恐らく業務でOpenShiftを扱う際はkubeadminのような広い権限は与えられていないと思うので、ノード上のキャッシュを消すことは難しいかもしれません。
  • そうなると、過去使ったタグでレジストリにPushするのは避けた方が良さそうです。(当たり前かもしれませんが)
  • 過去使ったタグでレジストリにPushされる可能性が0でない場合は、Alwaysにしておけば安全な気がします。

Discussion