Update: 2026-07-21 16:59:10
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
# ============================================================
|
||||
# انسخه إلى .env واملأه من قيم اللايف — لا تخترع قيماً جديدة
|
||||
# cp .env.example .env
|
||||
# ============================================================
|
||||
|
||||
# --- عام ---
|
||||
PHP_VERSION=8.2
|
||||
HTTP_PORT=8181
|
||||
|
||||
# فارغ أثناء التجربة = حارس REGION_MISMATCH في connect.php يسمح بأي host
|
||||
# عند الربط بدومين حقيقي: ضع الدومين هنا
|
||||
APP_DOMAIN=
|
||||
|
||||
# --- قواعد البيانات (من داخل شبكة الحاويات: host = mysql) ---
|
||||
MYSQL_ROOT_PASSWORD=change-me
|
||||
DB_HOST=mysql
|
||||
DB_PORT=3306
|
||||
DB_USER=root
|
||||
DB_PASS=${MYSQL_ROOT_PASSWORD}
|
||||
# أسماء القواعد — طابقها مع mysql/init/00-databases.sql ومع ما يتوقعه الكود
|
||||
DB_NAME_PRIMARY=siro_primary
|
||||
DB_NAME_RIDE=siro_ride
|
||||
DB_NAME_TRACKING=siro_tracking
|
||||
DB_NAME_WALLET=siro_wallet
|
||||
DB_NAME_LOCATION=siro_location
|
||||
|
||||
# --- Redis (نسخة واحدة تكفي النسخة المستنسخة — الفصل في اللايف كان فصل سيرفرات) ---
|
||||
REDIS_HOST=redis
|
||||
REDIS_PORT=6379
|
||||
REDIS_LOCATION_HOST=redis
|
||||
REDIS_LOCATION_PORT=6379
|
||||
|
||||
# --- JWT (انسخ السر الحقيقي من .env اللايف — التطبيق لن يغيّر توكناته) ---
|
||||
JWT_SECRET_KEY=
|
||||
JWT_SECRET=
|
||||
|
||||
# --- التخاطب الداخلي بين الباك إند والسوكيتات (أسماء الخدمات لا IPs) ---
|
||||
LOCATION_SERVER_URL=http://socket_driver:2021
|
||||
RIDE_SOCKET_URL=http://socket_passenger:3031
|
||||
INTERNAL_KEY=
|
||||
|
||||
# --- مزودات خارجية (OTP / دفع / خرائط) — انسخ من اللايف عند الحاجة للاختبار ---
|
||||
# SMS_USERNAME=
|
||||
# SMS_PASSWORD_EGYPT=
|
||||
# ...
|
||||
@@ -0,0 +1,79 @@
|
||||
# سيرو في حاويات — دليل التشغيل والفحص
|
||||
|
||||
> الهدف: نسخة سيرو كاملة على سيرفر واحد داخل compose واحدة، والتطبيق لا يتغير فيه
|
||||
> شيء سوى الـ base URLs (تُبنى مرة واحدة بـ `.env` الجديد — envied وقت الترجمة).
|
||||
> القرارات والأسباب في `Tripz/docs/30-siro-port-plan.md` §5.
|
||||
|
||||
## 0) قبل أي شيء — على السيرفر
|
||||
```bash
|
||||
df -h / # ⚠️ الصندوق المشترك مزمنياً ~90% ممتلئ — الصور والقواعد تحتاج ~4-6GB
|
||||
docker image prune -f # نظّف أولاً، وإن لم تكفِ المساحة فالفحص على صندوق آخر
|
||||
```
|
||||
|
||||
## 1) الإعداد (مرة واحدة)
|
||||
```bash
|
||||
cd /path/to/Siro/docker
|
||||
cp .env.example .env && nano .env # املأ الأسرار من قيم اللايف — خاصة JWT_SECRET وكلمات MySQL
|
||||
# ⚠️ طابق أسماء القواعد في mysql/init/00-databases.sql مع ما يتوقعه الكود
|
||||
docker compose build
|
||||
# تبعيات composer للخدمات التي لا تحمل vendor/ داخلها:
|
||||
docker compose run --rm socket_driver composer install
|
||||
docker compose run --rm socket_passenger composer install
|
||||
docker compose run --rm --workdir /var/www/backend php composer install
|
||||
```
|
||||
|
||||
## 2) التشغيل والاستيراد
|
||||
```bash
|
||||
docker compose up -d
|
||||
# استيراد السكيمات (أول مرة فقط):
|
||||
docker compose exec -T mysql mysql -uroot -p"$MYSQL_ROOT_PASSWORD" siro_primary < ../backend/schema_primary.sql
|
||||
docker compose exec -T mysql mysql -uroot -p"$MYSQL_ROOT_PASSWORD" siro_ride < ../backend/schema_ride.sql
|
||||
docker compose exec -T mysql mysql -uroot -p"$MYSQL_ROOT_PASSWORD" siro_tracking < ../backend/schema_tracking.sql
|
||||
docker compose exec -T mysql mysql -uroot -p"$MYSQL_ROOT_PASSWORD" siro_wallet < ../payment_server/WalletDB.sql
|
||||
docker compose exec -T mysql mysql -uroot -p"$MYSQL_ROOT_PASSWORD" siro_location < ../loction_server/locationDB.sql
|
||||
```
|
||||
|
||||
## 3) التحقق
|
||||
```bash
|
||||
docker compose ps # الست خدمات up
|
||||
curl -s http://localhost:8080/backend/index.html # nginx→ملفات
|
||||
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8080/backend/connect.php # يرد (401/400 طبيعي بلا JWT)
|
||||
curl -s http://localhost:8080/fpm-status # حالة حوض fpm
|
||||
docker compose logs socket_driver | tail # Workerman أقلع على 2020/2021
|
||||
```
|
||||
اتصال التطبيق للتجربة: `http://IP:8080/backend` + سوكيت `http://IP:2020` — فقط بدّل
|
||||
الـ base URLs في `.env` تبع التطبيق وأعد البناء. `APP_DOMAIN` فارغ = حارس
|
||||
REGION_MISMATCH متعطل أثناء التجربة.
|
||||
|
||||
## 4) اختبار الضغط (الهدف: 10-20 ألف رحلة/ساعة)
|
||||
```bash
|
||||
cd ../stress_test && npm install
|
||||
# دخان أولاً (الأداة القديمة، دفعة صغيرة):
|
||||
node load_test.js --trips=10 -b http://localhost:8080/backend -s http://localhost:2020
|
||||
# ثم المعدل المستمر:
|
||||
node rate_test.js --rate 10000 --duration 10 -b http://localhost:8080/backend -s http://localhost:2020
|
||||
node rate_test.js --rate 20000 --duration 10 -b http://localhost:8080/backend -s http://localhost:2020
|
||||
node rate_test.js --rate 30000 --duration 5 ... # حد الانهيار — أين تبدأ p95 بالقفز؟
|
||||
```
|
||||
**أثناء التشغيل راقب من طرفية ثانية:**
|
||||
```bash
|
||||
docker stats # cpu/ذاكرة كل حاوية
|
||||
docker compose exec mysql sh -c 'tail -f /var/lib/mysql/slow.log' # استعلامات >200ms
|
||||
watch -n2 'curl -s http://localhost:8080/fpm-status' # طابور fpm (listen queue)
|
||||
```
|
||||
|
||||
### قواعد قراءة النتيجة بصدق
|
||||
1. **سيناريو الاختبار أخف من الرحلة الحقيقية بـ 3-5×** (4 نداءات API + 6 نبضات موقع؛
|
||||
الحقيقية فيها تسعير وبحث وقرب ودردشة). لتبنّي رقم "10k رحلة حقيقية بالذروة"
|
||||
يجب أن يعبر الاختبار **30k** بنجاح >99% و p95<500ms.
|
||||
2. الصندوق مشغول ~40% بغيرنا — **افحص خارج الذروة وبجولات قصيرة (5-10 د)**،
|
||||
وأعد الجولة مرتين للتأكد من ثبات الرقم.
|
||||
3. مولّد الحمل على نفس الصندوق يسرق ~5% CPU عند هذه المعدلات — مقبول؛ الأدق
|
||||
تشغيله من جهاز آخر نحو `http://IP:8080`.
|
||||
4. المُلتزم به للعملاء = الرقم الذي عبر الاختبار، لا أكثر.
|
||||
|
||||
## 5) ملاحظات أداء محسومة (لماذا الحاويات لا تبطئنا)
|
||||
- الحاوية على لينكس = عمليات عادية بنفس النواة (namespaces/cgroups) — **لا محاكاة ولا آلة افتراضية**. حمل CPU/ذاكرة ≈ صفر.
|
||||
- المكان الوحيد الذي يُخسر فيه أداء فعلاً: كتابة قاعدة البيانات فوق overlayfs — **محلول** بـ volume مسمّى (`mysql-data`).
|
||||
- أداء PHP الحقيقي يصنعه opcache + مقاس حوض fpm + فهارس MySQL — كلها مضبوطة في `php/` وليست متعلقة بالدوكر أصلاً.
|
||||
- التقسيم إلى 6 حاويات لا يكلف أداءً (نفس العمليات) ويعطي: إعادة تشغيل خدمة وحدها، حدود ذاكرة لكل خدمة، لوغات منفصلة، واستنساخ عميل = `clone + .env + up`.
|
||||
@@ -0,0 +1,124 @@
|
||||
# ============================================================
|
||||
# سيرو في حاويات — نسخة واحدة كاملة على سيرفر واحد
|
||||
# ============================================================
|
||||
# المبدأ: التقسيم بالدور (nginx / fpm / سوكيتات / mysql / redis)
|
||||
# لا حاوية-واحدة-فيها-كل-شيء، ولا تفتيت microservices.
|
||||
# الأداء = نفس العمليات على نفس النواة؛ الفرق تشغيلي فقط.
|
||||
#
|
||||
# التشغيل: docker compose up -d --build (انظر README.md)
|
||||
# ============================================================
|
||||
|
||||
name: siro
|
||||
|
||||
services:
|
||||
nginx:
|
||||
image: nginx:1.27-alpine
|
||||
ports:
|
||||
- "${HTTP_PORT:-8080}:80"
|
||||
volumes:
|
||||
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
- ../backend:/var/www/backend:ro
|
||||
- ../payment_server/v2:/var/www/v2:ro
|
||||
depends_on:
|
||||
- php
|
||||
mem_limit: 256m
|
||||
restart: unless-stopped
|
||||
|
||||
# php-fpm واحدة تخدم كل الخدمات الطلبية (backend + payment v2)
|
||||
# التقسيم لأبعد من هذا لا يضيف أداءً — نفس الصورة، نفس النواة.
|
||||
php:
|
||||
build:
|
||||
context: ./php
|
||||
dockerfile: Dockerfile.fpm
|
||||
args:
|
||||
PHP_VERSION: "${PHP_VERSION:-8.2}"
|
||||
volumes:
|
||||
- ../backend:/var/www/backend
|
||||
- ../payment_server/v2:/var/www/v2
|
||||
- ./php/opcache.ini:/usr/local/etc/php/conf.d/zz-opcache.ini:ro
|
||||
- ./php/www-pool.conf:/usr/local/etc/php-fpm.d/zz-pool.conf:ro
|
||||
env_file: .env
|
||||
depends_on:
|
||||
- mysql
|
||||
- redis
|
||||
mem_limit: 2g
|
||||
restart: unless-stopped
|
||||
|
||||
# سوكيت السائقين (Workerman + PHPSocketIO) — WS بورت 2020 + HTTP داخلي 2021
|
||||
# عملية دائمة، لا علاقة لها بـ fpm — حاوية مستقلة إجبارياً.
|
||||
socket_driver:
|
||||
build:
|
||||
context: ./php
|
||||
dockerfile: Dockerfile.socket
|
||||
args:
|
||||
PHP_VERSION: "${PHP_VERSION:-8.2}"
|
||||
command: ["php", "driver_socket.php", "start"]
|
||||
working_dir: /app
|
||||
volumes:
|
||||
- ../loction_server:/app
|
||||
env_file: .env
|
||||
ports:
|
||||
- "2020:2020" # WS للسائقين — عام
|
||||
# 2021 داخلي فقط: الباك إند يناديه عبر http://socket_driver:2021
|
||||
depends_on:
|
||||
- redis
|
||||
mem_limit: 768m
|
||||
restart: unless-stopped
|
||||
|
||||
# سوكيت الركاب (ride_server) — WS بورت 3030 + HTTP داخلي 3031
|
||||
socket_passenger:
|
||||
build:
|
||||
context: ./php
|
||||
dockerfile: Dockerfile.socket
|
||||
args:
|
||||
PHP_VERSION: "${PHP_VERSION:-8.2}"
|
||||
command: ["php", "passenger_socket.php", "start"]
|
||||
working_dir: /app
|
||||
volumes:
|
||||
- ../ride_server:/app
|
||||
env_file: .env
|
||||
ports:
|
||||
- "3030:3030"
|
||||
depends_on:
|
||||
- redis
|
||||
mem_limit: 512m
|
||||
restart: unless-stopped
|
||||
|
||||
mysql:
|
||||
image: mysql:8.4
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD:?set in .env}"
|
||||
# بيانات القاعدة على volume مسمّى — يتجاوز overlayfs = أداء قرص شبه أصلي.
|
||||
volumes:
|
||||
- mysql-data:/var/lib/mysql
|
||||
- ./mysql/init:/docker-entrypoint-initdb.d:ro
|
||||
command:
|
||||
- --innodb-buffer-pool-size=1G
|
||||
- --max-connections=300
|
||||
- --innodb-flush-log-at-trx-commit=1
|
||||
- --slow-query-log=1
|
||||
- --long-query-time=0.2
|
||||
- --slow-query-log-file=/var/lib/mysql/slow.log
|
||||
ports:
|
||||
- "127.0.0.1:33060:3306" # للإدارة من السيرفر نفسه فقط — ليس للعامة
|
||||
mem_limit: 2560m
|
||||
restart: unless-stopped
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
command:
|
||||
- redis-server
|
||||
- --maxmemory
|
||||
- 400mb
|
||||
- --maxmemory-policy
|
||||
- noeviction
|
||||
- --appendonly
|
||||
- "yes"
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
mem_limit: 512m
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
mysql-data:
|
||||
redis-data:
|
||||
@@ -0,0 +1,7 @@
|
||||
-- تُنشأ القواعد فارغة عند أول إقلاع فقط (volume جديد).
|
||||
-- ⚠️ عدّل الأسماء لتطابق ما تتوقعه ملفات .env في اللايف قبل أول تشغيل.
|
||||
CREATE DATABASE IF NOT EXISTS siro_primary CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE DATABASE IF NOT EXISTS siro_ride CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE DATABASE IF NOT EXISTS siro_tracking CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE DATABASE IF NOT EXISTS siro_wallet CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE DATABASE IF NOT EXISTS siro_location CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,40 @@
|
||||
# نقطة واحدة لكل الخدمات الطلبية — التطبيق يغيّر الدومين فقط:
|
||||
# /backend/... → الباك إند الرئيسي (نفس شكل مسارات jordan-siro)
|
||||
# /v2/main/... → خادم المدفوعات
|
||||
# السوكيتات تُنشر مباشرة من حاوياتها (2020 / 3030) — لا تمر من هنا.
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /var/www;
|
||||
index index.php index.html;
|
||||
client_max_body_size 25m;
|
||||
|
||||
access_log off; # أثناء اختبار الضغط؛ فعّله عند الحاجة
|
||||
error_log /dev/stderr warn;
|
||||
|
||||
location ~ \.php$ {
|
||||
try_files $uri =404;
|
||||
include fastcgi_params;
|
||||
fastcgi_pass php:9000;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_read_timeout 60s;
|
||||
fastcgi_buffers 16 16k;
|
||||
fastcgi_buffer_size 32k;
|
||||
}
|
||||
|
||||
# حالة حوض fpm — من داخل شبكة الحاويات أو من السيرفر نفسه فقط
|
||||
location = /fpm-status {
|
||||
allow 172.16.0.0/12;
|
||||
allow 127.0.0.1;
|
||||
deny all;
|
||||
include fastcgi_params;
|
||||
fastcgi_pass php:9000;
|
||||
fastcgi_param SCRIPT_NAME /status;
|
||||
fastcgi_param SCRIPT_FILENAME /status;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# صورة الخدمات الطلبية (backend + payment v2)
|
||||
ARG PHP_VERSION=8.2
|
||||
FROM php:${PHP_VERSION}-fpm
|
||||
|
||||
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
|
||||
RUN chmod +x /usr/local/bin/install-php-extensions && \
|
||||
install-php-extensions mysqli pdo_mysql redis opcache gd zip @composer
|
||||
|
||||
WORKDIR /var/www
|
||||
@@ -0,0 +1,10 @@
|
||||
# صورة السوكيتات الدائمة (Workerman) — driver_socket و passenger_socket
|
||||
# ext-event ضرورية فوق ~1024 اتصال متزامن (Workerman يلتقطها تلقائياً إن وُجدت)
|
||||
ARG PHP_VERSION=8.2
|
||||
FROM php:${PHP_VERSION}-cli
|
||||
|
||||
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
|
||||
RUN chmod +x /usr/local/bin/install-php-extensions && \
|
||||
install-php-extensions pcntl posix sockets event redis @composer
|
||||
|
||||
WORKDIR /app
|
||||
@@ -0,0 +1,9 @@
|
||||
; opcache هو الفرق الحقيقي في أداء PHP — ليس الدوكر
|
||||
opcache.enable=1
|
||||
opcache.memory_consumption=192
|
||||
opcache.interned_strings_buffer=16
|
||||
opcache.max_accelerated_files=20000
|
||||
; الكود bind-mounted في التجربة → إعادة تحقق كل 60 ثانية
|
||||
; عند التحويل لمنتج (كود مخبوز في الصورة): اجعلها 0 مع validate_timestamps=0
|
||||
opcache.validate_timestamps=1
|
||||
opcache.revalidate_freq=60
|
||||
@@ -0,0 +1,12 @@
|
||||
; حوض fpm مضبوط لصندوق مشترك 6 أنوية / 12GB (حصة الحاوية 2GB)
|
||||
; قانون Little: 170 rps × 0.05 ثانية ≈ 9 طلبات متزامنة فقط — 48 عاملاً فيها هامش واسع
|
||||
[www]
|
||||
pm = dynamic
|
||||
pm.max_children = 48
|
||||
pm.start_servers = 8
|
||||
pm.min_spare_servers = 4
|
||||
pm.max_spare_servers = 16
|
||||
pm.max_requests = 1000
|
||||
pm.status_path = /status
|
||||
slowlog = /proc/self/fd/2
|
||||
request_slowlog_timeout = 3s
|
||||
Reference in New Issue
Block a user