230 lines
10 KiB
Markdown
230 lines
10 KiB
Markdown
# Liara Deployment Playbook (عمومی)
|
||
|
||
راهنمای عمومی دیپلوی روی Liara — جمعبندی مشکلات رایج و راهحلهاشون.
|
||
برای **هر پروژهای** قابل استفادهست. جای `<...>`ها مقدار پروژهی خودت رو بذار.
|
||
|
||
> قرارداد نامگذاری در این سند:
|
||
> `<app>` = اسم اپ، `<frontend-app>` = اپ فرانت، `<panel-app>` = اپ بکاند/پنل،
|
||
> `<db-internal-host>` = هاست داخلی دیتابیس، `<TOKEN>` = API token لیارا،
|
||
> `<port>` = پورتی که اپ روش گوش میده (Docker static: معمولاً 7860، Node: 3000).
|
||
|
||
---
|
||
|
||
## ۱) CLI — احراز هویت
|
||
|
||
**مشکل:** دستورها با `Authentication failed` رد میشن، حتی بعد از `liara login` (توکن بین شلها/محیطها share نمیشه).
|
||
|
||
**راهحل:** روی **هر** دستور `--api-token` بده:
|
||
```bash
|
||
liara deploy --app <app> --api-token "<TOKEN>"
|
||
```
|
||
توکن از داشبورد Liara → API.
|
||
|
||
---
|
||
|
||
## ۲) `liara deploy` روی prompt پورت هنگ میکنه
|
||
|
||
**مشکل:** دیپلوی روی `Enter the port your app listens to:` گیر میکنه و چیزی به Liara نمیرسه (مخصوصاً در حالت غیرتعاملی).
|
||
|
||
**راهحل:** پورت رو صریح بده — هم فلگ، هم `liara.json`:
|
||
```bash
|
||
liara deploy --app <app> --port <port> ...
|
||
```
|
||
```json
|
||
{ "platform": "docker", "port": <port> }
|
||
```
|
||
اپ هم باید روی `process.env.PORT` گوش بده (Liara اون رو inject میکنه).
|
||
|
||
---
|
||
|
||
## ۳) `Dockerfile cannot be empty`
|
||
|
||
**علت:** `.dockerignore` خود `Dockerfile` رو exclude کرده.
|
||
|
||
**راهحل:** `Dockerfile` رو از `.dockerignore` **بردار** — Liara باید Dockerfile رو توی آرشیو ببینه.
|
||
|
||
---
|
||
|
||
## ۴) Docker Hub از ایران بلاکه (مهمترین برای Docker)
|
||
|
||
**مشکل:** `FROM <image>` با `Get "https://registry-1.docker.io/...": Client.Timeout exceeded` تایماوت میخوره.
|
||
|
||
**راهحل:** image پایه رو از **میرور Liara** بکش:
|
||
```dockerfile
|
||
# ایمیج رسمی (node, nginx, postgres, python, ...):
|
||
FROM docker-mirror.liara.ir/library/<image>:<tag>
|
||
# ایمیج یوزر/سازمان:
|
||
FROM docker-mirror.liara.ir/<org>/<image>:<tag>
|
||
```
|
||
|
||
میرورهای دیگر Liara (در صورت نیاز داخل build):
|
||
| نوع | میرور |
|
||
|---|---|
|
||
| Docker Hub | `docker-mirror.liara.ir` |
|
||
| Alpine (apk) | `https://linux-mirror.liara.ir/repository/alpine/` |
|
||
| npm | `https://package-mirror.liara.ir/repository/npm/` |
|
||
|
||
> `npm ci`/`pip install` معمولاً از ایران بدون میرور کار میکنن؛ فقط Docker Hub همیشه مشکلسازه.
|
||
> اگه داخل build `apk add` داری، میرور apk رو هم ست کن.
|
||
|
||
---
|
||
|
||
## ۵) لوکیشن build
|
||
|
||
**نکتهی کلیدی:** **لوکیشن build ≠ لوکیشن میزبانی.** اپ همیشه روی منطقهی خودش سرو میشه؛ `--build-location` فقط میگه image کجا کامپایل شه.
|
||
|
||
- build ایران + میرورهای بالا → سریع و بدون تحریم:
|
||
```bash
|
||
liara deploy --app <app> --build-location iran ...
|
||
```
|
||
- اگه از میرور استفاده نمیکنی، build ایران به Docker Hub نمیرسه (از مورد ۴ استفاده کن).
|
||
|
||
---
|
||
|
||
## ۶) متغیرهای build-time در فرانت (Vite / CRA / Next static)
|
||
|
||
**مشکل:** متغیر API URL رو بهعنوان env روی اپ ست کردی ولی فرانت بهش وصل نشد.
|
||
|
||
**علت:** باندلرها متغیرهای `VITE_*` / `REACT_APP_*` / `NEXT_PUBLIC_*` رو **موقع build داخل باندل مینویسن** — env runtime روی کانتینر استاتیک بیاثره.
|
||
|
||
**راهحل:** توی Dockerfile قبل از build:
|
||
```dockerfile
|
||
ARG VITE_API_URL=https://<panel-app>.liara.run
|
||
ENV VITE_API_URL=$VITE_API_URL
|
||
RUN npm run build
|
||
```
|
||
|
||
---
|
||
|
||
## ۷) اتصال اپ به دیتابیس (Private Network)
|
||
|
||
**مشکلها:**
|
||
- آدرس داخلی دیتابیس → `getaddrinfo ENOTFOUND <db-internal-host>`.
|
||
- آدرس عمومی دیتابیس از داخل اپ Liara → `ECONNREFUSED`.
|
||
|
||
**علت:** اپهای Liara به endpoint عمومی DB وصل نمیشن؛ و هاست داخلی فقط وقتی resolve میشه که اپ و DB **روی یک Private Network مشترک** باشن.
|
||
|
||
**راهحل:**
|
||
1. هم دیتابیس هم اپ رو به **یک Private Network** وصل کن.
|
||
2. **connection string کاملِ** داخلی (نه فقط host:port):
|
||
```
|
||
DATABASE_URL=postgresql://<user>:<pass>@<db-internal-host>:5432/<db>
|
||
```
|
||
3. روی شبکهی داخلی TLS لازم نیست (برعکس endpoint عمومی که TLS میخواد).
|
||
|
||
---
|
||
|
||
## ۸) اپ موقع بوت کرش میکنه چون env ست نیست
|
||
|
||
**نشانه:** کانتینر `unhealthy` / `exit 255` و لاگ مثل `FATAL: <X> is not set`.
|
||
|
||
**راهحل:** env های حیاتی رو **قبل** از انتظارِ سالمبودن ست کن. تغییر env خودش ریاستارت میکنه.
|
||
ست با CLI (بهجای داشبورد):
|
||
```bash
|
||
liara env:set "KEY=VALUE" "KEY2=VALUE2" --app <app> --force --api-token "<TOKEN>"
|
||
```
|
||
> اگه کد بدون یه env حیاتی `process.exit` میکنه، تا ست نشه اصلاً بالا نمیاد.
|
||
|
||
---
|
||
|
||
## ۹) فایلسیستم Ephemeral
|
||
|
||
**مشکلها:** دیتابیس فایلی (SQLite) و پوشهی آپلود با هر ریدیپلوی پاک میشن؛ نوشتن/`mkdir` روی مسیرهای read-only کرش میکنه.
|
||
|
||
**راهحل:**
|
||
- دیتای دائمی → **دیتابیس مدیریتشده**.
|
||
- فایل/مدیا → **Object Storage** (مورد ۱۰).
|
||
- اگه واقعاً دیسک لازمه → یه **Disk** بساز و mount کن؛ مسیر رو **env-based** کن.
|
||
- هر نوشتن روی دیسک رو دفاعی بنویس که موقع بوت کرش نکنه:
|
||
```js
|
||
const dir = process.env.UPLOADS_DIR || path.join(__dirname, 'uploads');
|
||
try { if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); } catch {}
|
||
```
|
||
|
||
---
|
||
|
||
## ۱۰) Object Storage (S3) برای مدیا
|
||
|
||
- S3-compatible؛ endpoint مثل `storage.iran.liara.site` + bucket + access key + secret key.
|
||
- در کد: SDK استری (`@aws-sdk/client-s3`)، `forcePathStyle: true`، endpoint = `https://<endpoint>`.
|
||
- URL عمومی فایل: `https://<bucket>.<endpoint>/<key>`.
|
||
- **باکت رو public کن** وگرنه فایلها 403 میشن.
|
||
|
||
---
|
||
|
||
## ۱۱) پلتفرم اپ ثابته
|
||
|
||
نمیشه پلتفرم یه اپ رو عوض کرد (Node↔Docker↔Static). اگه اشتباه ساختی، **پاک و با پلتفرم درست دوباره بساز** (env و اتصال شبکهی خصوصی رو هم از نو ست کن).
|
||
|
||
---
|
||
|
||
## ۱۲) CORS
|
||
|
||
**مشکل:** بکاند درخواستها رو با `Not allowed by CORS` رد میکنه (حتی origin خودش).
|
||
|
||
**راهحل:** allow-list رو شامل **دامنهی خود اپ + دامنهی فرانت** کن:
|
||
```
|
||
ALLOWED_ORIGINS=https://<panel-app>.liara.run,https://<frontend-app>.liara.run
|
||
```
|
||
|
||
---
|
||
|
||
## ۱۳) Seed / یوزر ادمین
|
||
|
||
دیتابیس تازه خالیه → لاگین «رمز اشتباه». seed رو از Console اپ بزن. اگه اسکریپت seed یوزر موجود رو رد میکنه و رمز عوض نمیشه، از **upsert** استفاده کن.
|
||
نکتهی مهم برای اسکریپتهای seed: آدرس و رمز رو **env-based** کن تا هم لوکال هم روی Liara کار کنه:
|
||
```js
|
||
const BASE = process.env.SEED_BASE || `http://localhost:${process.env.PORT || 3000}`;
|
||
```
|
||
> روش بدون TLS-dependency: اسکریپت seed که فقط `fetch` میزنه رو میتونی از ماشین خودت با `SEED_BASE=https://<panel-app>.liara.run` اجرا کنی.
|
||
|
||
---
|
||
|
||
## ۱۴) همگامسازی lockfile
|
||
|
||
بعد از هر تغییر وابستگیها، **لوکال `npm install`** بزن تا `package-lock.json` بهروز شه، بعد دیپلوی — وگرنه `npm ci` روی Liara بهخاطر mismatch خطا میده. `node_modules` رو پوش نکن (`.liaraignore` / `.dockerignore`).
|
||
|
||
---
|
||
|
||
## ۱۵) لاگ و exit code گمراهکننده
|
||
|
||
- `liara deploy` گاهی با `exit code 2` تموم میشه ولی واقعاً **موفق** بوده (خطا فقط مرحلهی خوندن لاگ بعد از دیپلوی).
|
||
- `liara logs` گاهی لاگ **کششده** میده.
|
||
|
||
**راهحل:** وضعیت واقعی رو از خروجی کامل دیپلوی (`√ Release created` / `Deployment finished successfully`) و یه **درخواست HTTP واقعی به دامنه** بگیر؛ به exit code تنها اعتماد نکن.
|
||
|
||
---
|
||
|
||
## فایلهای پایهی هر اپ
|
||
|
||
`liara.json` (Docker):
|
||
```json
|
||
{ "platform": "docker", "port": <port> }
|
||
```
|
||
`liara.json` (Node):
|
||
```json
|
||
{ "platform": "node" }
|
||
```
|
||
`.liaraignore`:
|
||
```
|
||
node_modules
|
||
.env
|
||
*.db
|
||
uploads
|
||
```
|
||
|
||
---
|
||
|
||
## چکلیست دیپلوی (ترتیب درست)
|
||
|
||
1. **دیتابیس** بساز → Private Network روشن.
|
||
2. **اپ بکاند** بساز → به همون Private Network وصلش کن.
|
||
3. env های بکاند رو ست کن (`DATABASE_URL` داخلیِ کامل، سکرتها، `ALLOWED_ORIGINS`، کلیدهای Object Storage، `NODE_ENV=production`).
|
||
4. `package-lock.json` رو لوکال sync کن.
|
||
5. (Docker) Dockerfile: میرور `docker-mirror.liara.ir` + `ARG` برای متغیرهای build-time. `.dockerignore` نباید `Dockerfile` رو حذف کنه.
|
||
6. `liara deploy --app <app> --port <port> --build-location iran --api-token "<TOKEN>"`.
|
||
7. سلامت رو با درخواست HTTP به دامنه چک کن (نه فقط exit code).
|
||
8. **seed محتوا/ادمین** (قبل از دیپلوی فرانت، تا سایت خالی نباشه).
|
||
9. باکت Object Storage رو public کن.
|
||
10. **فرانت** رو با متغیر build-time = دامنهی بکاند دیپلوی کن؛ `ALLOWED_ORIGINS` بکاند رو شامل دامنهی فرانت کن.
|