自作のDockerImageを使ってkubernetesにpodをデプロイしたら"Completed"になってしまう件

こんにちは。
株式会社クラスアクト インフラストラクチャ事業部の大塚です。

この記事ではDockerHubに自作しているイメージファイルを元にkubernetes環境上にpodをデプロイする方法と、デプロイしたものの勝手にstatusが"Completed"となってしまい「podに接続（≒exec）出来ない！」って状態の原因と解決策的なのを備忘録的に書いておきたいと思います。
※勉強し直しの状態なので、ここに書いていることが本当に原因なのかは保証しません。参考程度にお願いします。

# podとは

公式サイトから引用します。
>Podは、Kubernetes内で作成・管理できるコンピューティングの最小のデプロイ可能なユニットです。
Pod(Podという名前は、たとえばクジラの群れ(pod of whales)やえんどう豆のさや(pea pod)などの表現と同じような意味です)は、1つまたは複数のコンテナのグループであり、ストレージやネットワークの共有リソースを持ち、コンテナの実行方法に関する仕様を持っています。

引用元：

https://kubernetes.io/ja/docs/concepts/workloads/pods/

以下の動画コンテンツにおいては"podとは仮想ホストのようなもの"という表現がされておりますが、podというのが理解しにくければ仮想ホストなんだなと思ってもらうとイメージしやすいかもしれません。実際、k8sではpod単位でveth（≒仮想NIC）が割当たるのでそこからも腑に落ちやすい印象を私は感じました。
デプロイした仮想ホスト（＝pod）上にコンテナをデプロイすることはdockerと同じです。

https://www.udemy.com/course/kubernetes-docker-container-devops-kanzen-nyumon/


イメージ化してみます。
以下が通常のdocker環境になります。
何らかのOSで動いているサーバ上にdockerをインストールし、docker runコマンド等を叩くことで必要なコンテナをデプロイしていくようなイメージですね。
![kubernetes.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/9c69bb3f-8374-2bbb-ccd6-f6600bee92b3.png)

これがkubernetesになると以下の様な環境になるということだと思います。
![kubernetes-ページ2.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/1bc3d109-a071-e05d-3ec1-1591a6ec2b75.png)

これらの図を横に並べてみます。
赤点線の部分がニアリーイコールだという事かと思います。

![kubernetes-ページ3.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/b75b6994-147e-c497-1ae8-2b8cffd51c94.png)

# 今回使用する自作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ブラウザから確認します。
![2023043001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/25646dc3-c5c4-cbf4-9271-e78111a4f4c1.png)

# 自作の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
```

私なりの結論を話しますと<font color=red>**【自作したdocker imageでデプロイしたpodでは稼働しているプロセスが無いからステータスが"Completed"になってしまう】**</font>ということだと思っております。
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
```