dockerでゾンビ化したポートを解放する

ローカルPC上のdockerは唐突に終了する場合が多くあります。このような利用を続けていたら、コンテナの利用していたポートをつかんだままになり、コンテナ再起動時にエラーに遭遇しました。

ERROR: for mysql  Cannot start service mysql: driver failed programming external connectivity on endpoint data_mysql_1 (fdc978c2f074ff810c5ddaf6e1bd36b6bf47f54be440a2ab8abea1b009bec421): Bind for 0.0.0.0:3306 failed: port is already allocated
ERROR: Encountered errors while bringing up the project.

ホスト上のlsofコマンドで確認すると、確かにdockerのプロセスがホストのポートを利用しています。通常はdockerの再起動でポートが解放されるのですが、今回はdockerを終了するとlsofのリストから消えるものの、再起動すると再び占有し始めます。

$ sudo lsof -i -P | grep "LISTEN"
launchd       1           root   30u  IPv6 0xed5e2ddbacdcee97      0t0  TCP localhost:631 (LISTEN)
launchd       1           root   42u  IPv4 0xed5e2ddbacdd8de7      0t0  TCP localhost:631 (LISTEN)
launchd       1           root   43u  IPv4 0xed5e2ddbacdd8de7      0t0  TCP localhost:631 (LISTEN)
launchd       1           root   46u  IPv6 0xed5e2ddbacdcee97      0t0  TCP localhost:631 (LISTEN)
com.docke 90233          chuma   23u  IPv4 0xed5e2ddbdf063aa7      0t0  TCP *:3306 (LISTEN)
com.docke 90233          chuma   24u  IPv6 0xed5e2ddbe1a9ee97      0t0  TCP localhost:3306 (LISTEN)

Cannot start containers: port is already allocatedで同様の件が報告されていて、ネットワークのキャッシュらしきファイルを消すことで回復するようです。

この操作は、dockerホスト上で行う必要があり、LinuxやVirtualbox環境であればホストVMにログインすれば良いのですが、 Docker for Mac のxhive環境へのログイン環境は謎に包まれています。

が、実はscreenコマンドで端末に接続するttyインターフェースがあるようです( Host path of volumeより)。

$ screen ~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/tty

端末に接続すると植田まさし調のクジラ表示ののち、rootユーザーでログインできます(パスワードなし)。

Welcome to Moby
Kernel 4.4.20-moby on an x86_64 (/dev/ttyS0)

                        ##         .
                  ## ## ##        ==
               ## ## ## ## ##    ===
           /"""""""""""""""""___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ /  ===- ~~~
           \______ o           __/
             \    \         __/
              \____\_______/

moby login: root
Welcome to Moby, based on Alpine Linux.
moby:~# docker rm $(docker ps -qa)
moby:~# rm /var/lib/docker/network/files/local-kv.db 

このように接続してしまえば普通のLinuxホストとして操作できます。

また、端末からの切断はscreenの標準的な手順に沿って \C-a k のようです(y/nの確認プロンプトが出る)。

この状態でdockerを再起動するとポートが使われていない状態に戻ります。(なお、doker上でネットワークを作成していた場合はこの操作で消えるのでdocker netword createコマンドで再度作成する必要があります)

$ sudo lsof -i -P | grep "LISTEN"
launchd       1           root   30u  IPv6 0xed5e2ddbacdcee97      0t0  TCP localhost:631 (LISTEN)
launchd       1           root   42u  IPv4 0xed5e2ddbacdd8de7      0t0  TCP localhost:631 (LISTEN)
launchd       1           root   43u  IPv4 0xed5e2ddbacdd8de7      0t0  TCP localhost:631 (LISTEN)
launchd       1           root   46u  IPv6 0xed5e2ddbacdcee97      0t0  TCP localhost:631 (LISTEN)
中馬崇尋
Chuma Takahiro