feat(deploy): add deployment configuration for Liara with Docker and Supervisor

This commit is contained in:
hamed
2026-06-29 15:03:30 +03:30
parent 75dcf0d2a8
commit efee966efb
6 changed files with 528 additions and 6 deletions
+173
View File
@@ -0,0 +1,173 @@
# راهنمای دیپلوی ClinicPro روی لیارا (Docker)
این راهنما قدم‌به‌قدم نشان می‌دهد چطور بک‌اند Symfony را روی **لیارا** با پلتفرم **Docker** بالا بیاوری.
> چرا متفاوت با Coolify؟ لیارا **از docker-compose پشتیبانی نمی‌کند**. استک ۳-سرویسی compose (وب + ۲ worker) به یک **برنامهٔ داکر تکی** تبدیل شده که هر سه پروسه را با Supervisor اجرا می‌کند. MariaDB و Redis سرویس‌های مدیریت‌شدهٔ جدا روی شبکهٔ خصوصی‌اند.
---
## نمای کلی معماری
```
┌─────────────────────────── Liara private network ───────────────────────────┐
│ │
│ ┌────────────────────────────┐ ┌───────────────┐ ┌──────────────┐ │
│ │ clinicpro-api (Docker) │ │ MariaDB 11.8 │ │ Redis │ │
│ │ Supervisor: │◄────►│ (managed) │ │ (managed) │ │
│ │ • php-fpm + nginx :8080 │ └───────────────┘ └──────────────┘ │
│ │ • worker-async │ │
│ │ • worker-scheduler │ disks: jwt / uploads / var-uploads │
│ └─────────────┬──────────────┘ │
└─────────────────┼────────────────────────────────────────────────────────────┘
│ :8080 (تنها پورت HTTP بیرونی)
https://<your-domain>
```
فایل‌های کلیدی در ریپو:
- [`liara.json`](../liara.json) — پیکربندی دیپلوی (platform، port، دیسک‌ها، healthcheck)
- [`Dockerfile`](../Dockerfile) — image چندمرحله‌ای (بدون تغییر برای لیارا)
- [`docker/supervisord.conf`](../docker/supervisord.conf) — اجرای php-fpm + nginx + دو worker
- [`docker/entrypoint.sh`](../docker/entrypoint.sh) — wait-for-DB، تولید JWT، migration
- [`.env.liara.example`](../.env.liara.example) — مرجع متغیرهای محیطی
---
## پیش‌نیازها
```bash
npm i -g @liara/cli
liara login
```
مقادیری که از قبل آماده کن:
```bash
# APP_SECRET
php -r "echo bin2hex(random_bytes(32)).\"\n\";"
# JWT_PASSPHRASE
openssl rand -hex 32
# دامنه‌های فرانت (ALLOWED_FRONTEND_HOSTS + CORS_ALLOW_ORIGIN)
ddev exec php docker/gen-cors-env.php
```
---
## گام ۱ — ساخت دیتابیس و Redis
در [کنسول لیارا](https://console.liara.ir):
1. **MariaDB 11.8** بساز. (نسخه باید با `serverVersion=mariadb-11.8.0` در `DATABASE_URL` یکی باشد.)
2. **Redis** بساز.
3. روی هر دو **شبکهٔ خصوصی** را فعال کن.
4. **هاست خصوصی** و کاربر/رمز هرکدام را از صفحهٔ سرویس یادداشت کن (مثلاً `clinicpro-db`، `clinicpro-redis`).
---
## گام ۲ — ساخت برنامهٔ داکر
1. در کنسول یک **App** از نوع **Docker** بساز با شناسهٔ `clinicpro-api` (همان `app` در `liara.json`).
2. آن را به **همان شبکهٔ خصوصیِ** DB و Redis متصل کن.
---
## گام ۳ — ساخت دیسک‌های ماندگار
در صفحهٔ برنامه، بخش **Disks**، این سه دیسک را با **همین نام‌ها** بساز (مسیر mount از `liara.json` خوانده می‌شود):
| نام دیسک | mountTo | محتوا |
|---|---|---|
| `jwt` | `/app/config/jwt` | کلید JWT (نباید هر دیپلوی نو شود) |
| `uploads` | `/app/public/uploads` | فایل‌های عمومی آپلودی |
| `var-uploads` | `/app/var/uploads` | فایل‌های خصوصی آپلودی |
> ⚠️ بدون دیسک `jwt`، هر دیپلوی کلید جدید می‌سازد و **همهٔ توکن‌های صادرشده باطل** می‌شوند.
---
## گام ۴ — ست‌کردن متغیرهای محیطی
از روی [`.env.liara.example`](../.env.liara.example) مقادیر را در تب **Environment** یا با CLI ست کن:
```bash
liara env set APP_ENV=prod APP_DEBUG=0 --app clinicpro-api
liara env set APP_SECRET=<...> JWT_PASSPHRASE=<...> --app clinicpro-api
# هاست‌ها = هاست خصوصی سرویس‌های لیارا
liara env set DATABASE_URL="mysql://<user>:<pass>@<db-private-host>:3306/<db>?serverVersion=mariadb-11.8.0&charset=utf8mb4" --app clinicpro-api
liara env set REDIS_URL="redis://<redis-private-host>:6379" --app clinicpro-api
liara env set MESSENGER_TRANSPORT_DSN="redis://<redis-private-host>:6379/messages" --app clinicpro-api
liara env set APP_BASE_URL=https://<domain> DEFAULT_URI=https://<domain> --app clinicpro-api
liara env set TRUSTED_PROXIES="10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1" --app clinicpro-api
liara env set ALLOWED_FRONTEND_HOSTS="<output>" CORS_ALLOW_ORIGIN="<output>" --app clinicpro-api
```
نکته‌ها:
- `RUN_INIT` را ست **نکن**`entrypoint.sh` پیش‌فرض `1` می‌گیرد و چون تک‌برنامه است، تولید JWT + migration یک‌بار اجرا می‌شود.
- کلیدهای SMS و درگاه پرداخت از **دیتابیس** خوانده می‌شوند، نه env.
---
## گام ۵ — دیپلوی
از ریشهٔ `clinicpro/`:
```bash
liara deploy
```
`liara.json` تنظیمات platform/port/disks/healthcheck را می‌دهد، پس فلگ اضافه لازم نیست. در اولین بالا آمدن:
1. `entrypoint.sh` تا ~۶۰ ثانیه صبر می‌کند تا DB جواب دهد.
2. کلید JWT می‌سازد (روی دیسک `jwt`).
3. کش prod را warmup می‌کند.
4. `doctrine:migrations:migrate` را اجرا می‌کند.
5. Supervisor، nginx/php-fpm و هر دو worker را بالا می‌آورد.
---
## گام ۶ — اتصال دامنه و تأیید
1. در بخش **Domains** برنامه، دامنهٔ API را وصل کن و TLS بگیر.
2. تست سلامت:
```bash
curl -i https://<domain>/health # باید 200 بدهد
```
3. لاگ‌ها:
```bash
liara logs --app clinicpro-api --follow
```
باید php-fpm، nginx، `worker-async` و `worker-scheduler` هر چهار بالا باشند.
4. ورود به پنل ادمین `/admin` و تست یک endpoint برای تأیید اتصال DB/Redis.
---
## دیپلوی‌های بعدی
```bash
git pull # یا تغییرات محلی
liara deploy
```
migrationهای جدید خودکار در `entrypoint.sh` اجرا می‌شوند. دیسک‌ها و env بین دیپلوی‌ها حفظ می‌شوند.
---
## رفع اشکال
| نشانه | علت محتمل | راه‌حل |
|---|---|---|
| `Database not reachable after 60s` در لاگ | شبکهٔ خصوصی وصل نیست یا `DATABASE_URL` غلط | DB و app روی یک شبکهٔ خصوصی باشند؛ هاست خصوصی و رمز را چک کن |
| توکن‌ها بعد از هر دیپلوی باطل | دیسک `jwt` mount نشده | دیسک `jwt` روی `/app/config/jwt` بساز |
| `/health` غیر-۲۰۰ | DB/Redis در دسترس نیست یا migration نخورده | لاگ entrypoint را ببین |
| خطای CORS از فرانت | `CORS_ALLOW_ORIGIN`/`ALLOWED_FRONTEND_HOSTS` قدیمی | `docker/gen-cors-env.php` را دوباره بزن و env را به‌روزرسانی کن |
| آپلودها بعد از دیپلوی گم می‌شوند | دیسک uploads mount نشده | دیسک‌های `uploads` و `var-uploads` را بساز |
---
## گزینهٔ جایگزین — worker مجزا
اگر خواستی workerها ایزوله باشند (به‌جای Supervisor تک‌کانتینر)، همین ریپو را به‌عنوان برنامهٔ داکر دومی با `liara.json` متفاوت دیپلوی کن که `command` را به `php bin/console messenger:consume ...` override کند، `RUN_INIT=0` بدهد و **بدون** پورت/دامنه باشد. گران‌تر (دو برنامهٔ داکر) ولی ایزوله‌تر. برای اکثر موارد گزینهٔ پیش‌فرض (Supervisor) کافی است.