From b8668f2c6c5970fbee23bca4bb3bfde48cab1c8d Mon Sep 17 00:00:00 2001 From: striker Date: Wed, 6 May 2026 22:20:23 +0300 Subject: [PATCH] =?UTF-8?q?feat(docker):=20containerize=20sag24.ru=20?= =?UTF-8?q?=E2=80=94=20multi-stage=20Next.js=20build=20+=20nginx/php-fpm?= =?UTF-8?q?=20runtime?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Single-container approach (nginx + php-fpm 8.3 + supervisord) to match web.hhivp.com deployment paradigm (host nginx → docker по порту). - Dockerfile: builder (node:22-alpine npm ci+next build) → runtime (php:8.3-fpm-alpine) - docker/nginx.conf: serves static from /var/www/sag24.ru/public_html, fastcgi_pass на 127.0.0.1:9000 - docker/supervisord.conf: запускает php-fpm + nginx - docker-compose.yml: для деплоя на web в /opt/docker/sites/sag24-ru/, mount contact-config.php read-only - .dockerignore: исключает node_modules/.git/out из контекста Container exposes :8080 (бесроутовый bind), маппится на host :4070 на web. Watchtower label включён (auto-deploy на изменения образа в registry). --- .dockerignore | 17 +++++++++++ .gitignore | 4 +++ Dockerfile | 59 +++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 33 ++++++++++++++++++++++ docker/nginx.conf | 62 +++++++++++++++++++++++++++++++++++++++++ docker/supervisord.conf | 24 ++++++++++++++++ 6 files changed, 199 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 docker/nginx.conf create mode 100644 docker/supervisord.conf diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..fb8d2a6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,17 @@ +node_modules +.next +out +dist +public_html +.git +.gitignore +.gitattributes +.vscode +.idea +.DS_Store +*.log +docker-compose.override.yml +README.md +CLAUDE.md +.env +.env.* diff --git a/.gitignore b/.gitignore index 6857fe2..938b59f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ out/ .next/ .DS_Store *.local + +# Docker deploy artifacts (на сервере) +secrets/ +docker-compose.override.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b37c9cc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,59 @@ +# syntax=docker/dockerfile:1.7 + +# ─── Stage 1: build static export (Next.js 15) ─────────────────────────────── +FROM node:22-alpine AS builder + +WORKDIR /app + +COPY package.json package-lock.json ./ +RUN npm ci --no-audit --no-fund + +COPY . . +# distDir defaults to "out" (see next.config.ts), не используем BUILD_DIR. +RUN npm run build + +# ─── Stage 2: runtime — nginx + php-fpm 8.3 ────────────────────────────────── +FROM php:8.3-fpm-alpine AS runtime + +RUN apk add --no-cache \ + nginx \ + supervisor \ + tzdata \ + ca-certificates \ + && rm -rf /var/cache/apk/* + +# PHP runtime tweaks: trust proxy headers, нормальные лимиты. +RUN { \ + echo 'expose_php = Off'; \ + echo 'max_execution_time = 30'; \ + echo 'post_max_size = 8M'; \ + echo 'upload_max_filesize = 8M'; \ + echo 'date.timezone = Europe/Moscow'; \ + } > /usr/local/etc/php/conf.d/zz-sag24.ini + +# Listen на TCP, чтобы nginx внутри контейнера мог дойти. (Default www pool listens on 9000.) +RUN sed -ri 's|^;?listen = .*|listen = 127.0.0.1:9000|' /usr/local/etc/php-fpm.d/www.conf \ + && sed -ri 's|^;?clear_env = .*|clear_env = no|' /usr/local/etc/php-fpm.d/www.conf + +# nginx + supervisor configs +COPY docker/nginx.conf /etc/nginx/nginx.conf +COPY docker/supervisord.conf /etc/supervisord.conf + +# Web root +RUN mkdir -p /var/www/sag24.ru/public_html \ + && mkdir -p /var/log/supervisor /var/log/nginx /run/nginx + +COPY --from=builder /app/out/ /var/www/sag24.ru/public_html/ + +# Pre-create temp dir for PHP rate limiter (sys_get_temp_dir = /tmp by default). +RUN chmod 1777 /tmp + +# php-fpm + nginx как непривилегированный (php-fpm уже www-data). +# Порт 8080 чтобы не требовать root для bind. +EXPOSE 8080 + +# Healthcheck — статика отдаётся. +HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ + CMD wget -qO- http://127.0.0.1:8080/ru/ >/dev/null || exit 1 + +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b5b9c25 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,33 @@ +services: + sag24-ru: + build: + context: . + dockerfile: Dockerfile + image: sag24-ru:latest + container_name: sag24-ru + restart: unless-stopped + ports: + - "127.0.0.1:4070:8080" + volumes: + # Секреты для контактной формы — вне образа, монтируется с хоста. + # На web путь: /opt/docker/sites/sag24-ru/secrets/contact-config.php + # contact.php ожидает файл по пути dirname(__DIR__, 2) == /var/www/sag24.ru + - ./secrets/contact-config.php:/var/www/sag24.ru/contact-config.php:ro + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/ru/"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s + +# Деплой на web (45.10.53.206): +# - /opt/docker/sites/sag24-ru/ ← git clone сюда +# ├── docker-compose.yml, Dockerfile, docker/, src/, ... (репо целиком) +# └── secrets/ +# └── contact-config.php (вручную, не в git, .gitignored) +# +# Команды: +# cd /opt/docker/sites/sag24-ru +# git pull +# docker compose build +# docker compose up -d diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 0000000..f884e19 --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,62 @@ +worker_processes auto; +error_log /dev/stderr warn; +pid /run/nginx/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + sendfile on; + tcp_nopush on; + keepalive_timeout 30; + server_tokens off; + + # Доверять X-Forwarded-For от host nginx на web (loopback proxy). + set_real_ip_from 127.0.0.1/32; + set_real_ip_from 172.16.0.0/12; + real_ip_header X-Forwarded-For; + real_ip_recursive on; + + log_format main '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"$http_referer" "$http_user_agent"'; + access_log /dev/stdout main; + + gzip on; + gzip_types text/plain text/css application/javascript application/json image/svg+xml; + gzip_min_length 1024; + gzip_vary on; + + server { + listen 8080 default_server; + server_name _; + + root /var/www/sag24.ru/public_html; + index index.html; + + # Старые URL без локали → /ru/ + rewrite ^/uslugi(/.*)?$ /ru/uslugi$1 redirect; + + location ~ ^/api/.*\.php$ { + try_files $uri =404; + fastcgi_pass 127.0.0.1:9000; + fastcgi_index contact.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } + + location = /robots.txt { + log_not_found off; + access_log off; + } + + error_page 404 /404.html; + location / { + try_files $uri $uri/ $uri/index.html =404; + } + } +} diff --git a/docker/supervisord.conf b/docker/supervisord.conf new file mode 100644 index 0000000..9ec69e6 --- /dev/null +++ b/docker/supervisord.conf @@ -0,0 +1,24 @@ +[supervisord] +nodaemon=true +logfile=/dev/stdout +logfile_maxbytes=0 +pidfile=/run/supervisord.pid +user=root + +[program:php-fpm] +command=/usr/local/sbin/php-fpm --nodaemonize +autorestart=true +priority=10 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:nginx] +command=/usr/sbin/nginx -g "daemon off;" +autorestart=true +priority=20 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0