Kubernetes:新しいイメージでデプロイできない問題
新しいイメージをK8sにデプロイできない
「新しいイメージをDockerレジストリにpushしたのに、Kubernetesでデプロイできない」 と、同僚のDanさん(仮名)からHELP!のslackメッセージが飛んできました。
あ~自分もぶち当たった問題だなぁ、どれどれ…と思い内容を読むと、色々な問題や、勘違いに出くわしていることに気が付きました。
私自身も「これが原因だったのか!」と改めて知れたことがあったので記録しておこうと思います。
「pullは、毎回リポジトリから取得してるんだよね?」
⇒いいえ、設定によります!
Kuberenetesに触れる前に、Dockerで ①レジストリにPushする方法 ②リポジトリからPullした時の動作 をおさらいしておきます。
1.Docker イメージをレジストリにPushする方法
①操作している環境にあるイメージの一覧を表示する
以下のコマンドを入力します。
$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE docker-whale latest 7d9495d03763 38 minutes ago 273.7 MB
こんな風に出力した、とします。(docs.docker.jpより拝借)
※以下を同じように手元で行う場合は、docker-whaleイメージをPullしてください。
$ docker image pull maryatdocker/docker-whale
docker-whaleイメージのイメージIDは 7d9495d03763
リポジトリ名は docker-whale
であることがわかります。
このイメージをレジストリにアップロード(Push)するには、Pushしたいイメージに
・どのレジストリに
・どんな名前で
・何バージョンとしてアップロードするか
の情報を持たせてあげる必要があります。
言うなればイメージの住所と名前、年齢みたいなものでしょうか?
②Pushしたいイメージのイメージ名をフォーマット
以下のコマンドを入力します。
$ docker tag イメージID Dockerレジストリのホスト名/名前空間(namespace)/リポジトリ:タグ
DockerHubの場合、フォーマット部分は dockerhub.io/アカウント名/リポジトリ名:タグ
※レジストリのホスト名は省略可能
当方Azure使いなので、ACRの場合は レジストリ名.azurecr.io/リポジトリ名:タグ
となります。
名前空間/イメージ名のところは(正確には違いますが)「ディレクトリ・フォルダ」/「ファイル名」みたいな印象を受けました。
Danさんのレジストリは、名前空間部でバージョン分けしているような構成(その代わりタグがどれもv1)になっていました。
③レジストリにログイン
DockerHubの場合
$ docker login --username=ユーザ名 --email=メールアドレス
ACRの場合 Azureにログイン
$ az login
ACRに接続
$ acr login --name レジストリ名
④Docker Imageを Push
$ docker push フォーマットしたイメージ名
ここまでで、レジストリにDockerイメージを配置できたかと思います。
2.リポジトリからPullした時の動作
①ローカルマシン上のイメージを確認する
以下のコマンドを入力します。
$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE testregi.azurecr.io/default/whale v1 7d9495d03763 5 minutes ago 273.7 MB docker-whale latest 7d9495d03763 2 hours ago 273.7 MB
先ほどタグ付けしたイメージがあることを確認できます。
②Docker リポジトリからPullする
試しに先ほどPushしたDockerイメージをPullします。
が、いったんローカルホストにあるイメージに影響されないよう、今回使ったイメージを削除しておきます。
docker rmi
コマンドで、イメージを削除します。
$ docker rmi -f 7d9495d03763 $ docker rmi -f docker-whale
イメージIDまたはイメージ名を指定し、削除できました。
docker run
コマンドで、Dockerコンテナを起動します。
$ docker run testregi.azurecr.io/default/whale Unable to find image 'testregi.azurecr.io/default/whale' locally latest: Pulling from testregi.azurecr.io/default/whale e190868d63f8: Pull complete 8eed3712d2cf: Download complete ... _______________________________________ / What a computer is to me is the most \ | remarkable tool that we have ever come | | up with. It's the equivalent of a | | bicycle for our minds. | | | \ -- Steve Jobs (1955-2011) / -------------------------------------- \ \ \ ## . ## ## ## == ## ## ## ## === /""""""""""""""""___/ === ~~~/~~ ~~~~ ~~~ ~~~~ ~~ ~___/ ====- ~~~ \______ o __/ \ \ __/ \____\______/
Jobsのお言葉が現れました。
Unable to find image ~ locally:イメージがローカルホスト上にないため、latest: Pulling from ~ イメージが~からダウンロードされたことがわかります。
では試しに、もう一度同じコマンドを実行してみます。
$ docker run testregi.azurecr.io/default/whale _______________________________________ / Well, we'll really have a party, but \ | we've gotta post a guard outside. | | | \ -- Eddie Cochran, "Come On Everybody" / --------------------------------------- \ \ \ ## . ## ## ## == ## ## ## ## === /""""""""""""""""___/ === ~~~ /~~ ~~~~ ~~~ ~~~~ ~~ ~ / ===- ~~~ \______ o __/ \ \ __/ \____\______/
今回はダウンロードする動作や処理の宣言がなく、1回目よりも高速に実行されました(吹き出しの言葉が毎回変わることに今更気づいた)。 つまりローカルホスト上にあるDockerイメージを使って実行したのです。
Dockerはコンテナを実行する際、以下の順で動作します。
①ローカルホスト上のリポジトリに指定したDockerイメージがあるか確認し、あればそれを使う ②なければ、指定したコンテナレジストリからDockerイメージをダウンロードし実行する
そう!これが言いたくて、ここまでDockerについて書き続けてきたんです! 道のりが長い…
3.KubernetesでPodをデプロイしたときの動作
始めのDanさんの疑問に戻ります。 「KubernetesでPodデプロイした時、Dockerコンテナは毎回必ずリポジトリから取得してるんだよね?」
答: Kubernetesでも、上記と同様の動作をします(設定によりますが)。
「新しいイメージをレジストリにpushしたのに、Kubernetesでデプロイできない」という問題、実はKubernetesのNodeが持つDockerイメージが影響していました。
Nodeとは(Kubernetesチュートリアルより): Kubernetesにおけるワーカーマシンのこと。Nodeはマスターによって管理され、複数のPodを持つことができる。 そして少なくとも以下が動作します。
Kubelet: KubernetesマスターとNode間の通信を担当するプロセス。マシン上で実行されているPodとコンテナを管理します。
レジストリからコンテナイメージを取得し、コンテナを解凍し、アプリケーションを実行することを担当する、Docker、rktのようなコンテナランタイム。
つまりKubernetesのNodeでもNode上にあるDockerイメージを利用するため、一見リポジトリのDockerイメージが使えないような動作をしていたんです。
①ノードの一覧を表示する
以下のコマンドを実行します。
$ kubectl get nodes NAME LABELS STATUS node01 kubernetes.io/hostname=node01 Ready
②指定したノードの詳細情報をYAML形式で表示する
以下のコマンドを実行します。
$ kubectl get nodes node1 -o yaml
指定したノードに関する大量の情報が出力されます。
今回はノードに保持されているイメージの存在を確認したいので、status.images
部分を確認してみます。
status: images: - names: - nginx@sha256:afdgsj452cja92jjgd7dksfjcasadkslawfoasvsak52dsamho - nginx:1.13 sizeBytes: 108958610
いました。※省略しています
先ほどの(長ったらしい)Dockerイメージの取得について振り返ります。
①ローカルホスト上のリポジトリに指定したDockerイメージがあるか確認し、あればそれを使う ②なければ、指定したコンテナレジストリからDockerイメージをダウンロードし実行する
Kubernetesのノードも、どうやらこれと同じことをしてくれるみたいなのです。
https://kubernetes.io/docs/concepts/containers/images/
Using a Private Registryの説明でこのような記載がありました。
Pre-pulling Images
- all pods can use any images cached on a node
またUpdating Imagesの説明で、デフォルトのPullに関するポリシーはIfNotPresent
であり、「既にイメージが存在する場合KubeletはPullしない」とのことです。
何が何でもいつもリポジトリからPullしてほしい場合は
以下のいずれかを満たせばいつも・強制的にPullしてくれます。
- set the
imagePullPolicy
of the container toAlways
.- omit the
imagePullPolicy
and use:latest
as the tag for the image to use.- omit the
imagePullPolicy
and the tag for the image to use.- enable the AlwaysPullImages admission controller.
・spec.containers[n].imagePullPolicyをAlwaysにする
・imagePullPolicyを省略し、latestタグを使用する
・imagePullPolicyとタグを省略する
・アドミッションコントローラでAlwaysPullImagesを有効にする
おわりに
基本的にイメージは、保守性の観点からもバージョン管理(タグ管理)するべきものだと考えています(latest極力使わない、とフォロワーさんからも教わりました)。
今回のケースは、そもそもタグ付けの記載方法が誤っていたことと、Kubernetesの仕様が相まって起こった現象でした。
同僚のDanさんには、現象と原因についてお伝えしました。
…あー長くなってしまったーおしまい!