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