3.9 KiB
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:
nginx: Handles incoming HTTP traffic and reverse proxies.php: A single PHP-FPM container that handles all HTTP API requests (backend + payment).socket_driver: PHP CLI container running Workerman for driver WebSockets (Port 2020).socket_passenger: PHP CLI container running Workerman for passenger WebSockets (Port 3030).mysql: Database container.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
backenddirectory directly into thephpcontainer. - The
.envfile can safely reside in the rootbackendor project directory on the server as it currently does. - By passing
env_file: .envto the containers indocker-compose.yml, Docker injects the environment variables natively, making them available via PHP'sgetenv(). 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.