こんにちは。
株式会社クラスアクト インフラストラクチャ事業部の大塚です。
この記事ではDockerHubに自作しているイメージファイルを元にkubernetes環境上にpodをデプロイする方法と、デプロイしたものの勝手にstatusが"Completed"となってしまい「podに接続(≒exec)出来ない!」って状態の原因と解決策的なのを備忘録的に書いておきたいと思います。
※勉強し直しの状態なので、ここに書いていることが本当に原因なのかは保証しません。参考程度にお願いします。
podとは
公式サイトから引用します。
Podは、Kubernetes内で作成・管理できるコンピューティングの最小のデプロイ可能なユニットです。
Pod(Podという名前は、たとえばクジラの群れ(pod of whales)やえんどう豆のさや(pea pod)などの表現と同じような意味です)は、1つまたは複数のコンテナのグループであり、ストレージやネットワークの共有リソースを持ち、コンテナの実行方法に関する仕様を持っています。
引用元:
以下の動画コンテンツにおいては"podとは仮想ホストのようなもの"という表現がされておりますが、podというのが理解しにくければ仮想ホストなんだなと思ってもらうとイメージしやすいかもしれません。実際、k8sではpod単位でveth(≒仮想NIC)が割当たるのでそこからも腑に落ちやすい印象を私は感じました。
デプロイした仮想ホスト(=pod)上にコンテナをデプロイすることはdockerと同じです。
イメージ化してみます。
以下が通常のdocker環境になります。
何らかのOSで動いているサーバ上にdockerをインストールし、docker runコマンド等を叩くことで必要なコンテナをデプロイしていくようなイメージですね。
これがkubernetesになると以下の様な環境になるということだと思います。
これらの図を横に並べてみます。
赤点線の部分がニアリーイコールだという事かと思います。
今回使用する自作DockerImage
以下の手順で作成したものです。コンテナは往々にしてpingやtraceroute等、デバッグ等によく使うコマンドが入っておらず、必要になるたびにインストールしていました。しかし「だるい・・」と思い、それ用のコンテナイメージを作ってしまおうという思考から作成に至りました。
まずdocker環境において、ubuntu22.04イメージを元にデプロイしたコンテナを用意します。
デプロイしたコンテナにexecで入り、apt updateとapt upgradeを実行します。
root@docker:~# docker run --name deb-ubuntu -itd ubuntu:22.04
c83adeb801759ad124b4cea3b650d2775c6ff2174f42d12ec6e0f10385f3d516
root@docker:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c83adeb80175 ubuntu:22.04 "/bin/bash" 4 seconds ago Up 3 seconds deb-ubuntu
c03147d2f83c httpd:latest "httpd-foreground" 5 hours ago Up 5 hours 80/tcp httpd.1.t5zt9glbr4tlgjofhcozjz7bi
521b69dd00e9 nginx:latest "/docker-entrypoint.…" 36 hours ago Up 36 hours 80/tcp my-nginx.3.hux0zd2goc6974s25n4vvafcj
8240d0af95a3 portainer/portainer-ce "/portainer" 7 days ago Up 36 hours 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp, 9443/tcp portainer
root@docker:~# docker exec -it deb-ubuntu /bin/bash
root@c83adeb80175:/# apt update
root@c83adeb80175:/# apt upgrade
その後に以下のコマンドを実行します。
それぞれ上から順番にping,ip,vi,traceroute,nslookup,curl,systemctlコマンドのインストールになります。
root@c83adeb80175:/# apt-get install iputils-ping net-tools
root@c83adeb80175:/# apt-get install iproute2
root@c83adeb80175:/# apt-get install vim
root@c83adeb80175:/# apt-get install traceroute
root@c83adeb80175:/# apt-get install dnsutils
root@c83adeb80175:/# apt-get install curl
root@c83adeb80175:/# apt-get install systemctl
このコンテナをイメージ化してdocker hubにpushします。
root@docker:~# docker login
Authenticating with existing credentials...
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
root@docker:~# docker commit deb-ubuntu deb-ubuntu-image
sha256:7afcd01fe171f2e55cc8b1286f8359503fb120e65ff8fae4c181fc0065e642b3
root@docker:~# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
deb-ubuntu-image latest 7afcd01fe171 6 seconds ago 263MB
root@docker:~# docker tag deb-ubuntu-image shotaohtsuka/deb-ubuntu-image:latest
root@docker:~# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
deb-ubuntu-image latest 7afcd01fe171 4 minutes ago 263MB
shotaohtsuka/deb-ubuntu-image latest 7afcd01fe171 4 minutes ago 263MB
root@docker:~# docker push shotaohtsuka/deb-ubuntu-image
Using default tag: latest
The push refers to repository [docker.io/shotaohtsuka/deb-ubuntu-image]
e3c2f7fb5b3e: Pushed
b93c1bd012ab: Mounted from shotaohtsuka/my-django-image
latest: digest: sha256:83ede37b63b9d5962404f55629388daab6f0439a0daf5a80bd6f919c04810610 size: 741
docker hubにアップロードされていることをWebブラウザから確認します。
自作のDockerImageをkubernetesにデプロイする
yamlファイルを使ってpodをデプロイするのが鉄板かもしれませんが、今回はそれは使いません。kubectl runコマンドを使ってデプロイしていきます。実際に入力するコマンドは以下となります。
--restart=Neverを指定することでpodをデプロイし、その後にdocker imageを指定しています。docker imageはとりあえずhubに問い合わせに行くようです。設定によっては問い合わせに行かないようにすることもできるッぽい?
podのデプロイ後、ステータスを確認してみるとcompletedになってしまっています。。。
root@sv-ohtsuka-k8s-master:~# kubectl run --restart=Never --image=shotaohtsuka/deb-ubuntu-image:latest testpod
pod/testpod created
root@sv-ohtsuka-k8s-master:~# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 4h5m
testpod 0/1 Completed 0 6s
この状態だとpodを指定して接続しようとしてもはじかれてしまいます。
それはそうです。動いておりませんので汗
以下がその実行結果です。pod指定のあと-cでpodの中のコンテナを指定しています。
root@sv-ohtsuka-k8s-master:~# kubectl exec -it testpod -c testpod -- /bin/bash
error: cannot exec into a container in a completed pod; current phase is Succeeded
podの詳細を確認してみます。
確認する為にはdescribeを使用します。
エラーなどは特に確認出来ませんね。
root@sv-ohtsuka-k8s-master:~# kubectl describe pod testpod
Name: testpod
Namespace: default
Priority: 0
Node: sv-ohtsuka-k8s-master/172.19.0.223
Start Time: Sun, 30 Apr 2023 02:55:56 +0000
Labels: run=testpod
Annotations: cni.projectcalico.org/podIP:
cni.projectcalico.org/podIPs:
Status: Succeeded
IP: 10.1.114.84
IPs:
IP: 10.1.114.84
Containers:
testpod:
Container ID: containerd://c30fae9c825ad5e791395b9f3cff36379db8b726c55c2ecb9a6bd22ad94fef60
Image: shotaohtsuka/deb-ubuntu-image:latest
Image ID: docker.io/shotaohtsuka/deb-ubuntu-image@sha256:83ede37b63b9d5962404f55629388daab6f0439a0daf5a80bd6f919c04810610
Port: <none>
Host Port: <none>
State: Terminated
Reason: Completed
Exit Code: 0
Started: Sun, 30 Apr 2023 02:55:58 +0000
Finished: Sun, 30 Apr 2023 02:55:58 +0000
Ready: False
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-l6qnh (ro)
Conditions:
Type Status
Initialized True
Ready False
ContainersReady False
PodScheduled True
Volumes:
kube-api-access-l6qnh:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 7s default-scheduler Successfully assigned default/testpod to sv-ohtsuka-k8s-master
Normal Pulling 7s kubelet Pulling image "shotaohtsuka/deb-ubuntu-image:latest"
Normal Pulled 6s kubelet Successfully pulled image "shotaohtsuka/deb-ubuntu-image:latest" in 1.245030855s (1.245046541s including waiting)
Normal Created 5s kubelet Created container testpod
Normal Started 5s kubelet Started container testpod
一方で、podをデプロイする時に-itオプションを追加するとpodにアクセスすることが出来ます。
但し、podが抜けてしまうとステータスが"Completed"になることは変わらず・・・
※pod内でpsコマンドを叩いていますが出力結果を覚えておいてください。
root@sv-ohtsuka-k8s-master:~# kubectl run testpod -it --restart=Never --image=shotaohtsuka/deb-ubuntu-image
If you don't see a command prompt, try pressing enter.
root@testpod:/# apt-get install apache2
root@testpod:/# service apache2 start
* Starting Apache httpd web server apache2 AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 10.1.114.80. Set the 'ServerName' directive globally to suppress this message
*
root@testpod:/# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 03:14 pts/0 00:00:00 /bin/bash
root 10 1 0 03:14 pts/0 00:00:00 ps -ef
root@testpod:/# exit
exit
E0429 23:02:16.688728 3188213 v2.go:105] EOF
root@sv-ohtsuka-k8s-master:~# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 20m
testpod 0/1 Completed 0 4m16s
なぜ自動的にpodのstatusが"Completed"になってしまうのか?
タイトルの内容を検証するためにnginx用のpodをデプロイしてみます。
nignxのイメージからpodをデプロイするとステータスがruuningになるかと思います。
root@sv-ohtsuka-k8s-master:~# kubectl run --restart=Never --image=nginx:latest nginx
pod/nginx created
root@sv-ohtsuka-k8s-master:~# kubectl get pod
NAME READY STATUS RESTARTS AGE
testpod 0/1 Completed 0 14m
nginx 1/1 Running 0 6s
この状態であればexecでpod内のコンテナにアクセスすることが出来ます。
ここでもpsコマンドを叩いておきます。デフォルトではpsコマンドが入っておりませんので、入れておきます。
nginxのコンテナなので当たり前ですが、nginxのプロセスが動いておりますね。
また上記と異なり、podからexitアウトしてもCompleted状態にならず、runningになります。
root@sv-ohtsuka-k8s-master:~# kubectl exec -it nginx -c nginx -- /bin/bash
root@nginx:/# apt update
root@nginx:/# apt upgrade
root@nginx:/# apt-get install procps
root@nginx:/# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 22:41 ? 00:00:00 nginx: master process nginx -g daemon off;
nginx 29 1 0 22:41 ? 00:00:00 nginx: worker process
nginx 30 1 0 22:41 ? 00:00:00 nginx: worker process
nginx 31 1 0 22:41 ? 00:00:00 nginx: worker process
nginx 32 1 0 22:41 ? 00:00:00 nginx: worker process
root 45 0 0 22:43 pts/0 00:00:00 /bin/bash
root 818 45 0 22:44 pts/0 00:00:00 ps -ef
root@nginx:/# exit
exit
root@sv-ohtsuka-k8s-master:~# kubectl get pod
NAME READY STATUS RESTARTS AGE
testpod 0/1 Completed 0 9m21s
nginx 1/1 Running 0 5m30s
私なりの結論を話しますと【自作したdocker imageでデプロイしたpodでは稼働しているプロセスが無いからステータスが"Completed"になってしまう】ということだと思っております。
psコマンドの出力結果を並べてみます。
「自作したイメージでデプロイしたpodでもプロセスが走っているではないか!」というツッコミが入りそうですが、出力されている2つのプロセスはpodに接続しているプロセス(/bin/bash)及び実行したコマンド自身のプロセス(ps -ef)になります。
その為、このpodから抜けてしまうとpodで稼働しているプロセスが0になり、それをみたkubernetesは「稼働させる必要ないやん!」となって勝手にCompleted状態にするのかと。
一方でnignx側は/bin/bashとps -ef以外にも走っているプロセスがありますので、このpodから抜けてもステータスがCompleted状態にならずrunning状態になるのだと思います。
★自作イメージでデプロイしたpod
root@testpod:/# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 03:14 pts/0 00:00:00 /bin/bash
root 10 1 0 03:14 pts/0 00:00:00 ps -ef
★nginxイメージでデプロイしたpod
root@nginx:/# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 22:41 ? 00:00:00 nginx: master process nginx -g daemon off;
nginx 29 1 0 22:41 ? 00:00:00 nginx: worker process
nginx 30 1 0 22:41 ? 00:00:00 nginx: worker process
nginx 31 1 0 22:41 ? 00:00:00 nginx: worker process
nginx 32 1 0 22:41 ? 00:00:00 nginx: worker process
root 45 0 0 22:43 pts/0 00:00:00 /bin/bash
root 818 45 0 22:44 pts/0 00:00:00 ps -ef
この推測から、自作したイメージからデプロイしたpodをrunning状態にし続けたい場合、exitアウトしてもpodの中で何か1つでもプロセスを走らせ続ければ良い環境にしてしまえばいいと判断をすることが出来ます。
手法はnohupを使うなど色々ありそうですが、今回は一旦podにapache2を入れてサービスを起動させてみます。
気を付けないといけないのは、apache2のプロセスを走らせた後にexitアウトしてしまうと、プロセスが消えてしまったので、teraterm等で接続している場合右上の「×」を押して抜けてください。
root@sv-ohtsuka-k8s-master:~# kubectl run testpod -it --restart=Never --image=shotaohtsuka/deb-ubuntu-image
If you don't see a command prompt, try pressing enter.
root@testpod:/# apt-get install apache2
root@testpod:/# service apache2 start
* Starting Apache httpd web server apache2 AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 10.1.114.80. Set the 'ServerName' directive globally to suppress this message
*
root@testpod:/# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 22:58 pts/0 00:00:00 /bin/bash
root 558 1 0 23:01 ? 00:00:00 /usr/sbin/apache2 -k start
www-data 561 558 0 23:01 ? 00:00:00 /usr/sbin/apache2 -k start
www-data 562 558 0 23:01 ? 00:00:00 /usr/sbin/apache2 -k start
root 619 1 0 23:01 pts/0 00:00:00 ps -ef
★このタイミングでteratermの「×」を押してexit out
このタイミングで再度kubernetes環境に接続podの状態を確認してみるとrunningで固定化されています。
root@sv-ohtsuka-k8s-master:~# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 27m
testpod 1/1 Running 0 86s
こうなっていれば、このコンテナにexecで入ってexitアウトしても大丈夫です。
root@sv-ohtsuka-k8s-master:~# kubectl exec -it testpod -- /bin/bash
root@testpod:/# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 23:08 pts/0 00:00:00 /bin/bash
root 551 1 0 23:09 ? 00:00:00 /usr/sbin/apache2 -k start
www-data 554 551 0 23:09 ? 00:00:00 /usr/sbin/apache2 -k start
www-data 555 551 0 23:09 ? 00:00:00 /usr/sbin/apache2 -k start
root 613 0 0 23:12 pts/1 00:00:00 /bin/bash
root 622 613 0 23:12 pts/1 00:00:00 ps -ef
root@testpod:/# exit
root@sv-ohtsuka-k8s-master:~# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 30m
testpod 1/1 Running 0 4m19s