feat(deploy): add deployment configuration for Liara with Docker and Supervisor
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
# دیپلوی 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/*` لازم نیست.
|
||||
@@ -0,0 +1,64 @@
|
||||
# ============================================================
|
||||
# Liara Environment Variables — ClinicPro (Docker, single app)
|
||||
# ------------------------------------------------------------
|
||||
# Liara does NOT support docker-compose. The whole app (web + both message
|
||||
# consumers) runs as ONE Docker app via Supervisor (docker/supervisord.conf).
|
||||
# MariaDB and Redis are SEPARATE Liara managed services on the PRIVATE NETWORK.
|
||||
#
|
||||
# Set these in the Liara console (Environment tab) or via the CLI:
|
||||
# liara env set KEY=VALUE --app clinicpro-api
|
||||
# Build/deploy config (platform, port, disks, healthcheck) lives in liara.json.
|
||||
# ============================================================
|
||||
|
||||
APP_ENV=prod
|
||||
APP_DEBUG=0
|
||||
|
||||
# ── Secrets (REQUIRED — set before the first deploy) ──
|
||||
APP_SECRET= # php -r "echo bin2hex(random_bytes(32));"
|
||||
JWT_PASSPHRASE= # openssl rand -hex 32 (JWT keypair is generated with it on first boot)
|
||||
|
||||
# ── Database / Redis (point at the Liara MANAGED services via PRIVATE hostnames) ──
|
||||
# Create a MariaDB 11.8 service + a Redis service in Liara, enable private network,
|
||||
# put both + this app on the SAME private network, then copy their private hosts here.
|
||||
# serverVersion MUST match the MariaDB service (11.8).
|
||||
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"
|
||||
# If the Redis service has a password: redis://:<pass>@<redis-private-host>:6379
|
||||
|
||||
# ── JWT key paths (keys live on the persistent 'jwt' disk, see liara.json) ──
|
||||
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
|
||||
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
|
||||
|
||||
# ── Backend's own domain (API host — used for payment callbacks & absolute URLs) ──
|
||||
APP_BASE_URL=https://<your-liara-domain>
|
||||
DEFAULT_URI=https://<your-liara-domain>
|
||||
|
||||
# ── Reverse proxy (Liara router) — trust X-Forwarded-* from the private ranges ──
|
||||
TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1
|
||||
|
||||
# ── Frontend domains (MANY) — GENERATED from docker/frontend-domains.json ──
|
||||
# To add/remove a city domain: edit that file, then run:
|
||||
# ddev exec php docker/gen-cors-env.php (or: php docker/gen-cors-env.php on the server)
|
||||
# and paste the new output into both vars below.
|
||||
ALLOWED_FRONTEND_HOSTS=<output of docker/gen-cors-env.php>
|
||||
CORS_ALLOW_ORIGIN=<output of docker/gen-cors-env.php>
|
||||
|
||||
# ── api.ir identity inquiry (Shahkar + IbanMatch) ──
|
||||
# Empty token => fail-closed (representative verification is rejected).
|
||||
API_IR_BASE_URL=https://s.api.ir
|
||||
API_IR_TOKEN=
|
||||
|
||||
# ── Fixed app params (same values as the compose stack) ──
|
||||
REFRESH_TOKEN_TTL=2592000
|
||||
OTP_TTL=1200
|
||||
MAX_FILE_SIZE_BYTES=5242880
|
||||
UPLOAD_DIR=var/uploads
|
||||
|
||||
# ── Notes ──
|
||||
# • Do NOT set RUN_INIT — entrypoint.sh defaults it to 1, and this is a single app,
|
||||
# so JWT keygen + doctrine migrations run once on boot (correct).
|
||||
# • SMS keys (kavenegar/rangineh) and payment gateway keys (mellat/sep) are read
|
||||
# from the DB ("Site Settings"), NOT from env. No need to set them here.
|
||||
# • Liara exposes only ONE external HTTP port (8080, from liara.json). MariaDB/Redis
|
||||
# are reachable on the private network only.
|
||||
@@ -38,3 +38,30 @@ stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
# Message consumers. On Coolify these were separate compose services; Liara has
|
||||
# no docker-compose, so they run here inside the single Docker app. --time-limit
|
||||
# makes each consumer exit cleanly every hour; autorestart brings it back (guards
|
||||
# against memory growth / stale DB+Redis connections). Mirrors the compose commands.
|
||||
[program:worker-async]
|
||||
command=php bin/console messenger:consume async --time-limit=3600 --memory-limit=128M -v
|
||||
autorestart=true
|
||||
priority=30
|
||||
# messenger handles SIGTERM gracefully — finish the in-flight message, then exit.
|
||||
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
|
||||
|
||||
@@ -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) کافی است.
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"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" }
|
||||
]
|
||||
}
|
||||
+11
-5
@@ -8,11 +8,17 @@
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"skipLibCheck": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["assets/admin/*"]
|
||||
}
|
||||
"@/*": [
|
||||
"./assets/admin/*"
|
||||
]
|
||||
},
|
||||
"include": ["assets/admin/**/*"],
|
||||
"exclude": ["node_modules", "public"]
|
||||
},
|
||||
"include": [
|
||||
"assets/admin/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"public"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user