Files
Siro/docs/30-siro-port-plan.md

3.9 KiB

Siro Backend Migration Plan & Docker Architecture (Docs/30)

1. Overview

This document outlines the architectural decisions made for migrating the Siro backend to a containerized Docker environment. It addresses questions regarding performance overhead, container granularity, and technology stack choices (PHP vs Node.js).

2. Docker Overhead vs Bare Metal

  • Finding: Docker on Linux is not a virtual machine. It utilizes Linux namespaces and cgroups to isolate processes.
  • CPU & Memory: The CPU and Memory overhead of running PHP inside Docker is effectively zero compared to bare metal. The processes run natively on the host kernel.
  • Disk I/O: The only measurable overhead comes from writing to the container's overlayfs (file system).
  • Solution: We mitigate this by using named volumes for MySQL data (mysql-data), which bypasses overlayfs and provides near-native disk performance.
  • Conclusion: Docker is heavily recommended. It gives you instant reproducibility (clone -> .env -> up) with a negligible performance difference (0-3%). The real performance factors are PHP opcache, MySQL indexes, and fpm pool sizes, all of which are properly tuned in the container configurations.

3. Container Strategy: Single vs Multiple Containers

  • Finding: Grouping everything (Nginx, PHP-FPM, MySQL, Redis, Node/Workerman) into a single container is considered a Docker anti-pattern.
  • Decision: The architecture uses 6 specialized containers running within a single server using Docker Compose:
    1. nginx: Handles incoming HTTP traffic and reverse proxies.
    2. php: A single PHP-FPM container that handles all HTTP API requests (backend + payment).
    3. socket_driver: PHP CLI container running Workerman for driver WebSockets (Port 2020).
    4. socket_passenger: PHP CLI container running Workerman for passenger WebSockets (Port 3030).
    5. mysql: Database container.
    6. redis: Caching container.
  • Why? Performance is identical to a monolithic single-container setup because all processes still run on the same server/kernel. However, this split gives critical operational advantages: independent service restarts, individual memory limits (e.g., preventing MySQL from consuming all RAM and crashing PHP), and separated logs. Over-segmenting beyond this (microservices) would be overkill.

4. WebSockets: PHP (Workerman) vs Node.js

  • Finding: Node.js uses an event-loop and is natively non-blocking, making it extremely lightweight for massive WebSocket concurrency. PHP-FPM allocates a process per request, which is bad for WebSockets.
  • However, the current implementation uses Workerman for PHP WebSockets. Workerman implements the exact same event-loop model as Node.js within PHP.
  • Scale: At a target of 10,000 to 20,000 trips per hour, the system experiences roughly 3 to 6 trips per second. This load is well within the capabilities of both Workerman and Node.js.
  • Conclusion: There is no need to rewrite the WebSockets in Node.js. Workerman handles the event-driven load efficiently. The real bottleneck will be the database and complex business logic, not the socket engine.

5. Environment Variables (.env)

  • The architecture mounts the backend directory directly into the php container.
  • The .env file can safely reside in the root backend or project directory on the server as it currently does.
  • By passing env_file: .env to the containers in docker-compose.yml, Docker injects the environment variables natively, making them available via PHP's getenv(). This requires zero code changes to the PHP application.

6. Stress Testing

  • A continuous stress test script (rate_test.js) is used instead of a batch load tester.
  • It generates JWTs natively in Node to avoid blocking on PHP processes.
  • The success criteria for 10,000 trips/hour is less than 1% failure rate and a p95 response time under 500ms.