🌵

OpenShift Localで検証する imagePullPolicy の挙動

に公開

はじめに

「コンテナイメージを更新したはずなのに、古いイメージがデプロイされた…」
OpenShiftを使い始めて間もないころ、このような事象に直面しました。
原因は、deployment.ymlimagePullPolicyの設定により、ワーカーノード上にキャッシュされていた古いイメージが優先的に使われていたことでした。その際に、「キャッシュがあるなら覗いてみたい。削除して事象が解決するか試したい。」そんな思いに駆られました。
しかし、一般的に社内環境では権限の制約等により、ノードのキャッシュを直接確認できないケースが多いと思います。

そんなときにおすすめなのが、OpenShift Localです。
OpenShift Localを使えば、自分だけのOpenShift環境を簡単に構築できます。
本記事では、Kubernetesの設定項目であるimagePullPolicyを題材に、OpenShift Localで動作検証を行う手順を紹介します。
なお、本検証は自己啓発の一環として実施したものです。

OpenShift Localの検証環境を準備

OpenShift Localとは

OpenShift Localは、Red Hat OpenShiftのローカル開発環境を提供するツールです。OpenShiftの主要な機能をローカルマシン上で簡易的に再現できるため、開発者がクラウド環境に依存せずにアプリケーションの開発・テストを行うことができます。

OpenShift Localのセットアップ

注意事項
掲載したソースコードはサンプルになります。
本ソースコードを使用することで発生するいかなる損害や不利益について、当社は一切の責任を負いませんので自己の責任においてご利用ください。

セットアップは公式ドキュメントの「Installing CRC」に従って行いました。
https://crc.dev/docs/installing/

私は自宅に余っていた、Windows上にセットアップしました。
手順どおりにcrc startまで完了後、crc oc-env | Invoke-Expressionを実行することでocコマンドが使えるようになります。
このコマンドは、crc oc-env で環境変数設定用のスクリプトを取得し、Invoke-Expression で現在の PowerShell セッションに反映させる、という処理です。
実行後、ocコマンドが利用可能になりました。

ここから先は、OpenShift Local上にコンテナを立てて、imagePullPolicyの設定による挙動の違いを確認していきます。

※ちなみに、crcというコマンド名は、OpenShift Localが以前 「CodeReady Containers(CRC)」という名称で提供されていたことに由来するようです。

検証用Podの作成

imagePullPolicy の挙動を確認するため、検証用のコンテナを作成します。

  1. ベースイメージをインポート
oc import-image jboss-eap-7/eap74-openjdk11-openshift-rhel8:7.4.21-2.1743444175 --from=registry.redhat.io/jboss-eap-7/eap74-openjdk11-openshift-rhel8:7.4.21-2.1743444175 --confirm
  1. BuildConfigを作成
oc new-build --image-stream="custom-image-sample/eap74-openjdk11-openshift-rhel8:7.4.21-2.1743444175" --binary=true --name=custom-image-java -n custom-image-sample
  1. ビルドを実行
    --from-dirで指定しているdeploymentフォルダにwarファイルを配置しています。
oc start-build custom-image-java --from-dir=deployment -n custom-image-sample --follow
  1. アプリをデプロイ
oc new-app custom-image-java
  1. Podの起動を確認
oc get pod
NAME                                 READY   STATUS      RESTARTS   AGE
custom-image-java-6b76949855-68kfd   1/1     Running     0          3m33s

Podが正常に起動しました。ここからはdeployment.ymlimagePullPolicyの設定を変更し、その挙動の違いを確認します。

imagePullPolicyについて

imagePullPolicydeployment.ymlなどに定義されるコンテナのデプロイに関する設定です。
imagePullPolicyには次の3種類があります。

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

NeverAlwaysの逆なので、AlwaysIfNotPresentの違いを理解すれば、imagePullPolicyの挙動を把握できます。
AlwaysIfNotPresentの挙動の違いをフローチャートにすると、以下のようになります。

さらに、表にまとめると次のとおりです。

No. imagePullPolicy ノードにイメージあり レジストリにイメージあり 想定挙動
1 Always -(※) × イメージが見つからずエラー
2 Always -(※) レジストリのイメージをデプロイ
3 IfNotPresent × × イメージが見つからずエラー
4 IfNotPresent × レジストリのイメージをデプロイ
5 IfNotPresent × ノードにキャッシュされているイメージをデプロイ
6 IfNotPresent ノードにキャッシュされているイメージをデプロイ

※No.1とNo.2はノード上のイメージ有無に依存しませんが、以降の検証では、ノードにイメージがあっても使用されないことを確認するため、存在する状態で検証します。

検証方法

上記のNo.1~6の条件を再現し、それぞれが「想定挙動」どおりになるかを確認します。
条件を作り出すには、次の3つの操作が必要です。それぞれの手順をあらかじめ整理しておきましょう。
imagePullPolicyの設定変更
②ノード上のイメージ操作
③レジストリ上のイメージ操作

①imagePullPolicyの設定変更

以下のようなdeployment.ymlを用意し、imagePullPolicyの部分をAlwaysIfNotPresentに変更してoc applyで適用します。

containers:
    - image: ~ custom-image-java:v1
    imagePullPolicy: <Always> or <IfNotPresent>

②ノード上のイメージ操作

管理者権限(kubeadmin)でログイン後、oc debugをすると、対象ノードのコンテナランタイム環境にアクセスできます。

$ oc debug node/crc

sh-5.1#

ノード上のコンテナイメージを確認するには、以下のコマンドを実行します。先ほどビルドしたコンテナがpushされていることが確認できます。

# chroot /host crictl images
...(省略)
image-registry.openshift-image-registry.svc:5000/sample/sample-image      v1                  dcc0a20c03b65       1.09GB
...

削除する場合は、タグを指定し、rmiコマンドでイメージIDを指定します。

chroot /host crictl rmi dcc0a20c03b65

③レジストリ上のイメージ操作

レジストリ上のコンテナイメージは、以下のコマンドで確認できます。

oc get imagestream

※ ImageStream は、OpenShift が管理するコンテナイメージの 参照情報(メタデータ) であり、oc get imagestream で取得されるのは レジストリそのものではありません。ただし、OpenShift では通常、ImageStream を通じてレジストリ内のイメージを管理するため、今回の検証でも ImageStream を介して操作します。

削除する場合は以下のコマンドを実行します。

oc delete is <image-stream名> 

検証結果

以上の手順で、検証条件を再現できるようになりました。ここからは結果を示します。

No.1(imagePullPolicyAlways、ノード:あり、レジストリ:なし)

$ oc get pod
NAME                                 READY   STATUS             RESTARTS   AGE
custom-image-java-5f4d6597d7-gq7zg   0/1     ImagePullBackOff   0          5s

STATUSImagePullBackOffであることから、ノード上にイメージがあっても使用されず、デプロイに失敗したことが確認できました。

No.2(imagePullPolicyAlways、ノード:あり、レジストリ:あり)

ノード上にキャッシュされているイメージと、レジストリ上に登録されているイメージのどちらが使用されたかを判別するために、ノード上にキャッシュされるイメージには node.war を含め、レジストリ上のイメージには registry.war を含めるようにします。

oc get pod
NAME                                 READY   STATUS    RESTARTS   AGE
custom-image-java-5f4d6597d7-ktnk8   1/1     Running   0          1s

STATUSRunningで、コンテナは正常にデプロイされています。

さらに、デプロイされたWARファイルをログで確認します。

# oc logs custom-image-java-7b4df8f765-52lx5
...(省略)
[org.jboss.as.server.deployment] (MSC service thread 1-3) WFLYSRV0027: Starting deployment of "registry.war" (runtime-name: "registry.war")
....

registry.warがデプロイされていることから、レジストリ上のイメージがデプロイされていることが確認できました。

No.3(imagePullPolicyIfNotPresent、ノード:なし、レジストリ:なし)

$ oc get pod
NAME                                 READY   STATUS             RESTARTS   AGE
custom-image-java-5f4d6597d7-glwp9   0/1     ImagePullBackOff   0          3s

→ ノードにもレジストリにもイメージがないため、デプロイに失敗しています。

No.4(imagePullPolicyIfNotPresent、ノード:なし、レジストリ:あり)

$ oc get pod
NAME                                 READY   STATUS    RESTARTS   AGE
custom-image-java-65f5bfc8d9-bvwgt   1/1     Running   0          4s

→ 正常にデプロイされました。ノードにイメージがないため、レジストリ上のイメージが使用されています。

No.5(imagePullPolicyIfNotPresent、ノード:あり、レジストリ:なし)

$ oc get pod
NAME                                 READY   STATUS        RESTARTS   AGE
custom-image-java-65f5bfc8d9-8v5jh   1/1     Running       0          13s

→ 正常にデプロイされました。レジストリにイメージがないため、ノード上のキャッシュイメージが使用されています。

No.6(imagePullPolicyIfNotPresent、ノード:あり、レジストリ:あり)

No.2と同様に、node.warregistry.warを含むイメージを準備した状況にしておきます。

$ oc get pod
NAME                                 READY   STATUS    RESTARTS   AGE
custom-image-java-7b4df8f765-52lx5   1/1     Running   0          56s

STATUSRunningで、コンテナは正常にデプロイされています。

さらに、デプロイされた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.war)が使用されたことが確認できました。

まとめ

以上の検証結果より、imagePullPolicyAlwaysIfNotPresentの場合、挙動は「検証方法」で示した表どおりであることが確認できました。

おわりに

今回の検証を通じて、imagePullPolicyの設定がコンテナのデプロイ挙動に与える影響を、OpenShift Localを使って確認できました。

特に、IfNotPresent、かつ、ノードとレジストリの両方にイメージが存在するケース(上表No.6)では、ノードのキャッシュが優先されるため、「新しいコンテナイメージをレジストリに格納したのに古いイメージがデプロイされる」という事象が発生するため、注意が必要です。

社内環境では権限の制約によりノードの状態を確認しづらい場合がありますが、OpenShift Localを使えば自由に操作・観察できるため、学習や検証の場として非常に有効だと感じました。

Discussion