LoginSignup
40

More than 3 years have passed since last update.

Docker Containerの終了時にExit Code 137 (SIGKILL)が出る時の対処法

Last updated at Posted at 2019-02-18

概要

これの日本語版のようなもの

問題

複数のコマンドを扱うDockerを使うとき、こんな感じのスクリプトになりませんか?

run.sh
#!/bin/sh -e

pip install --upgrade poetry
poetry config settings.virtualenvs.create false
poetry install
python ./manage.py runserver 0.0.0.0:8000
backend.dockerfile
from python:3.6
env PYTHONUNBUFFERED 1
run mkdir /code
workdir /code
docker-compose.yml
version: '3'

services:
  db:
    image: postgres:alpine
    environment:
      POSTGRES_PASSWORD: fingine
      POSTGRES_USER: fingine
      POSTGRES_DB: fingine
  backend:
    build:
      context: ./docker
      dockerfile: backend.dockerfile
    command: bash -c ./run.sh
    stop_signal: SIGINT
    env_file: ./docker/dev.env
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

これ、docker-compose upした後にdocker-compose stopすると、fingine_backend_1 exited with code 137と、エラーコード137、つまりSIGKILLが返された上で終了する。つまり、終了まで10秒ほど待たされる上に強制終了を食らう

で、調べるとやれ、メモリが足りないだのtrapコマンドを仕掛けろだのと的外れな回答ばかりで嫌気が差した。だから書いてやった。

解決法

注目すべき点はexecビルトインユーティリティコマンドにあります。通常、shell芸で書かれたコマンドは新しいプロセスを生成してそこでコマンドを実行します。この新しく生成されたプロセスでは、後述する問題によってシグナルを受信することができません。

しかし、execにコマンドライン引数としてコマンドとその引数を指定すると、コマンドのプロセスを生成せずに、シェルのプロセスとそのコマンドのプロセスを置き換えます。つまり、先程のrun.shを次のように書き換えると解決するのでは・・・と考えますよね?

run.sh
#!/bin/sh -e

pip install --upgrade poetry
poetry config settings.virtualenvs.create false
poetry install
exec python ./manage.py runserver 0.0.0.0:8000  # <-- これ。

実はこれだと不十分っす。 というのも、Dockerの終了シグナルはコンテナ内のプロセスIDが1のプロセスに対してのみ発火します。さあ、docker-compose.ymlを見てみましょう。

docker-compose.yml
version: '3'

services:
  db:
    image: postgres:alpine
    environment:
      POSTGRES_PASSWORD: fingine
      POSTGRES_USER: fingine
      POSTGRES_DB: fingine
  backend:
    build:
      context: ./docker
      dockerfile: backend.dockerfile
    command: bash -c ./run.sh  # <-- これ
    stop_signal: SIGINT
    env_file: ./docker/dev.env
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

要するに、bashコマンドをrun.shを引数にとって実行している、つまり、run.shの実行には新しいプロセスが生成される、と。 そういうことですね。

というわけで、このdocker-compose.ymlに記述されているrun.shの呼び出しをbashではなく、execを使って呼び出されるようにする必要があります。

幸いなことに、dockerには手軽にexecを使ったコマンドを記述する方法があり、今回はこれを用いることにします。

docker-compose.yml
version: '3'

services:
  db:
    image: postgres:alpine
    environment:
      POSTGRES_PASSWORD: fingine
      POSTGRES_USER: fingine
      POSTGRES_DB: fingine
  backend:
    build:
      context: ./docker
      dockerfile: backend.dockerfile
    command: ['./run.sh'] # <-- これ。配列として書き直すとexecに続くコマンドとして認識される。
    stop_signal: SIGINT
    env_file: ./docker/dev.env
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

さあこれでdocker-compose upしてdocker-compose stopしてみましょう。コンテナを10秒も待つことなく、fingine_backend_1 exited with code 0、つまりコード0、正常終了させる事ができます。素晴らしい!!

最後に

Schlawackさんの記事によって筆者のdockerの問題が解決した事に謝辞を申し上げます。

参考記事

Hynek Schlawack, Why Your Dockerized Application Isn’t Receiving Signals, 19 June 2017

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
40