12
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

kind (Kubernetes IN Docker)におけるローカルイメージpull失敗とimagePullPolicyについて

Last updated at Posted at 2021-09-30

kind (Kubernetes IN Docker)クラスタでローカルdockerイメージ利用する際にちょっとハマってしまった。 結論からいうと、imagePullPolicyの理解が足りていなかっただけなのだが、同じ様にハマってしまう人も多いのではないかと思うので、設定・確認につかったコマンドを含めて記録として残しておく。

kindクラスタの作成

まずは、1 x control planeノードと2 x workerノードなkindクラスタを作成したいので、次のようなkindクラスタ構成ファイルを作成する

cat << EOF | > kind-cluster.yaml 
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4 
nodes:
- role: control-plane
- role: worker
- role: worker
EOF

続いて、kind cliコマンドで、k8s v1.19.11のノードイメージと上記で作成したkind構成ファイルを指定してkindクラスタを作成する

K8S_NODE_IMAGE=v1.19.11
kind create cluster --name my-kind-cluster \
--image=kindest/node:${K8S_NODE_IMAGE} \
--config kind-cluster.yaml

作成されたkindクラスタを確認する

# kind クラスタ一覧を表示
kind get clusters

> my-kind-cluster

# kubectlでノード一覧を表示
kubectl get nodes

NAME                            STATUS   ROLES    AGE     VERSION
my-kind-cluster-control-plane   Ready    master   3m32s   v1.19.11
my-kind-cluster-worker          Ready    <none>   2m59s   v1.19.11
my-kind-cluster-worker2         Ready    <none>   2m59s   v1.19.11

サンプルアプリ用イメージの作成

サンプルイメージ用のDockerfileを作成

cat << EOF | > Dockerfile
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
EOF

作成したDockerfileでサンプルイメージをビルド

docker build -t sample-app .

sample-app:latestが作成されていることを確認

docker images

REPOSITORY                     TAG                 IMAGE ID       CREATED          SIZE
sample-app                     latest              e31ba19f294d   23 minutes ago   22.9MB

latestタグの付いたdockerイメージをkindクラスタノードにロード

kind load docker-imageコマンドで作成したサンプルイメージをkindクラスタノードにロードする。

kind load docker-image sample-app:latest --name my-kind-cluster

Image: "sample-app:latest" with ID "sha256:e31ba19f294d720f3d5dfeaa421d6ca84a25fa0f324824df7608370a67c8ead6" not yet present on node "my-kind-cluster-worker2", loading...
Image: "sample-app:latest" with ID "sha256:e31ba19f294d720f3d5dfeaa421d6ca84a25fa0f324824df7608370a67c8ead6" not yet present on node "my-kind-cluster-control-plane", loading...
Image: "sample-app:latest" with ID "sha256:e31ba19f294d720f3d5dfeaa421d6ca84a25fa0f324824df7608370a67c8ead6" not yet present on node "my-kind-cluster-worker", loading...

上記出力内容からcontrol planeノードとworkerノード x 2 全てにイメージがロードされたことが分かる。

さらに、各ノードにロードされているイメージ一覧を確認してみる。これは各ノード用に立ち上がったdockerコンテナ中にロードされたイメージ一覧を見ればよい。

まずは、次のコマンドで各ノード用に立ち上がったdockerコンテナ一覧を確認。

docker ps

CONTAINER ID   IMAGE                   COMMAND                  CREATED          STATUS          PORTS                       NAMES
87e9f413cb38   kindest/node:v1.19.11   "/usr/local/bin/entr…"   28 minutes ago   Up 28 minutes                               my-kind-cluster-worker2
3140c46409fc   kindest/node:v1.19.11   "/usr/local/bin/entr…"   28 minutes ago   Up 28 minutes   127.0.0.1:60149->6443/tcp   my-kind-cluster-control-plane
ef0eba3986d9   kindest/node:v1.19.11   "/usr/local/bin/entr…"   28 minutes ago   Up 28 minutes                               my-kind-cluster-worker

それでは、この中のmy-kind-cluster-workerという名前のworkerノード用コンテナにロードされたイメージ一覧を確認する。

kindのノード内部ではコンテナランタイムにdockerdではなくCRI互換のcontainerdを使っている。 よって、デーモンとのやり取りにはdockerコマンドではなくcrictl (Container Runtime Interface CLI)を利用する。 以下のようにmy-kind-cluster-workerコンテナ内のイメージ一覧をcrictlコマンドで取得する。

docker exec -it my-kind-cluster-worker crictl images

IMAGE                                      TAG                  IMAGE ID            SIZE
docker.io/kindest/kindnetd                 v20210326-1e038dc5   6de166512aa22       54MB
docker.io/library/sample-app               latest               e31ba19f294d7       24.5MB
docker.io/rancher/local-path-provisioner   v0.0.14              e422121c9c5f9       13.4MB
k8s.gcr.io/build-image/debian-base         v2.1.0               c7c6c86897b63       21.1MB
k8s.gcr.io/coredns                         1.7.0                bfe3a36ebd252       14MB
k8s.gcr.io/etcd                            3.4.13-0             0369cf4303ffd       86.7MB
k8s.gcr.io/kube-apiserver                  v1.19.11             2fb26b7ebd23a       120MB
k8s.gcr.io/kube-controller-manager         v1.19.11             1af1564e1890d       112MB
k8s.gcr.io/kube-proxy                      v1.19.11             d2c2a8b355f56       120MB
k8s.gcr.io/kube-scheduler                  v1.19.11             120d339f7d67a       47.7MB
k8s.gcr.io/pause                           3.5                  ed210e3e4a5ba       301kB

sample-app:latestがworkerノードにロードされていることが確認できる。

サンプルイメージ(sample-app:latest)のpullに失敗

それではサンプルイメージ(sample-app:latest)を指定したKubernetes deploymentリソースを作成して、kindクラスタにデプロイしてみる。

cat << EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-app
spec:
  selector:
    matchLabels:
      app: sample-app
  replicas: 1
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
      - name: sample-app
        image: sample-app:latest
        ports:
        - containerPort: 80
EOF

kubectl apply -f deployment.yaml

すると、ImagePullBackOffで、起動に失敗する。

kubectl get pod

NAMESPACE            NAME                                                    READY   STATUS             RESTARTS   AGE
default              sample-app-6cb94bb4b8-8xvsr                             0/1     ImagePullBackOff   0          88s

kubectt describeでEvents情報を参照すると、どうやらworkerノードのローカルにsample-app:latestがロードされているにもかかわらず、リモート(docker.io/library/sample-app:latest)イメージをpullしようとして失敗していたことが分かる。

kubectl describe pod sample-app-6cb94bb4b8-8xvsr

Events:
  Type     Reason     Age                     From               Message
  ----     ------     ----                    ----               -------
  Normal   Scheduled  8m50s                   default-scheduler  Successfully assigned default/sample-app-6cb94bb4b8-8xvsr to my-kind-cluster-worker2
  Normal   Pulling    7m15s (x4 over 8m50s)   kubelet            Pulling image "sample-app:latest"
  Warning  Failed     7m13s (x4 over 8m48s)   kubelet            Failed to pull image "sample-app:latest": rpc error: code = Unknown desc = failed to pull and unpack image "docker.io/library/sample-app:latest": failed to resolve reference "docker.io/library/sample-app:latest": pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
  Warning  Failed     7m13s (x4 over 8m48s)   kubelet            Error: ErrImagePull
  Normal   BackOff    6m47s (x7 over 8m47s)   kubelet            Back-off pulling image "sample-app:latest"
  Warning  Failed     3m47s (x20 over 8m47s)  kubelet            Error: ImagePullBackOff

imagePullPolicyについて

行き詰まってしまったのでググってみると、kind公式ドキュメントにしっかりとNOTEが書いてあった。

スクリーンショット 2021-10-01 1.50.22.png

雑にサマると、これはそもそもimagePullPolicyの動作仕様の話であり、latestイメージタグを使わないか、imagePullPolicyをIfNotPresentNeverに設定せよということである。

  • イメージタグがlatestもしくは省略されている時(デフォルトでlatest)の場合はimagePullPolicyはAlwayとして認識される。つまり、毎回kubeletはコンテナレジストリに同じ名前のイメージを問い合わせる(ただ、同一イメージダイジェストがキャッシュされていればそのローカルキャッシュを使用する)

  • イメージタグがlatestではない場合はimagePullPolicyはIfNotPresentとして認識される。つまり、ローカルに無いときにのみコンテナレジストリに同じ名前のイメージを問い合わせる

ということで、ここではlatestタグを使うことをやめ、すなわちIfNotPresentなimagePullPolicyとして設定されるようにして、Workerノードのローカルイメージが使われるようにした。

新しいタグのサンプルイメージをデプロイ

まずは、さきほどロードしたsample-app:latestイメージを消しておく(別にこれはやる必要はないけど、不要なものは消しておきたい性格なので)

docker exec -it my-kind-cluster-worker crictl images

IMAGE                                      TAG                  IMAGE ID            SIZE
docker.io/kindest/kindnetd                 v20210326-1e038dc5   6de166512aa22       54MB
docker.io/library/sample-app               latest               e31ba19f294d7       24.5MB

docker exec -it my-kind-cluster-worker crictl rmi e31ba19f294d7

Deleted: docker.io/library/sample-app:latest

つぎに、新しいタグ(ここでは0.0.1)を付与したサンプルイメージをkindクラスターにロードする。

# sample-appに0.0.1タグを付与
docker tag sample-app sample-app:0.0.1

# sample-app:0.0.1イメージをkindクラスタにロード
kind load docker-image sample-app:0.0.1 --name my-kind-cluster

さきほどと同じようにmy-kind-cluster-workerコンテナ内のイメージ一覧をcrictlコマンドで取得して、workerノードにsample-app:0.0.1がロードされたことを確認する。

docker exec -it my-kind-cluster-worker crictl images

IMAGE                                      TAG                  IMAGE ID            SIZE
docker.io/kindest/kindnetd                 v20210326-1e038dc5   6de166512aa22       54MB
docker.io/library/sample-app               0.0.1                 d2c2a8b355f56       24.5MB

それではさきほど作成したKubernetes deployment(sample-app)にサンプルイメージ (sample-app:0.0.1)を指定してみる

kubectl set image deployment sample-app sample-app=sample-app:0.0.1

ところが、 latestタグでないにも関わらずさきほどと同じ用にImagePullBackOffで起動に失敗する。

kubectl describe pod sample-app-856987d994-ws7kh

Events:
  Type     Reason     Age                From               Message
  ----     ------     ----               ----               -------
  Normal   Scheduled  93s                default-scheduler  Successfully assigned default/sample-app-856987d994-ws7kh to my-kind-cluster-worker
  Normal   Pulling    45s (x3 over 91s)  kubelet            Pulling image "sample-app:0.0.1"
  Warning  Failed     44s (x3 over 89s)  kubelet            Failed to pull image "sample-app:0.0.1": rpc error: code = Unknown desc = failed to pull and unpack image "docker.io/library/sample-app:0.0.1": failed to resolve reference "docker.io/library/sample-app:0.0.1": pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
  Warning  Failed     44s (x3 over 89s)  kubelet            Error: ErrImagePull
  Normal   BackOff    6s (x5 over 89s)   kubelet            Back-off pulling image "sample-app:0.0.1"
  Warning  Failed     6s (x5 over 89s)   kubelet            Error: ImagePullBackOff

どうしたものかと思いdeploymentの内容を確認してみると、最初のデプロイで作成されたdeploymentのpodテンプレートで、imagePullPolicyがAlwaysのままだったという残念なミスだった。

spec:
...
  template:
...
    spec:
      containers:
      - image: sample-app:0.0.1
        imagePullPolicy: Always   # <<<< ここ

そこで、imagePullPolicyをIfNotPresentにして更新してみたところ無事ローカルイメージが使われ、次のようにサンプルイメージの起動が成功した。

kubectl get pod

NAMESPACE            NAME                                                    READY   STATUS    RESTARTS   AGE
default              sample-app-58fdfdbc4d-4bnwf                             1/1     Running   0          1m

なお、これは単に私がkubectl set image deploymentでイメージのみを更新してしまったことが原因であり、次のように新しくdeploymentから作り直せばimagePullPolicyはIfNotPresentとして設定されるのでこのような問題は起こらない。

kubectl delete -f deployment.yaml

cat << EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-app
spec:
  selector:
    matchLabels:
      app: sample-app
  replicas: 1
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
      - name: sample-app
        image: sample-app:0.0.1
        ports:
        - containerPort: 80
EOF

kubectl apply -f deployment.yaml

結論

ここでは kindクラスタへのデプロイメントを題材としているが、 結局のところkindは関係なく、imagePullPolicyを理解していれば回避できた問題だった。 latestタグなイメージにおけるデフォルトのimagePullPolicyはAlwaysということで。

12
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?