# ============================================================ # تربز في حاويات — نسخة واحدة كاملة على سيرفر واحد # ============================================================ # المبدأ: التقسيم بالدور (nginx / fpm / سوكيتات / mysql / redis) # لا حاوية-واحدة-فيها-كل-شيء، ولا تفتيت microservices. # الأداء = نفس العمليات على نفس النواة؛ الفرق تشغيلي فقط. # # التشغيل: docker compose up -d --build (انظر README.md) # # البورتات: تربز تستضيف على نفس سيرفر نسخ أخرى (siro/intaleq)، # فكل منفذ هنا مخصص لتربز ولا يتصادم معها: # HTTP 8183 · driver 12022 · passenger 13033 · food 14042 # mysql 33063 · phpmyadmin 8084 · coturn 3479 (UDP 49301–49400) # ============================================================ name: tripz-core services: nginx: image: nginx:1.27-alpine ports: - "${HTTP_PORT:-8183}:80" volumes: - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro - ../backend:/var/www/backend:ro - ../payment_server/v2:/var/www/v2:ro - ../loction_server:/var/www/loction_server:ro - ../dashboard:/var/www/dashboard:ro depends_on: - php - php_transit - php_food mem_limit: 256m restart: unless-stopped # php-fpm واحدة تخدم كل الخدمات الطلبية (backend + payment v2) # التقسيم لأبعد من هذا لا يضيف أداءً — نفس الصورة، نفس النواة. php: build: context: .. dockerfile: docker/php/Dockerfile.fpm args: PHP_VERSION: "${PHP_VERSION:-8.2}" volumes: - ../backend:/var/www/backend - ../payment_server/v2:/var/www/v2 - ../loction_server:/var/www/loction_server - ./php/opcache.ini:/usr/local/etc/php/conf.d/zz-opcache.ini:ro - ./php/php-prod.ini:/usr/local/etc/php/conf.d/zz-prod.ini:ro - ./php/www-pool.conf:/usr/local/etc/php-fpm.d/zz-pool.conf:ro - ./keys:/keys:ro env_file: .env depends_on: - mysql - redis mem_limit: 2g restart: unless-stopped # php-fpm معزولة لتخدم المواصلات (transit) — حاوية مستقلة عن الباك إند الرئيسي. # المشترك الوحيد مع النظام الأساسي: JWT والمصادقة والرحلات الداخلية عبر backend/core # (نُركّبها read-only)، ونفس mysql/redis. لا صلة بـ backend الرئيسي ولا payment v2. php_transit: build: context: .. dockerfile: docker/php/Dockerfile.fpm.transit args: PHP_VERSION: "${PHP_VERSION:-8.2}" volumes: - ../backend/core:/var/www/backend/core:ro - ../backend/functions.php:/var/www/backend/functions.php:ro # نفس سبب php_food: JwtService يحتاج Firebase\JWT من backend/vendor، # والصورة بلا composer. للقراءة فقط. - ../backend/vendor:/var/www/backend/vendor:ro - ../backend/transit:/var/www/backend/transit - ./php/opcache.ini:/usr/local/etc/php/conf.d/zz-opcache.ini:ro - ./php/php-prod.ini:/usr/local/etc/php/conf.d/zz-prod.ini:ro - ./php/www-pool.transit.conf:/usr/local/etc/php-fpm.d/zz-pool.conf:ro - ./keys:/keys:ro env_file: .env depends_on: - mysql - redis mem_limit: 512m restart: unless-stopped # php-fpm معزولة لوحدة طلبات الطعام — نفس منطق عزل php_transit. # المشترك الوحيد: JWT (backend/core) ونفس mysql/redis. قاعدة tripz_food منفصلة. php_food: build: context: .. dockerfile: docker/php/Dockerfile.fpm.transit args: PHP_VERSION: "${PHP_VERSION:-8.2}" volumes: - ../backend/core:/var/www/backend/core:ro - ../backend/functions.php:/var/www/backend/functions.php:ro # core/bootstrap.php يحمّل backend/vendor/autoload.php، وJwtService # يحتاج Firebase\JWT. الصورة هنا بلا composer عمداً، ودون هذا التركيب # كانت كل نقطة طعام تسقط بـ Class "Firebase\JWT\JWT" not found. # للقراءة فقط: مكتبات مشتركة لا كود مشترك — العزل باقٍ. - ../backend/vendor:/var/www/backend/vendor:ro - ../backend/food:/var/www/backend/food - ./php/opcache.ini:/usr/local/etc/php/conf.d/zz-opcache.ini:ro - ./php/php-prod.ini:/usr/local/etc/php/conf.d/zz-prod.ini:ro - ./php/food-pool.conf:/usr/local/etc/php-fpm.d/zz-pool.conf:ro - ./keys:/keys:ro env_file: .env depends_on: - mysql - redis mem_limit: 768m restart: unless-stopped # سوكيت الطعام — WS بورت 4040 + HTTP داخلي 4041 socket_food: build: context: ./php dockerfile: Dockerfile.socket args: PHP_VERSION: "${PHP_VERSION:-8.2}" command: ["php", "food_socket.php", "start"] working_dir: /app volumes: - ../food_server:/app - ./keys:/keys:ro env_file: .env ports: # نفس منطق سوكيت السائقين: لا نفتح 4040 للعالم مباشرة — nginx على # المضيف يستمع 4040 بالشهادة ويمرّر إلى 14042 هنا. 4041 داخلي فقط # (الباك إند يناديه عبر http://socket_food:4041). - "127.0.0.1:${FOOD_SOCKET_PORT:-14042}:4040" depends_on: - redis mem_limit: 512m 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 - ./keys:/keys:ro env_file: .env ports: # لا نفتح 2020 للعالم مباشرة: Workerman يتكلم نصاً صريحاً، والتطبيق يطلب # TLS على هذا المنفذ فتتجمّد المصافحة وينتهي بـ timeout. # nginx على المضيف يستمع على 2020 بالشهادة ويمرّر إلى 12022 هنا. # انظر nginx/tripz-sockets-tls.conf # المنفذ قابل للضبط: على سيرفر تعمل عليه أكثر من نسخة (siro/tripz/intaleq) # لا يتصادم. اضبط DRIVER_SOCKET_PORT في .env. - "127.0.0.1:${DRIVER_SOCKET_PORT:-12022}:2020" # و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 - ./keys:/keys:ro env_file: .env ports: # نفس منطق سوكيت السائقين: nginx على المضيف يستمع على 3030 بالشهادة # ويمرّر إلى 13033 هنا. و3031 داخلي فقط (http://socket_passenger:3031). # ⚠️ 13032 محجوزة من نسخة intaleq على السيرفر نفسه — لهذا استخدمنا 13033. - "127.0.0.1:${PASSENGER_SOCKET_PORT:-13033}: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: # للإدارة من السيرفر نفسه فقط — ليس للعامة. # 33061 يحتجزه intaleq-mysql-1 و33062 يحتجزه siro-mysql-1. - "127.0.0.1:33063:3306" mem_limit: 2560m restart: unless-stopped phpmyadmin: image: phpmyadmin/phpmyadmin environment: PMA_HOST: mysql PMA_PORT: 3306 UPLOAD_LIMIT: 300M ports: # مقيّد على loopback عمداً — لا تكشفه للإنترنت بلا TLS. # الوصول عبر نفق SSH فقط: ssh -N -L 8084:127.0.0.1:8084 USER@SERVER - "127.0.0.1:8084:80" depends_on: - mysql mem_limit: 256m 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 # خادم TURN لمكالمات الصوت (رحلات + توصيل الطعام). # network_mode: host إجباري هنا لا تفضيلاً: coturn يضع عنوان الخادم داخل # حمولة ICE، ولو مرّ عبر جسر دوكر لأعلن عنواناً داخلياً (172.x) فيفشل # التمرير. ومع host يسري جدار الحماية عادةً بدل أن يلتفّ عليه docker-proxy. # # ⚠️ منفذ TURN مختلف عن نسخة سيرو (3478 محجوزة هناك): هنا 3479. coturn: image: coturn/coturn:4.6-alpine network_mode: host volumes: - ./coturn/turnserver.conf:/etc/coturn/turnserver.conf:ro command: > -c /etc/coturn/turnserver.conf --realm=${TURN_REALM:-api.tripz-egypt.com} --static-auth-secret=${TURN_STATIC_AUTH_SECRET:?TURN_STATIC_AUTH_SECRET is required} --external-ip=${TURN_EXTERNAL_IP:?TURN_EXTERNAL_IP is required} --listening-ip=${TURN_EXTERNAL_IP} --relay-ip=${TURN_EXTERNAL_IP} mem_limit: 256m restart: unless-stopped volumes: mysql-data: redis-data: