(2020/09/25)
本記事は、下記記事からトラブルシュート部分を抜き出し、追記修正したものです。
最小構成の CentOS 8 に SSH 接続可能な Docker コンテナを作ってみる
概要
SSH 接続できる Docker コンテナでしばらく遊んでいると、コンテナを載せている VM に SSH 接続ができなくなるという問題が発生したため、その調査と解決策についてまとめる。
環境
- Windows10 Home (1909) (ホスト)
- VirtualBox 6.1.4
- CentOS 8.1.1911 (ゲスト)
- Docker 19.03.8
- Docker Compose 1.25.4
問題と対処
本記事では、どこからのコマンド実行かを明示するため、コマンド行の先頭に装置情報を付加する。
先頭が[centos@dockertest ~]$
となっているものはゲスト VM 上、test~:$
となっているものはコンテナ上でのコマンド実行を表すものとする。
問題発生
立てたコンテナに対して SSH アクセスするスクリプトを組んだりしてしばらく遊んでいると、あるとき突然ホストからゲスト VM への SSH 接続ができなくなってしまった。
具体的には、 TeraTerm でアクセスしようとすると、画面が真っ黒のまま応答が無い。
また、他のゲスト VM からコンテナを載せたゲスト VM へssh
でアクセスしてみると、下記のメッセージが出て接続が弾かれてしまう。
[centos@othervm ~]$ ssh -p 2222 centos@192.168.11.4
shell request failed on channel 0
調査
上記メッセージ(shell request failed on channel 0
)をもとにググってみると、下記のような記事がヒット。
参考:ssh ログインができない「shell request failed on channel 0」の対応方法
環境等異なるところはあるものの、プロセス周りが怪しいと当たりをつけて、動いているプロセスを調べてみる。
[centos@dockertest ~]$ ps aux | grep ssh
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
(~中略~)
22 7762 0.0 0.0 0 0 ? Z 02:27 0:00 [sshd] <defunct>
22 7764 0.0 0.0 0 0 ? Z 02:27 0:00 [sshd] <defunct>
22 7766 0.0 0.0 0 0 ? Z 02:27 0:00 [sshd] <defunct>
22 7768 0.0 0.0 0 0 ? Z 02:27 0:00 [sshd] <defunct>
22 7770 0.0 0.0 0 0 ? Z 02:27 0:00 [sshd] <defunct>
22 7772 0.0 0.0 0 0 ? Z 02:27 0:00 [sshd] <defunct>
22 7774 0.0 0.0 0 0 ? Z 02:27 0:00 [sshd] <defunct>
22 7776 0.0 0.0 0 0 ? Z 02:27 0:00 [sshd] <defunct>
22 7778 0.0 0.0 0 0 ? Z 02:27 0:00 [sshd] <defunct>
22 7780 0.0 0.0 0 0 ? Z 02:27 0:00 [sshd] <defunct>
22 7782 0.0 0.0 0 0 ? Z 02:27 0:00 [sshd] <defunct>
22 7784 0.0 0.0 0 0 ? Z 02:27 0:00 [sshd] <defunct>
22 7786 0.0 0.0 0 0 ? Z 02:27 0:00 [sshd] <defunct>
22 7788 0.0 0.0 0 0 ? Z 02:27 0:00 [sshd] <defunct>
(~中略~)
お、おおう……。
「Z
」や「defunct
」という表記から、どうも大量のssh
プロセスがゾンビ化して、使用可能なプロセスIDを食いつぶしているらしいということがわかる。
ゲスト VM やコンテナを再起動すると一旦は再接続できるようになるものの、SSH 接続する度にゾンビプロセスが増えていくため、恒久対処にはならず、時限爆弾を抱えることになる。
[centos@dockertest ~]$ ps aux | grep Z | wc -l
2
(この間にホストから TeraTerm でコンテナに SSH 接続 ⇒ 切断を実行)
[centos@dockertest ~]$ ps aux | grep Z | wc -l
3
※「2」はps
コマンド出力のヘッダと実行コマンド自身(grep Z
)がヒットしているだけなので、結果が「3」以上であればゾンビプロセスが発生していることがわかる。
原因
調べてみると、一般的に Linux ではinit
というプロセスがすべてのプロセスの祖先になっていて、これがうまい具合にプロセス管理をしてくれ、通常はゾンビプロセスが発生しないようになっているらしい。
ところが、今回作成したDocker コンテナにはinit
プロセスが存在しないので、プロセス管理の恩恵を受けられない。その結果、一度接続したssh
プロセスが、その役目を終えたあとに行き場を失ってゾンビ化し、それが積み重なっていっていずれ新たなssh
プロセスが起動できなくなる、ということのようだ。
ちなみに、init
はプロセスIDとして1番(PID 1)が割り当てられるようだが、コンテナの PID 1 にはDockerfile
でCMD
命令に記述しているtail
が割り当てられている。
test:~$ ps
PID USER TIME COMMAND
1 root 0:00 tail -f /dev/null
...
なお、ゾンビプロセスに関する詳しい原理については下記記事が参考になった。
解決策
方法はいろいろありそうだが、調査過程でinit
について学んだので、これを活かす方向で解決してみたい。
Docker Compose が version 3.7 以上であれば、docker-compose.yml
にinit: true
を追加すれば、コンテナでinit
が動くようになるとのこと。
version: '3.8'
services:
test:
build: .
container_name: test
hostname: test
ports:
- "2222:22"
tty: true
init: true # この行を追加
実際に上記の通りファイルを書き換えて、再度コンテナを立ち上げてみる。
[centos@dockertest ~]$ docker-compose down
[centos@dockertest ~]$ docker-compose build
[centos@dockertest ~]$ docker-compose up -d
コンテナが立ち上がったら、再度ホストから TeraTerm でコンテナに SSH 接続し、プロセスを確認してみる。
test:~$ ps
PID USER TIME COMMAND
1 root 0:00 /sbin/docker-init -- /bin/sh -c /etc/init.d/sshd start && tail -f /dev/null
...
おお、COMMAND
部分がちゃんと変わっている。
念の為、SSH 接続してゾンビプロセスが増えないことも確かめる。
[centos@dockertest ~]$ ps aux | grep Z | wc -l
2
(この間にホストから TeraTerm でコンテナに SSH 接続 ⇒ 切断を実行)
[centos@dockertest ~]$ ps aux | grep Z | wc -l
2
よきかな。
まとめ
- SSH できる Docker コンテナを作るときは
init
を動かすようにしよう - Docker コンテナを動かすときはゾンビプロセスの増殖に気をつけよう
プロセス管理絡みの話はこれまで特に気にしていなかったところだったので、いろいろ勉強になった。
そもそも Docker の思想として SSH 接続できるコンテナが是とされているのか、という問題はあるが、その辺りは追々勉強していきたい。