Initial commit

This commit is contained in:
Петрищев Алексей 2025-11-13 12:22:20 +03:00
commit 7d96bc9b05
23 changed files with 938 additions and 0 deletions

17
.env Normal file
View File

@ -0,0 +1,17 @@
# .env
APP_ENV=dev
# APP_ENV=prod
# MySQL root
MYSQL_ROOT_PASSWORD=0ntbbW5M3Mcx6tivDGJJ
# Основная база данных
MYSQL_DATABASE=app_db
MYSQL_USER=app_user
MYSQL_PASSWORD=app_pass
# Для certbot / nginx
DOMAIN=zootovar-spb.ru
EMAIL=info@zootovar-spb.ru

113
README.md Normal file
View File

@ -0,0 +1,113 @@
# Bambolo App — Dockerized Development & Production Environment
## 📦 Структура контейнеров (Docker Compose)
| Сервис | Назначение |
|--------------------|----------------------------------------------------------------------------|
| `php` | Основной PHP-FPM backend, обрабатывает web-запросы |
| `php-cron` | Контейнер для выполнения cron задач (в dev-среде — неактивен) |
| `bambolo-worker` | Gearman-воркер для фоновых задач |
| `nginx` | Веб-сервер с SSL (Let's Encrypt или self-signed) |
| `mariadb` | База данных MariaDB 10.6 |
| `memcached` | Кэш-сервер |
| `gearman` | Сервер очередей задач Gearman |
| `manticore_search` | Поисковый движок Manticore (основные индексы) |
| `manticore_logs` | Manticore для realtime-логов |
| `certbot` | Автоматическое продление SSL-сертификатов от Let's Encrypt |
---
## 🗂 Структура проекта
```text
.
├── app/ # Код проекта (монтируется в контейнеры)
│ └── www/ # Публичная директория
│ └── index.php
├── config/
│ ├── php/ # Конфигурация PHP (php.ini, extensions)
│ ├── cron/ # Файлы cron для php-cron контейнера
│ ├── nginx/ # nginx конфиги по средам
│ ├── certs/ # SSL сертификаты Let's Encrypt
│ └── manticore_search/ # Конфиг поиска
│ └── manticore_logs/ # Конфиг поиска для realtime
├── db/
│ ├── mysql/ # Том для MariaDB
│ ├── init/ # SQL-инициализация базы
│ ├── manticore_search/ # Данные поиска
│ └── manticore_logs/
├── logs/ # Логи всех сервисов
├── sessions/ # Сессии PHP
├── docker/ # Dockerfile'ы и entrypoint-скрипты
│ ├── dockerfile_php
│ ├── dockerfile_cron
│ ├── dockerfile_worker
│ └── entrypoints/
│ └── ...
├── .env # Переменные окружения
├── docker-compose.yml
└── README.md
```
---
## 🚀 Как пользоваться
### 1. Сборка контейнеров
```bash
docker-compose build php-base
docker-compose build
```
### 2. Запуск окружения
```bash
docker-compose up -d
```
При первом запуске нужно не забыть сделать
```bash
composer update
```
### 3. Переменные окружения (`.env`)
```ini
APP_ENV=dev # или prod
MYSQL_ROOT_PASSWORD=root
MYSQL_DATABASE=bambolo
MYSQL_USER=bambolo
MYSQL_PASSWORD=secret
```
### 4. Cron и воркеры
- В `APP_ENV=dev`:
- `php-cron` спит (`tail -f /dev/null`)
- `bambolo-worker` можно тоже "усыпить" при необходимости
- В `prod`: запускается cron + воркеры через `command:` с проверкой переменной окружения
---
## 🧪 Полезные команды
```bash
docker-compose exec php bash # доступ внутрь PHP
docker-compose exec mariadb mysql -u... # консоль базы
docker-compose logs -f manticore_search # логи поиска
docker-compose run --rm php-cron sh # отладка cron
```
---
## 🛡 Защита от случайных обновлений
Все образы используют зафиксированные версии (`nginx:1.24.0`, `mariadb:10.6.16`, и т.д.), чтобы избежать неожиданных изменений при `docker pull`.
---
## 🤝 Авторы
Bambolo Dev Team, 2025

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDCTCCAfGgAwIBAgIUFLAUw+h50C1UfSrbmuwG0c7ie7IwDQYJKoZIhvcNAQEL
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1MDQxNjA4NTg0NFoXDTI2MDQx
NjA4NTg0NFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAs3ks5jJYQRb3Do9JtIBL84X6b94BJ3AkxhtQTuEmzFO1
+OZgsyT2eVrbpfQq8MKAQG2c4x9Np3pbX5tA5ee/mWIKGorbG4KdvXTErXqZyVyA
zddsQ6B3Rp7yZuXhRuTcp5pt5dYwg/9YANIm8ftjPtSJe1DXO65dznu7QKzdOIyl
S0Xjs3KxgmaMHIDkTlEXTBoxwSU9eU3tjlQXWIv8nN/ab+py2msTcsJdfrO29CjN
cTm482yVaelHADgAh1mP3g92BDCR8mr11nZZNecFb2VZhGnz2Ipd03B99ELPumeD
o/TL8zW8Taxan/Hy5OFETkl5Fe1PfQ9kI4W0RzacEQIDAQABo1MwUTAdBgNVHQ4E
FgQUvmj+Ljw/DGDzWUacdIFmjWbW/+4wHwYDVR0jBBgwFoAUvmj+Ljw/DGDzWUac
dIFmjWbW/+4wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAoqnC
457WLvpgFcDsQCjujz3Qv7EeK24csJfdMGBqs/1qkulF4jRwXzwe6sRyKsvuVYOW
WwtM8EgtBA9mVOL6mpudEZP0/WKeDDNxuspmc5uXtYeOZOu/1HgNSVFhY5eQW09E
OGsyY32K1wMoZT5qrRXjnOB1Us+U5G0D98KSW+Xs3m6p+FgTantQZ1D2xKQhzVbR
q47a9rLhdl7rb70HXKD6h0vyc1iVeF6HMSNDuGDT5lVHig3+oS6CwI/ovqE7Etbq
Mu6/uU7QOE5Zp33u9gRk11rzWyBm5NthjakssyArz9k8be5XCeOBTYjNEZopNRVW
V9K6Hckw4vKhMXdgZQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzeSzmMlhBFvcO
j0m0gEvzhfpv3gEncCTGG1BO4SbMU7X45mCzJPZ5Wtul9CrwwoBAbZzjH02neltf
m0Dl57+ZYgoaitsbgp29dMStepnJXIDN12xDoHdGnvJm5eFG5Nynmm3l1jCD/1gA
0ibx+2M+1Il7UNc7rl3Oe7tArN04jKVLReOzcrGCZowcgOROURdMGjHBJT15Te2O
VBdYi/yc39pv6nLaaxNywl1+s7b0KM1xObjzbJVp6UcAOACHWY/eD3YEMJHyavXW
dlk15wVvZVmEafPYil3TcH30Qs+6Z4Oj9MvzNbxNrFqf8fLk4UROSXkV7U99D2Qj
hbRHNpwRAgMBAAECggEALZGW1NvzjJNIJ2o6TJpGro5WCBFWOa0/qQ1GVsDUGQ0o
JwpXgyTfb/Ch1IEqZfb8mV87dm98SAnJKX1B/R3aiBdceWDIQTPo2THMvj9izL2v
9pO76W/U2RmCcp3rlRSG8gdD4BeCOgGbVpoFSWuKx2kvfyAHhh+/sa243bMGeFTQ
PJZH+bMkh5tNllRGGpQ/i1+XflQWjLp4wgeg98eVIIQf4/hdkSVCMkY+Cf4Vtr8C
cxrwIon2vt12MEmuKhKo6j07Evj1Aj6ppQJrU0fJNeDforNtzfolPQWM50sbwSt4
+9VKHnykKdLD9x7TMMRL+kBYVHnnDXgEmbADM3IkAQKBgQDtD3B5ccVrY1ZFnwvF
fEBSdJmU1MAcOibZK5kX9FQ3r0eC0BQRNID5947zP6TGoUJ/xn45b49bZ5JaYCot
L6fvBfyMbjr7YlFqmXAwXu7ZeDMT/wxIxRWm66bhYQ1fKG4o+USOpqp1+YrN32q6
Neqr/4OznJJrZaLnSPlOiwh/DwKBgQDBz+sBpDanrdXbY1Yo7jbEWvY6/HaIIF4i
OV7j1NMj0aMqsygUs6fVLkVhIpBrkpSvW4LVNyerGqTgmSnpMUf7efLUqNnlZ+uT
jRWpfZFVWvrNV8fAt0WmTYd9XK98MlrryP3ut8NbZf5iQvJiyDOw8gd0ak4o5rMa
Hql9zhEy3wKBgHUxj8oKC64WMt1CTmB4F5hr2k0wjDSoLvJn0kx8VnIc7f5mfbUv
vp9U2k+44+3qZOSkLVyZoUZvwnN5XQBvsdbBn+OQzwndxiAr8MGI/Q13ldDJ4rnK
7PRTRXHgN+sWIreQ22qmTFj8X7l9PNcHtpcHP4W43s3HNiye79j7dNzTAoGBALws
8qDqXsKZTq2vGkWtXHFzW+VToIZ03tDd4RrWVZZOgd0Ai+bltAuQ3H3+QDih3kkQ
UcxQu+wud202aPHoDlrFQZdmxgEt0BW4AxNIPqagKijblK+xgieA2Q9HwX6VqZ+K
y7pOo5gHRGEFXS+58C5aIBDQ8khWDglLQgdK33oDAoGAA8h7C43Otljgjpt3gV3w
tGJFkkEv2RXhHA69dozV5tuI6sGRlumYkPYGAyDSjoQMWJ+3TutYykqTdLBnvlVe
gUMLOLWmSuw8/Sp5rC142Xx0Z+M5vJacGwQYhHLFyqPaoh3O5npKtVPv1J0WViv9
Pzsd+llGQp/QURBlTRxHtDo=
-----END PRIVATE KEY-----

1
config/cron/app.cron Normal file
View File

@ -0,0 +1 @@
# placeholder

View File

@ -0,0 +1,9 @@
searchd {
listen = 0.0.0.0:9312
listen = 0.0.0.0:9306:mysql
log = /var/log/manticore/searchd_logs.log
query_log = /var/log/manticore/query_logs.log
pid_file = /var/run/manticore/searchd_logs.pid
data_dir = /var/manticore
}

View File

@ -0,0 +1,3 @@
#!/bin/sh
envsubst < /etc/manticoresearch/manticore.template > /etc/manticoresearch/manticore.conf
envsubst < /etc/manticoresearch/manticore.template

View File

@ -0,0 +1,142 @@
#####
# Pitsbikes
###########################
source pitbike
{
type = mysql
sql_host = ${DB_HOST}
sql_user = ${DB_USERNAME}
sql_pass = ${DB_PASSWORD}
sql_db = ${DB_DATABASE}
sql_port = 3306 # optional, default is 3306
sql_query_pre = SET NAMES utf8
sql_query_pre = SET SESSION query_cache_type=OFF
sql_query = SELECT `shop_item`.id, create_time, `shop_item`.`title` , description, page_keywords, mini_desc, page_title, page_h1, rating, price, ( SELECT group_concat( `shop_item_chars_vals`.`val` SEPARATOR ', ' ) FROM `shop_item_chars` LEFT JOIN `shop_item_chars_vals` ON (`shop_item_chars_vals`.`id` = `shop_item_chars`.`attr_value` ) WHERE `shop_item_chars`.`item_id` = `shop_item`.`id` ) AS tags,( SELECT group_concat( concat_ws(', ', `title`,`page_keywords`,`page_title`,`page_h1`) SEPARATOR ', ' ) FROM `shop_catalog` WHERE `shop_catalog`.`template_id` = `shop_item`.`template_id` ) AS catalog, `sizes`, `not_in_face`,`new` FROM shop_item where !`_sys_deleted` AND !`_sys_unvisible`
sql_attr_bool = not_in_face
sql_attr_bool = new
sql_attr_uint = rating
sql_attr_uint = price
sql_attr_multi = uint tag_id from query; SELECT `item_id` AS `doc_id` , `attr_value` AS `filter_id` FROM `shop_item_chars`
sql_attr_multi = uint size_id from query; SELECT `shop_item`.`id` AS `doc_id` , `shop_size`.`id` AS `filter_id` FROM `shop_size` RIGHT JOIN `shop_item` ON ( find_in_set( `shop_size`.`id` , `shop_item`.`sizes` ) )
sql_attr_multi = uint size_all_id from query; SELECT `shop_item`.`id` AS `doc_id` , `shop_size`.`id` AS `filter_id` FROM `shop_size` RIGHT JOIN `shop_item` ON ( find_in_set( `shop_size`.`id` , `shop_item`.`sizes_all` ) )
sql_attr_multi = uint place_id from query; SELECT `item_id` AS `doc_id` , `place` AS `filter_id` FROM `shop_goods` WHERE `status` = 2 AND `order_id` = 0 GROUP BY `item_id`, `place`
sql_attr_multi = uint catalog_id from query; SELECT `item_id` AS `doc_id` , `catalog_id` AS `filter_id` FROM `shop_catalog_items`
}
source pitbike_adm
{
type = mysql
sql_host = ${DB_HOST}
sql_user = ${DB_USERNAME}
sql_pass = ${DB_PASSWORD}
sql_db = ${DB_DATABASE}
sql_port = 3306 # optional, default is 3306
sql_query_pre = SET NAMES utf8
sql_query_pre = SET SESSION query_cache_type=OFF
sql_query = SELECT `shop_item`.id,`shop_item`.`_sys_unvisible`, create_time, `shop_item`.`title`, `mainpic_preview` , description, page_keywords, mini_desc, page_title, page_h1, rating, price, ( SELECT group_concat( `shop_item_chars_vals`.`val` SEPARATOR ', ' ) FROM `shop_item_chars` LEFT JOIN `shop_item_chars_vals` ON (`shop_item_chars_vals`.`id` = `shop_item_chars`.`attr_value` ) WHERE `shop_item_chars`.`item_id` = `shop_item`.`id` ) AS tags,( SELECT group_concat( concat_ws(', ', `title`,`page_keywords`,`page_title`,`page_h1`) SEPARATOR ', ' ) FROM `shop_catalog` WHERE `shop_catalog`.`template_id` = `shop_item`.`template_id` ) AS catalog, `sizes`, `not_in_face`,`new` FROM shop_item where !`_sys_deleted`
sql_attr_bool = not_in_face
sql_attr_bool = new
sql_attr_bool = _sys_unvisible
sql_attr_uint = rating
sql_attr_uint = price
sql_attr_uint = mainpic_preview
sql_attr_multi = uint tag_id from query; SELECT `item_id` AS `doc_id` , `attr_value` AS `filter_id` FROM `shop_item_chars`
sql_attr_multi = uint size_id from query; SELECT `shop_item`.`id` AS `doc_id` , `shop_size`.`id` AS `filter_id` FROM `shop_size` RIGHT JOIN `shop_item` ON ( find_in_set( `shop_size`.`id` , `shop_item`.`sizes` ) )
sql_attr_multi = uint size_all_id from query; SELECT `shop_item`.`id` AS `doc_id` , `shop_size`.`id` AS `filter_id` FROM `shop_size` RIGHT JOIN `shop_item` ON ( find_in_set( `shop_size`.`id` , `shop_item`.`sizes_all` ) )
sql_attr_multi = uint place_id from query; SELECT `item_id` AS `doc_id` , `place` AS `filter_id` FROM `shop_goods` WHERE `status` = 2 AND `order_id` = 0 GROUP BY `item_id`, `place`
sql_attr_multi = uint catalog_id from query; SELECT `item_id` AS `doc_id` , `catalog_id` AS `filter_id` FROM `shop_catalog_items`
}
source pitbike_cats
{
type = mysql
sql_host = ${DB_HOST}
sql_user = ${DB_USERNAME}
sql_pass = ${DB_PASSWORD}
sql_db = ${DB_DATABASE}
sql_port = 3306 # optional, default is 3306
sql_query_pre = SET NAMES utf8
sql_query_pre = SET SESSION query_cache_type=OFF
sql_query = SELECT id, title, page_keywords, page_h1, page_title FROM `shop_catalog` WHERE `_sys_unvisible` = 0 AND `_sys_deleted` = 0
}
index pitbike
{
source = pitbike
path = /usr/share/manticore/pitbike
docinfo = extern
mlock = 0
morphology = stem_enru, soundex, metaphone
min_word_len = 2
min_infix_len = 3
index_exact_words = 1
html_strip = 1
}
index pitbike_adm
{
source = pitbike_adm
path = /usr/share/manticore/pitbike_adm
docinfo = extern
mlock = 0
morphology = stem_enru, soundex, metaphone
min_word_len = 2
min_infix_len = 3
index_exact_words = 1
html_strip = 1
}
index pitbike_cats
{
source = pitbike_cats
path = /usr/share/manticore/pitbike_cats
docinfo = extern
mlock = 0
morphology = stem_enru, soundex, metaphone
index_exact_words = 1
min_word_len = 2
min_infix_len = 3
html_strip = 1
}
#############################################################################
## indexer settings
#############################################################################
indexer
{
mem_limit = 64M
}
#############################################################################
## searchd settings
#############################################################################
searchd
{
#listen = 0.0.0.0:9312
#listen = 127.0.0.1:3307:mysql41
listen = 0.0.0.0:9312
listen = 0.0.0.0:9306:mysql
listen = 0.0.0.0:9308:http
pseudo_sharding = 0
log = /var/log/manticore/searchd.log
query_log = /var/log/manticore/query.log
read_timeout = 5
client_timeout = 300
max_children = 30
pid_file = /var/run/manticore/searchd.pid
seamless_rotate = 1
preopen_indexes = 1
unlink_old = 1
}

View File

@ -0,0 +1,96 @@
server {
listen 80;
server_name localhost .localhost;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name localhost .localhost;
root /var/www/html/www;
index index.php;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
ssl_certificate /etc/ssl/selfsigned/cert.pem;
ssl_certificate_key /etc/ssl/selfsigned/key.pem;
client_max_body_size 128M;
location / {
rewrite ^/(.*)/$ /$1 permanent;
if ($scheme != 'https') {
return 301 https://$host$request_uri;
}
if ($host != "localhost") {
return 301 https://localhost$request_uri;
}
rewrite ^/index.php$ / permanent;
try_files $uri $uri/ /index.php?q=$uri&$args;
}
location /admin {
try_files $uri $uri/ /admin/index.php?q=$uri&$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html/www$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_read_timeout 180;
proxy_buffers 8 32k;
proxy_buffer_size 64k;
}
location ~* \.(jpg|jpeg|gif|webp|png|css|zip|tgz|gz|rar|bz2|doc|xls|pdf|tar|wav|bmp|swf|ico|txt|js|htc|flv|psd|cdr|woff|ttf|svg|eot|woff2)$ {
root /var/www/html/www;
add_header Access-Control-Allow-Origin *;
try_files $uri $uri/ @static;
access_log off;
expires 30d;
}
location @static {
rewrite "^/images/(\d{2})/(\d{2})/(\d{1,}).jpg$" /img.php?id=$3 last;
rewrite "^/images/([0-9]{2})([0-9]{2})([0-9]{1,}).webp$" /images/$1/$2/$1$2$3.webp permanent;
rewrite "^/video/(\d{1,}).flv$" /video.php?id=$1 last;
rewrite "^/avatars/(\d{1,}).jpg$" /avatars.php?id=$1 last;
rewrite "^/avatars/(\d{1,}-\w+).jpg$" /avatars.php?id=$1 last;
}
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location ~ /\.ht {
deny all;
}
location = /status {
allow 127.0.0.1;
deny all;
access_log off;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/html/www/index.php;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass php:9000;
}
}

View File

@ -0,0 +1,102 @@
server {
listen 80;
server_name bambolo.ru .bambolo.ru;
location /.well-known/acme-challenge/ {
root /var/www/html/www;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name bambolo.ru .bambolo.ru;
root /var/www/html/www;
index index.php;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
ssl_certificate /etc/letsencrypt/live/bambolo.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/bambolo.ru/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
client_max_body_size 128M;
location / {
rewrite ^/(.*)/$ /$1 permanent;
if ($scheme != 'https') {
return 301 https://$host$request_uri;
}
if ($host != "bambolo.ru") {
return 301 https://bambolo.ru$request_uri;
}
rewrite ^/index.php$ / permanent;
try_files $uri $uri/ /index.php?q=$uri&$args;
}
location /admin {
try_files $uri $uri/ /admin/index.php?q=$uri&$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html/www$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_read_timeout 180;
proxy_buffers 8 32k;
proxy_buffer_size 64k;
}
location ~* \.(jpg|jpeg|gif|webp|png|css|zip|tgz|gz|rar|bz2|doc|xls|pdf|tar|wav|bmp|swf|ico|txt|js|htc|flv|psd|cdr|woff|ttf|svg|eot|woff2)$ {
root /var/www/html/www;
add_header Access-Control-Allow-Origin *;
try_files $uri $uri/ @static;
access_log off;
expires 30d;
}
location @static {
rewrite "^/images/(\d{2})/(\d{2})/(\d{1,}).jpg$" /img.php?id=$3 last;
rewrite "^/images/([0-9]{2})([0-9]{2})([0-9]{1,}).webp$" /images/$1/$2/$1$2$3.webp permanent;
rewrite "^/video/(\d{1,}).flv$" /video.php?id=$1 last;
rewrite "^/avatars/(\d{1,}).jpg$" /avatars.php?id=$1 last;
rewrite "^/avatars/(\d{1,}-\w+).jpg$" /avatars.php?id=$1 last;
}
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location ~ /\.ht {
deny all;
}
location = /status {
allow 127.0.0.1;
deny all;
access_log off;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/html/www/index.php;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass php:9000;
}
}

59
config/php/php.ini Normal file
View File

@ -0,0 +1,59 @@
[PHP]
; Включение установленных модулей
extension=bcmath
extension=calendar
extension=exif
extension=gettext
extension=gd
extension=intl
extension=mbstring
extension=mysqli
extension=opcache
extension=pcntl
extension=pdo
extension=pdo_mysql
extension=posix
extension=shmop
extension=snmp
extension=soap
extension=sockets
extension=sysvmsg
extension=sysvsem
extension=sysvshm
extension=xsl
extension=zip
extension=memcached
extension=gearman
extension=imagick
extension=igbinary
extension=msgpack
extension=geoip
extension=fann
extension=sodium
; Остальные параметры
display_errors = Off
log_errors = On
memory_limit = 1024M
max_execution_time = 30
post_max_size = 100M
upload_max_filesize = 200M
max_input_vars = 10000
session.save_handler = files
session.save_path = "/var/lib/php/sessions"
session.gc_probability = 1
session.gc_divisor = 1000
session.gc_maxlifetime = 1440
session.sid_length = 26
session.sid_bits_per_character = 6
short_open_tag = On
output_buffering = 4096
pdo_mysql.cache_size = 2000
mysqli.allow_persistent = On
mysqli.cache_size = 2000
mysqli.max_persistent = -1
mysqli.max_links = -1
opcache.enable = 1
error_log = /var/log/php-error.log
date.timezone = Europe/Moscow

22
config/php/www.conf Normal file
View File

@ -0,0 +1,22 @@
[www]
user = www-data
group = www-data
listen = 9000
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 70
pm.start_servers = 30
pm.min_spare_servers = 30
pm.max_spare_servers = 50
request_terminate_timeout = 30
chdir = /
catch_workers_output = yes
php_flag[display_errors] = off
php_admin_value[error_log] = /var/log/fpm-php.www.log
php_admin_flag[log_errors] = on

58
deploy.cmd Normal file
View File

@ -0,0 +1,58 @@
@echo off
chcp 65001 >nul
setlocal
set ACTION=%1
if "%ACTION%"=="" set ACTION=help
if "%ACTION%"=="help" (
echo.
echo [DEPLOY] Справка по командам:
echo ---------------------------------------------
echo base-build — Сборка базового php-образа
echo build — Сборка всех контейнеров
echo up — Запуск контейнеров
echo down — Остановка и удаление контейнеров
echo restart — Перезапуск всех контейнеров
echo logs — Просмотр логов всех сервисов
echo php-log — Логи только php-fpm
echo cron-log — Логи только cron-контейнера
echo worker-log — Логи фонового воркера
echo help — Показать эту справку
echo ---------------------------------------------
goto :eof
)
echo [INFO] Старт скрипта deploy.cmd
echo [DEBUG] ACTION=%ACTION%
if "%ACTION%"=="base-build" (
echo [INFO] Сборка базового образа bambolo/php-base:latest
docker build -f docker/dockerfile_base -t bambolo/php-base:latest .
) else if "%ACTION%"=="build" (
echo [INFO] Сборка всех контейнеров...
docker-compose build
) else if "%ACTION%"=="up" (
echo [INFO] Запуск контейнеров...
docker-compose up -d
) else if "%ACTION%"=="down" (
echo [INFO] Остановка и удаление контейнеров...
docker-compose down
) else if "%ACTION%"=="restart" (
echo [INFO] Перезапуск контейнеров...
docker-compose down
docker-compose up -d
) else if "%ACTION%"=="logs" (
docker-compose logs -f
) else if "%ACTION%"=="php-log" (
docker-compose logs -f php
) else if "%ACTION%"=="cron-log" (
docker-compose logs -f php-cron
) else if "%ACTION%"=="worker-log" (
docker-compose logs -f bambolo-worker
) else (
echo [ERROR] Неизвестная команда: %ACTION%
echo Для справки используйте: deploy.cmd help
)
echo [DONE]

1
docker-build-base.cmd Normal file
View File

@ -0,0 +1 @@
docker-compose -f docker-compose.build.yml build

7
docker-compose.build.yml Normal file
View File

@ -0,0 +1,7 @@
services:
php-base:
build:
context: .
dockerfile: docker/dockerfile_base
image: angels-it/php-base

174
docker-compose.yml Normal file
View File

@ -0,0 +1,174 @@
services:
php:
build:
context: .
dockerfile: docker/dockerfile_php
environment:
DB_HOST: mariadb
DB_DATABASE: ${MYSQL_DATABASE}
DB_USERNAME: ${MYSQL_USER}
DB_PASSWORD: ${MYSQL_PASSWORD}
image: angels-it/php-app
volumes:
- ./app:/var/www/html
- ./sessions:/var/lib/php/sessions
- ./config/php:/usr/local/etc/php
- ./logs/php:/var/log
networks:
- app_network
php-cron:
build:
context: .
dockerfile: docker/dockerfile_cron
image: angels-it/php-cron
environment:
APP_ENV: ${APP_ENV}
DB_HOST: mariadb
DB_DATABASE: ${MYSQL_DATABASE}
DB_USERNAME: ${MYSQL_USER}
DB_PASSWORD: ${MYSQL_PASSWORD}
volumes:
- ./app:/var/www/html
- ./config/php:/usr/local/etc/php
- ./config/cron:/etc/cron.d
- ./logs/php-cron:/var/log
command: >
/bin/sh -c '
if [ "$${APP_ENV}" = "prod" ]; then
echo "[cron] production mode — starting cron";
cron -f;
else
echo "[cron] dev mode — sleeping";
tail -f /dev/null;
fi
'
restart: always
networks:
- app_network
# bambolo-worker:
# build:
# context: .
# dockerfile: docker/dockerfile_worker
# image: angels-it/worker
# environment:
# DB_HOST: mariadb
# DB_DATABASE: ${MYSQL_DATABASE}
# DB_USERNAME: ${MYSQL_USER}
# DB_PASSWORD: ${MYSQL_PASSWORD}
# volumes:
# - ./app:/var/www/html
# - ./config/php:/usr/local/etc/php
# - ./logs/worker:/var/log
# restart: always
# networks:
# - app_network
nginx:
image: nginx:1.24.0
ports:
- "80:80"
- "443:443"
depends_on:
- php
volumes:
- ./app:/var/www/html
- ./config/nginx/default.conf.${APP_ENV}:/etc/nginx/conf.d/default.conf
- ./config/certs:/etc/letsencrypt
- ./config/certs-data:/data/letsencrypt
- ./config/certs/selfsigned:/etc/ssl/selfsigned
- ./logs/nginx:/var/log/nginx
networks:
- app_network
mariadb:
image: mariadb:10.6
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
volumes:
- ./db/mysql:/var/lib/mysql
- ./db/init:/docker-entrypoint-initdb.d
command: [
'--default_authentication_plugin=mysql_native_password',
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_unicode_ci',
'--sql_mode=NO_ENGINE_SUBSTITUTION'
]
networks:
- app_network
memcached:
image: memcached:1.6.38
networks:
- app_network
# gearman:
# image: artefactual/gearmand:1.1.21.2-alpine
# restart: unless-stopped
# networks:
# - app_network
manticore_search:
build:
context: .
dockerfile: docker/dockerfile_manticore_search
image: manticoresearch/manticore:6.3.8
user: root
volumes:
- ./db/manticore_search:/var/manticore
- ./config/manticore_search:/etc/manticoresearch
- ./logs/manticore_search:/var/log/manticore
environment:
DB_HOST: mariadb
DB_DATABASE: ${MYSQL_DATABASE}
DB_USERNAME: ${MYSQL_USER}
DB_PASSWORD: ${MYSQL_PASSWORD}
networks:
- app_network
manticore_logs:
image: manticoresearch/manticore:6.3.8
volumes:
- ./db/manticore_logs:/var/manticore
- ./config/manticore_logs:/etc/manticoresearch
- ./logs/manticore_logs:/var/log/manticore
networks:
- app_network
certbot:
image: certbot/certbot
volumes:
- ./config/certs:/etc/letsencrypt
- ./config/certs-data:/data/letsencrypt
- ./app:/var/www/html/
- ./logs/certbot:/var/log/letsencrypt
entrypoint: /bin/sh -c
command: >
"trap exit TERM;
while :; do
certbot renew --webroot -w /var/www/html/www --quiet;
sleep 12h & wait $${!};
done"
networks:
- app_network
phpmyadmin:
image: phpmyadmin/phpmyadmin:latest
ports:
- "8080:80"
environment:
- PMA_HOST=mariadb
- PMA_PORT=3306
networks:
- app_network
networks:
app_network:
driver: bridge

1
docker-down.cmd Normal file
View File

@ -0,0 +1 @@
docker-compose down

1
docker-up.cmd Normal file
View File

@ -0,0 +1 @@
docker-compose up -d

67
docker/dockerfile_base Normal file
View File

@ -0,0 +1,67 @@
FROM php:7.4-fpm
# Установка системных зависимостей по частям для кэшируемости и отладки
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential autoconf gcc make
RUN apt-get install -y --no-install-recommends \
libfreetype6-dev libjpeg62-turbo-dev libpng-dev libwebp-dev libxpm-dev
RUN apt-get install -y --no-install-recommends \
libonig-dev libxml2-dev libzip-dev zlib1g-dev
RUN apt-get install -y --no-install-recommends \
libicu-dev libxslt1-dev
RUN apt-get install -y --no-install-recommends \
libmemcached-dev libmagickwand-dev libpspell-dev libsnmp-dev
RUN apt-get install -y --no-install-recommends \
libreadline-dev libfann-dev libgeoip-dev libgearman-dev
RUN apt-get install -y --no-install-recommends \
unzip zip cron supervisor
# Настройка GD
RUN docker-php-ext-configure gd \
--with-freetype \
--with-jpeg \
--with-webp \
--with-xpm
# Установка PHP-расширений по частям
RUN docker-php-ext-install -j$(nproc) bcmath calendar exif gettext gd
RUN docker-php-ext-install -j$(nproc) intl mbstring mysqli opcache pcntl
RUN docker-php-ext-install -j$(nproc) pdo pdo_mysql posix shmop
RUN docker-php-ext-install -j$(nproc) snmp soap sockets sysvmsg sysvsem sysvshm
RUN docker-php-ext-install -j$(nproc) xsl zip
RUN apt-get update && apt-get install -y libsodium-dev \
&& docker-php-ext-install sodium
# Очистка
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Установка PECL-расширений
RUN pecl install memcached-3.2.0 && docker-php-ext-enable memcached
RUN pecl install gearman-2.1.0 && docker-php-ext-enable gearman
RUN pecl install imagick-3.7.0 && docker-php-ext-enable imagick
RUN pecl install igbinary-3.2.14 && docker-php-ext-enable igbinary
RUN pecl install msgpack-2.1.2 && docker-php-ext-enable msgpack
RUN pecl install geoip-1.1.1 && docker-php-ext-enable geoip
RUN pecl install fann-1.1.1 && docker-php-ext-enable fann
RUN ln -sf /usr/share/zoneinfo/Europe/Moscow /etc/localtime && echo "Europe/Moscow" > /etc/timezone
# Установка composer
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
# Общий конфиг
COPY ./config/php/php.ini /usr/local/etc/php/
COPY ./config/php/www.conf /usr/local/etc/php-fpm.d/www.conf
# Рабочая директория
WORKDIR /var/www/html

7
docker/dockerfile_cron Normal file
View File

@ -0,0 +1,7 @@
FROM angels-it/php-base:latest
COPY ./config/cron /etc/cron.d
RUN chmod 0644 /etc/cron.d/* && crontab /etc/cron.d/*
WORKDIR /var/www/html
CMD ["cron", "-f"]

View File

@ -0,0 +1,3 @@
FROM manticoresearch/manticore:6.3.8
RUN apt-get update && apt-get install -y gettext-base

4
docker/dockerfile_php Normal file
View File

@ -0,0 +1,4 @@
FROM angels-it/php-base:latest
WORKDIR /var/www/html
CMD ["php-fpm"]

4
docker/dockerfile_worker Normal file
View File

@ -0,0 +1,4 @@
FROM angels-it/php-base:latest
WORKDIR /var/www/html/www/admin/workers/
CMD ["php", "/var/www/html/www/admin/workers/_gearman_client.php"]