From b5982b79074b02295ec957a6056a9fbb526b1f3f Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 24 Oct 2026 17:34:45 +0500 Subject: [PATCH] Migrated app to separate Docker services and improved development/production setup. Updated configurations, dependencies, and environment variables. --- .env.example | 6 +- app/.dockerignore | 15 +++ app/docker/Dockerfile | 129 +++++++++++++++++++++ app/docker/php/conf/app-fpm.conf | 28 +++++ app/docker/php/start.sh | 62 ++++++++++ app/docker/web/Dockerfile.dockerignore | 3 + app/docker/web/conf/default.conf.template | 44 ++++++++ app/docker/web/conf/fastcgi_params | 22 ++++ app/docker/web/start.sh | 35 ++++++ docker-compose.yml | 132 +++++++++++++++++----- docker-compose_prod.yml | 124 ++++++++++++-------- docker/app/.dockerignore | 6 - docker/app/Dockerfile | 94 --------------- docker/app/docker-entrypoint_dev.sh | 103 ----------------- docker/app/docker-entrypoint_prod.sh | 111 ------------------ docker/app/unit-config.json | 69 ----------- docker/nginx-captcha/Dockerfile | 3 - docker/nginx-captcha/nginx.conf | 35 ------ 18 files changed, 522 insertions(+), 499 deletions(-) create mode 100644 app/.dockerignore create mode 100644 app/docker/Dockerfile create mode 100644 app/docker/php/conf/app-fpm.conf create mode 100755 app/docker/php/start.sh 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 delete mode 100644 docker/app/.dockerignore delete mode 100644 docker/app/Dockerfile delete mode 100644 docker/app/docker-entrypoint_dev.sh delete mode 100644 docker/app/docker-entrypoint_prod.sh delete mode 100644 docker/app/unit-config.json delete mode 100644 docker/nginx-captcha/Dockerfile delete mode 100644 docker/nginx-captcha/nginx.conf diff --git a/.env.example b/.env.example index a7f2fd4..995849d 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,9 @@ -DOCKER_APP_PORT=8080 -DOCKER_CAPTCHA_NGINX_PORT=8081 +DOCKER_WEB_PORT=8080 +DOCKER_CAPTCHA_PORT=8081 DOCKER_WEBSOCKET_PORT=8082 MYSQL_ROOT_PASSWORD=root_pass DB_DATABASE=capcha DB_USERNAME=capcha DB_PASSWORD=capcha_pass +UID=1000 +GID=1000 \ No newline at end of file diff --git a/app/.dockerignore b/app/.dockerignore new file mode 100644 index 0000000..0542eae --- /dev/null +++ b/app/.dockerignore @@ -0,0 +1,15 @@ +**/.env +**/*.env +**/.env.example +**/storage/app/* +**/storage/debugbar +**/storage/framework/cache/* +**/storage/framework/sessions/* +**/storage/framework/views/* +**/storage/framework/testing/* +**/storage/translation_service/* +**/storage/logs/* +**/vendor/ +**/node_modules/ +**/public/build/ +**/public/storage diff --git a/app/docker/Dockerfile b/app/docker/Dockerfile new file mode 100644 index 0000000..1c771fa --- /dev/null +++ b/app/docker/Dockerfile @@ -0,0 +1,129 @@ +FROM docker.io/php:8.5-fpm-alpine3.23 AS php + +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} \ + && docker-php-ext-configure intl --enable-intl && \ + docker-php-ext-configure bcmath --enable-bcmath && \ + docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp && \ + docker-php-ext-install -j$(nproc) gd && \ + docker-php-ext-install bcmath &&\ + docker-php-ext-install pdo \ + mysqli pdo_mysql \ + intl mbstring \ + zip pcntl \ + exif bz2 \ + calendar \ + && pear update-channels && pecl update-channels \ + && pecl install redis && docker-php-ext-enable redis \ + && rm -rf /tmp/pear \ + && docker-php-source delete \ + && apk del .phpize-deps \ + && rm -rf /var/cache/apk/* && rm -rf /etc/apk/cache \ + && rm -rf /usr/share/php && rm -rf /tmp/* \ + && rm "$PHP_INI_DIR/php.ini-development" \ + && mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \ + && mkdir -p /tmp/php/upload \ + && mkdir -p /tmp/php/sys \ + && mkdir -p /tmp/php/session \ + && 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 docker.io/nginx:1.31-alpine AS nginx + +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 nginx AS web_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 php AS app_build_for_production +WORKDIR /home/app + +COPY application /home/app + +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 \ + && rm -f /home/app/.env + +FROM php AS app_production + +COPY --from=app_build_for_production /home/app /var/www/html +COPY --from=web_build_for_production /home/app/public/build/ /var/www/html/public/build +COPY docker/php/start.sh /usr/local/bin/start + +WORKDIR /var/www/html + +RUN chown -R www-data:www-data /var/www/html \ + && chmod 755 /usr/local/bin/start + +STOPSIGNAL SIGTERM + +USER www-data + +EXPOSE 9000 +CMD ["/usr/local/bin/start"] + +FROM nginx AS web_production + +COPY --from=web_build_for_production /home/app/public /usr/share/nginx/html +RUN rm -f /usr/share/nginx/html/50x.html /usr/share/nginx/html/index.php /usr/share/nginx/html/.htaccess + +WORKDIR /usr/share/nginx/html + +EXPOSE 80 +CMD ["/usr/local/bin/start"] + +# develop +FROM php AS app_develop + +WORKDIR /var/www/html + +COPY docker/php/start.sh /usr/local/bin/start + +STOPSIGNAL SIGTERM + +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 + +USER www-data + +EXPOSE 9000 +CMD ["/usr/local/bin/start"] + +FROM php AS artisan +WORKDIR /var/www/html +STOPSIGNAL SIGTERM +ENTRYPOINT ["php", "/var/www/html/artisan"] + +FROM php AS composer +WORKDIR /var/www/html +STOPSIGNAL SIGTERM +RUN apk --no-cache add git \ + && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer +RUN mkdir "/.composer" && chmod -R 0777 "/.composer" +ENTRYPOINT ["composer"] + +FROM php AS npm +RUN mkdir "/.npm" && chmod -R 0777 "/.npm" +WORKDIR /var/www/html +STOPSIGNAL SIGTERM +RUN apk --no-cache add nodejs npm +ENTRYPOINT ["npm"] + +FROM nginx AS web_develop +WORKDIR /usr/share/nginx/html + +EXPOSE 80 +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..15097b9 --- /dev/null +++ b/app/docker/php/conf/app-fpm.conf @@ -0,0 +1,28 @@ +[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} +php_admin_value[session.upload_progress.enabled] = ${PHP_FPM_SESSION_UPLOAD_PROGRESS_ENABLED} +php_admin_value[fatal_error_backtraces] = ${PHP_FPM_FATAL_ERROR_BACKTRACES} + +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..070fd5d --- /dev/null +++ b/app/docker/php/start.sh @@ -0,0 +1,62 @@ +#!/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}" + + export PHP_FPM_FATAL_ERROR_BACKTRACES="${PHP_FPM_FATAL_ERROR_BACKTRACES:-0}" + export PHP_FPM_SESSION_UPLOAD_PROGRESS_ENABLED="${PHP_FPM_SESSION_UPLOAD_PROGRESS_ENABLED:-0}" + + 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/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..19660a4 --- /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.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..0baa7ca --- /dev/null +++ b/app/docker/web/conf/fastcgi_params @@ -0,0 +1,22 @@ +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 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.yml b/docker-compose.yml index 0b2b24e..f9454e6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,71 +1,117 @@ -version: '3.7' +#version: '3.7' services: + web: + build: + context: app + dockerfile: docker/Dockerfile + target: WEB_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: . - dockerfile: ./docker/app/Dockerfile - target: DEVELOP + context: app + dockerfile: docker/Dockerfile + target: APP_DEVELOP depends_on: - - captcha-nginx - ports: - - ${DOCKER_APP_PORT}:9000 + - captcha-web volumes: - - ./app:/var/www/html - captcha-nginx: - build: - context: ./docker/nginx-captcha - dockerfile: Dockerfile + - ./app/application:/var/www/html + environment: + CONTAINER_ROLE: app + APP_ENV: local + + captcha-web: + # image: korelf/service-captcha-web:0.9.0 # docker hub + image: docker.mdhub.kor-elf.net/kor-elf/service-captcha-web:0.9.0 # MDHub + # restart: always depends_on: - captcha-app - - captcha-swagger ports: - - ${DOCKER_CAPTCHA_NGINX_PORT}:80 + - ${DOCKER_CAPTCHA_PORT}:80 + environment: + IS_GZIP: 0 + FRONTEND_PORT: ${DOCKER_CAPTCHA_PORT} + BACKEND_HOST: captcha-app + BACKEND_PORT: 9000 + TRUSTED_PROXY_CIDR: 172.16.0.0/12 + # Enable swagger + ENABLE_API_DOCS: 1 + SWAGGER_HOST: captcha-swagger + SWAGGER_PORT: 8080 + volumes: + - ./captcha-app/app/storage/app/public:/usr/share/nginx/html/storage + captcha-app: - image: korelf/service-captcha:0.8.2 - cap_drop: - - ALL - cap_add: - - SETGID - - SETUID - - CHOWN - - FOWNER - ports: - - "9000" + # image: korelf/service-captcha-app:0.9.0 # docker hub + image: docker.mdhub.kor-elf.net/kor-elf/service-captcha-app:0.9.0 # MDHub + # restart: always depends_on: - - captcha-redis - db + - captcha-redis + - captcha-reverb env_file: docker/captcha-app/.env + environment: + CONTAINER_ROLE: app + APP_ENV: production + volumes: + - ./docker/captcha-app/app/storage/app:/var/www/html/storage/app + - ./docker/captcha-app/app/storage/logs:/var/www/html/storage/logs captcha-queue: - image: korelf/service-captcha:0.8.2 + # image: korelf/service-captcha-app:0.9.0 # docker hub + image: docker.mdhub.kor-elf.net/kor-elf/service-captcha-app:0.9.0 # MDHub # restart: always depends_on: - db - captcha-redis environment: CONTAINER_ROLE: queue + APP_ENV: production + volumes: + - ./docker/captcha-app/app/storage/app:/var/www/html/storage/app + - ./docker/captcha-app/app/storage/logs:/var/www/html/storage/logs env_file: docker/captcha-app/.env captcha-reverb: - image: korelf/service-captcha:0.8.2 + # image: korelf/service-captcha-app:0.9.0 # docker hub + image: docker.mdhub.kor-elf.net/kor-elf/service-captcha-app:0.9.0 # MDHub # restart: always depends_on: - db - captcha-redis environment: CONTAINER_ROLE: websockets + APP_ENV: production env_file: docker/captcha-app/.env ports: - ${DOCKER_WEBSOCKET_PORT}:9000 + volumes: + - ./docker/captcha-app/app/storage/app:/var/www/html/storage/app + - ./docker/captcha-app/app/storage/logs:/var/www/html/storage/logs captcha-scheduler: - image: korelf/service-captcha:0.8.2 + # image: korelf/service-captcha-app:0.9.0 # docker hub + image: docker.mdhub.kor-elf.net/kor-elf/service-captcha-app:0.9.0 # MDHub # restart: always depends_on: - db - captcha-redis environment: CONTAINER_ROLE: scheduler + APP_ENV: production env_file: docker/captcha-app/.env - + volumes: + - ./docker/captcha-app/app/storage/app:/var/www/html/storage/app + - ./docker/captcha-app/app/storage/logs:/var/www/html/storage/logs captcha-swagger: image: swaggerapi/swagger-ui environment: @@ -87,4 +133,30 @@ services: MYSQL_USER: ${DB_USERNAME} MYSQL_PASSWORD: ${DB_PASSWORD} volumes: - - ./docker/db/data:/var/lib/mysql \ No newline at end of file + - ./docker/db/data:/var/lib/mysql + artisan: + build: + context: app + dockerfile: docker/Dockerfile + target: ARTISAN + user: "${UID}:${GID}" + volumes: + - ./app/application:/var/www/html + + composer: + build: + context: app + dockerfile: docker/Dockerfile + target: COMPOSER + user: "${UID}:${GID}" + volumes: + - ./app/application:/var/www/html + + npm: + build: + context: app + dockerfile: docker/Dockerfile + target: NPM + user: "${UID}:${GID}" + volumes: + - ./app/application:/var/www/html diff --git a/docker-compose_prod.yml b/docker-compose_prod.yml index 78fe05b..3f6e239 100644 --- a/docker-compose_prod.yml +++ b/docker-compose_prod.yml @@ -1,87 +1,119 @@ -version: '3.7' +#version: '3.7' services: + web: + build: + context: app + dockerfile: docker/Dockerfile + target: WEB_PRODUCTION + # restart: always + 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: . - dockerfile: ./docker/app/Dockerfile - target: PRODUCTION - depends_on: - - captcha-nginx - cap_drop: - - ALL - cap_add: - - SETGID - - SETUID - - CHOWN - - FOWNER - ports: - - ${DOCKER_APP_PORT}:9000 - captcha-nginx: - build: - context: ./docker/nginx-captcha - dockerfile: Dockerfile + context: app + dockerfile: docker/Dockerfile + target: APP_PRODUCTION + # restart: always depends_on: - captcha-app - - captcha-swagger - ports: - - ${DOCKER_CAPTCHA_NGINX_PORT}:80 - captcha-app: - image: korelf/service-captcha:0.8.2 - cap_drop: - - ALL - cap_add: - - SETGID - - SETUID - - CHOWN - - FOWNER - ports: - - "9000" + volumes: + - ./app/application:/var/www/html + environment: + CONTAINER_ROLE: app + APP_ENV: production + + captcha-web: + # image: korelf/service-captcha-web:0.9.0 # docker hub + image: docker.mdhub.kor-elf.net/kor-elf/service-captcha-web:0.9.0 # MDHub + # restart: always depends_on: + - captcha-app + ports: + - ${DOCKER_CAPTCHA_PORT}:80 + environment: + IS_GZIP: 0 + FRONTEND_PORT: ${DOCKER_CAPTCHA_PORT} + BACKEND_HOST: captcha-app + BACKEND_PORT: 9000 + TRUSTED_PROXY_CIDR: 172.16.0.0/12 + # Enable swagger + ENABLE_API_DOCS: 1 + SWAGGER_HOST: captcha-swagger + SWAGGER_PORT: 8080 + volumes: + - ./captcha-app/app/storage/app/public:/usr/share/nginx/html/storage + + captcha-app: + # image: korelf/service-captcha-app:0.9.0 # docker hub + image: docker.mdhub.kor-elf.net/kor-elf/service-captcha-app:0.9.0 # MDHub + # restart: always + depends_on: + - db - captcha-redis - captcha-reverb - - db env_file: docker/captcha-app/.env environment: CONTAINER_ROLE: app - UNIT_SOURCE: '"172.16.0.0/12"' + APP_ENV: production + volumes: + - ./docker/captcha-app/app/storage/app:/var/www/html/storage/app + - ./docker/captcha-app/app/storage/logs:/var/www/html/storage/logs captcha-queue: - image: korelf/service-captcha:0.8.2 + # image: korelf/service-captcha-app:0.9.0 # docker hub + image: docker.mdhub.kor-elf.net/kor-elf/service-captcha-app:0.9.0 # MDHub # restart: always depends_on: - db - captcha-redis environment: CONTAINER_ROLE: queue + APP_ENV: production + volumes: + - ./docker/captcha-app/app/storage/app:/var/www/html/storage/app + - ./docker/captcha-app/app/storage/logs:/var/www/html/storage/logs env_file: docker/captcha-app/.env captcha-reverb: - image: korelf/service-captcha:0.8.2 + # image: korelf/service-captcha-app:0.9.0 # docker hub + image: docker.mdhub.kor-elf.net/kor-elf/service-captcha-app:0.9.0 # MDHub # restart: always depends_on: - db - captcha-redis environment: CONTAINER_ROLE: websockets + APP_ENV: production env_file: docker/captcha-app/.env ports: - ${DOCKER_WEBSOCKET_PORT}:9000 + volumes: + - ./docker/captcha-app/app/storage/app:/var/www/html/storage/app + - ./docker/captcha-app/app/storage/logs:/var/www/html/storage/logs captcha-scheduler: - image: korelf/service-captcha:0.8.2 + # image: korelf/service-captcha-app:0.9.0 # docker hub + image: docker.mdhub.kor-elf.net/kor-elf/service-captcha-app:0.9.0 # MDHub # restart: always depends_on: - db - captcha-redis environment: CONTAINER_ROLE: scheduler + APP_ENV: production env_file: docker/captcha-app/.env - - captcha-swagger: - image: swaggerapi/swagger-ui - environment: - URLS: "[ { url: '/swagger.json', name: '/swagger.json' } ]" - BASE_URL: /api-docs - ports: - - "8080" + volumes: + - ./docker/captcha-app/app/storage/app:/var/www/html/storage/app + - ./docker/captcha-app/app/storage/logs:/var/www/html/storage/logs captcha-redis: image: redis:3.0-alpine db: diff --git a/docker/app/.dockerignore b/docker/app/.dockerignore deleted file mode 100644 index 996b0fd..0000000 --- a/docker/app/.dockerignore +++ /dev/null @@ -1,6 +0,0 @@ -**/*.env -**/*.env.example -app/bootstrap/cache/* -app/storage/** -app/vendor/** -app/node_modules/** diff --git a/docker/app/Dockerfile b/docker/app/Dockerfile deleted file mode 100644 index 2c444a7..0000000 --- a/docker/app/Dockerfile +++ /dev/null @@ -1,94 +0,0 @@ -FROM docker.io/php:8.2-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.2-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/app/unit-config.json /docker-entrypoint.d/config.json - -RUN apk --no-cache add pcre2 libbz2 libpng libwebp libjpeg-turbo icu-libs freetype oniguruma libzip \ - && 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} \ - && docker-php-ext-configure intl --enable-intl && \ - docker-php-ext-configure bcmath --enable-bcmath && \ - docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp && \ - docker-php-ext-install -j$(nproc) gd && \ - docker-php-ext-install bcmath &&\ - docker-php-ext-install pdo \ - mysqli pdo_mysql \ - intl mbstring \ - zip pcntl \ - exif opcache bz2 \ - calendar \ - && pear update-channels && pecl update-channels \ - && pecl install redis && docker-php-ext-enable redis \ - && rm -rf /tmp/pear \ - && docker-php-source delete \ - && apk del .phpize-deps \ - && rm -rf /var/cache/apk/* && rm -rf /etc/apk/cache \ - && rm -rf /usr/share/php && rm -rf /tmp/* \ - && rm "$PHP_INI_DIR/php.ini-development" \ - && mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \ - && 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 - -FROM BUILD as APP_BUILD_FOR_PRODUCTION -WORKDIR /home/app - -COPY app /home/app - -RUN apk --no-cache add git nodejs npm \ - && 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 - -# -FROM BUILD AS PRODUCTION - -COPY --from=APP_BUILD_FOR_PRODUCTION /home/app /var/www/html -COPY docker/app/docker-entrypoint_prod.sh /home/unit/docker-entrypoint.sh - -WORKDIR /var/www/html - -RUN chmod 755 /home/unit/docker-entrypoint.sh - -STOPSIGNAL SIGTERM - -ENTRYPOINT ["/home/unit/docker-entrypoint.sh"] -EXPOSE 9000 -CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock", "--user", "unit", "--group", "unit"] - - -# -FROM BUILD AS DEVELOP - -WORKDIR /var/www/html - -COPY docker/app/docker-entrypoint_dev.sh /home/unit/docker-entrypoint.sh - -STOPSIGNAL SIGTERM - -RUN chmod 755 /home/unit/docker-entrypoint.sh \ - && 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"] -EXPOSE 9000 -CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock", "--user", "unit", "--group", "unit"] diff --git a/docker/app/docker-entrypoint_dev.sh b/docker/app/docker-entrypoint_dev.sh deleted file mode 100644 index 5a3ecc3..0000000 --- a/docker/app/docker-entrypoint_dev.sh +++ /dev/null @@ -1,103 +0,0 @@ -#!/bin/sh - -set -euo pipefail - -WAITLOOPS=5 -SLEEPSEC=1 -unitd="unitd" - -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 [ "$unitd" = "unitd" ] || [ "$unitd" = "unitd-debug" ]; 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..." - - 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 - - if [ ! -z ${UNIT_SOURCE+x} ] - then - echo "[${UNIT_SOURCE}]" > /docker-entrypoint.d/unit_source.json - curl_put "/docker-entrypoint.d/unit_source.json" "config/listeners/*:9000/forwarded/source" - fi - - 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/docker/app/docker-entrypoint_prod.sh b/docker/app/docker-entrypoint_prod.sh deleted file mode 100644 index 63f1c27..0000000 --- a/docker/app/docker-entrypoint_prod.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/sh - -set -euo pipefail - -WAITLOOPS=5 -SLEEPSEC=1 -unitd="unitd" - -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 [ "$unitd" = "unitd" ] || [ "$unitd" = "unitd-debug" ]; 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..." - - 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 - - if [ ! -z ${UNIT_SOURCE+x} ] - then - echo "[${UNIT_SOURCE}]" > /docker-entrypoint.d/unit_source.json - curl_put "/docker-entrypoint.d/unit_source.json" "config/listeners/*:9000/forwarded/source" - fi - - 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 migrate --force - -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/docker/app/unit-config.json b/docker/app/unit-config.json deleted file mode 100644 index 1b73dff..0000000 --- a/docker/app/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/docker/nginx-captcha/Dockerfile b/docker/nginx-captcha/Dockerfile deleted file mode 100644 index 515243a..0000000 --- a/docker/nginx-captcha/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM nginx:alpine3.18-slim - -COPY nginx.conf /etc/nginx/conf.d/default.conf diff --git a/docker/nginx-captcha/nginx.conf b/docker/nginx-captcha/nginx.conf deleted file mode 100644 index 8f94fe8..0000000 --- a/docker/nginx-captcha/nginx.conf +++ /dev/null @@ -1,35 +0,0 @@ -server { - listen 80 default_server; - listen [::]:80 default_server; - - client_max_body_size 1024M; - - root /var/www/html/public; - - location / { - location /api-docs { - proxy_pass http://captcha-swagger:8080; - proxy_set_header Host $http_host; - proxy_set_header X-Forwarded-Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - proxy_redirect off; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - } - location / { - proxy_pass http://captcha-app:9000; - proxy_set_header Host $http_host; - proxy_set_header X-Forwarded-Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - proxy_redirect off; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - } - } -}