SIGTERM не получен java-процессом, используя "докер-стоп" и официальное изображение Java

Я запускаю Java-приложение dropwizard в контейнере Docker, используя изображение java:7u79 на основе debian/jessie.

Приложение My Java обрабатывает сигнал SIGTERM для изящества. Обработка SIGTERM отлично работает, когда я запускаю приложение без Docker.

Когда я запускаю его в контейнере Docker, SIGTERM не достигает приложения Java, когда я выдаю команду docker stop. Он убивает процесс внезапно через 10 секунд.

Мой Dockerfile:

FROM java:7u79

COPY dropwizard-example-1.0.0.jar /opt/dropwizard/
COPY example.keystore /opt/dropwizard/
COPY example.yml /opt/dropwizard/

WORKDIR /opt/dropwizard

RUN java -jar dropwizard-example-1.0.0.jar db migrate /opt/dropwizard/example.yml

CMD java -jar dropwizard-example-1.0.0.jar server /opt/dropwizard/example.yml

EXPOSE 8080 8081

Что не так с этим Dockerfile? Есть ли другой способ решить эту проблему?

Ответы

Ответ 1

Предполагая, что вы запустите службу Java, определив следующее в Dockerfile:

CMD java -jar ...

Когда вы теперь вводите контейнер и перечисляете процессы, например. docker exec -it <containerName> ps AHf (я не пробовал это с java, но с изображением ubuntu), вы видите, что ваш процесс Java не является корневым процессом (а не процессом с PID 1), а дочерним процессом /bin/sh процесс:

UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 18:27 ?        00:00:00 /bin/sh -c java -jar ...
root         8     1  0 18:27 ?        00:00:00   java -jar ...

Итак, в основном у вас есть оболочка Linux, которая является основным процессом с PID 1, который имеет дочерний процесс (Java) с PID 8.

Чтобы обеспечить правильную обработку сигналов, вы должны избегать этого родительского процесса оболочки. Это можно сделать, используя встроенную команду оболочки exec. Это приведет к процессу дочернего процесса над родительским процессом. Поэтому в конце прежний родительский процесс больше не существует. И дочерний процесс становится процессом с PID 1. Попробуйте следующее в Dockerfile:

CMD exec java -jar ...

В листинге процесса должно отображаться следующее:

UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 18:30 ?        00:00:00 java -jar ...

Теперь у вас есть только один процесс с PID 1. Как правило, хорошей практикой является наличие в контейнерах докеров только одного процесса - с PID 1 (или если вам действительно нужно больше процессов, то вы должны использовать, например, supervisord как ПИД-1, который сам позаботится о обработке сигналов для своих дочерних процессов).

С этой настройкой SIGTERM будет обрабатываться непосредственно процессом Java. Нет никакого процесса оболочки больше, между которыми может нарушить обработку сигнала.

ИЗМЕНИТЬ

Тот же эффект exec может быть достигнут с помощью другого синтаксиса CMD, который делает это неявно (спасибо Andy за его комментарий):

CMD ["java", "-jar", "..."]

Ответ 2

@h3nrik ответ прав, но иногда вам действительно нужно использовать script для настройки запуска. Просто используйте команду exec, чтобы сделать трюк в большинстве случаев:

#!/bin/sh

#--- Preparations

exec java -jar ...

Смотрите это замечательное сообщение