Initial commit
This commit is contained in:
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Ignore local environment files
|
||||
.env*
|
||||
|
||||
# Ignore logs
|
||||
logs/
|
||||
|
||||
# Common OS/editor junk
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
*.swp
|
||||
108
README.md
Normal file
108
README.md
Normal file
@@ -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.
|
||||
62
api-v1/Dockerfile
Normal file
62
api-v1/Dockerfile
Normal file
@@ -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"]
|
||||
4
api-v1/config/php-fpm.conf
Normal file
4
api-v1/config/php-fpm.conf
Normal file
@@ -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
|
||||
28
api-v1/config/www.conf
Normal file
28
api-v1/config/www.conf
Normal file
@@ -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
|
||||
44
api-v1/src/index.php
Normal file
44
api-v1/src/index.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* API v1 Entry Point
|
||||
* Docker Container: php-fpm-v1
|
||||
* Endpoint: /api/v1/
|
||||
*/
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Проверяем подключение к PostgreSQL
|
||||
try {
|
||||
$pdo = new PDO(
|
||||
"pgsql:host=" . getenv('DB_HOST') . ";dbname=" . getenv('DB_NAME'),
|
||||
getenv('DB_USER'),
|
||||
getenv('DB_PASS')
|
||||
);
|
||||
$db_status = "connected";
|
||||
} catch (PDOException $e) {
|
||||
$db_status = "error: " . $e->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);
|
||||
56
api-v2/Dockerfile
Normal file
56
api-v2/Dockerfile
Normal file
@@ -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"]
|
||||
4
api-v2/config/php-fpm.conf
Normal file
4
api-v2/config/php-fpm.conf
Normal file
@@ -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
|
||||
27
api-v2/config/www.conf
Normal file
27
api-v2/config/www.conf
Normal file
@@ -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
|
||||
45
api-v2/src/index.php
Normal file
45
api-v2/src/index.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* API v2 Entry Point
|
||||
* Docker Container: php-fpm-v2
|
||||
* Endpoint: /api/v2/
|
||||
*/
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
try {
|
||||
$pdo = new PDO(
|
||||
"pgsql:host=" . getenv('DB_HOST') . ";dbname=api_v2",
|
||||
getenv('DB_USER'),
|
||||
getenv('DB_PASS')
|
||||
);
|
||||
$db_status = "connected";
|
||||
} catch (PDOException $e) {
|
||||
$db_status = "error: " . $e->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);
|
||||
6
database/init/01_init.sql
Normal file
6
database/init/01_init.sql
Normal file
@@ -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;
|
||||
185
docker-compose.yml
Normal file
185
docker-compose.yml
Normal file
@@ -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
|
||||
18
docs/README.md
Normal file
18
docs/README.md
Normal file
@@ -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`).
|
||||
23
docs/deployment.md
Normal file
23
docs/deployment.md
Normal file
@@ -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`.
|
||||
30
docs/env-examples.md
Normal file
30
docs/env-examples.md
Normal file
@@ -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.
|
||||
28
docs/gitea.md
Normal file
28
docs/gitea.md
Normal file
@@ -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 <URL-вашего-репозитория>
|
||||
|
||||
# Коммиты и пуш
|
||||
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).
|
||||
24
docs/monitoring.md
Normal file
24
docs/monitoring.md
Normal file
@@ -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).
|
||||
16
docs/overview.md
Normal file
16
docs/overview.md
Normal file
@@ -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`.
|
||||
17
docs/troubleshooting.md
Normal file
17
docs/troubleshooting.md
Normal file
@@ -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` (при включении логгирования).
|
||||
11
monitoring/alert.rules.yml
Normal file
11
monitoring/alert.rules.yml
Normal file
@@ -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 минуты."
|
||||
15
monitoring/alertmanager.yml
Normal file
15
monitoring/alertmanager.yml
Normal file
@@ -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
|
||||
28
monitoring/prometheus.yml
Normal file
28
monitoring/prometheus.yml
Normal file
@@ -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
|
||||
81
nginx/nginx.conf
Normal file
81
nginx/nginx.conf
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user