From 99618bc8f9a01c481a2f2b9df7c68c9df3d2fb49 Mon Sep 17 00:00:00 2001 From: Gilad Peleg Date: Sun, 14 Sep 2025 12:29:40 +0300 Subject: [PATCH] Make container shutdown instant & graceful: add dumb-init and signal-aware startup.sh (no tail -f) (#913) --- Dockerfile | 100 +++++++++++++++++++++++++++-------------------------- startup.sh | 49 ++++++++++++++++++++++---- 2 files changed, 94 insertions(+), 55 deletions(-) diff --git a/Dockerfile b/Dockerfile index 67c19ef..df49722 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,49 +1,51 @@ -# Use the php:8.2-fpm-alpine base image -FROM php:8.2-fpm-alpine - -# Set working directory to /var/www/html -WORKDIR /var/www/html - -# Update packages and install dependencies -RUN apk upgrade --no-cache && \ - apk add --no-cache shadow sqlite-dev libpng libpng-dev libjpeg-turbo libjpeg-turbo-dev freetype freetype-dev curl autoconf libgomp icu-dev icu-data-full nginx dcron tzdata imagemagick imagemagick-dev libzip-dev sqlite libwebp-dev && \ - docker-php-ext-install pdo pdo_sqlite calendar && \ - docker-php-ext-enable pdo pdo_sqlite && \ - docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp && \ - docker-php-ext-install -j$(nproc) gd intl zip && \ - apk add --no-cache --virtual .build-deps $PHPIZE_DEPS && \ - pecl install imagick && \ - docker-php-ext-enable imagick && \ - apk del .build-deps - -# Copy your PHP application files into the container -COPY . . - -# Copy Nginx configuration -COPY nginx.conf /etc/nginx/nginx.conf -COPY nginx.default.conf /etc/nginx/http.d/default.conf - -# Remove nginx conf files from webroot -RUN rm -rf /var/www/html/nginx.conf && \ - rm -rf /var/www/html/nginx.default.conf - -# Copy the custom crontab file -COPY cronjobs /etc/cron.d/cronjobs - -# Convert the line endings, allow read access to the cron file, and create cron log folder -RUN dos2unix /etc/cron.d/cronjobs && \ - chmod 0644 /etc/cron.d/cronjobs && \ - /usr/bin/crontab /etc/cron.d/cronjobs && \ - mkdir /var/log/cron && \ - chown -R www-data:www-data /var/www/html && \ - chmod +x /var/www/html/startup.sh && \ - echo 'pm.max_children = 15' >> /usr/local/etc/php-fpm.d/zz-docker.conf && \ - echo 'pm.max_requests = 500' >> /usr/local/etc/php-fpm.d/zz-docker.conf - -# Expose port 80 for Nginx -EXPOSE 80 - -ARG SOFTWARE_VERSION=1.20.0 - -# Start both PHP-FPM, Nginx -CMD ["sh", "-c", "/var/www/html/startup.sh"] +# Use the php:8.2-fpm-alpine base image +FROM php:8.2-fpm-alpine + +# Set working directory to /var/www/html +WORKDIR /var/www/html + +# Update packages and install dependencies +RUN apk upgrade --no-cache && \ + apk add --no-cache dumb-init shadow sqlite-dev libpng libpng-dev libjpeg-turbo libjpeg-turbo-dev freetype freetype-dev curl autoconf libgomp icu-dev icu-data-full nginx dcron tzdata imagemagick imagemagick-dev libzip-dev sqlite libwebp-dev && \ + docker-php-ext-install pdo pdo_sqlite calendar && \ + docker-php-ext-enable pdo pdo_sqlite && \ + docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp && \ + docker-php-ext-install -j$(nproc) gd intl zip && \ + apk add --no-cache --virtual .build-deps $PHPIZE_DEPS && \ + pecl install imagick && \ + docker-php-ext-enable imagick && \ + apk del .build-deps + +# Copy your PHP application files into the container +COPY . . + +# Copy Nginx configuration +COPY nginx.conf /etc/nginx/nginx.conf +COPY nginx.default.conf /etc/nginx/http.d/default.conf + +# Remove nginx conf files from webroot +RUN rm -rf /var/www/html/nginx.conf && \ + rm -rf /var/www/html/nginx.default.conf + +# Copy the custom crontab file +COPY cronjobs /etc/cron.d/cronjobs + +# Convert the line endings, allow read access to the cron file, and create cron log folder +RUN dos2unix /etc/cron.d/cronjobs && \ + chmod 0644 /etc/cron.d/cronjobs && \ + /usr/bin/crontab /etc/cron.d/cronjobs && \ + mkdir /var/log/cron && \ + chown -R www-data:www-data /var/www/html && \ + chmod +x /var/www/html/startup.sh && \ + echo 'pm.max_children = 15' >> /usr/local/etc/php-fpm.d/zz-docker.conf && \ + echo 'pm.max_requests = 500' >> /usr/local/etc/php-fpm.d/zz-docker.conf + +# Expose port 80 for Nginx +EXPOSE 80 + +ARG SOFTWARE_VERSION=1.20.0 + +ENTRYPOINT ["dumb-init", "--"] + +# Start both PHP-FPM, Nginx +CMD ["/var/www/html/startup.sh"] diff --git a/startup.sh b/startup.sh index df370e1..7db3022 100644 --- a/startup.sh +++ b/startup.sh @@ -1,5 +1,7 @@ #!/bin/sh +set -euo pipefail + echo "Startup script is running..." > /var/log/startup.log # Default the PUID and PGID environment variables to 82, otherwise @@ -14,11 +16,46 @@ chown -R www-data:www-data /var/www/html chown -R www-data:www-data /tmp chmod -R 770 /tmp -# Start both PHP-FPM and Nginx -php-fpm & nginx -g 'daemon off;' & touch ~/startup.txt +# PIDs we’ll track +PHP_FPM_PID= +NGINX_PID= +CROND_PID= +shutdown_in_progress=0 -# Start the cron daemon -crond +shutdown_once() { + exit_signal=$? + kill_signal=$(kill -l "$exit_signal" 2>/dev/null || echo "$exit_signal") + + [ "$shutdown_in_progress" -eq 1 ] && return 0 + shutdown_in_progress=1 + + echo "Got signal: $kill_signal - Shutting down gracefully... " + # nginx wants QUIT for graceful + nginx -s quit || true + # php-fpm graceful quit as well + [ -n "${PHP_FPM_PID}" ] && kill -QUIT "${PHP_FPM_PID}" 2>/dev/null || true + # cron can just get TERM + [ -n "${CROND_PID}" ] && kill -TERM "${CROND_PID}" 2>/dev/null || true + echo "Graceful shutdown complete." +} + +# Handle all common stop signals +trap 'shutdown_once' SIGTERM SIGINT SIGQUIT + +# Start both PHP-FPM and Nginx +echo "Launching php-fpm" +php-fpm -F & +PHP_FPM_PID=$! + +echo "Launching crond" +crond -f & +CROND_PID=$! + +echo "Launching nginx" +nginx -g 'daemon off;' & +NGINX_PID=$! + +touch ~/startup.txt # Wait one second before running scripts sleep 1 @@ -51,5 +88,5 @@ crontab -d -u root # Run checkforupdates.php /usr/local/bin/php /var/www/html/endpoints/cronjobs/checkforupdates.php -# Keep the container running indefinitely (this won't exit) -tail -f /dev/null \ No newline at end of file +# Essentially wait until all child processes exit +wait