From 3444c2473ae66d6d0dcfd87d4f209455daf7aef5 Mon Sep 17 00:00:00 2001 From: Mistral OZ - MIO Date: Thu, 5 Nov 2026 20:59:36 +0100 Subject: [PATCH] Stop the apache and fpm images right away during a long startup step The stop signals of these images (SIGWINCH, SIGQUIT) are trapped by the entrypoint, and bash only runs a trap once the foreground command ends: a docker stop during a STARTUP_COMMAND, or during the configuration of the extensions on a loaded host, waited for the end of the step and the container was killed after the stop timeout. The startup steps now run in the background, and the entrypoint waits for them. --- CHANGELOG.md | 3 +++ tests-suite/tool-startup-command.sh | 17 +++++++++++++++++ utils/docker-entrypoint-as-root.sh | 23 +++++++++++++++-------- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97a9191b..dd094f1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ ### Minor changes +* **2026-11-05** + * The *apache* and *fpm* images stop right away when a `docker stop` is requested during a long startup step (e.g. a `STARTUP_COMMAND_*`, or the configuration of the extensions on a loaded host): they were killed after the stop timeout (exit code 137 after 10 s) + * **2026-11-03** * The `php` command is about 20 ms faster on each call: it detects the changes of the `PHP_*` environment variables without starting PHP a first time. The configuration is now also regenerated when a `PHP_*` variable is removed * The README recommends the fpm variant with its built-in Apache to serve a web application, and links to the v6 roadmap. The php2shell fix is now mentioned in the supported versions diff --git a/tests-suite/tool-startup-command.sh b/tests-suite/tool-startup-command.sh index ec6f6150..ef64b378 100755 --- a/tests-suite/tool-startup-command.sh +++ b/tests-suite/tool-startup-command.sh @@ -35,3 +35,20 @@ test_phpStartupWarning() { assert_equals "0" "$?" "Docker run failed" assert_equals "startup-ok" "$RESULT" } +############################################################ +## A docker stop during a long startup command stops the container right away +############################################################ +test_stopDuringStartupCommand() { + NAME="test-startup-stop-$(unused_port)" + docker run --name "${NAME}" ${RUN_OPTIONS} -d -e STARTUP_COMMAND_1="sleep 30" \ + "${REPO}:${TAG_PREFIX}${PHP_VERSION}-${BRANCH}-slim-${BRANCH_VARIANT}${ARCH_SUFFIX}" sleep 300 > /dev/null + for _ in $(seq 1 60); do + docker exec "${NAME}" pgrep -fx "sleep 30" > /dev/null 2>&1 && break + sleep 0.5 + done + START=$(date +%s) + docker stop "${NAME}" > /dev/null 2>&1 + DURATION=$(( $(date +%s) - START )) + docker rm -f "${NAME}" > /dev/null 2>&1 + assert "test ${DURATION} -lt 5" "Stopping during the startup command took ${DURATION}s (killed by timeout?)" +} diff --git a/utils/docker-entrypoint-as-root.sh b/utils/docker-entrypoint-as-root.sh index ec6cdcb4..7cdcf0c2 100755 --- a/utils/docker-entrypoint-as-root.sh +++ b/utils/docker-entrypoint-as-root.sh @@ -8,6 +8,13 @@ if [[ "$IMAGE_VARIANT" == "apache" ]]; then trap 'exit 0' WINCH fi +# Runs a startup step in the background and waits for it, so that a stop signal interrupts a long step (e.g. a +# STARTUP_COMMAND): bash only runs a trap once the foreground command ends +startup_step() { + "$@" <&0 & + wait $! +} + # Let's write a file saying the container is started (we are no longer in build mode, useful for php_proxy.sh) touch /opt/container_started @@ -152,11 +159,11 @@ unset REMOTE_HOST_FOUND source /usr/local/bin/php_env_fingerprint.sh php_env_fingerprint > /opt/php_env_fingerprint -/usr/bin/real_php -d display_errors=stderr /usr/local/bin/generate_conf.php > /etc/php/${PHP_VERSION}/mods-available/generated_conf.ini -PHP_VERSION="${PHP_VERSION}" /usr/bin/real_php -d display_errors=stderr /usr/local/bin/setup_extensions.php | sudo bash +startup_step /usr/bin/real_php -d display_errors=stderr /usr/local/bin/generate_conf.php > /etc/php/${PHP_VERSION}/mods-available/generated_conf.ini +startup_step bash -c '/usr/bin/real_php -d display_errors=stderr /usr/local/bin/setup_extensions.php | sudo bash' if [[ -n "$COMPOSER_VERSION" ]]; then - /usr/local/bin/select_composer_version.sh "$COMPOSER_VERSION" + startup_step /usr/local/bin/select_composer_version.sh "$COMPOSER_VERSION" fi if [[ -n "$LOCALES" ]]; then @@ -164,22 +171,22 @@ if [[ -n "$LOCALES" ]]; then echo 'Locales are not available in this image. If you are using the thecodingmachine/php "slim" variant, do not forget to add "ARG INSTALL_LOCALES=1" in your Dockerfile. Check the documentation for more details.' >&2 exit 1 fi - locale-gen --keep-existing $LOCALES > /dev/null + startup_step locale-gen --keep-existing $LOCALES > /dev/null fi # output on the logs can be done by writing on the "tini" PID. Useful for CRONTAB TINI_PID=`ps -e | grep tini | awk '{print $1;}'` -/usr/bin/real_php -d display_errors=stderr /usr/local/bin/generate_cron.php $TINI_PID > /tmp/generated_crontab +startup_step /usr/bin/real_php -d display_errors=stderr /usr/local/bin/generate_cron.php $TINI_PID > /tmp/generated_crontab chmod 0644 /tmp/generated_crontab if [[ "$WITH_APACHE" == "1" ]]; then - /usr/bin/real_php -d display_errors=stderr /usr/local/bin/enable_apache_mods.php | bash + startup_step bash -c '/usr/bin/real_php -d display_errors=stderr /usr/local/bin/enable_apache_mods.php | bash' fi if [ -e /etc/container/startup.sh ]; then - sudo -E -u "#$DOCKER_USER_ID" /etc/container/startup.sh + startup_step sudo -E -u "#$DOCKER_USER_ID" /etc/container/startup.sh fi -sudo -E -u "#$DOCKER_USER_ID" sh -c "/usr/bin/real_php -d display_errors=stderr /usr/local/bin/startup_commands.php | bash" +startup_step sudo -E -u "#$DOCKER_USER_ID" sh -c "/usr/bin/real_php -d display_errors=stderr /usr/local/bin/startup_commands.php | bash" if [[ "$APACHE_DOCUMENT_ROOT" == /* ]]; then export ABSOLUTE_APACHE_DOCUMENT_ROOT="$APACHE_DOCUMENT_ROOT"