сигнальная ловушка в Docker

Вопрос или проблема

У меня есть скрипт, который должен запускать другую программу в цикле. Но я хотел бы захватить сигналы, чтобы остановить цикл корректно.

Вот мой воспроизводимый код:

loop.sh:

#!/bin/bash
COMMAND="logic.sh"
terminate=false

trap 'terminate=true' SIGTERM SIGINT

while true; do
    if $terminate; then
        echo "Получен сигнал завершения, жду завершения текущего процесса..."
        wait
        echo "Процесс завершен. Выход..."
        exit 0
    fi
    $COMMAND &
    wait $!
done

logic.sh:

#!/bin/bash
sleep 5
date +%s

Dockerfile:

# FROM debian
FROM php:8.3-fpm
ADD loop.sh logic.sh /usr/local/bin/
CMD ["loop.sh"]

compose.yml:

services:
  app:
    build: .
    entrypoint: ''
    init: true

Чтобы протестировать это, я запускаю docker compose up --build; а в другом терминале docker compose stop.
Если вы видите Получен сигнал завершения, жду завершения текущего процесса..., затем временную метку, значит это работает.
При использовании FROM php:8.3-fpm это не работает, как ожидалось… но при использовании FROM debian — да.

Почему это работает по-другому? Как я могу заставить это работать с FROM php:...?
Спасибо

Ответ или решение

Проблема с обработкой сигналов в Docker при использовании PHP

Введение

При разработке контейнеризованных приложений часто возникает необходимость обрабатывать системные сигналы, такие как SIGTERM или SIGINT, чтобы корректно завершать процессы внутри контейнера. В данной статье мы обсудим проблему, с которой вы столкнулись при использовании скриптов в Docker с различными базовыми образами, а также предложим пути её решения.

Описание проблемы

У вас есть скрипт loop.sh, который запускает другой скрипт logic.sh в цикле и должен корректно завершать свою работу при получении сигнала остановки. Скрипт работает корректно, когда базовым образом является debian, но не выполняет обработку сигналов должным образом, когда основным образом является php:8.3-fpm. Причина этой разницы заключается в том, как различные образы Docker обрабатывают сигналы и процессы.

Основная проблема

Используя образ php:8.3-fpm, ваш скрипт, прописанный в CMD, может быть заменен на фоновый процесс, что не позволяет ему адекватно обрабатывать сигналы. Образ php-fpm реализует свою собственную систему,, которая может игнорировать или не передавать сигналы дочерним процессам.

Вот что происходит в ваших скриптах:

  • loop.sh использует механизм trap для захвата сигналов и установки флага terminate, что позволяет корректно завершить текущий процесс.
  • При отправке сигнала через docker-compose stop, контейнер получает сигнал, который в обычном случае должен вызывать обработку в loop.sh.

Проблема с обработкой сигналов в образе php:8.3-fpm

  1. Побочный процесс: php-fpm запускает свои собственные процессы. Когда вы запускаете ваш скрипт через CMD, он не становится «верхним» (или PID 1) процессом, который мог бы обрабатывать сигналы должным образом.

  2. Необработанные сигналы: В процессе работы php-fpm может игнорировать или обрабатывать сигналы другими способами, что нарушает вашу логику обработки сигналов в loop.sh.

Решения

1. Изменение уровня доступа в контейнере

Для того чтобы ваш скрипт loop.sh мог корректно обрабатывать сигналы, необходимо сделать его "верхним" процессом в контейнере. Это можно сделать, используя параметр --init в вашем docker-compose.yml, как уже указано, но также важно убедиться, что в качестве entrypoint установлен именно ваш скрипт, а не php-fpm.

Пример Dockerfile:

FROM php:8.3-fpm
COPY loop.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/loop.sh
CMD ["/usr/local/bin/loop.sh"]

В docker-compose.yml:

services:
  app:
    build: .
    entrypoint: ["/usr/local/bin/loop.sh"]
    init: true

2. Использование образа Alpine

Если поведение php-fpm становится слишком сложным, можно рассмотреть использование образа на основе Alpine, который может быть более легковесным и хорошо управляет сигналами:

FROM php:8.3-fpm-alpine
COPY loop.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/loop.sh
CMD ["/usr/local/bin/loop.sh"]

Заключение

Корректная обработка сигналов в контейнерах Docker является важной частью обеспечения их стабильности. При работе с образом php:8.3-fpm необходимо учитывать специфику работы с фоновыми процессами и обработкой сигналов. Приведенные выше рекомендации помогут вам адаптировать ваш код и скрипты для корректной работы в Docker. Если у вас остались вопросы или требуется дополнительное объяснение, не стесняйтесь обращаться.

Оцените материал
Добавить комментарий

Капча загружается...