Files
clinicpro/.claude/prompt/deploy-liara-docker.md
T

237 lines
14 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# دیپلوی ClinicPro (Symfony) روی لیارا با Docker
## پروژه
`clinicpro` (Backend Symfony 7.4 + پنل ادمین React، image داکر چندمرحله‌ای).
این یک **راهنمای دیپلوی + تسک پیاده‌سازی** است. هدف: انتقال استک فعلی (که برای **Coolify / docker-compose** ساخته شده) به **لیارا**، جایی که docker-compose پشتیبانی نمی‌شود.
> منبع: مستندات لیارا — quick-start، deploy-docker-compose، set-envs، use-disk، configure-supercronic.
---
## زمینه
پروژه همین الان یک استک داکر کامل دارد که برای Coolify تنظیم شده:
- `Dockerfile` — multi-stage (vendor → assets → runtime PHP-FPM + Nginx via Supervisor)، non-root `www-data`، `EXPOSE 8080`، healthcheck روی `/health`.
- `docker-compose.yml`**۳ سرویس**: `app` (وب) + `worker-async` + `worker-scheduler`؛ MariaDB و Redis سرویس‌های جدا (Coolify Database Resources).
- `docker/supervisord.conf` — فعلاً **فقط** `php-fpm` و `nginx` را اجرا می‌کند (workerها داخلش نیستند؛ در compose سرویس مجزا بودند).
- `docker/entrypoint.sh` — وقتی `RUN_INIT=1`: صبر برای DB → تولید کلید JWT → `cache:clear/warmup``doctrine:migrations:migrate`.
- `.env.coolify.example` — مرجع کامل متغیرهای محیطی.
- دیسک‌های ماندگار (volume) فعلی: `jwt_keys → /app/config/jwt`، `uploads_public → /app/public/uploads`، `uploads_var → /app/var/uploads`.
---
## مشکل / هدف
**لیارا از docker-compose مستقیم پشتیبانی نمی‌کند** («لیارا، به صورت مستقیم از Docker Compose پشتیبانی نمی‌کند»). پس ساختار ۳-سرویسی + DB/Redis جداگانه نمی‌تواند عیناً منتقل شود. باید بازچینش شود:
| جزء فعلی (compose) | معادل در لیارا |
|---|---|
| سرویس `app` (PHP-FPM+Nginx) | یک **برنامه داکر** روی لیارا (همین Dockerfile، پورت `8080`) |
| `worker-async` + `worker-scheduler` | داخل **همان** image با Supervisor اجرا شوند (توصیه‌شده، تک‌برنامه) — یا برنامه‌های داکر مجزا |
| سرویس MariaDB جدا | **دیتابیس مدیریت‌شدهٔ MariaDB لیارا** (شبکهٔ خصوصی) |
| سرویس Redis جدا | **Redis مدیریت‌شدهٔ لیارا** (شبکهٔ خصوصی) |
| volumeها (`jwt`, `uploads`) | **دیسک‌های لیارا** که در `liara.json` mount می‌شوند |
| env tab کولیفای | `liara env set` / کنسول لیارا |
**تصمیم معماری (پیش‌فرض توصیه‌شده): تک‌برنامهٔ داکر.** هر دو worker را به `supervisord.conf` اضافه کن تا در همان کانتینر کنار php-fpm/nginx اجرا شوند. ارزان‌ترین و نزدیک‌ترین گزینه به image فعلی. (دلیل اینکه supercronic مناسب نیست: هر دو worker پروسهٔ **بلندمدت** `messenger:consume` هستند نه job دوره‌ای cron — جای آن‌ها Supervisor است نه crontab. supercronic فقط اگر scheduler را به یک دستور one-shot تبدیل کنی به کار می‌آید؛ در «گزینه‌های جایگزین» پایین آمده.)
---
## فایل‌های مرتبط
| فایل | نقش | تغییر |
|------|-----|------|
| `Dockerfile` | image رانتایم | احتمالاً بدون تغییر (سازگار با لیارا است) |
| `docker/supervisord.conf` | پروسه‌منیجر کانتینر | **افزودن دو program برای workerها** |
| `docker/entrypoint.sh` | init و migration | بازبینی wait-for-DB (بدون compose `depends_on`) |
| `liara.json` | پیکربندی دیپلوی لیارا | **فایل جدید — ساخته شود** |
| `.env.liara.example` | مرجع env لیارا | **فایل جدید — اختیاری ولی توصیه‌شده** |
| `.dockerignore` | استثناهای build | بدون تغییر (`.env` عمداً نگه داشته می‌شود) |
---
## وضعیت فعلی (کد واقعی)
`docker/supervisord.conf` فقط دو program دارد:
```ini
[program:php-fpm]
command=php-fpm -F
autorestart=true
priority=10
...
[program:nginx]
command=nginx -g 'daemon off;'
autorestart=true
priority=20
...
```
`docker-compose.yml` workerها را اینطور اجرا می‌کند (همین دستورها باید به supervisord منتقل شوند):
```yaml
worker-async:
command: php bin/console messenger:consume async --time-limit=3600 --memory-limit=128M -v
worker-scheduler:
command: php bin/console messenger:consume scheduler_default --time-limit=3600 -v
```
---
## وظایف
### ۱. افزودن workerها به Supervisor
به انتهای `docker/supervisord.conf` این دو program را اضافه کن. مهم: فقط **یک** پروسه باید init/migration بزند؛ این کار را entrypoint با `RUN_INIT` کنترل می‌کند و چون اینجا تک‌کانتینر است مشکلی نیست (entrypoint قبل از `exec supervisord` یک‌بار اجرا می‌شود، نه per-program).
```ini
[program:worker-async]
command=php bin/console messenger:consume async --time-limit=3600 --memory-limit=128M -v
autorestart=true
priority=30
# مصرف‌کنندهٔ messenger با SIGTERM پیام در حال پردازش را تمام و سپس خارج می‌شود.
stopsignal=TERM
stopwaitsecs=30
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:worker-scheduler]
command=php bin/console messenger:consume scheduler_default --time-limit=3600 -v
autorestart=true
priority=30
stopsignal=TERM
stopwaitsecs=30
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
```
> `--time-limit=3600` باعث می‌شود پروسه هر ساعت سالم خارج شود و Supervisor با `autorestart=true` دوباره بالا بیاورد (جلوگیری از نشت حافظه / اتصال‌های بیات). دقیقاً رفتار compose.
### ۲. ساخت `liara.json` در ریشهٔ پروژه
```json
{
"app": "clinicpro-api",
"platform": "docker",
"port": 8080,
"healthCheck": {
"command": "/usr/local/bin/healthcheck.sh",
"interval": 30,
"timeout": 5,
"initialDelaySeconds": 60
},
"disks": [
{ "name": "jwt", "mountTo": "/app/config/jwt" },
{ "name": "uploads", "mountTo": "/app/public/uploads" },
{ "name": "var-uploads", "mountTo": "/app/var/uploads" }
]
}
```
نکات:
- `platform: "docker"` → لیارا از `Dockerfile` ریشه build می‌کند.
- `port: 8080` چون nginx داخل image روی 8080 (non-root) گوش می‌دهد و `EXPOSE 8080` ست شده.
- مسیر mount دیسک‌ها **absolute** و با احتساب `WORKDIR /app` نوشته شده (طبق مستند use-disk).
- پیش از دیپلوی، در کنسول لیارا این سه دیسک را با همین نام‌ها بساز: `jwt`، `uploads`، `var-uploads`.
- اگر فیلد `healthCheck` در نسخهٔ liara.json پشتیبانی نشد، حذفش کن و healthcheck را از کنسول ست کن؛ خود image هم `HEALTHCHECK` داخلی دارد.
### ۳. ساخت دیتابیس و Redis مدیریت‌شدهٔ لیارا
در کنسول لیارا:
1. یک دیتابیس **MariaDB 11.8** بساز (هماهنگ با `serverVersion=mariadb-11.8.0`). شبکهٔ خصوصی را فعال کن.
2. یک **Redis** بساز، شبکهٔ خصوصی فعال.
3. هاست داخلی هرکدام را از صفحهٔ سرویس بردار (روی شبکهٔ خصوصی، مثلاً `clinicpro-db` / `clinicpro-redis`).
4. برنامهٔ داکر و دو سرویس را روی **همان شبکهٔ خصوصی** قرار بده.
### ۴. ست‌کردن متغیرهای محیطی روی لیارا
با CLI (`liara env set KEY=VALUE --app clinicpro-api`) یا تب Environment در کنسول. حداقل متغیرهای موردنیاز (از `.env.coolify.example` گرفته شده، فقط هاست‌ها به سرویس‌های لیارا اشاره می‌کنند):
```bash
APP_ENV=prod
APP_DEBUG=0
APP_SECRET=<php -r "echo bin2hex(random_bytes(32));">
JWT_PASSPHRASE=<openssl rand -hex 32>
# هاست داخلی = نام سرویس دیتابیس لیارا روی شبکهٔ خصوصی
DATABASE_URL=mysql://<user>:<pass>@<db-private-host>:3306/<db>?serverVersion=mariadb-11.8.0&charset=utf8mb4
REDIS_URL=redis://<redis-private-host>:6379
MESSENGER_TRANSPORT_DSN=redis://<redis-private-host>:6379/messages
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
APP_BASE_URL=https://<your-liara-domain>
DEFAULT_URI=https://<your-liara-domain>
TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1
ALLOWED_FRONTEND_HOSTS=<از docker/gen-cors-env.php>
CORS_ALLOW_ORIGIN=<از docker/gen-cors-env.php>
API_IR_BASE_URL=https://s.api.ir
API_IR_TOKEN=<token>
REFRESH_TOKEN_TTL=2592000
OTP_TTL=1200
MAX_FILE_SIZE_BYTES=5242880
UPLOAD_DIR=var/uploads
```
نکات:
- در compose این مقادیر در `x-app-env` بودند؛ روی لیارا چون env-tab وجود دارد همه باید اینجا ست شوند.
- `ALLOWED_FRONTEND_HOSTS` و `CORS_ALLOW_ORIGIN` را با اجرای `php docker/gen-cors-env.php` تولید کن (از `docker/frontend-domains.json` می‌خواند).
- نیازی به ست‌کردن `RUN_INIT` نیست؛ entrypoint پیش‌فرض `1` می‌گیرد و چون تک‌برنامه است درست است.
- کلیدهای SMS و درگاه پرداخت از DB خوانده می‌شوند، نه env.
### ۵. اولین دیپلوی
```bash
# نصب CLI (یک‌بار)
npm i -g @liara/cli
liara login
# از ریشهٔ clinicpro:
liara deploy --app clinicpro-api --platform docker --port 8080
```
`liara.json` بیشتر این فلگ‌ها را پوشش می‌دهد، پس `liara deploy` خالی هم کافی است. در اولین بالا آمدن، `entrypoint.sh` صبر می‌کند تا DB جواب دهد، کلید JWT می‌سازد (روی دیسک `jwt` ماندگار)، و migrationها را می‌زند.
### ۶. تأیید سلامت
- لاگ‌ها: `liara logs --app clinicpro-api` — باید php-fpm، nginx، و هر دو worker بالا باشند.
- `GET https://<domain>/health``200`.
- ورود ادمین در `/admin` و تست یک endpoint تا اتصال DB/Redis تأیید شود.
---
## گزینه‌های جایگزین (در صورت نیاز، نه پیش‌فرض)
**الف) workerها به‌صورت برنامهٔ داکر مجزا** (به‌جای افزودن به Supervisor): همین repo را دوبار دیگر با `liara.json` متفاوت دیپلوی کن که `command` را override کند تا فقط `messenger:consume ...` اجرا شود و `RUN_INIT=0` و **بدون** پورت/healthcheck وب. گران‌تر (سه برنامهٔ داکر) ولی ایزوله‌تر.
**ب) supercronic برای scheduler:** اگر بخواهی scheduler را به‌جای مصرف‌کنندهٔ بلندمدت، به cron تبدیل کنی:
- در `Dockerfile`: `COPY --from=liaracloud/supercronic:v0.1.11 /usr/local/bin/supercronic /usr/local/bin/supercronic`
- فایل `crontab` در ریشه: `* * * * * cd /app && php bin/console messenger:consume scheduler_default --limit=1`
- یک `[program:supercronic]` در supervisord با `command=supercronic /app/crontab`.
- توجه: این الگو فقط برای کارهای **دوره‌ای** مناسب است؛ `worker-async` (SMS) باید مصرف‌کنندهٔ بلندمدت بماند. مگر نیاز خاص، **گزینهٔ پیش‌فرض (Supervisor) را نگه دار.**
---
## نکات مهم
- **بدون docker-compose:** هیچ `depends_on`/`service_healthy` روی لیارا نیست؛ منطق wait-for-DB در `entrypoint.sh` (تا ~۶۰s) دقیقاً برای همین لازم است — دست نخورد.
- **دیسک نه volume:** اگر روزی `VOLUME` در Dockerfile اضافه شد، طبق مستند لیارا حذفش کن و از دیسک لیارا استفاده کن. الان Dockerfile دستور `VOLUME` ندارد — خوب است.
- **ماندگاری کلید JWT:** دیسک `jwt` حتماً mount شود، وگرنه هر دیپلوی کلید نو می‌سازد و همهٔ توکن‌های صادرشده باطل می‌شوند (`--skip-if-exists` فقط وقتی دیسک ماندگار باشد کار می‌کند).
- **`.env` عمداً در image است** (به‌خاطر `Dotenv::bootEnv`)؛ مقادیر واقعی از env لیارا می‌آیند و Dotenv متغیر ازقبل‌ست‌شده را بازنویسی نمی‌کند (`clear_env=no` در `docker/php/zz-pool.conf`). این رفتار را خراب نکن.
- **هماهنگی نسخهٔ DB:** `serverVersion` در `DATABASE_URL` باید با نسخهٔ دیتابیس مدیریت‌شدهٔ لیارا یکی باشد (11.8).
- **پورت تک‌وب:** لیارا فقط یک پورت HTTP بیرونی می‌دهد (8080)؛ MariaDB/Redis فقط روی شبکهٔ خصوصی در دسترس‌اند — درست با معماری ما می‌خواند.
- **CORS چنددامنه:** بک‌اند برای ده‌ها دامنهٔ فرانت سرویس می‌دهد؛ بعد از تغییر `docker/frontend-domains.json` دوباره `gen-cors-env.php` بزن و env را به‌روزرسانی کن.
- این تغییر فقط زیرساخت دیپلوی است و هیچ endpoint/route را عوض نمی‌کند، پس به‌روزرسانی `docs/api/*` لازم نیست.