From b35f04d220806a32dfb01e0b28575176d790e60b Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Wed, 15 Jul 2026 00:12:32 +0500 Subject: [PATCH] Replace Unit configuration with Nginx + PHP-FPM setup in Docker - Remove outdated Unit-based Docker configuration and scripts. - Introduce Nginx + PHP-FPM stack with environment-based configurations. - Add custom Nginx setup for frontend-backend routing. - Update Dockerfiles, Docker Compose, and related scripts for new architecture. --- .env.example | 2 +- app/docker/docker-entrypoint_dev.sh | 112 -------------------- app/docker/docker-entrypoint_prod.sh | 122 ---------------------- app/docker/{ => php}/Dockerfile | 50 +++------ app/docker/php/conf/app-fpm.conf | 26 +++++ app/docker/php/start.sh | 59 +++++++++++ app/docker/start.sh | 21 ---- app/docker/unit-config.json | 69 ------------ app/docker/web/Dockerfile | 31 ++++++ app/docker/web/Dockerfile.dockerignore | 3 + app/docker/web/conf/default.conf.template | 44 ++++++++ app/docker/web/conf/fastcgi_params | 23 ++++ app/docker/web/start.sh | 35 +++++++ docker-compose-prod.yml | 33 ++++-- docker-compose.yml | 38 +++++-- 15 files changed, 293 insertions(+), 375 deletions(-) delete mode 100644 app/docker/docker-entrypoint_dev.sh delete mode 100644 app/docker/docker-entrypoint_prod.sh rename app/docker/{ => php}/Dockerfile (61%) create mode 100644 app/docker/php/conf/app-fpm.conf create mode 100755 app/docker/php/start.sh delete mode 100755 app/docker/start.sh delete mode 100644 app/docker/unit-config.json create mode 100644 app/docker/web/Dockerfile create mode 100644 app/docker/web/Dockerfile.dockerignore create mode 100644 app/docker/web/conf/default.conf.template create mode 100644 app/docker/web/conf/fastcgi_params create mode 100644 app/docker/web/start.sh diff --git a/.env.example b/.env.example index e8286b0..a0a680a 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -DOCKER_APP_PORT=8080 +DOCKER_WEB_PORT=8080 DOCKER_CAPTCHA_PORT=8081 DOCKER_CAPTCHA_WEBSOCKET_PORT=8082 DOCKER_DB_PORT=3306 diff --git a/app/docker/docker-entrypoint_dev.sh b/app/docker/docker-entrypoint_dev.sh deleted file mode 100644 index 9fc4564..0000000 --- a/app/docker/docker-entrypoint_dev.sh +++ /dev/null @@ -1,112 +0,0 @@ -#!/bin/sh - -set -euo pipefail - -WAITLOOPS=5 -SLEEPSEC=1 -unitd="unitd" -role=${CONTAINER_ROLE:-app} - -curl_put() -{ - RET=$(/usr/bin/curl -s -w '%{http_code}' -X PUT --data-binary @$1 --unix-socket /var/run/control.unit.sock http://localhost/$2) - RET_BODY=$(echo $RET | /bin/sed '$ s/...$//') - RET_STATUS=$(echo $RET | /usr/bin/tail -c 4) - if [ "$RET_STATUS" -ne "200" ]; then - echo "$0: Error: HTTP response status code is '$RET_STATUS'" - echo "$RET_BODY" - return 1 - else - echo "$0: OK: HTTP response status code is '$RET_STATUS'" - echo "$RET_BODY" - fi - return 0 -} - -if [ "$role" = "app" ]; then - echo "$0: Launching Unit daemon to perform initial configuration..." - /usr/sbin/$unitd --control unix:/var/run/control.unit.sock - for i in $(/usr/bin/seq $WAITLOOPS); do - if [ ! -S /var/run/control.unit.sock ]; then - echo "$0: Waiting for control socket to be created..." - /bin/sleep $SLEEPSEC - else - break - fi - done - - # even when the control socket exists, it does not mean unit has finished initialisation - # this curl call will get a reply once unit is fully launched - /usr/bin/curl -s -X GET --unix-socket /var/run/control.unit.sock http://localhost/ - - if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -print -quit 2>/dev/null | /bin/grep -q .; then - echo "$0: /docker-entrypoint.d/ is not empty, applying initial configuration..." - - if [[ -n "${UNIT_SOURCE:-}" ]]; then - config="/docker-entrypoint.d/config.json" - tmp="$(mktemp)" - jq --arg src "${UNIT_SOURCE}" ' - .listeners["*:9000"].forwarded.source = - ( $src - | split(",") - | map( gsub("^\\s+|\\s+$"; "") ) # trim пробелы - | map( select(. != "") ) # убрать пустые - ) - ' "$config" > "$tmp" - mv "$tmp" "$config" - fi - - echo "$0: Looking for certificate bundles in /docker-entrypoint.d/..." - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.pem"); do - echo "$0: Uploading certificates bundle: $f" - curl_put $f "certificates/$(basename $f .pem)" - done - - echo "$0: Looking for JavaScript modules in /docker-entrypoint.d/..." - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.js"); do - echo "$0: Uploading JavaScript module: $f" - curl_put $f "js_modules/$(basename $f .js)" - done - - echo "$0: Looking for configuration snippets in /docker-entrypoint.d/..." - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.json"); do - echo "$0: Applying configuration $f"; - curl_put $f "config" - done - - echo "$0: Looking for shell scripts in /docker-entrypoint.d/..." - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.sh"); do - echo "$0: Launching $f"; - "$f" - done - - # warn on filetypes we don't know what to do with - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -not -name "*.sh" -not -name "*.json" -not -name "*.pem" -not -name "*.js"); do - echo "$0: Ignoring $f"; - done - fi - - echo "$0: Stopping Unit daemon after initial configuration..." - kill -TERM $(/bin/cat /var/run/unit.pid) - - for i in $(/usr/bin/seq $WAITLOOPS); do - if [ -S /var/run/control.unit.sock ]; then - echo "$0: Waiting for control socket to be removed..." - /bin/sleep $SLEEPSEC - else - break - fi - done - if [ -S /var/run/control.unit.sock ]; then - kill -KILL $(/bin/cat /var/run/unit.pid) - rm -f /var/run/control.unit.sock - fi - - echo - echo "$0: Unit initial configuration complete; ready for start up..." - echo -fi - -chmod -R 777 /var/www/html/storage /var/www/html/bootstrap/cache - -exec "$@" diff --git a/app/docker/docker-entrypoint_prod.sh b/app/docker/docker-entrypoint_prod.sh deleted file mode 100644 index 8093a90..0000000 --- a/app/docker/docker-entrypoint_prod.sh +++ /dev/null @@ -1,122 +0,0 @@ -#!/bin/sh - -set -euo pipefail - -WAITLOOPS=5 -SLEEPSEC=1 -unitd="unitd" -role=${CONTAINER_ROLE:-app} - -curl_put() -{ - RET=$(/usr/bin/curl -s -w '%{http_code}' -X PUT --data-binary @$1 --unix-socket /var/run/control.unit.sock http://localhost/$2) - RET_BODY=$(echo $RET | /bin/sed '$ s/...$//') - RET_STATUS=$(echo $RET | /usr/bin/tail -c 4) - if [ "$RET_STATUS" -ne "200" ]; then - echo "$0: Error: HTTP response status code is '$RET_STATUS'" - echo "$RET_BODY" - return 1 - else - echo "$0: OK: HTTP response status code is '$RET_STATUS'" - echo "$RET_BODY" - fi - return 0 -} - -if [ "$role" = "app" ]; then - echo "$0: Launching Unit daemon to perform initial configuration..." - /usr/sbin/$unitd --control unix:/var/run/control.unit.sock - for i in $(/usr/bin/seq $WAITLOOPS); do - if [ ! -S /var/run/control.unit.sock ]; then - echo "$0: Waiting for control socket to be created..." - /bin/sleep $SLEEPSEC - else - break - fi - done - - # even when the control socket exists, it does not mean unit has finished initialisation - # this curl call will get a reply once unit is fully launched - /usr/bin/curl -s -X GET --unix-socket /var/run/control.unit.sock http://localhost/ - - if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -print -quit 2>/dev/null | /bin/grep -q .; then - echo "$0: /docker-entrypoint.d/ is not empty, applying initial configuration..." - - if [[ -n "${UNIT_SOURCE:-}" ]]; then - config="/docker-entrypoint.d/config.json" - tmp="$(mktemp)" - jq --arg src "${UNIT_SOURCE}" ' - .listeners["*:9000"].forwarded.source = - ( $src - | split(",") - | map( gsub("^\\s+|\\s+$"; "") ) # trim пробелы - | map( select(. != "") ) # убрать пустые - ) - ' "$config" > "$tmp" - mv "$tmp" "$config" - fi - - echo "$0: Looking for certificate bundles in /docker-entrypoint.d/..." - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.pem"); do - echo "$0: Uploading certificates bundle: $f" - curl_put $f "certificates/$(basename $f .pem)" - done - - echo "$0: Looking for JavaScript modules in /docker-entrypoint.d/..." - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.js"); do - echo "$0: Uploading JavaScript module: $f" - curl_put $f "js_modules/$(basename $f .js)" - done - - echo "$0: Looking for configuration snippets in /docker-entrypoint.d/..." - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.json"); do - echo "$0: Applying configuration $f"; - curl_put $f "config" - done - - echo "$0: Looking for shell scripts in /docker-entrypoint.d/..." - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.sh"); do - echo "$0: Launching $f"; - "$f" - done - - # warn on filetypes we don't know what to do with - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -not -name "*.sh" -not -name "*.json" -not -name "*.pem" -not -name "*.js"); do - echo "$0: Ignoring $f"; - done - fi - - echo "$0: Stopping Unit daemon after initial configuration..." - kill -TERM $(/bin/cat /var/run/unit.pid) - - for i in $(/usr/bin/seq $WAITLOOPS); do - if [ -S /var/run/control.unit.sock ]; then - echo "$0: Waiting for control socket to be removed..." - /bin/sleep $SLEEPSEC - else - break - fi - done - if [ -S /var/run/control.unit.sock ]; then - kill -KILL $(/bin/cat /var/run/unit.pid) - rm -f /var/run/control.unit.sock - fi - - echo - echo "$0: Unit initial configuration complete; ready for start up..." - echo -fi - -php artisan config:cache -php artisan event:cache -php artisan route:cache -php artisan view:cache -php artisan storage:link -if [ "$role" = "app" ]; then - php artisan migrate --force -fi -chown -R unit:unit /var/www/html -chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache -chmod -R 777 /var/www/html/storage /var/www/html/bootstrap/cache - -exec "$@" diff --git a/app/docker/Dockerfile b/app/docker/php/Dockerfile similarity index 61% rename from app/docker/Dockerfile rename to app/docker/php/Dockerfile index fd1d63f..61efbcd 100644 --- a/app/docker/Dockerfile +++ b/app/docker/php/Dockerfile @@ -1,23 +1,4 @@ -FROM docker.io/php:8.3-zts-alpine3.18 AS unit_builder - -ARG UNIT_VERSION=1.31.1 - -RUN apk --no-cache add pcre2-dev gcc git musl-dev make && \ - mkdir -p /usr/lib/unit/modules && \ - git clone https://github.com/nginx/unit.git && \ - cd unit && \ - git checkout $UNIT_VERSION && \ - ./configure --prefix=/var --statedir=/var/lib/unit --runstatedir=/var/run --control=unix:/run/unit/control.unit.sock --log=/var/log/unit.log --user=www-data --group=www-data --tmpdir=/tmp --modulesdir=/var/lib/unit/modules && \ - ./configure php && \ - make && \ - make install - -FROM docker.io/php:8.3-zts-alpine3.18 AS build - -COPY --from=unit_builder /var/sbin/unitd /usr/sbin/unitd -COPY --from=unit_builder /var/lib/unit/ /var/lib/unit/ - -COPY docker/unit-config.json /docker-entrypoint.d/config.json +FROM docker.io/php:8.5-fpm-alpine3.23 AS build RUN apk --no-cache add pcre2 libbz2 libpng libwebp libjpeg-turbo icu-libs freetype oniguruma libzip jq \ && apk add --no-cache --virtual .phpize-deps icu-dev libpng-dev bzip2-dev libwebp-dev libjpeg-turbo-dev freetype-dev oniguruma-dev libzip-dev pcre2-dev ${PHPIZE_DEPS} \ @@ -30,7 +11,7 @@ RUN apk --no-cache add pcre2 libbz2 libpng libwebp libjpeg-turbo icu-libs freety mysqli pdo_mysql \ intl mbstring \ zip pcntl \ - exif opcache bz2 \ + exif bz2 \ calendar \ && pear update-channels && pecl update-channels \ && pecl install redis && docker-php-ext-enable redis \ @@ -44,36 +25,35 @@ RUN apk --no-cache add pcre2 libbz2 libpng libwebp libjpeg-turbo icu-libs freety && mkdir -p /tmp/php/upload \ && mkdir -p /tmp/php/sys \ && mkdir -p /tmp/php/session \ - && chown -R www-data:www-data /tmp/php \ - && ln -sf /dev/stdout /var/log/unit.log \ - && addgroup -S unit && adduser -S unit -G unit + && chown -R www-data:www-data /tmp/php + +COPY docker/php/conf/app-fpm.conf /usr/local/etc/php-fpm.d/app-fpm.conf FROM build AS app_build_for_production WORKDIR /home/app COPY application /home/app -RUN apk --no-cache add git nodejs npm \ +RUN apk --no-cache add git \ && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \ && composer install --optimize-autoloader --no-dev \ - && npm install && npm run build \ - && rm -rf /home/app/node_modules /home/app/.env + && rm -rf /home/app/.env FROM build AS production COPY --from=app_build_for_production /home/app /var/www/html -COPY docker/docker-entrypoint_prod.sh /home/unit/docker-entrypoint.sh -COPY docker/start.sh /usr/local/bin/start +COPY docker/php/start.sh /usr/local/bin/start WORKDIR /var/www/html -RUN chmod 755 /home/unit/docker-entrypoint.sh \ +RUN chown -R www-data:www-data /var/www/html \ && chmod 755 /usr/local/bin/start STOPSIGNAL SIGTERM -ENTRYPOINT ["/home/unit/docker-entrypoint.sh"] +USER www-data + EXPOSE 9000 CMD ["/usr/local/bin/start"] @@ -82,17 +62,15 @@ FROM build AS develop WORKDIR /var/www/html -COPY docker/docker-entrypoint_dev.sh /home/unit/docker-entrypoint.sh -COPY docker/start.sh /usr/local/bin/start +COPY docker/php/start.sh /usr/local/bin/start STOPSIGNAL SIGTERM -RUN chmod 755 /home/unit/docker-entrypoint.sh \ - && chmod 755 /usr/local/bin/start \ +RUN chmod 755 /usr/local/bin/start \ && apk --no-cache add git nodejs npm \ && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer -ENTRYPOINT ["/home/unit/docker-entrypoint.sh"] +USER www-data EXPOSE 9000 CMD ["/usr/local/bin/start"] diff --git a/app/docker/php/conf/app-fpm.conf b/app/docker/php/conf/app-fpm.conf new file mode 100644 index 0000000..0c37c38 --- /dev/null +++ b/app/docker/php/conf/app-fpm.conf @@ -0,0 +1,26 @@ +[www] +php_admin_value[upload_tmp_dir] = "/tmp/php/upload" +php_admin_value[sys_temp_dir] = "/tmp/php/sys" +php_admin_value[session.save_path] = "/tmp/php/session" +php_admin_value[open_basedir] = "/var/www/html:/tmp/php:." +php_admin_value[expose_php] = 0 +php_admin_value[memory_limit] = ${PHP_FPM_MEMORY_LIMIT} +php_admin_value[upload_max_filesize] = ${PHP_FPM_UPLOAD_MAX_FILESIZE} +php_admin_value[post_max_size] = ${PHP_FPM_POST_MAX_SIZE} +php_admin_value[display_errors] = ${PHP_FPM_DISPLAY_ERRORS} +php_admin_value[opcache.enable] = ${PHP_FPM_OPCACHE_ENABLE} +php_admin_value[opcache.validate_timestamps] = ${PHP_FPM_OPCACHE_VALIDATE_TIMESTAMPS} + +pm = dynamic +pm.max_children = ${PHP_FPM_PM_MAX_CHILDREN} +pm.start_servers = ${PHP_FPM_PM_START_SERVERS} +pm.min_spare_servers = ${PHP_FPM_PM_MIN_SPARE_SERVERS} +pm.max_spare_servers = ${PHP_FPM_PM_MAX_SPARE_SERVERS} +pm.max_requests = ${PHP_FPM_PM_MAX_REQUESTS} + + +listen = 127.0.0.1:9000 +listen.mode = 0660 +listen.owner = www-data +listen.group = www-data +security.limit_extensions = .php diff --git a/app/docker/php/start.sh b/app/docker/php/start.sh new file mode 100755 index 0000000..08f6b0d --- /dev/null +++ b/app/docker/php/start.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env sh +set -e +role=${CONTAINER_ROLE:-app} + +if [ "$APP_ENV" = "production" ]; then + php artisan config:cache + php artisan event:cache + php artisan route:cache + php artisan view:cache + php artisan storage:link +fi + +if [ "$role" = "app" ]; then + + export PHP_FPM_MEMORY_LIMIT="${PHP_FPM_MEMORY_LIMIT:-256m}" + export PHP_FPM_UPLOAD_MAX_FILESIZE="${PHP_FPM_UPLOAD_MAX_FILESIZE:-20m}" + export PHP_FPM_POST_MAX_SIZE="${PHP_FPM_POST_MAX_SIZE:-20m}" + + export PHP_FPM_PM_MAX_CHILDREN="${PHP_FPM_PM_MAX_CHILDREN:-10}" + export PHP_FPM_PM_START_SERVERS="${PHP_FPM_PM_START_SERVERS:-5}" + export PHP_FPM_PM_MIN_SPARE_SERVERS="${PHP_FPM_PM_MIN_SPARE_SERVERS:-2}" + export PHP_FPM_PM_MAX_SPARE_SERVERS="${PHP_FPM_PM_MAX_SPARE_SERVERS:-5}" + export PHP_FPM_PM_MAX_REQUESTS="${PHP_FPM_PM_MAX_REQUESTS:-500}" + + if [ "$APP_ENV" = "production" ]; then + export PHP_FPM_DISPLAY_ERRORS="${PHP_FPM_DISPLAY_ERRORS:-0}" + export PHP_FPM_OPCACHE_ENABLE="${PHP_FPM_OPCACHE_ENABLE:-1}" + export PHP_FPM_OPCACHE_VALIDATE_TIMESTAMPS="${PHP_FPM_OPCACHE_VALIDATE_TIMESTAMPS:-0}" + + php artisan migrate --force + else + export PHP_FPM_DISPLAY_ERRORS="${PHP_FPM_DISPLAY_ERRORS:-1}" + export PHP_FPM_OPCACHE_ENABLE="${PHP_FPM_OPCACHE_ENABLE:-0}" + export PHP_FPM_OPCACHE_VALIDATE_TIMESTAMPS="${PHP_FPM_OPCACHE_VALIDATE_TIMESTAMPS:-1}" + fi + + if [ ! -w /var/www/html/storage ]; then + echo "ERROR: /var/www/html/storage must be writable." + echo "Fix permissions on host/volume for the www-data user." + exit 1 + fi + + exec php-fpm +elif [ "$role" = "queue" ]; then + echo "Running the queue..." + while [ true ] + do + php /var/www/html/artisan queue:work --verbose --sleep=5 --tries=100 --backoff=10 --max-time=3600 --queue=high,normal,low,default + done +elif [ "$role" = "scheduler" ]; then + while [ true ] + do + php /var/www/html/artisan schedule:run --verbose --no-interaction & + sleep 60 + done +else + echo "Could not match the container role \"$role\"" + exit 1 +fi diff --git a/app/docker/start.sh b/app/docker/start.sh deleted file mode 100755 index 691e23a..0000000 --- a/app/docker/start.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env sh -set -e -role=${CONTAINER_ROLE:-app} -if [ "$role" = "app" ]; then - exec unitd --no-daemon --control unix:/var/run/control.unit.sock --user unit --group unit -elif [ "$role" = "queue" ]; then - echo "Running the queue..." - while [ true ] - do - php /var/www/html/artisan queue:work --verbose --sleep=5 --tries=100 --backoff=10 --max-time=3600 --queue=high,normal,low,default - done -elif [ "$role" = "scheduler" ]; then - while [ true ] - do - php /var/www/html/artisan schedule:run --verbose --no-interaction & - sleep 60 - done -else - echo "Could not match the container role \"$role\"" - exit 1 -fi diff --git a/app/docker/unit-config.json b/app/docker/unit-config.json deleted file mode 100644 index 1b73dff..0000000 --- a/app/docker/unit-config.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "listeners": { - "*:9000": { - "pass": "routes", - "forwarded": { - "client_ip": "X-Forwarded-For", - "recursive": false, - "source": [ - - ] - } - } - }, - - "routes": [ - { - "match": { - "uri": [ - "/index.php/", - "~^/index\\.php/.*", - "~\\.php$" - ] - }, - "action": { - "return": 404 - } - }, - { - "action": { - "share": "/var/www/html/public$uri", - "fallback": { - "pass": "applications/laravel" - } - } - } - ], - - "applications": { - "laravel": { - "type": "php", - "root": "/var/www/html/public", - "working_directory": "/var/www/html", - "user": "www-data", - "group": "www-data", - "script": "index.php", - "processes": { - "max": 10, - "spare": 5, - "idle_timeout": 20 - }, - "options": { - "file": "/usr/local/etc/php/php.ini", - "admin": { - "upload_tmp_dir": "/tmp/php/upload", - "sys_temp_dir": "/tmp/php/sys", - "session.save_path": "/tmp/php/session", - "open_basedir": "/var/www/html:/tmp/php:.", - "memory_limit": "256M", - "upload_max_filesize": "20M", - "post_max_size": "20M", - "expose_php": "0" - }, - "user": { - "display_errors": "0" - } - } - } - } -} diff --git a/app/docker/web/Dockerfile b/app/docker/web/Dockerfile new file mode 100644 index 0000000..8c55a71 --- /dev/null +++ b/app/docker/web/Dockerfile @@ -0,0 +1,31 @@ +FROM docker.io/nginx:1.31-alpine AS build + +COPY docker/web/conf/default.conf.template /etc/nginx/templates/default.conf.template +COPY docker/web/conf/fastcgi_params /etc/nginx/fastcgi_params +COPY docker/web/start.sh /usr/local/bin/start + +RUN chmod 755 /usr/local/bin/start + +FROM build AS app_build_for_production + +WORKDIR /home/app +COPY application /home/app + +RUN apk --no-cache add git nodejs npm \ + && npm install && npm run build \ + && rm -rf /home/app/node_modules + +FROM build AS production + +COPY --from=app_build_for_production /home/app/public /usr/share/nginx/html + +WORKDIR /usr/share/nginx/html + +EXPOSE 80 +CMD ["/usr/local/bin/start"] + +FROM build AS develop +WORKDIR /usr/share/nginx/html + +EXPOSE 80 +CMD ["/usr/local/bin/start"] diff --git a/app/docker/web/Dockerfile.dockerignore b/app/docker/web/Dockerfile.dockerignore new file mode 100644 index 0000000..1e5f2e7 --- /dev/null +++ b/app/docker/web/Dockerfile.dockerignore @@ -0,0 +1,3 @@ +application/public/*.php +application/public/**/*.php +application/public/.htaccess \ No newline at end of file diff --git a/app/docker/web/conf/default.conf.template b/app/docker/web/conf/default.conf.template new file mode 100644 index 0000000..5942f95 --- /dev/null +++ b/app/docker/web/conf/default.conf.template @@ -0,0 +1,44 @@ +map $FRONTEND_PORT $host_with_port { + "" $host; + default "$host:$FRONTEND_PORT"; +} + +server { + listen 80 default_server; + listen [::]:80 default_server; + + client_max_body_size ${NGINX_CLIENT_MAX_BODY_SIZE}; + + disable_symlinks if_not_owner from=/usr/share/nginx/html; + + index index.php index.html; + root /usr/share/nginx/html; + + ${GZIP_BLOCK} + ${REAL_IP_BLOCK} + + location / { + location / { + try_files $uri @fallback; + } + location ~ \.php$ { + return 404; + } + location ~ /\. { + return 404; + } + location ~ /\.(ht|svn|git) { + return 404; + } + } + + location @fallback { + fastcgi_pass ${BACKEND_HOST}:${BACKEND_PORT}; + fastcgi_index index.php; + fastcgi_param REDIRECT_STATUS 200; + fastcgi_param SCRIPT_FILENAME /var/www/html/public/index.php; + fastcgi_param SCRIPT_NAME /index.php; + fastcgi_param HTTP_HOST $host_with_port; + include /etc/nginx/fastcgi_params; + } +} \ No newline at end of file diff --git a/app/docker/web/conf/fastcgi_params b/app/docker/web/conf/fastcgi_params new file mode 100644 index 0000000..a0dffef --- /dev/null +++ b/app/docker/web/conf/fastcgi_params @@ -0,0 +1,23 @@ +fastcgi_param QUERY_STRING $query_string; +fastcgi_param REQUEST_METHOD $request_method; +fastcgi_param CONTENT_TYPE $content_type; +fastcgi_param CONTENT_LENGTH $content_length; + +fastcgi_param SCRIPT_NAME $fastcgi_script_name; +fastcgi_param REQUEST_URI $request_uri; +fastcgi_param DOCUMENT_URI $document_uri; +fastcgi_param DOCUMENT_ROOT $document_root; +fastcgi_param SERVER_PROTOCOL $server_protocol; +fastcgi_param HTTPS $https if_not_empty; + +fastcgi_param GATEWAY_INTERFACE CGI/1.1; +fastcgi_param SERVER_SOFTWARE nginx; + +fastcgi_param REMOTE_ADDR $remote_addr; +fastcgi_param REMOTE_PORT $remote_port; +fastcgi_param SERVER_ADDR $server_addr; +fastcgi_param SERVER_PORT $server_port; +fastcgi_param SERVER_NAME $server_name; + +fastcgi_param HTTP_X_FORWARDED_FOR $proxy_add_x_forwarded_for; +fastcgi_param HTTP_X_REAL_IP $remote_addr; diff --git a/app/docker/web/start.sh b/app/docker/web/start.sh new file mode 100644 index 0000000..5d08752 --- /dev/null +++ b/app/docker/web/start.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env sh + +if [ "$IS_GZIP" = "1" ]; then + GZIP_BLOCK='gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + gzip_min_length 1024;' +else + GZIP_BLOCK='' +fi +export GZIP_BLOCK + +if [ -n "$TRUSTED_PROXY_CIDR" ]; then + REAL_IP_BLOCK="set_real_ip_from $TRUSTED_PROXY_CIDR; + real_ip_header X-Forwarded-For; + real_ip_recursive on;" +else + REAL_IP_BLOCK="" +fi +export REAL_IP_BLOCK + +export BACKEND_HOST="${BACKEND_HOST:-app}" +export BACKEND_PORT="${BACKEND_PORT:-9000}" +export FRONTEND_PORT="${FRONTEND_PORT:-''}" +export NGINX_CLIENT_MAX_BODY_SIZE="${NGINX_CLIENT_MAX_BODY_SIZE:-'20m'}" + +envsubst '$BACKEND_HOST $BACKEND_PORT $GZIP_BLOCK $REAL_IP_BLOCK $FRONTEND_PORT $NGINX_CLIENT_MAX_BODY_SIZE' \ + < /etc/nginx/templates/default.conf.template \ + > /etc/nginx/conf.d/default.conf + +exec nginx -g 'daemon off;' diff --git a/docker-compose-prod.yml b/docker-compose-prod.yml index 575e94f..958f4df 100644 --- a/docker-compose-prod.yml +++ b/docker-compose-prod.yml @@ -1,30 +1,47 @@ #version: '3.7' services: + web: + build: + context: app + dockerfile: docker/web/Dockerfile + target: DEVELOP + depends_on: + - app + ports: + - ${DOCKER_WEB_PORT}:80 + environment: + IS_GZIP: 1 + # FRONTEND_PORT: ${DOCKER_WEB_PORT} + BACKEND_HOST: app + BACKEND_PORT: 9000 + TRUSTED_PROXY_CIDR: 172.16.0.0/12 + volumes: + - ./app/application/public:/usr/share/nginx/html + - ./app/application/storage/app/public:/usr/share/nginx/html/storage app: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: PRODUCTION # restart: always depends_on: - db - app-redis - captcha-app - ports: - - ${DOCKER_APP_PORT}:9000 volumes: - ./app/application:/var/www/html environment: CONTAINER_ROLE: app - UNIT_SOURCE: "172.16.0.0/12" + APP_ENV: production queue: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: PRODUCTION # restart: always depends_on: + - app - db - app-redis environment: @@ -35,10 +52,11 @@ services: scheduler: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: PRODUCTION # restart: always depends_on: + - app - db - app-redis environment: @@ -75,6 +93,7 @@ services: image: korelf/service-captcha:0.8.2 # restart: always depends_on: + - captcha-app - db - captcha-redis environment: @@ -84,6 +103,7 @@ services: image: korelf/service-captcha:0.8.2 # restart: always depends_on: + - captcha-app - db - captcha-redis environment: @@ -95,6 +115,7 @@ services: image: korelf/service-captcha:0.8.2 # restart: always depends_on: + - captcha-app - db - captcha-redis environment: diff --git a/docker-compose.yml b/docker-compose.yml index 570c3f2..b756861 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,28 +1,46 @@ #version: '3.7' services: + web: + build: + context: app + dockerfile: docker/web/Dockerfile + target: DEVELOP + depends_on: + - app + ports: + - ${DOCKER_WEB_PORT}:80 + environment: + IS_GZIP: 0 + FRONTEND_PORT: ${DOCKER_WEB_PORT} + BACKEND_HOST: app + BACKEND_PORT: 9000 +# TRUSTED_PROXY_CIDR: 172.16.0.0/12 + volumes: + - ./app/application/public:/usr/share/nginx/html + - ./app/application/storage/app/public:/usr/share/nginx/html/storage app: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: DEVELOP depends_on: - db - app-redis - captcha-app - ports: - - ${DOCKER_APP_PORT}:9000 volumes: - ./app/application:/var/www/html environment: CONTAINER_ROLE: app + APP_ENV: local queue: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: DEVELOP depends_on: - db + - app - app-redis environment: CONTAINER_ROLE: queue @@ -32,10 +50,11 @@ services: scheduler: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: DEVELOP depends_on: - db + - app - app-redis environment: CONTAINER_ROLE: scheduler @@ -69,6 +88,7 @@ services: image: korelf/service-captcha:0.8.2 # restart: always depends_on: + - captcha-app - db - captcha-redis environment: @@ -81,6 +101,7 @@ services: image: korelf/service-captcha:0.8.2 # restart: always depends_on: + - captcha-app - db - captcha-redis environment: @@ -95,6 +116,7 @@ services: image: korelf/service-captcha:0.8.2 # restart: always depends_on: + - captcha-app - db - captcha-redis environment: @@ -124,7 +146,7 @@ services: artisan: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: ARTISAN user: "${UID}:${GID}" volumes: @@ -133,7 +155,7 @@ services: composer: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: COMPOSER user: "${UID}:${GID}" volumes: @@ -142,7 +164,7 @@ services: npm: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: NPM user: "${UID}:${GID}" volumes: