From 8d1473935a8f3931bc3fb494502127c094ad579f Mon Sep 17 00:00:00 2001 From: 1xDed Date: Tue, 28 Oct 2025 01:39:22 +0300 Subject: [PATCH] Initial commit --- .gitignore | 10 ++ README.md | 108 +++++++++++++++++++++ api-v1/Dockerfile | 62 ++++++++++++ api-v1/config/php-fpm.conf | 4 + api-v1/config/www.conf | 28 ++++++ api-v1/src/index.php | 44 +++++++++ api-v2/Dockerfile | 56 +++++++++++ api-v2/config/php-fpm.conf | 4 + api-v2/config/www.conf | 27 ++++++ api-v2/src/index.php | 45 +++++++++ database/init/01_init.sql | 6 ++ docker-compose.yml | 185 ++++++++++++++++++++++++++++++++++++ docs/README.md | 18 ++++ docs/deployment.md | 23 +++++ docs/env-examples.md | 30 ++++++ docs/gitea.md | 28 ++++++ docs/monitoring.md | 24 +++++ docs/overview.md | 16 ++++ docs/troubleshooting.md | 17 ++++ monitoring/alert.rules.yml | 11 +++ monitoring/alertmanager.yml | 15 +++ monitoring/prometheus.yml | 28 ++++++ nginx/nginx.conf | 81 ++++++++++++++++ 23 files changed, 870 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 api-v1/Dockerfile create mode 100644 api-v1/config/php-fpm.conf create mode 100644 api-v1/config/www.conf create mode 100644 api-v1/src/index.php create mode 100644 api-v2/Dockerfile create mode 100644 api-v2/config/php-fpm.conf create mode 100644 api-v2/config/www.conf create mode 100644 api-v2/src/index.php create mode 100644 database/init/01_init.sql create mode 100644 docker-compose.yml create mode 100644 docs/README.md create mode 100644 docs/deployment.md create mode 100644 docs/env-examples.md create mode 100644 docs/gitea.md create mode 100644 docs/monitoring.md create mode 100644 docs/overview.md create mode 100644 docs/troubleshooting.md create mode 100644 monitoring/alert.rules.yml create mode 100644 monitoring/alertmanager.yml create mode 100644 monitoring/prometheus.yml create mode 100644 nginx/nginx.conf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ddd8d0f --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Ignore local environment files +.env* + +# Ignore logs +logs/ + +# Common OS/editor junk +.DS_Store +Thumbs.db +*.swp \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f069843 --- /dev/null +++ b/README.md @@ -0,0 +1,108 @@ +# Nginx + PHP-FPM (API v1/v2) + PostgreSQL + Memcached + +Проект разворачивает два API (v1 и v2) на отдельных контейнерах PHP-FPM, фронтирует их через Nginx и подключает PostgreSQL и Memcached. Конфигурация рассчитана на локальную разработку и может быть адаптирована для staging/production. + +## Возможности +- Два независимых API: `/api/v1/` и `/api/v2/`. +- Nginx проксирует запросы к соответствующим пулам PHP-FPM. +- Проверки соединений с PostgreSQL и Memcached в каждом API. +- Разделённые логи для Nginx и каждого PHP-FPM. +- Переменные окружения вынесены в `.env` файлы (безопаснее, чем хранить пароли в compose). + +## Требования +- Установленный Docker Desktop (Windows/macOS) или Docker Engine (Linux). +- Docker Compose v2 (входит в Docker Desktop). + +## Быстрый запуск (локально) +1. Открой папку проекта: `c:\docker\nginxphp\server\project`. +2. Убедись, что созданы и заполнены файлы: + - `.env.v1` — переменные для API v1 + - `.env.v2` — переменные для API v2 + - `.env.db` — переменные для PostgreSQL + Пример значений уже положен в репозиторий (локальные пароли можно заменить). +3. Запусти: + ```powershell + docker compose up -d --build + ``` +4. Проверь эндпоинты: + - `http://localhost/api/v1/` + - `http://localhost/api/v2/` + Оба вернут JSON со статусами PostgreSQL и Memcached, версией API и PHP. + +## Структура +- `docker-compose.yml` — оркестрация контейнеров. +- `nginx/nginx.conf` — маршрутизация `/api/v1` и `/api/v2` к нужным upstream (PHP-FPM v1/v2). +- `api-v1/` — Dockerfile, конфиги PHP-FPM (`config`), код (`index.php`). +- `api-v2/` — Dockerfile, конфиги PHP-FPM (`config`), код (`index.php`). +- `database/init/01_init.sql` — создаёт БД `api_v1` и `api_v2`, выдаёт права `api_user`. +- `logs/nginx`, `logs/php-v1`, `logs/php-v2` — каталоги для логов. +- `.env.v1`, `.env.v2`, `.env.db` — переменные окружения для сервисов. +- `.gitignore` — исключает `.env*` и `logs/` из VCS. + +## Развёртывание: подробная инструкция +### Локальная среда +- Заполни `.env.v1`, `.env.v2`, `.env.db` корректными значениями (БД, пользователи, пароли). +- Запусти: `docker compose up -d --build`. +- Просмотри логи при необходимости: + - `docker compose logs -f nginx` + - `docker compose logs -f php-fpm-v1` + - `docker compose logs -f php-fpm-v2` + - `docker compose logs -f postgres` +- Остановить и очистить: + ```powershell + docker compose down -v + ``` + Команда удалит контейнеры и именованный том `postgres_data`. + +### Staging/Production рекомендации +- Секреты: не хранить пароли в `.env` на диске. Используй Docker Swarm/Kubernetes secrets, HashiCorp Vault или инъекцию env через CI/CD. +- SSL/HTTP2: поставь обратный прокси с TLS (например, Nginx/Traefik) и включи HSTS/CSP/безопасные заголовки. +- Бэкапы: настроить регулярные бэкапы `postgres_data`. +- Доступы БД: разнести пользователей по API (отдельные `DB_USER`/пароли и права на свои базы). +- Обновления: регулярно обновлять базовые образы и пакеты в Dockerfile. +- Мониторинг: добавить healthchecks, метрики и алёрты. + +## Что было исправлено и почему +- Nginx: убраны вложенные `location` и добавлены независимые блоки для `/api/v1` и `/api/v2`. +- `SCRIPT_FILENAME`: передаётся абсолютный путь `/var/www/html/index.php` (код не монтируется в Nginx). +- PHP-FPM: `clear_env = no`, чтобы PHP видел переменные из `.env`/compose. +- Dockerfile: удалены лишние пакеты; оставлены PHP 8.2 и нужные расширения. Исправлено отсутствие `php8.2-json` (встроено в ядро). +- Права: вместо `777` используются более строгие `755/775` и корректный владелец `www`. +- БД: добавлен скрипт инициализации, создающий `api_v1` и `api_v2`. + +## Безопасность +- Секреты вынесены в `.env` файлы и исключены из VCS (`.gitignore`). +- Для продакшна — использовать secrets-менеджер вместо `.env`. +- Выключен `display_errors` в FPM; включать только на dev. +- Разграничение прав БД и ротация паролей. + +## Диагностика проблем +- Ошибка FPM "failed to open error_log": убедись, что логи монтируются в `/var/log/php-fpm` (уже настроено в compose). +- Ошибка пакета `php8.2-json`: не устанавливается отдельно (часть ядра). Удалено из Dockerfile. +- Полный сброс окружения: + ```powershell + docker compose down -v + docker compose up -d --build + ``` + +## 📚 Документация +Подробная документация находится в каталоге `docs/`: +- Навигация и быстрый старт: `docs/README.md` +- Обзор и требования: `docs/overview.md` +- Запуск и деплой: `docs/deployment.md` +- Мониторинг (метрики и алёрты): `docs/monitoring.md` +- Примеры `.env`: `docs/env-examples.md` +- Публикация в Gitea: `docs/gitea.md` +- Диагностика: `docs/troubleshooting.md` + +## 📚 Раздел: Secrets Management + +Секреты вынесены из `docker-compose.yml` в отдельные env-файлы, игнорируемые системой контроля версий. + +- Файлы: `.env.v1`, `.env.v2`, `.env.db` (локальные секреты). +- Пример содержимого и рекомендации см. в `docs/env-examples.md`. + +### Почему так безопаснее +- Нет паролей в `docker-compose.yml` — меньше риск утечки в VCS. +- Разделение по сервисам — проще ротация и изоляция. +- В продакшене рекомендуем использовать Swarm/K8s Secrets, Vault, SSM Parameter Store. \ No newline at end of file diff --git a/api-v1/Dockerfile b/api-v1/Dockerfile new file mode 100644 index 0000000..6f95c2b --- /dev/null +++ b/api-v1/Dockerfile @@ -0,0 +1,62 @@ +# Используем официальный образ Debian +FROM debian:bookworm-slim + +LABEL maintainer="your-email@example.com" +LABEL version="1.0" +LABEL description="PHP-FPM container for API v1 with PostgreSQL and Memcached" + +ENV LANG C.UTF-8 +ENV LC_ALL C.UTF-8 + +# Базовые утилиты и репозиторий PHP +RUN apt-get update && apt-get install -y \ + curl \ + wget \ + gnupg \ + lsb-release \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +RUN wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg \ + && echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list + +# PHP, расширения, клиент PostgreSQL +RUN apt-get update && apt-get install -y --no-install-recommends \ + php8.2-fpm \ + php8.2-cli \ + php8.2-pgsql \ + php8.2-memcached \ + php8.2-curl \ + php8.2-mbstring \ + php8.2-xml \ + php8.2-zip \ + php8.2-gd \ + php8.2-intl \ + php8.2-bcmath \ + php8.2-opcache \ + postgresql-client \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Логи и рантайм +RUN mkdir -p /var/log/php-fpm \ + && mkdir -p /var/run/php + +# Конфиги PHP-FPM +COPY config/php-fpm.conf /etc/php/8.2/fpm/php-fpm.conf +COPY config/www.conf /etc/php/8.2/fpm/pool.d/www.conf + +# Пользователь +RUN groupadd -g 1000 www \ + && useradd -u 1000 -ms /bin/bash -g www www + +WORKDIR /var/www/html +COPY . /var/www/html/ + +# Права доступа (без 777) +RUN chown -R www:www /var/www/html /var/log \ + && chmod -R 755 /var/www/html \ + && chmod -R 775 /var/log + +EXPOSE 9000 +CMD ["php-fpm8.2", "-F"] \ No newline at end of file diff --git a/api-v1/config/php-fpm.conf b/api-v1/config/php-fpm.conf new file mode 100644 index 0000000..e9a7f05 --- /dev/null +++ b/api-v1/config/php-fpm.conf @@ -0,0 +1,4 @@ +[global] +error_log = /var/log/php-fpm/v1-error.log +daemonize = no +include=/etc/php/8.2/fpm/pool.d/*.conf \ No newline at end of file diff --git a/api-v1/config/www.conf b/api-v1/config/www.conf new file mode 100644 index 0000000..a69a363 --- /dev/null +++ b/api-v1/config/www.conf @@ -0,0 +1,28 @@ +; Пул процессов для API v1 +[www] +user = www +group = www + +listen = 0.0.0.0:9000 +listen.mode = 0660 + +pm = dynamic +pm.max_children = 10 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 5 + +; Важно для доступа к env переменным из PHP +clear_env = no + +catch_workers_output = yes +php_flag[display_errors] = off +php_admin_value[error_log] = /var/log/php-fpm/v1-error.log +php_admin_flag[log_errors] = on + +; Метрики и пинг +pm.status_path = /status +ping.path = /ping +ping.response = pong + +security.limit_extensions = .php .php3 .php4 .php5 .php7 .php8 \ No newline at end of file diff --git a/api-v1/src/index.php b/api-v1/src/index.php new file mode 100644 index 0000000..7aa7fb8 --- /dev/null +++ b/api-v1/src/index.php @@ -0,0 +1,44 @@ +getMessage(); +} + +// Проверяем подключение к Memcached +try { + $memcached = new Memcached(); + $memcached->addServer(getenv('MEMCACHED_HOST'), 11211); + $memcached_status = $memcached->set('test_key', 'test_value', 10) ? "connected" : "error"; +} catch (Exception $e) { + $memcached_status = "error: " . $e->getMessage(); +} + +// Формируем ответ +$response = [ + 'api_version' => 'v1', + 'status' => 'success', + 'timestamp' => time(), + 'services' => [ + 'postgresql' => $db_status, + 'memcached' => $memcached_status, + ], + 'container' => gethostname(), + 'php_version' => PHP_VERSION, +]; + +echo json_encode($response, JSON_PRETTY_PRINT); \ No newline at end of file diff --git a/api-v2/Dockerfile b/api-v2/Dockerfile new file mode 100644 index 0000000..2212b1e --- /dev/null +++ b/api-v2/Dockerfile @@ -0,0 +1,56 @@ +# Аналогично API v1, кастомизация под v2 при необходимости +FROM debian:bookworm-slim + +LABEL maintainer="your-email@example.com" +LABEL version="1.0" +LABEL description="PHP-FPM container for API v2 with PostgreSQL and Memcached" + +ENV LANG C.UTF-8 +ENV LC_ALL C.UTF-8 + +RUN apt-get update && apt-get install -y \ + curl \ + wget \ + gnupg \ + lsb-release \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +RUN wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg \ + && echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list + +RUN apt-get update && apt-get install -y --no-install-recommends \ + php8.2-fpm \ + php8.2-cli \ + php8.2-pgsql \ + php8.2-memcached \ + php8.2-curl \ + php8.2-mbstring \ + php8.2-xml \ + php8.2-zip \ + php8.2-gd \ + php8.2-intl \ + php8.2-bcmath \ + php8.2-opcache \ + postgresql-client \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +RUN mkdir -p /var/log/php-fpm \ + && mkdir -p /var/run/php + +COPY config/php-fpm.conf /etc/php/8.2/fpm/php-fpm.conf +COPY config/www.conf /etc/php/8.2/fpm/pool.d/www.conf + +RUN groupadd -g 1001 www \ + && useradd -u 1001 -ms /bin/bash -g www www + +WORKDIR /var/www/html +COPY . /var/www/html/ + +RUN chown -R www:www /var/www/html /var/log \ + && chmod -R 755 /var/www/html \ + && chmod -R 775 /var/log + +EXPOSE 9000 +CMD ["php-fpm8.2", "-F"] \ No newline at end of file diff --git a/api-v2/config/php-fpm.conf b/api-v2/config/php-fpm.conf new file mode 100644 index 0000000..5134d62 --- /dev/null +++ b/api-v2/config/php-fpm.conf @@ -0,0 +1,4 @@ +[global] +error_log = /var/log/php-fpm/v2-error.log +daemonize = no +include=/etc/php/8.2/fpm/pool.d/*.conf \ No newline at end of file diff --git a/api-v2/config/www.conf b/api-v2/config/www.conf new file mode 100644 index 0000000..c085a5b --- /dev/null +++ b/api-v2/config/www.conf @@ -0,0 +1,27 @@ +; Пул процессов для API v2 +[www] +user = www +group = www + +listen = 0.0.0.0:9000 +listen.mode = 0660 + +pm = dynamic +pm.max_children = 10 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 5 + +clear_env = no + +catch_workers_output = yes +php_flag[display_errors] = off +php_admin_value[error_log] = /var/log/php-fpm/v2-error.log +php_admin_flag[log_errors] = on + +; Метрики и пинг +pm.status_path = /status +ping.path = /ping +ping.response = pong + +security.limit_extensions = .php .php3 .php4 .php5 .php7 .php8 \ No newline at end of file diff --git a/api-v2/src/index.php b/api-v2/src/index.php new file mode 100644 index 0000000..f8539f5 --- /dev/null +++ b/api-v2/src/index.php @@ -0,0 +1,45 @@ +getMessage(); +} + +try { + $memcached = new Memcached(); + $memcached->addServer(getenv('MEMCACHED_HOST'), 11211); + $memcached_status = $memcached->set('test_key_v2', 'test_value', 10) ? "connected" : "error"; +} catch (Exception $e) { + $memcached_status = "error: " . $e->getMessage(); +} + +$response = [ + 'api_version' => 'v2', + 'status' => 'success', + 'timestamp' => time(), + 'services' => [ + 'postgresql' => $db_status, + 'memcached' => $memcached_status, + ], + 'container' => gethostname(), + 'php_version' => PHP_VERSION, + 'features' => [ + 'advanced_caching', + 'extended_metrics' + ] +]; + +echo json_encode($response, JSON_PRETTY_PRINT); \ No newline at end of file diff --git a/database/init/01_init.sql b/database/init/01_init.sql new file mode 100644 index 0000000..22093c8 --- /dev/null +++ b/database/init/01_init.sql @@ -0,0 +1,6 @@ +-- Инициализация дополнительных баз данных для API v1 и API v2 +CREATE DATABASE api_v1; +CREATE DATABASE api_v2; + +GRANT ALL PRIVILEGES ON DATABASE api_v1 TO api_user; +GRANT ALL PRIVILEGES ON DATABASE api_v2 TO api_user; \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d4fe6f6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,185 @@ +services: + nginx: + image: nginx:1.24-alpine + container_name: nginx_proxy + ports: + - "80:80" + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./logs/nginx:/var/log/nginx + depends_on: + - php-fpm-v1 + - php-fpm-v2 + healthcheck: + test: ["CMD", "wget", "-q", "-O", "-", "http://localhost/"] + interval: 10s + timeout: 3s + retries: 5 + networks: + - app_network + restart: unless-stopped + + php-fpm-v1: + build: + context: ./api-v1 + dockerfile: Dockerfile + container_name: php_fpm_v1 + volumes: + - ./api-v1:/var/www/html + - ./logs/php-v1:/var/log/php-fpm + env_file: + - .env.v1 + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://nginx/api/v1/ > /dev/null"] + interval: 10s + timeout: 5s + retries: 5 + networks: + - app_network + restart: unless-stopped + + php-fpm-v2: + build: + context: ./api-v2 + dockerfile: Dockerfile + container_name: php_fpm_v2 + volumes: + - ./api-v2:/var/www/html + - ./logs/php-v2:/var/log/php-fpm + env_file: + - .env.v2 + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://nginx/api/v2/ > /dev/null"] + interval: 10s + timeout: 5s + retries: 5 + networks: + - app_network + restart: unless-stopped + + postgres: + image: postgres:15-alpine + container_name: postgres_db + env_file: + - .env.db + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] + interval: 10s + timeout: 5s + retries: 5 + volumes: + - postgres_data:/var/lib/postgresql/data + - ./database/init:/docker-entrypoint-initdb.d + ports: + - "5432:5432" + networks: + - app_network + restart: unless-stopped + + memcached: + image: memcached:1.6-alpine + container_name: memcached_server + healthcheck: + test: ["CMD-SHELL", "pidof memcached"] + interval: 10s + timeout: 3s + retries: 5 + ports: + - "11211:11211" + networks: + - app_network + restart: unless-stopped + + nginx_exporter: + image: nginx/nginx-prometheus-exporter:0.11.0 + container_name: nginx_exporter + command: ["-nginx.scrape-uri", "http://nginx/status"] + ports: + - "9113:9113" + depends_on: + - nginx + networks: + - app_network + restart: unless-stopped + + php_fpm_exporter: + image: hipages/php-fpm_exporter:latest + container_name: php_fpm_exporter + command: + - "server" + - "--phpfpm.scrape-uri=tcp://php-fpm-v1:9000/status" + - "--phpfpm.scrape-uri=tcp://php-fpm-v2:9000/status" + - "--web.listen-address=0.0.0.0:9253" + ports: + - "9253:9253" + depends_on: + - php-fpm-v1 + - php-fpm-v2 + networks: + - app_network + restart: unless-stopped + + postgres_exporter: + image: prometheuscommunity/postgres-exporter:latest + container_name: postgres_exporter + env_file: + - .env.db + environment: + DATA_SOURCE_NAME: "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable" + ports: + - "9187:9187" + depends_on: + - postgres + networks: + - app_network + restart: unless-stopped + + memcached_exporter: + image: prom/memcached-exporter:latest + container_name: memcached_exporter + command: ["--memcached.address=memcached:11211"] + ports: + - "9150:9150" + depends_on: + - memcached + networks: + - app_network + restart: unless-stopped + + prometheus: + image: prom/prometheus:latest + container_name: prometheus + command: ["--config.file=/etc/prometheus/prometheus.yml","--storage.tsdb.path=/prometheus","--web.enable-lifecycle"] + volumes: + - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - ./monitoring/alert.rules.yml:/etc/prometheus/alert.rules.yml:ro + - prometheus_data:/prometheus + ports: + - "9090:9090" + depends_on: + - nginx_exporter + - php_fpm_exporter + - postgres_exporter + - memcached_exporter + networks: + - app_network + restart: unless-stopped + + alertmanager: + image: prom/alertmanager:latest + container_name: alertmanager + volumes: + - ./monitoring/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro + ports: + - "9093:9093" + networks: + - app_network + restart: unless-stopped + +volumes: + postgres_data: + prometheus_data: + +networks: + app_network: + driver: bridge \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..c01b23e --- /dev/null +++ b/docs/README.md @@ -0,0 +1,18 @@ +# Документация проекта + +Добро пожаловать! Этот раздел содержит полную документацию по проекту, навигацию по подсекциям, примеры конфигураций и инструкции по развёртыванию и публикации в Gitea. + +## Навигация +- [Обзор и требования](./overview.md) +- [Запуск и деплой](./deployment.md) +- [Мониторинг: метрики и алёрты](./monitoring.md) +- [Примеры .env файлов](./env-examples.md) +- [Публикация в Gitea](./gitea.md) +- [Диагностика и троблшутинг](./troubleshooting.md) + +## Быстрый старт +- Скопируйте и заполните `.env.v1`, `.env.v2`, `.env.db` по образцам из [env-examples.md](./env-examples.md). +- Запустите `docker compose up -d` из корня проекта. +- Проверьте таргеты в Prometheus: `http://localhost:9090/targets`. +- Проверьте метрики экспортеров: Nginx `http://localhost:9113/metrics`, PHP-FPM `http://localhost:9253/metrics`, Postgres `http://localhost:9187/metrics`, Memcached `http://localhost:9150/metrics`. +- Alertmanager UI: `http://localhost:9093` (настройте получателей в `monitoring/alertmanager.yml`). \ No newline at end of file diff --git a/docs/deployment.md b/docs/deployment.md new file mode 100644 index 0000000..b3167cf --- /dev/null +++ b/docs/deployment.md @@ -0,0 +1,23 @@ +# Запуск и деплой + +## Локальный запуск (DEV) +1. Заполните `.env.v1`, `.env.v2`, `.env.db` по образцам из [env-examples.md](./env-examples.md). +2. Запустите: `docker compose up -d` в корне проекта. +3. Проверьте: + - API v1: `http://localhost/api/v1/` + - API v2: `http://localhost/api/v2/` + - Nginx статус: `http://localhost/status` + - Prometheus: `http://localhost:9090` + - Alertmanager: `http://localhost:9093` + +## Продакшен (общие рекомендации) +- Используйте секреты: Docker Swarm/K8s Secrets, Vault или SSM Parameter Store. +- Ограничьте `/status` (Nginx stub_status) по IP/ACL. +- Разнесите экспортеры и Prometheus на отдельные ноды. +- Настройте резервное копирование для Postgres и ретеншен для Prometheus. +- Добавьте Grafana для визуализации (рекомендуется). + +## Обновление стека +- Обновить конфиги: `monitoring/*`, `nginx/nginx.conf`, `.env.*`. +- Перезапуск: `docker compose up -d`. +- Проверка таргетов: `http://localhost:9090/targets`. \ No newline at end of file diff --git a/docs/env-examples.md b/docs/env-examples.md new file mode 100644 index 0000000..ed41f6e --- /dev/null +++ b/docs/env-examples.md @@ -0,0 +1,30 @@ +# Примеры .env файлов + +## .env.v1 +``` +DB_HOST=postgres +DB_NAME=app_db +DB_USER=app_user +DB_PASS=local_password_v1 +MEMCACHED_HOST=memcached +``` + +## .env.v2 +``` +DB_HOST=postgres +DB_NAME=app_db +DB_USER=app_user +DB_PASS=local_password_v2 +MEMCACHED_HOST=memcached +``` + +## .env.db +``` +POSTGRES_DB=app_db +POSTGRES_USER=app_user +POSTGRES_PASSWORD=local_db_password +``` + +## Примечания +- Эти файлы добавлены в `.gitignore` и не должны коммититься. +- В продакшене используйте секрет-менеджеры или переменные окружения CI/CD. \ No newline at end of file diff --git a/docs/gitea.md b/docs/gitea.md new file mode 100644 index 0000000..1b676d8 --- /dev/null +++ b/docs/gitea.md @@ -0,0 +1,28 @@ +# Публикация в Gitea + +## Подготовка репозитория +- Убедитесь, что `.env.*` и `logs/` в `.gitignore`. +- Проверьте `README.md` и раздел `docs/` на актуальность. + +## Создание репозитория в Gitea +1. Зайдите в Gitea и создайте новый репозиторий (Private/Public по вашему выбору). +2. Скопируйте URL репозитория (SSH/HTTPS). + +## Локальная привязка и пуш +```bash +# Инициализация (если не инициализировано) +git init + +# Добавление ремоута +git remote add origin + +# Коммиты и пуш +git add . +git commit -m "Initial commit" +git push -u origin main +``` + +## CI/CD (рекомендации) +- Включите защищённые секреты в Gitea/Runner. +- Запланируйте пайплайн: линтеры, сборка, запуск `docker compose config && docker compose up -d`. +- Храните prod-секреты вне репозитория (Secrets/Variables). \ No newline at end of file diff --git a/docs/monitoring.md b/docs/monitoring.md new file mode 100644 index 0000000..05c0f6f --- /dev/null +++ b/docs/monitoring.md @@ -0,0 +1,24 @@ +# Мониторинг: метрики и алёрты + +## Экспортеры и порты +- Nginx Exporter (`nginx_exporter`): `http://localhost:9113/metrics` — читает `http://nginx/status`. +- PHP-FPM Exporter (`php_fpm_exporter`): `http://localhost:9253/metrics` — опрашивает `tcp://php-fpm-v1:9000/status` и `tcp://php-fpm-v2:9000/status`. +- Postgres Exporter (`postgres_exporter`): `http://localhost:9187/metrics`. +- Memcached Exporter (`memcached_exporter`): `http://localhost:9150/metrics`. + +## Prometheus +- UI: `http://localhost:9090` +- Конфиг: `monitoring/prometheus.yml` +- Таргеты: `http://localhost:9090/targets` + +## Alertmanager +- UI: `http://localhost:9093` +- Конфиг: `monitoring/alertmanager.yml` +- Правила алёртов: `monitoring/alert.rules.yml` + +## Правила по умолчанию +- `InstanceDown`: `up == 0` в течение 1 минуты — критический алерт. + +## Расширение +- Добавьте свои правила в `monitoring/alert.rules.yml`. +- Настройте получателей в `monitoring/alertmanager.yml` (email/telegram/webhook). \ No newline at end of file diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 0000000..f227d1d --- /dev/null +++ b/docs/overview.md @@ -0,0 +1,16 @@ +# Обзор и требования + +Проект содержит два PHP API (v1 и v2), Nginx для роутинга, Postgres и Memcached. Добавлены healthchecks, метрики через экспортеры и базовые алёрты. + +## Компоненты +- Nginx (`nginx`) — прокси и роутинг `/api/v1` и `/api/v2`. +- PHP-FPM v1/v2 (`php-fpm-v1`, `php-fpm-v2`) — обработка PHP. +- Postgres (`postgres`) — база данных. +- Memcached (`memcached`) — кеш. +- Экспортеры: `nginx_exporter`, `php_fpm_exporter`, `postgres_exporter`, `memcached_exporter`. +- Мониторинг: `prometheus`, `alertmanager`. + +## Требования +- Docker 24+ и Docker Compose v2. +- Порты, свободные на хосте: `80, 5432, 11211, 9113, 9253, 9187, 9150, 9090, 9093`. +- Windows: запуск из PowerShell, пути проекта в `c:\docker\nginxphp\server\project`. \ No newline at end of file diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..00d4a15 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,17 @@ +# Диагностика и троблшутинг + +## Healthchecks +- `nginx`: проверка `http://localhost/`. +- `php-fpm-v1/v2`: проверка `http://nginx/api/v1/` и `http://nginx/api/v2/`. +- `postgres`: `pg_isready`. +- `memcached`: `pidof memcached`. + +## Типичные проблемы +- Порты заняты: проверьте, что нет конфликтующих сервисов. +- Ошибка доступа к `/status`: ограничьте доступ в продакшене, в DEV открыт. +- Postgres exporter: проверьте `DATA_SOURCE_NAME` и учётные данные. + +## Логи +- Nginx: `./logs/nginx`. +- PHP-FPM v1/v2: `./logs/php-v1`, `./logs/php-v2`. +- Postgres: внутри контейнера `/var/lib/postgresql/data/log` (при включении логгирования). \ No newline at end of file diff --git a/monitoring/alert.rules.yml b/monitoring/alert.rules.yml new file mode 100644 index 0000000..05357b0 --- /dev/null +++ b/monitoring/alert.rules.yml @@ -0,0 +1,11 @@ +groups: + - name: general + rules: + - alert: InstanceDown + expr: up == 0 + for: 1m + labels: + severity: critical + annotations: + summary: "Endpoint {{ $labels.instance }} недоступен" + description: "Таргет job={{ $labels.job }} на хосте {{ $labels.instance }} не отвечает более 1 минуты." \ No newline at end of file diff --git a/monitoring/alertmanager.yml b/monitoring/alertmanager.yml new file mode 100644 index 0000000..41ee5e9 --- /dev/null +++ b/monitoring/alertmanager.yml @@ -0,0 +1,15 @@ +global: + resolve_timeout: 5m + +route: + receiver: 'default' + group_by: ['alertname', 'job'] + group_wait: 10s + group_interval: 5m + repeat_interval: 1h + +receivers: + - name: 'default' + webhook_configs: + - url: 'http://host.docker.internal:5001/alerts' + send_resolved: true \ No newline at end of file diff --git a/monitoring/prometheus.yml b/monitoring/prometheus.yml new file mode 100644 index 0000000..a00e83a --- /dev/null +++ b/monitoring/prometheus.yml @@ -0,0 +1,28 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +scrape_configs: + - job_name: 'nginx' + static_configs: + - targets: ['nginx_exporter:9113'] + + - job_name: 'php-fpm' + static_configs: + - targets: ['php_fpm_exporter:9253'] + + - job_name: 'postgres' + static_configs: + - targets: ['postgres_exporter:9187'] + + - job_name: 'memcached' + static_configs: + - targets: ['memcached_exporter:9150'] + +alerting: + alertmanagers: + - static_configs: + - targets: ['alertmanager:9093'] + +rule_files: + - /etc/prometheus/alert.rules.yml \ No newline at end of file diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..85b2921 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,81 @@ +# Основные настройки Nginx +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + server_tokens off; + + # Upstream для PHP-FPM серверов + upstream php_fpm_v1 { + server php-fpm-v1:9000; + } + + upstream php_fpm_v2 { + server php-fpm-v2:9000; + } + + server { + listen 80; + server_name localhost; + + access_log /var/log/nginx/access.log main; + error_log /var/log/nginx/error.log warn; + + # API v1: фронт-контроллер index.php внутри контейнера php-fpm-v1 + location ~ ^/api/v1(?:/|$) { + include fastcgi_params; + fastcgi_pass php_fpm_v1; + fastcgi_param SCRIPT_FILENAME /var/www/html/index.php; + fastcgi_param PHP_VALUE "upload_max_filesize=100M \n post_max_size=100M"; + fastcgi_read_timeout 300; + } + + # API v2: фронт-контроллер index.php внутри контейнера php-fpm-v2 + location ~ ^/api/v2(?:/|$) { + include fastcgi_params; + fastcgi_pass php_fpm_v2; + fastcgi_param SCRIPT_FILENAME /var/www/html/index.php; + fastcgi_param PHP_VALUE "upload_max_filesize=100M \n post_max_size=100M"; + fastcgi_read_timeout 300; + } + + # Обслуживание статических файлов (если появятся) + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Безопасность: скрываем служебные файлы + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } + + location ~ /README\.md$ { + deny all; + access_log off; + log_not_found off; + } + + # Метрики Nginx: stub_status для экспорта + location /status { + stub_status; + access_log off; + # В DEV среде открыт; в PROD ограничить по IP/ACL + } + } +} \ No newline at end of file