警告
本記事は仮説を提示しているだけで、根本的なエラー原因を断定しているわけではありません。
問題の発生状況
環境
Rails, PostgreSQL構成のWebアプリケーションをDockerで包んで開発していました。
それぞれのバージョンは下記の通りです。
$ docker-compose -v
docker-compose version 1.29.2, build 5becea4c
$ docker -v
Docker version 20.10.12, build e91ed57
$ ruby -v
ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-linux]
$ rails -v
Rails 7.0.3.1
$ postgres --version
postgres (PostgreSQL) 14.4 (Debian 14.4-1.pgdg110+1)
遭遇した問題
docker-compose up
をフォアグラウンドで実行後、Ctrl+c
でコンテナを停止しようとしたところ、タイトルにあるERROR: 2
が発生しコンテナがKILLされてしまいました。
このエラーは本記事執筆時の半年くらい前(2021/11/01くらい?)から発生していました。
KILLされても特に問題なく放置していたのですが、終了ステータスが2となるのがなんだか気持ち悪いので対処することにしました。
詳しいターミナルの出力
~/devs/workspace $ docker-compose up
Recreating workspace-postgres-1 ... done
Recreating workspace-api-1 ... done
Attaching to workspace_postgres_1, workspace_api_1
postgres_1 |
postgres_1 | PostgreSQL Database directory appears to contain a database; Skipping initialization
postgres_1 |
postgres_1 | 2022-07-16 12:59:10.901 UTC [1] LOG: starting PostgreSQL 14.4 (Debian 14.4-1.pgdg110+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit
postgres_1 | 2022-07-16 12:59:10.901 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
postgres_1 | 2022-07-16 12:59:10.902 UTC [1] LOG: listening on IPv6 address "::", port 5432
postgres_1 | 2022-07-16 12:59:10.907 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
postgres_1 | 2022-07-16 12:59:10.920 UTC [27] LOG: database system was shut down at 2022-07-16 12:57:11 UTC
postgres_1 | 2022-07-16 12:59:10.927 UTC [1] LOG: database system is ready to accept connections
api_1 | => Booting Puma
api_1 | => Rails 7.0.3.1 application starting in development
api_1 | => Run `bin/rails server --help` for more startup options
api_1 | Puma starting in single mode...
api_1 | * Puma version: 5.6.4 (ruby 3.1.0-p0) ("Birdie's Version")
api_1 | * Min threads: 5
api_1 | * Max threads: 5
api_1 | * Environment: development
api_1 | * PID: 1
api_1 | * Listening on http://0.0.0.0:3000
api_1 | Use Ctrl-C to stop
^CGracefully stopping... (press Ctrl+C again to force)
Killing workspace_api_1 ... done
Killing workspace_postgres_1 ... done
ERROR: 2
対処法
早い話、Docker Compose V1
で発生しているエラーなので、docker compose
コマンドを使用してDocker Compose V2
でコンテナを起動するとエラーがなくなり正常終了します。
docker compose up # docker-composeの"-"がいらない!
Docker Compose
のV1,V2間の具体的な変更点はここでは取り扱わないので下記記事を参考にしてください。
原因
V2に移行後は正常終了するようになったみたいですが、Docker Compose
の内部実装を読む気力はない(ごめんなさい🙇♂️)ので、あくまで仮説を示します。仮説が根本原因だと鵜呑みにしないように注意してください。
詳しい方はご指摘いただけますと幸いです。
docker-compose
で起動されるプロセス
Docker Compose V1
でコンテナを起動(docker-compose up
)すると内部的に/usr/local/bin/docker-compose-v1 up
が子プロセスで実行されます。
$ ps -o pid,ppid,pgid,command | grep docker-compose
89200 85167 89199 grep docker-compose
89141 84878 89141 docker-compose up
89142 89141 89141 /usr/local/bin/docker-compose-v1 up
2つプロセスが立ち上がっているところが気になったので一つずつkill
コマンドでSIGINT
を送ってみることにしました。
※便宜上、pid=89141(docker-compose up)をプロセス1、pid=89142(/usr/local/bin/docker-compose-v1 up)をプロセス2と呼ぶことにします。
kill
コマンドでSIGINT
を送ってみる
$ kill -s INT 89141
# docker-compose upしている端末で
Gracefully stopping... (press Ctrl+C again to force)
Stopping workspace_api_1 ... done
Stopping workspace_postgres_1 ... done
# 期待通り正常終了
$ ps -eo pid,ppid,pgid,command | grep docker-compose
89537 85167 89536 grep docker-compose
# プロセスも残っていない
$ kill -s INT 89142
# docker-compose upしている端末で
Gracefully stopping... (press Ctrl+C again to force)
Stopping workspace_api_1 ... done
Stopping workspace_postgres_1 ... done
# 期待通り正常終了
$ ps -eo pid,ppid,pgid,command | grep docker-compose
89551 85167 89550 grep docker-compose
# プロセスも残っていない
1プロセスずつSIGINT
を送ると 正常に停止(Stop) しました。
Ctrl+C
では異常にKillされる一方で、kill
では正常にStopされるのはなんだかおかしいですね。
Ctrl+C
の挙動
Ctrl+C
を入力すると端末から実行プロセスの所属するプロセスグループに対してシグナルSIGINT
が送られます。
つまり、プロセス1,2両方に対してSIGINT
が送られることになります。
kill
コマンドでCtrl+C
の挙動を再現してみます。
$ kill -s INT 89141 89142
# docker-compose upしている端末で
Gracefully stopping... (press Ctrl+C again to force)
Killing workspace_api_1 ... done
Killing workspace_postgres_1 ... done
ERROR: 2
# 異常終了!!!!
Ctrl+C
の時と同様に異常終了しました。
ここまでの各プロセスの終了とコンテナの正常終了可否を表にすると下記のようになります。
プロセス1のみ | プロセス2のみ | プロセス1,2両方同時(Ctrl+C) |
---|---|---|
✅正常 | ✅正常 | ❌異常 |
結論(仮説):Docker Compose V1
ではコンテナの停止処理が2プロセスで依存し合っているのが原因!
おそらく、下図のように一方のプロセスでSIGINT
を受け取ったら、もう一方のプロセスにSIGINT
を送るようになっていたのでしょう。
この実装の上でCtrl+C
で止められると内部的には2回SIGINT
を受け取ることになり、コンテナの強制終了(Kill)が走ってしまったと考えられます。
また、半年ほど前からエラーが発生し出した原因は、元々1プロセスで動いていたdocker-compose up
が
Docker Compose V2
導入に伴ってユーザーがV1とV2の実装を切り替えられるように、2プロセスに分離したのが原因だと思われます。
対処法(再掲)
Docker Compose V1
で発生しているエラーなので、docker compose
コマンドを使用してDocker Compose V2
でコンテナを起動するとエラーがなくなり正常終了します。
docker compose up # docker-composeの"-"がいらない!
終わりに
議論の余地ありありの記事を書くことに抵抗はありますが、対処法のみを示すよりは仮説まで示した方が建設的だろうと思い、記事にさせていただきました。
詳しい方は是非マサカリをお願いします!!!