1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

docker-compose up を Ctrl+c(SIGINT) で停止させた時に ERROR: 2 になってしまう問題の対処法

Last updated at Posted at 2022-07-17

警告
本記事は仮説を提示しているだけで、根本的なエラー原因を断定しているわけではありません。

問題の発生状況

環境

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でコンテナを起動するとエラーがなくなり正常終了します。

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が子プロセスで実行されます。

docker-compose 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を送ってみる

プロセス1に対して
$ 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
# プロセスも残っていない
プロセス2に対して
$ 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の挙動を再現してみます。

プロセス1,2両方に対して
$ 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を送るようになっていたのでしょう。
一方のプロセスにSININTを送った図
この実装の上でCtrl+Cで止められると内部的には2回SIGINTを受け取ることになり、コンテナの強制終了(Kill)が走ってしまったと考えられます。
スクリーンショット 2022-07-17 16.22.53.png
また、半年ほど前からエラーが発生し出した原因は、元々1プロセスで動いていたdocker-compose up
Docker Compose V2導入に伴ってユーザーがV1とV2の実装を切り替えられるように、2プロセスに分離したのが原因だと思われます。

対処法(再掲)

Docker Compose V1で発生しているエラーなので、docker composeコマンドを使用してDocker Compose V2でコンテナを起動するとエラーがなくなり正常終了します。

V2の使用方法
docker compose up # docker-composeの"-"がいらない!

終わりに

議論の余地ありありの記事を書くことに抵抗はありますが、対処法のみを示すよりは仮説まで示した方が建設的だろうと思い、記事にさせていただきました。
詳しい方は是非マサカリをお願いします!!!

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?