docs: add portable from-zero deploy runbook and GitOps templates
Document server-side rollout (values, Sealed Secrets, logging, greenfield reset) with environment variables so any cluster can follow the same steps. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -14,6 +14,8 @@ custom `wp-content` entrypoint).
|
|||||||
> Iran-network workarounds — is documented step-by-step in **[RUNBOOK.fa.md](RUNBOOK.fa.md)** (Persian).
|
> Iran-network workarounds — is documented step-by-step in **[RUNBOOK.fa.md](RUNBOOK.fa.md)** (Persian).
|
||||||
|
|
||||||
> 🔄 **CI/CD (Gitea Actions → Kaniko → Harbor → Argo CD):** see **[RUNBOOK-CICD.fa.md](RUNBOOK-CICD.fa.md)** (Persian) and **[gitops/README.md](gitops/README.md)** for bootstrap (`seed-ci-images`, Sealed Secrets, two-repo GitOps layout).
|
> 🔄 **CI/CD (Gitea Actions → Kaniko → Harbor → Argo CD):** see **[RUNBOOK-CICD.fa.md](RUNBOOK-CICD.fa.md)** (Persian) and **[gitops/README.md](gitops/README.md)** for bootstrap (`seed-ci-images`, Sealed Secrets, two-repo GitOps layout).
|
||||||
|
>
|
||||||
|
> 🚀 **Deploy from zero (any cluster):** **[RUNBOOK-DEPLOY.fa.md](RUNBOOK-DEPLOY.fa.md)** — server checklist, values, secrets, logging, greenfield reset.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -111,6 +113,8 @@ cloud-host/
|
|||||||
├── README.md # This file
|
├── README.md # This file
|
||||||
├── ARCHITECTURE.md # Detailed system design
|
├── ARCHITECTURE.md # Detailed system design
|
||||||
├── RUNBOOK.fa.md # Persian runbook: local dev + abrban/k3s production deploy
|
├── RUNBOOK.fa.md # Persian runbook: local dev + abrban/k3s production deploy
|
||||||
|
├── RUNBOOK-DEPLOY.fa.md # Deploy platform from zero (any cluster): values, secrets, health checks
|
||||||
|
├── RUNBOOK-CICD.fa.md # CI/CD pipeline: Gitea Actions → Kaniko → Argo CD
|
||||||
├── CHANGELOG.md / CONTRIBUTING.md / UPGRADE.md / UPGRADE.en.md
|
├── CHANGELOG.md / CONTRIBUTING.md / UPGRADE.md / UPGRADE.en.md
|
||||||
├── docker-compose.yml # Local dev stack (Postgres + Redis + API + UI)
|
├── docker-compose.yml # Local dev stack (Postgres + Redis + API + UI)
|
||||||
│
|
│
|
||||||
@@ -211,9 +215,13 @@ to the backend URL.
|
|||||||
|
|
||||||
## Deploy on Kubernetes (Helm)
|
## Deploy on Kubernetes (Helm)
|
||||||
|
|
||||||
> This is the **generic** path. For the production `abrban.com` k3s cluster — base-image
|
> **Production GitOps (from zero):** [`RUNBOOK-DEPLOY.fa.md`](RUNBOOK-DEPLOY.fa.md) — variable table, values, Sealed Secrets, logging, health checks.
|
||||||
> mirroring, the Iran-network proxy/npmmirror, the wildcard TLS cert, registry bootstrap,
|
>
|
||||||
> and the exact image-build flow — follow **[RUNBOOK.fa.md](RUNBOOK.fa.md)**.
|
> **Production abrban.com specifics:** [`RUNBOOK.fa.md`](RUNBOOK.fa.md) — Iran network, Ceph, Harbor details.
|
||||||
|
>
|
||||||
|
> **CI/CD pipeline:** [`RUNBOOK-CICD.fa.md`](RUNBOOK-CICD.fa.md).
|
||||||
|
|
||||||
|
This section is the **generic Helm-only** path (Path B in RUNBOOK-DEPLOY) without Gitea/Argo.
|
||||||
|
|
||||||
**Prerequisites:** a Kubernetes cluster, an Ingress controller (Traefik on k3s by default,
|
**Prerequisites:** a Kubernetes cluster, an Ingress controller (Traefik on k3s by default,
|
||||||
or set `INGRESS_CLASS=nginx`), a default StorageClass for PVCs, and a container registry
|
or set `INGRESS_CLASS=nginx`), a default StorageClass for PVCs, and a container registry
|
||||||
|
|||||||
+18
-5
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
این مستند جریان کامل Build و Deploy پلتفرم را توضیح میدهد: از Push شدن کد روی `main` تا استقرار خودکار روی Kubernetes.
|
این مستند جریان کامل Build و Deploy پلتفرم را توضیح میدهد: از Push شدن کد روی `main` تا استقرار خودکار روی Kubernetes.
|
||||||
|
|
||||||
|
> **استقرار از صفر روی سرور جدید:** [`RUNBOOK-DEPLOY.fa.md`](RUNBOOK-DEPLOY.fa.md) — شامل جدول متغیرها، seal کردن Secretها، logging stack، greenfield reset، و چکلیست سلامت.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## معماری و جریان کلی
|
## معماری و جریان کلی
|
||||||
@@ -26,9 +28,10 @@ flowchart TD
|
|||||||
1. Developer روی شاخهٔ `main` در ریپوی اپلیکیشن (`git.abrban.com/abrban/cloud-host`) push میکند.
|
1. Developer روی شاخهٔ `main` در ریپوی اپلیکیشن (`git.abrban.com/abrban/cloud-host`) push میکند.
|
||||||
2. Workflow در [`.gitea/workflows/build-deploy.yaml`](.gitea/workflows/build-deploy.yaml) روی Runner با لیبل `abrban-builder` اجرا میشود.
|
2. Workflow در [`.gitea/workflows/build-deploy.yaml`](.gitea/workflows/build-deploy.yaml) روی Runner با لیبل `abrban-builder` اجرا میشود.
|
||||||
3. Runner کد را با توکن CI کلون میکند و تگ ایمیج (`YYYYMMDD-HHMM-<sha>`) را میسازد.
|
3. Runner کد را با توکن CI کلون میکند و تگ ایمیج (`YYYYMMDD-HHMM-<sha>`) را میسازد.
|
||||||
4. برای هر ایمیج (backend و frontend) یک Kaniko Job در namespace `cloudhost-builds` ساخته میشود که کد را کلون، ایمیج را build و به Harbor push میکند.
|
4. **Job تست بکاند** در namespace `cloudhost-builds` اجرا میشود (`npm ci` + `jest --ci`) — در صورت fail، بیلد ایمیج شروع نمیشود.
|
||||||
5. بعد از موفقیت هر دو Build، همان Runner ریپوی **`cloud-host-gitops`** را کلون میکند، مقدار `images.backend.tag` و `images.frontend.tag` را در `platform/values-abrban.yaml` عوض و commit/push میکند.
|
5. برای هر ایمیج (backend و frontend) یک Kaniko Job در namespace `cloudhost-builds` ساخته میشود که کد را کلون، ایمیج را build و به Harbor push میکند.
|
||||||
6. Argo CD (Application به نام `abrban-platform` با sync خودکار) تغییر را تشخیص میدهد و نسخهٔ جدید را در namespace `cloudhost` مستقر میکند.
|
6. بعد از موفقیت هر دو Build، همان Runner ریپوی **`cloud-host-gitops`** را کلون میکند، مقدار `images.backend.tag` و `images.frontend.tag` را در `platform/values-abrban.yaml` عوض و commit/push میکند (با retry و `git pull --rebase` در صورت race).
|
||||||
|
7. Argo CD (Application به نام `abrban-platform` با sync خودکار) تغییر را تشخیص میدهد و نسخهٔ جدید را در namespace `cloudhost` مستقر میکند.
|
||||||
|
|
||||||
> **جلوگیری از حلقهٔ CI:** کامیتِ Pipeline به ریپوی جدا (`cloud-host-gitops`) میرود که هیچ Workflowای ندارد؛ بنابراین Build دوباره trigger نمیشود.
|
> **جلوگیری از حلقهٔ CI:** کامیتِ Pipeline به ریپوی جدا (`cloud-host-gitops`) میرود که هیچ Workflowای ندارد؛ بنابراین Build دوباره trigger نمیشود.
|
||||||
|
|
||||||
@@ -249,9 +252,16 @@ git push origin main
|
|||||||
| `gitea-act-runner-token` | `gitea` | توکن ثبت Runner |
|
| `gitea-act-runner-token` | `gitea` | توکن ثبت Runner |
|
||||||
| `kaniko-harbor-auth` | `cloudhost-builds` | dockerconfig کاربر `harbor_registry_user` |
|
| `kaniko-harbor-auth` | `cloudhost-builds` | dockerconfig کاربر `harbor_registry_user` |
|
||||||
| `gitea-gitops-repo-creds` | `argocd` | repo credential ریپوی gitops (کاربر `ci`) |
|
| `gitea-gitops-repo-creds` | `argocd` | repo credential ریپوی gitops (کاربر `ci`) |
|
||||||
| `abrban-platform-secrets` | `cloudhost` | postgres-password، jwt-secret، jwt-refresh-secret، **cluster-kubeconfig-key** |
|
| `abrban-platform-secrets` | `cloudhost` | postgres-password، jwt-secret، jwt-refresh-secret، **cluster-kubeconfig-key**، **redis-password** |
|
||||||
|
| `elasticsearch-credentials` | `logging` | ELASTIC_PASSWORD، FLUENTBIT_PASSWORD (خارج از چارت پلتفرم — [`elasticsearch-credentials.example.yaml`](gitops/sealed-secrets/elasticsearch-credentials.example.yaml)) |
|
||||||
|
|
||||||
چارت Helm با `secrets.existingSecret: abrban-platform-secrets` در `platform/values-abrban.yaml` (ریپوی gitops) از Secret ازپیشساخته استفاده میکند — Argo CD با `helm template` نمیتواند Secret تصادفی بسازد (lookup خالی است و هر sync مقادیر JWT را عوض میکند).
|
چارت Helm با `secrets.existingSecret: abrban-platform-secrets` در `platform/values-abrban.yaml` (ریپوی gitops) از Secret ازپیشساخته استفاده میکند — Argo CD با `helm template` نمیتواند Secret تصادفی بسازد (lookup خالی است و هر sync مقادیر JWT/Redis را عوض میکند).
|
||||||
|
|
||||||
|
نمونهٔ کامل values: [`gitops/platform/values-abrban.example.yaml`](gitops/platform/values-abrban.example.yaml) — شامل mirror ایمیج postgres/redis، `BASE_IMAGE_REGISTRY`، و envهای Elastic.
|
||||||
|
|
||||||
|
### Greenfield / ارتقا از نسخهٔ قدیم
|
||||||
|
|
||||||
|
اگر کلاستر قبلاً با schema یا namespace قدیمی بالا آمده، قبل از deploy جدید **reset دیتابیس** لازم است. مراحل کامل (با متغیرهای قابلتنظیم برای هر محیط) در **[`RUNBOOK-DEPLOY.fa.md` — فاز ۶](RUNBOOK-DEPLOY.fa.md#فاز-۶--greenfield--ارتقا-از-نسخهٔ-قدیم)**.
|
||||||
|
|
||||||
### ساخت/بهروزرسانی یک SealedSecret
|
### ساخت/بهروزرسانی یک SealedSecret
|
||||||
|
|
||||||
@@ -305,4 +315,7 @@ Secretهایی که هنوز دستیاند (خارج از چرخهٔ CI): `a
|
|||||||
| دیدن تگهای موجود در registry | از داخل کلاستر: `wget -qO- "http://harbor_registry_user:<REG_PASS>@harbor-registry.cloudhost.svc.cluster.local:5000/v2/abrban/cloudhost-backend/tags/list"` |
|
| دیدن تگهای موجود در registry | از داخل کلاستر: `wget -qO- "http://harbor_registry_user:<REG_PASS>@harbor-registry.cloudhost.svc.cluster.local:5000/v2/abrban/cloudhost-backend/tags/list"` |
|
||||||
| Backend CrashLoop — CLUSTER_KUBECONFIG_KEY | Secret `abrban-platform-secrets` باید کلید `cluster-kubeconfig-key` داشته باشد و در values: `secrets.existingSecret: abrban-platform-secrets` |
|
| Backend CrashLoop — CLUSTER_KUBECONFIG_KEY | Secret `abrban-platform-secrets` باید کلید `cluster-kubeconfig-key` داشته باشد و در values: `secrets.existingSecret: abrban-platform-secrets` |
|
||||||
| Backend CrashLoop — DB auth | پسورد postgres در Secret با DB واقعی همخوان باشد (`ALTER USER ... WITH PASSWORD` در صورت rotate شدن Secret) |
|
| Backend CrashLoop — DB auth | پسورد postgres در Secret با DB واقعی همخوان باشد (`ALTER USER ... WITH PASSWORD` در صورت rotate شدن Secret) |
|
||||||
|
| Backend CrashLoop — Redis auth | Secret `abrban-platform-secrets` باید کلید `redis-password` داشته باشد؛ backend و Redis پلتفرم هر دو از آن استفاده میکنند |
|
||||||
|
| Backend CrashLoop — ELASTIC_PASSWORD | در production مقدار پیشفرض رد میشود — env در values-abrban.yaml باید رمز rotateشده داشته باشد |
|
||||||
|
| Workflow fail — tests | Job `test-be-*` در ns `cloudhost-builds` — `kubectl logs job/... -c test` |
|
||||||
| SealedSecret باز نمیشود | `kubectl get sealedsecrets -A` (ستون SYNCED) و لاگ `kubectl -n kube-system logs deploy/sealed-secrets-controller` |
|
| SealedSecret باز نمیشود | `kubectl get sealedsecrets -A` (ستون SYNCED) و لاگ `kubectl -n kube-system logs deploy/sealed-secrets-controller` |
|
||||||
|
|||||||
@@ -0,0 +1,426 @@
|
|||||||
|
# RUNBOOK — استقرار پلتفرم CloudHost از صفر
|
||||||
|
|
||||||
|
این سند **کارهایی را که روی سرور/کلاستر باید انجام دهید** مرحلهبهمرحله توضیح میدهد — از bootstrap زیرساخت تا اولین deploy موفق پس از hardening.
|
||||||
|
|
||||||
|
> **برای چه کسی است:** هر کسی که میخواهد CloudHost را روی یک کلاستر Kubernetes تازه (یا کلاستر دیگری غیر از abrban) بالا بیاورد.
|
||||||
|
>
|
||||||
|
> **چه چیزی اینجا نیست:** جزئیات معماری اپ → [`RUNBOOK.fa.md`](RUNBOOK.fa.md)؛ جزئیات pipeline CI → [`RUNBOOK-CICD.fa.md`](RUNBOOK-CICD.fa.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## قبل از شروع — جدول متغیرها
|
||||||
|
|
||||||
|
همهٔ دستورات زیر از این متغیرها استفاده میکنند. **یکبار** آنها را برای محیط خودتان پر کنید:
|
||||||
|
|
||||||
|
| متغیر | توضیح | مثال abrban | مثال محیط جدید |
|
||||||
|
|-------|--------|-------------|----------------|
|
||||||
|
| `PLATFORM_NS` | namespace پلتفرم | `cloudhost` | `cloudhost` |
|
||||||
|
| `BUILD_NS` | namespace بیلد Kaniko | `cloudhost-builds` | `cloudhost-builds` |
|
||||||
|
| `LOGGING_NS` | namespace Elasticsearch | `logging` | `logging` |
|
||||||
|
| `REGISTRY_HOST` | آدرس pull ایمیج (Ingress/registry عمومی) | `registry.abrban.com` | `registry.example.com` |
|
||||||
|
| `REGISTRY_PROJECT` | پروژه Harbor برای ایمیجهای platform | `abrban` | `cloudhost` |
|
||||||
|
| `REGISTRY_PUSH` | endpoint داخلی push (بدون TLS) | `harbor-registry.cloudhost.svc.cluster.local:5000` | `registry.registry.svc:5000` |
|
||||||
|
| `GIT_HOST` | URL گیت (Gitea/GitHub) | `git.abrban.com` | `git.example.com` |
|
||||||
|
| `APP_REPO` | ریپوی کد + چارت | `abrban/cloud-host` | `org/cloud-host` |
|
||||||
|
| `GITOPS_REPO` | ریپوی state (values + sealed secrets) | `abrban/cloud-host-gitops` | `org/cloud-host-gitops` |
|
||||||
|
| `VALUES_FILE` | فایل values در gitops | `platform/values-abrban.yaml` | `platform/values-production.yaml` |
|
||||||
|
| `PLATFORM_SECRET` | Secret پلتفرم (JWT, DB, Redis, …) | `abrban-platform-secrets` | `cloudhost-platform-secrets` |
|
||||||
|
| `ARGO_APP` | نام Application در Argo CD | `abrban-platform` | `cloudhost-platform` |
|
||||||
|
| `DOMAIN_LANDING` | لندینگ | `abrban.com` | `example.com` |
|
||||||
|
| `DOMAIN_PANEL` | پنل | `panel.abrban.com` | `panel.example.com` |
|
||||||
|
| `DOMAIN_API` | API | `api.abrban.com` | `api.example.com` |
|
||||||
|
| `DOMAIN_APPS` | دامنهٔ اپهای کاربر | `apps.abrban.com` | `apps.example.com` |
|
||||||
|
| `STORAGE_CLASS` | StorageClass PVCها | `local-path` | `standard` |
|
||||||
|
| `INGRESS_CLASS` | Ingress controller | `traefik` | `nginx` |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# نمونه — قبل از اجرای دستورات export کنید:
|
||||||
|
export PLATFORM_NS=cloudhost
|
||||||
|
export BUILD_NS=cloudhost-builds
|
||||||
|
export LOGGING_NS=logging
|
||||||
|
export REGISTRY_HOST=registry.example.com
|
||||||
|
export REGISTRY_PROJECT=cloudhost
|
||||||
|
export REGISTRY_PUSH=harbor-registry.cloudhost.svc.cluster.local:5000
|
||||||
|
export GIT_HOST=git.example.com
|
||||||
|
export APP_REPO=org/cloud-host
|
||||||
|
export GITOPS_REPO=org/cloud-host-gitops
|
||||||
|
export VALUES_FILE=platform/values-production.yaml
|
||||||
|
export PLATFORM_SECRET=cloudhost-platform-secrets
|
||||||
|
export ARGO_APP=cloudhost-platform
|
||||||
|
export DOMAIN_LANDING=example.com
|
||||||
|
export DOMAIN_PANEL=panel.example.com
|
||||||
|
export DOMAIN_API=api.example.com
|
||||||
|
export DOMAIN_APPS=apps.example.com
|
||||||
|
export STORAGE_CLASS=standard
|
||||||
|
export INGRESS_CLASS=nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## دو مسیر استقرار
|
||||||
|
|
||||||
|
| | **مسیر A — GitOps (توصیه Production)** | **مسیر B — Helm مستقیم** |
|
||||||
|
|---|--------------------------------------|---------------------------|
|
||||||
|
| CI/CD | Gitea Actions → Kaniko → Argo CD | build/push دستی + `helm upgrade` |
|
||||||
|
| Values | ریپوی جدا `GITOPS_REPO` | فایل محلی `my-values.yaml` |
|
||||||
|
| Secretها | Sealed Secrets در gitops | inline در values یا Secret دستی |
|
||||||
|
| مستند | **همین سند +** [`RUNBOOK-CICD.fa.md`](RUNBOOK-CICD.fa.md) | [`README.md`](README.md) بخش Deploy |
|
||||||
|
|
||||||
|
بقیهٔ این سند **مسیر A** را پوشش میدهد. برای مسیر B به انتهای سند بروید.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## مسیر A — GitOps: فاز ۰ تا ۷
|
||||||
|
|
||||||
|
### فاز ۰ — پیشنیازهای سختافزاری و شبکه
|
||||||
|
|
||||||
|
- [ ] کلاستر Kubernetes (k3s یا دیگر) با kubectl از ماشین admin
|
||||||
|
- [ ] DNS: رکوردهای A/CNAME برای `$DOMAIN_LANDING`, `$DOMAIN_PANEL`, `$DOMAIN_API`, `$REGISTRY_HOST`, `$GIT_HOST`, Argo CD
|
||||||
|
- [ ] گواهی TLS (wildcard یا cert-manager + `clusterIssuer`)
|
||||||
|
- [ ] دسترسی `kubectl` به کلاستر
|
||||||
|
- [ ] `helm`, `kubeseal` (برای Sealed Secrets) روی ماشین admin
|
||||||
|
- [ ] دو ریپوی Git: `$APP_REPO` (کد) و `$GITOPS_REPO` (خالی یا با skeleton)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### فاز ۱ — Bootstrap زیرساخت (یکبار per cluster)
|
||||||
|
|
||||||
|
این مراحل در [`gitops/README.md`](gitops/README.md) هم هست؛ خلاصه:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd cloud-host # ریپوی اپلیکیشن
|
||||||
|
|
||||||
|
# 1) mirror رجیستری k3s → Harbor (یا registry خودتان)
|
||||||
|
./scripts/apply-k3s-registries.sh # در صورت k3s؛ برای کلاستر دیگر mirror معادل تنظیم کنید
|
||||||
|
|
||||||
|
# 2) Argo CD
|
||||||
|
helm upgrade --install argocd argo/argo-cd -n argocd --create-namespace \
|
||||||
|
-f gitops/argocd/values-bootstrap.yaml --timeout 15m --wait
|
||||||
|
|
||||||
|
# 3) Gitea (یا GitHub/GitLab — workflow را متناسب تنظیم کنید)
|
||||||
|
helm upgrade --install gitea gitea-charts/gitea -n gitea --create-namespace \
|
||||||
|
-f gitops/gitea/values.yaml --timeout 15m --wait
|
||||||
|
|
||||||
|
# 4) Secretهای TLS + registry-pull + egress در nsهای لازم
|
||||||
|
# (wildcard TLS و registry-pull-secret را یکبار در $PLATFORM_NS بسازید، سپس کپی)
|
||||||
|
for ns in argocd gitea $BUILD_NS; do
|
||||||
|
kubectl -n $PLATFORM_NS get secret <wildcard-tls-secret> -o yaml \
|
||||||
|
| sed "s/namespace: ${PLATFORM_NS}/namespace: ${ns}/" | kubectl apply -f -
|
||||||
|
kubectl -n $PLATFORM_NS get secret registry-pull-secret -o yaml \
|
||||||
|
| sed "s/namespace: ${PLATFORM_NS}/namespace: ${ns}/" | kubectl apply -f -
|
||||||
|
done
|
||||||
|
|
||||||
|
# 5) Seed ایمیجهای CI (act-runner, alpine-git, node, kaniko) — فایل را برای REGISTRY_* خودتان ویرایش کنید
|
||||||
|
kubectl apply -f gitops/jobs/seed-ci-images.yaml
|
||||||
|
kubectl -n $PLATFORM_NS wait --for=condition=complete job/seed-ci-images --timeout=15m
|
||||||
|
|
||||||
|
# 6) Sealed Secrets controller
|
||||||
|
helm repo add sealed-secrets https://bitnami.github.io/sealed-secrets
|
||||||
|
helm upgrade --install sealed-secrets sealed-secrets/sealed-secrets \
|
||||||
|
-n kube-system -f gitops/sealed-secrets/values.yaml --timeout 10m --wait
|
||||||
|
|
||||||
|
# 7) Gitea Actions runner + Secret CI_TOKEN در ریپوی app
|
||||||
|
kubectl apply -f gitops/gitea/act-runner.yaml
|
||||||
|
# در Gitea: Settings → Actions → Secrets → CI_TOKEN = PAT کاربر ci
|
||||||
|
|
||||||
|
# 8) Argo CD Application (chart از app repo، values از gitops repo)
|
||||||
|
# قبل از apply: repoURLها در gitops/argocd/application-platform.yaml را با GIT_HOST/APP_REPO/GITOPS_REPO همخوان کنید
|
||||||
|
kubectl apply -f gitops/argocd/application-platform.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
**بررسی فاز ۱:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get nodes
|
||||||
|
kubectl -n argocd get pods
|
||||||
|
kubectl -n gitea get pods
|
||||||
|
kubectl -n kube-system get pods -l app.kubernetes.io/name=sealed-secrets
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### فاز ۲ — آمادهسازی ریپوی GitOps (values)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# کلون ریپوی gitops (کنار ریپوی app یا هر مسیر دلخواه)
|
||||||
|
git clone "https://${GIT_HOST}/${GITOPS_REPO}.git" cloud-host-gitops
|
||||||
|
cd cloud-host-gitops
|
||||||
|
|
||||||
|
# کپی template values از ریپوی app
|
||||||
|
cp ../cloud-host/gitops/platform/values-abrban.example.yaml "${VALUES_FILE}"
|
||||||
|
```
|
||||||
|
|
||||||
|
**فایل values را برای محیط خودتان ویرایش کنید** — حداقل این فیلدها:
|
||||||
|
|
||||||
|
| بخش | چه چیزی عوض شود |
|
||||||
|
|-----|------------------|
|
||||||
|
| `images.postgres/redis/busybox` | مسیر mirror در `$REGISTRY_HOST` (مثلاً `proxy-dockerhub/library/postgres:16-alpine`) |
|
||||||
|
| `images.backend/frontend.repository` | `$REGISTRY_HOST/$REGISTRY_PROJECT/cloudhost-backend` |
|
||||||
|
| `secrets.existingSecret` | `$PLATFORM_SECRET` |
|
||||||
|
| `ingress.*.host` | `$DOMAIN_LANDING`, `$DOMAIN_PANEL`, `$DOMAIN_API` |
|
||||||
|
| `ingress.className` | `$INGRESS_CLASS` |
|
||||||
|
| `global.storageClass` | `$STORAGE_CLASS` |
|
||||||
|
| `backend.env.PLATFORM_DOMAIN` | `$DOMAIN_APPS` |
|
||||||
|
| `backend.env.FRONTEND_URL` | `https://${DOMAIN_PANEL},https://${DOMAIN_LANDING}` |
|
||||||
|
| `backend.env.REGISTRY_URL` | push داخلی: `$REGISTRY_PUSH/$REGISTRY_PROJECT` |
|
||||||
|
| `backend.env.REGISTRY_PULL_URL` | `$REGISTRY_HOST/$REGISTRY_PROJECT` |
|
||||||
|
| `backend.env.BASE_IMAGE_REGISTRY` | prefix mirror برای Dockerfileهای کاربر |
|
||||||
|
| `backend.env.ELASTIC_*` | بعد از فاز ۴ پر میشود |
|
||||||
|
| `postgres/redis.imagePullSecrets` | `[{ name: registry-pull-secret }]` |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add "${VALUES_FILE}"
|
||||||
|
git commit -m "chore: initial platform values for $(hostname -s 2>/dev/null || echo production)"
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
> **نکته:** CI فقط `images.backend.tag` و `images.frontend.tag` را عوض میکند — بقیهٔ فایل دست شماست.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### فاز ۳ — Secretهای پلتفرم (Sealed Secrets)
|
||||||
|
|
||||||
|
Secret پلتفرم **نباید** در values بهصورت plaintext commit شود. از SealedSecret استفاده کنید.
|
||||||
|
|
||||||
|
**کلیدهای الزامی** در `$PLATFORM_SECRET`:
|
||||||
|
|
||||||
|
| کلید | کاربرد |
|
||||||
|
|------|--------|
|
||||||
|
| `postgres-password` | Postgres پلتفرم + migration Job |
|
||||||
|
| `jwt-secret` | JWT access (حداقل ۳۲ کاراکتر تصادفی) |
|
||||||
|
| `jwt-refresh-secret` | JWT refresh |
|
||||||
|
| `cluster-kubeconfig-key` | رمزگذاری kubeconfig کلاسترها (۶۴ hex یا passphrase قوی) |
|
||||||
|
| `redis-password` | Redis پلتفرم + backend (Bull queues) |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd cloud-host-gitops
|
||||||
|
|
||||||
|
# تولید رمزهای تصادفی (یا خودتان مقدار قوی بگذارید)
|
||||||
|
PG_PASS="$(openssl rand -base64 24)"
|
||||||
|
JWT="$(openssl rand -base64 32)"
|
||||||
|
JWT_REFRESH="$(openssl rand -base64 32)"
|
||||||
|
KUBE_KEY="$(openssl rand -hex 32)"
|
||||||
|
REDIS_PASS="$(openssl rand -base64 24)"
|
||||||
|
|
||||||
|
kubectl -n $PLATFORM_NS create secret generic "$PLATFORM_SECRET" \
|
||||||
|
--from-literal=postgres-password="$PG_PASS" \
|
||||||
|
--from-literal=jwt-secret="$JWT" \
|
||||||
|
--from-literal=jwt-refresh-secret="$JWT_REFRESH" \
|
||||||
|
--from-literal=cluster-kubeconfig-key="$KUBE_KEY" \
|
||||||
|
--from-literal=redis-password="$REDIS_PASS" \
|
||||||
|
--dry-run=client -o json \
|
||||||
|
| kubeseal \
|
||||||
|
--controller-name=sealed-secrets-controller \
|
||||||
|
--controller-namespace=kube-system \
|
||||||
|
--format yaml \
|
||||||
|
> "sealed-secrets/${PLATFORM_SECRET}.yaml"
|
||||||
|
|
||||||
|
kubectl apply -f "sealed-secrets/${PLATFORM_SECRET}.yaml"
|
||||||
|
git add "sealed-secrets/${PLATFORM_SECRET}.yaml"
|
||||||
|
git commit -m "chore: seal platform secrets"
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
**بررسی:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl -n $PLATFORM_NS get secret "$PLATFORM_SECRET"
|
||||||
|
kubectl get sealedsecrets -A | grep "$PLATFORM_SECRET"
|
||||||
|
```
|
||||||
|
|
||||||
|
SealedSecretهای CI دیگر (kaniko، runner، repo creds) را طبق [`RUNBOOK-CICD.fa.md`](RUNBOOK-CICD.fa.md) بسازید.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### فاز ۴ — Logging stack + Secret Elasticsearch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd cloud-host
|
||||||
|
|
||||||
|
# 1) namespace logging (اگر در manifest نیست)
|
||||||
|
kubectl create namespace $LOGGING_NS --dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
|
||||||
|
# 2) Secret elasticsearch — خارج از git (plaintext commit ممنوع)
|
||||||
|
ELASTIC_PASS="$(openssl rand -base64 24)"
|
||||||
|
FLUENT_PASS="$(openssl rand -base64 24)"
|
||||||
|
|
||||||
|
kubectl -n $LOGGING_NS create secret generic elasticsearch-credentials \
|
||||||
|
--from-literal=ELASTIC_PASSWORD="$ELASTIC_PASS" \
|
||||||
|
--from-literal=FLUENTBIT_PASSWORD="$FLUENT_PASS"
|
||||||
|
|
||||||
|
# یا seal کنید:
|
||||||
|
kubectl -n $LOGGING_NS create secret generic elasticsearch-credentials \
|
||||||
|
--from-literal=ELASTIC_PASSWORD="$ELASTIC_PASS" \
|
||||||
|
--from-literal=FLUENTBIT_PASSWORD="$FLUENT_PASS" \
|
||||||
|
--dry-run=client -o json \
|
||||||
|
| kubeseal --controller-name=sealed-secrets-controller \
|
||||||
|
--controller-namespace=kube-system --format yaml \
|
||||||
|
> ../cloud-host-gitops/sealed-secrets/elasticsearch-credentials.yaml
|
||||||
|
|
||||||
|
# 3) deploy stack (بدون Secret inline — manifest فقط ConfigMap/Deployment دارد)
|
||||||
|
kubectl apply -f backend/k8s/logging/elasticsearch-stack.yaml
|
||||||
|
|
||||||
|
# 4) همان مقادیر را در values پلتفرم بگذارید (backend.env)
|
||||||
|
# ELASTIC_PASSWORD, FLUENTBIT_PASSWORD, KIBANA_SYSTEM_PASSWORD
|
||||||
|
# سپس commit/push در gitops repo
|
||||||
|
```
|
||||||
|
|
||||||
|
> backend در production بدون `ELASTIC_PASSWORD` معتبر **بالا نمیآید** (`validate-production-config`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### فاز ۵ — اولین Deploy
|
||||||
|
|
||||||
|
**روش ۱ — CI (توصیه):** push به `main` در `$APP_REPO` → workflow تست + Kaniko + آپدیت tag در gitops → Argo sync.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd cloud-host
|
||||||
|
git push origin main # یا push به Gitea remote
|
||||||
|
# پیگیری: Gitea Actions UI یا kubectl -n $BUILD_NS get jobs -w
|
||||||
|
```
|
||||||
|
|
||||||
|
**روش ۲ — دستی (bootstrap / بدون CI):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# build ایمیجها (روی ماشینی که به registry دسترسی دارد) یا trigger-platform-build.sh
|
||||||
|
TAG="$(date +%Y%m%d-%H%M)-manual"
|
||||||
|
VALUES="../cloud-host-gitops/${VALUES_FILE}"
|
||||||
|
./scripts/gitops-deploy.sh TAG="$TAG" VALUES="$VALUES"
|
||||||
|
```
|
||||||
|
|
||||||
|
**بررسی Argo:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl -n argocd get app "$ARGO_APP"
|
||||||
|
argocd app sync "$ARGO_APP" # در صورت sync خودکار غیرفعال
|
||||||
|
kubectl -n $PLATFORM_NS get pods
|
||||||
|
kubectl -n $PLATFORM_NS rollout status deploy/cloudhost-backend --timeout=300s
|
||||||
|
kubectl -n $PLATFORM_NS rollout status deploy/cloudhost-frontend --timeout=300s
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### فاز ۶ — Greenfield / ارتقا از نسخهٔ قدیم
|
||||||
|
|
||||||
|
اگر کلاستر **قبلاً** با نسخهٔ قدیمی CloudHost بالا آمده (namespace کوتاه UUID، migration بدون `schema_migrations`)، **قبل از deploy جدید** دیتابیس را reset کنید.
|
||||||
|
|
||||||
|
> ⚠️ **فقط greenfield / بدون دادهٔ واقعی.** در production با داده، اول backup بگیرید.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1) backend را متوقف کنید
|
||||||
|
kubectl -n $PLATFORM_NS scale deploy/cloudhost-backend --replicas=0
|
||||||
|
|
||||||
|
# 2) schema را از نو بسازید
|
||||||
|
kubectl -n $PLATFORM_NS exec deploy/cloudhost-postgres -- \
|
||||||
|
psql -U cloudhost -c 'DROP SCHEMA public CASCADE; CREATE SCHEMA public;'
|
||||||
|
|
||||||
|
# 3) Argo sync — migration Job (pre-upgrade hook) base schema + migrations را اجرا میکند
|
||||||
|
argocd app sync "$ARGO_APP"
|
||||||
|
|
||||||
|
# 4) backend را بالا بیاورید
|
||||||
|
kubectl -n $PLATFORM_NS scale deploy/cloudhost-backend --replicas=1
|
||||||
|
```
|
||||||
|
|
||||||
|
**تغییرات breaking که reset میخواهند:**
|
||||||
|
|
||||||
|
| تغییر | اثر |
|
||||||
|
|-------|-----|
|
||||||
|
| namespace کاربر `user-<uuid-32>` بهجای `user-<8char>` | namespaceهای قدیمی دیگر استفاده نمیشوند — اپها redeploy |
|
||||||
|
| `000_base_schema.sql` + `schema_migrations` | DB باید از نو migrate شود |
|
||||||
|
| `redis-password` جدید | Secret + restart Redis و backend |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### فاز ۷ — چکلیست تأیید سلامت
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Podها
|
||||||
|
kubectl -n $PLATFORM_NS get deploy,pods
|
||||||
|
kubectl -n $LOGGING_NS get pods
|
||||||
|
|
||||||
|
# API
|
||||||
|
curl -sf "https://${DOMAIN_API}/api/v1/health" && echo OK
|
||||||
|
curl -sf "https://${DOMAIN_API}/api/v1/ready" && echo OK
|
||||||
|
|
||||||
|
# Frontend
|
||||||
|
curl -sf -o /dev/null -w '%{http_code}\n' "https://${DOMAIN_LANDING}"
|
||||||
|
curl -sf -o /dev/null -w '%{http_code}\n' "https://${DOMAIN_PANEL}"
|
||||||
|
|
||||||
|
# Migration
|
||||||
|
kubectl -n $PLATFORM_NS logs job/$(kubectl -n $PLATFORM_NS get jobs -o name | grep migration | tail -1 | cut -d/ -f2) 2>/dev/null || true
|
||||||
|
|
||||||
|
# Redis auth
|
||||||
|
kubectl -n $PLATFORM_NS exec deploy/cloudhost-redis -- redis-cli ping
|
||||||
|
|
||||||
|
# Backup CronJob (اگر enabled)
|
||||||
|
kubectl -n $PLATFORM_NS get cronjobs
|
||||||
|
```
|
||||||
|
|
||||||
|
| علامت | اقدام |
|
||||||
|
|-------|-------|
|
||||||
|
| Backend CrashLoop — JWT/DB/Redis | Secret `$PLATFORM_SECRET` و keys — [`RUNBOOK-CICD.fa.md`](RUNBOOK-CICD.fa.md) عیبیابی |
|
||||||
|
| Backend CrashLoop — ELASTIC_PASSWORD | env در values + Secret logging |
|
||||||
|
| Migration fail | `kubectl logs` روی migration Job؛ `schema_migrations` و فایلهای `backend/migrations/` |
|
||||||
|
| Argo OutOfSync | `argocd app diff $ARGO_APP` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## مسیر B — Helm مستقیم (بدون GitOps)
|
||||||
|
|
||||||
|
برای lab، staging، یا کلاستری **بدون** Gitea/Argo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp backend/helm/cloudhost-platform/values-production.example.yaml my-values.yaml
|
||||||
|
# ویرایش: hosts, registry, secrets (jwtSecret, postgres.password, redis.password), ingress
|
||||||
|
|
||||||
|
docker build -t $REG/cloudhost-backend:1.0.0 ./backend
|
||||||
|
docker build -t $REG/cloudhost-frontend:1.0.0 \
|
||||||
|
--build-arg NEXT_PUBLIC_API_URL=https://${DOMAIN_API} ./frontend
|
||||||
|
docker push $REG/cloudhost-backend:1.0.0
|
||||||
|
docker push $REG/cloudhost-frontend:1.0.0
|
||||||
|
|
||||||
|
helm upgrade --install cloudhost ./backend/helm/cloudhost-platform \
|
||||||
|
-n $PLATFORM_NS --create-namespace \
|
||||||
|
-f my-values.yaml \
|
||||||
|
--set images.backend.repository=$REG/cloudhost-backend \
|
||||||
|
--set images.frontend.repository=$REG/cloudhost-frontend \
|
||||||
|
--set images.backend.tag=1.0.0 \
|
||||||
|
--set images.frontend.tag=1.0.0 \
|
||||||
|
--set global.storageClass=$STORAGE_CLASS
|
||||||
|
```
|
||||||
|
|
||||||
|
> در این مسیر `secrets.existingSecret` خالی بماند تا Helm Secret بسازد — **برای production با Argo CD توصیه نمیشود** (lookup در `helm template` خالی است).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## فایلهای مرجع در ریپو
|
||||||
|
|
||||||
|
| فایل | نقش |
|
||||||
|
|------|-----|
|
||||||
|
| [`gitops/platform/values-abrban.example.yaml`](gitops/platform/values-abrban.example.yaml) | Template values — کپی و rename برای محیط جدید |
|
||||||
|
| [`gitops/sealed-secrets/abrban-platform-secrets.example.yaml`](gitops/sealed-secrets/abrban-platform-secrets.example.yaml) | دستور seal Secret پلتفرم |
|
||||||
|
| [`gitops/sealed-secrets/elasticsearch-credentials.example.yaml`](gitops/sealed-secrets/elasticsearch-credentials.example.yaml) | دستور seal Secret logging |
|
||||||
|
| [`backend/helm/cloudhost-platform/values-production.example.yaml`](backend/helm/cloudhost-platform/values-production.example.yaml) | Template برای مسیر B |
|
||||||
|
| [`scripts/gitops-deploy.sh`](scripts/gitops-deploy.sh) | deploy دستی با Helm + values از gitops |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## خلاصهٔ ترتیب (Quick reference)
|
||||||
|
|
||||||
|
```
|
||||||
|
فاز ۰ DNS + kubectl + helm + kubeseal + دو ریپو
|
||||||
|
↓
|
||||||
|
فاز ۱ Argo + Gitea + registry + sealed-secrets + runner + Application
|
||||||
|
↓
|
||||||
|
فاز ۲ کپی values template → ویرایش → push gitops
|
||||||
|
↓
|
||||||
|
فاز ۳ seal platform secrets → push gitops
|
||||||
|
↓
|
||||||
|
فاز ۴ elasticsearch stack + secret + env در values
|
||||||
|
↓
|
||||||
|
فاز ۵ push main (CI) یا gitops-deploy.sh (دستی)
|
||||||
|
↓
|
||||||
|
فاز ۶ (در صورت upgrade) reset DB
|
||||||
|
↓
|
||||||
|
فاز ۷ health check
|
||||||
|
```
|
||||||
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
> **بهروزرسانی ۲۰۲۶:** pipeline بیلد فعلی از **Kaniko** + Dockerfileهای نگهداریشده توسط پلتفرم استفاده میکند (نه Nixpacks/MinIO). آرشیو سورس روی دیسک/PVC آپلود میشود. manifest بوتاسترپ namespace بیلد: [`backend/k8s/builds/cloudhost-builds-bootstrap.yaml`](backend/k8s/builds/cloudhost-builds-bootstrap.yaml).
|
> **بهروزرسانی ۲۰۲۶:** pipeline بیلد فعلی از **Kaniko** + Dockerfileهای نگهداریشده توسط پلتفرم استفاده میکند (نه Nixpacks/MinIO). آرشیو سورس روی دیسک/PVC آپلود میشود. manifest بوتاسترپ namespace بیلد: [`backend/k8s/builds/cloudhost-builds-bootstrap.yaml`](backend/k8s/builds/cloudhost-builds-bootstrap.yaml).
|
||||||
|
|
||||||
|
> **استقرار از صفر روی سرور:** [`RUNBOOK-DEPLOY.fa.md`](RUNBOOK-DEPLOY.fa.md) — مراحل values، Secretها، deploy، greenfield reset.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### ۱.۱ CloudHost چیست
|
### ۱.۱ CloudHost چیست
|
||||||
|
|||||||
+7
-4
@@ -1,5 +1,7 @@
|
|||||||
# GitOps stack for abrban.com
|
# GitOps stack for abrban.com
|
||||||
|
|
||||||
|
> **راهنمای استقرار از صفر (هر محیط):** [`RUNBOOK-DEPLOY.fa.md`](../RUNBOOK-DEPLOY.fa.md) — متغیرها، values، Sealed Secrets، logging، deploy، greenfield reset.
|
||||||
|
|
||||||
## DNS (A record → cluster IP `78.157.39.52`)
|
## DNS (A record → cluster IP `78.157.39.52`)
|
||||||
|
|
||||||
| Host | Purpose |
|
| Host | Purpose |
|
||||||
@@ -84,8 +86,9 @@ kubectl apply -f gitops/argocd/application-platform.yaml
|
|||||||
|
|
||||||
Gitea Actions: [.gitea/workflows/build-deploy.yaml](../.gitea/workflows/build-deploy.yaml)
|
Gitea Actions: [.gitea/workflows/build-deploy.yaml](../.gitea/workflows/build-deploy.yaml)
|
||||||
|
|
||||||
Push به `main` → Kaniko → push به `abrban/` → کامیت tag در ریپوی [cloud-host-gitops](https://git.abrban.com/abrban/cloud-host-gitops) → ArgoCD sync.
|
Push به `main` → **تست Jest** → Kaniko → push به `abrban/` → کامیت tag در ریپوی [cloud-host-gitops](https://git.abrban.com/abrban/cloud-host-gitops) → ArgoCD sync.
|
||||||
|
|
||||||
- مقادیر Production در ریپوی جدا `abrban/cloud-host-gitops` است (`platform/values-abrban.yaml`)؛ Application بهصورت multi-source تعریف شده.
|
- **استقرار اولیه از صفر:** [`RUNBOOK-DEPLOY.fa.md`](../RUNBOOK-DEPLOY.fa.md) (فاز ۲–۷)
|
||||||
- Secretهای CI بهصورت SealedSecret در همان ریپو هستند (کنترلر در `kube-system`، values در `gitops/sealed-secrets/values.yaml`).
|
- مقادیر Production: [`platform/values-abrban.example.yaml`](platform/values-abrban.example.yaml) → کپی به gitops و ویرایش
|
||||||
- مستند کامل: [RUNBOOK-CICD.fa.md](../RUNBOOK-CICD.fa.md)
|
- SealedSecretهای نمونه: [`sealed-secrets/abrban-platform-secrets.example.yaml`](sealed-secrets/abrban-platform-secrets.example.yaml)، [`sealed-secrets/elasticsearch-credentials.example.yaml`](sealed-secrets/elasticsearch-credentials.example.yaml)
|
||||||
|
- Pipeline و rollback: [RUNBOOK-CICD.fa.md](../RUNBOOK-CICD.fa.md)
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
# Production values template — copy and customize for YOUR environment.
|
||||||
|
#
|
||||||
|
# Full step-by-step (from zero, any cluster):
|
||||||
|
# See RUNBOOK-DEPLOY.fa.md — Phase 2 (values) and Phase 3 (secrets)
|
||||||
|
#
|
||||||
|
# Example for abrban.com:
|
||||||
|
# cp values-abrban.example.yaml ../cloud-host-gitops/platform/values-abrban.yaml
|
||||||
|
#
|
||||||
|
# CI only updates images.backend.tag and images.frontend.tag on each deploy.
|
||||||
|
|
||||||
|
namespace: cloudhost
|
||||||
|
createNamespace: false
|
||||||
|
|
||||||
|
global:
|
||||||
|
storageClass: local-path
|
||||||
|
|
||||||
|
images:
|
||||||
|
# Harbor proxy-cache — first pull is slow, no manual seed needed (see gitops/README.md)
|
||||||
|
postgres: registry.abrban.com/proxy-dockerhub/library/postgres:16-alpine
|
||||||
|
redis: registry.abrban.com/proxy-dockerhub/library/redis:7-alpine
|
||||||
|
busybox: registry.abrban.com/proxy-dockerhub/library/busybox:1.36
|
||||||
|
backend:
|
||||||
|
repository: registry.abrban.com/abrban/cloudhost-backend
|
||||||
|
tag: "1.0.0" # ← CI overwrites on each deploy
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
frontend:
|
||||||
|
repository: registry.abrban.com/abrban/cloudhost-frontend
|
||||||
|
tag: "1.0.0" # ← CI overwrites on each deploy
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
enabled: true
|
||||||
|
database: cloudhost
|
||||||
|
username: cloudhost
|
||||||
|
password: "" # managed in abrban-platform-secrets (postgres-password)
|
||||||
|
storage: 10Gi
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: registry-pull-secret
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 512Mi
|
||||||
|
limits:
|
||||||
|
cpu: "2"
|
||||||
|
memory: 2Gi
|
||||||
|
|
||||||
|
redis:
|
||||||
|
enabled: true
|
||||||
|
storage: 1Gi
|
||||||
|
password: "" # managed in abrban-platform-secrets (redis-password)
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: registry-pull-secret
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 50m
|
||||||
|
memory: 64Mi
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 512Mi
|
||||||
|
|
||||||
|
# GitOps: never let Helm generate random JWT/redis passwords on each sync.
|
||||||
|
# Create once with kubeseal — see gitops/sealed-secrets/abrban-platform-secrets.example.yaml
|
||||||
|
secrets:
|
||||||
|
existingSecret: abrban-platform-secrets
|
||||||
|
|
||||||
|
backend:
|
||||||
|
enabled: true
|
||||||
|
replicas: 1
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: registry-pull-secret
|
||||||
|
uploads:
|
||||||
|
size: 20Gi
|
||||||
|
sourceStorage:
|
||||||
|
enabled: false
|
||||||
|
existingSecret: ceph-app-sources-credentials
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 512Mi
|
||||||
|
limits:
|
||||||
|
cpu: "2"
|
||||||
|
memory: 2Gi
|
||||||
|
env:
|
||||||
|
NODE_ENV: production
|
||||||
|
PORT: "4000"
|
||||||
|
JWT_EXPIRES_IN: 15m
|
||||||
|
JWT_REFRESH_EXPIRES_IN: 7d
|
||||||
|
PLATFORM_DOMAIN: apps.abrban.com
|
||||||
|
PREVIEW_BASE_DOMAIN: apps.abrban.com
|
||||||
|
FRONTEND_URL: https://panel.abrban.com,https://abrban.com
|
||||||
|
REGISTRY_URL: harbor-registry.cloudhost.svc.cluster.local:5000/abrban
|
||||||
|
REGISTRY_PULL_URL: registry.abrban.com/abrban
|
||||||
|
BUILD_NAMESPACE: cloudhost-builds
|
||||||
|
BUILD_SERVICE_ACCOUNT: kaniko-builder
|
||||||
|
UPLOAD_DIR: /app/uploads
|
||||||
|
PLATFORM_CREATE_STORAGE_CLASS: "true"
|
||||||
|
PLATFORM_STORAGE_CLASS: cloudhost-expandable
|
||||||
|
PLATFORM_STORAGE_PROVISIONER: rancher.io/local-path
|
||||||
|
ELASTICSEARCH_HOST: elasticsearch.logging.svc.cluster.local
|
||||||
|
ELASTICSEARCH_AUTO_PORT_FORWARD: "false"
|
||||||
|
# Mirror prefix for user-app Dockerfiles and managed DB/Redis/RabbitMQ charts
|
||||||
|
BASE_IMAGE_REGISTRY: registry.abrban.com/proxy-dockerhub/library
|
||||||
|
# Must match elasticsearch-credentials Secret in logging namespace (not in Helm chart)
|
||||||
|
ELASTIC_PASSWORD: "CHANGE_VIA_SEALEDSECRET_OR_KUBECTL"
|
||||||
|
FLUENTBIT_PASSWORD: "CHANGE_VIA_SEALEDSECRET_OR_KUBECTL"
|
||||||
|
KIBANA_SYSTEM_PASSWORD: "CHANGE_VIA_SEALEDSECRET_OR_KUBECTL"
|
||||||
|
# Swagger disabled in production unless explicitly enabled
|
||||||
|
# SWAGGER_ENABLED: "true"
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
enabled: true
|
||||||
|
replicas: 1
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: registry-pull-secret
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: "1"
|
||||||
|
memory: 1Gi
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
className: traefik
|
||||||
|
frontend:
|
||||||
|
host: abrban.com
|
||||||
|
panel:
|
||||||
|
host: panel.abrban.com
|
||||||
|
api:
|
||||||
|
host: api.abrban.com
|
||||||
|
tls:
|
||||||
|
enabled: true
|
||||||
|
clusterIssuer: letsencrypt-prod
|
||||||
|
|
||||||
|
migrations:
|
||||||
|
enabled: true
|
||||||
|
image: registry.abrban.com/proxy-dockerhub/library/postgres:16-alpine
|
||||||
|
|
||||||
|
backups:
|
||||||
|
postgres:
|
||||||
|
enabled: true
|
||||||
|
schedule: "0 3 * * *"
|
||||||
|
storageSize: 10Gi
|
||||||
|
retentionDays: 7
|
||||||
|
|
||||||
|
monitoring:
|
||||||
|
enabled: false
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Example: seal platform secrets for namespace cloudhost.
|
||||||
|
# Full guide (any environment): RUNBOOK-DEPLOY.fa.md — Phase 3
|
||||||
|
# Real SealedSecret lives in cloud-host-gitops/sealed-secrets/ — never commit plaintext passwords.
|
||||||
|
#
|
||||||
|
# Required keys (must match backend Deployment + validate-production-config):
|
||||||
|
# postgres-password, jwt-secret, jwt-refresh-secret, cluster-kubeconfig-key, redis-password
|
||||||
|
#
|
||||||
|
# Generate (replace CHANGE_ME_* with strong random values):
|
||||||
|
#
|
||||||
|
# kubectl -n cloudhost create secret generic abrban-platform-secrets \
|
||||||
|
# --from-literal=postgres-password='CHANGE_ME_PG' \
|
||||||
|
# --from-literal=jwt-secret='CHANGE_ME_JWT_32CHARS_MIN' \
|
||||||
|
# --from-literal=jwt-refresh-secret='CHANGE_ME_REFRESH_32CHARS_MIN' \
|
||||||
|
# --from-literal=cluster-kubeconfig-key='0123456789abcdef0123456789abcdef' \
|
||||||
|
# --from-literal=redis-password='CHANGE_ME_REDIS' \
|
||||||
|
# --dry-run=client -o json \
|
||||||
|
# | kubeseal \
|
||||||
|
# --controller-name=sealed-secrets-controller \
|
||||||
|
# --controller-namespace=kube-system \
|
||||||
|
# --format yaml \
|
||||||
|
# > ../cloud-host-gitops/sealed-secrets/abrban-platform-secrets.yaml
|
||||||
|
#
|
||||||
|
# Then in platform/values-abrban.yaml:
|
||||||
|
# secrets:
|
||||||
|
# existingSecret: abrban-platform-secrets
|
||||||
|
#
|
||||||
|
# Apply:
|
||||||
|
# kubectl apply -f ../cloud-host-gitops/sealed-secrets/abrban-platform-secrets.yaml
|
||||||
|
#
|
||||||
|
# Rotate redis-password: update SealedSecret, sync Argo, restart backend + redis pods.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Example: seal Elasticsearch stack credentials (namespace logging).
|
||||||
|
# Full guide (any environment): RUNBOOK-DEPLOY.fa.md — Phase 4
|
||||||
|
# Apply elasticsearch-stack.yaml FIRST (without inline passwords), then create this Secret.
|
||||||
|
#
|
||||||
|
# kubectl -n logging create secret generic elasticsearch-credentials \
|
||||||
|
# --from-literal=ELASTIC_PASSWORD="$(openssl rand -base64 24)" \
|
||||||
|
# --from-literal=FLUENTBIT_PASSWORD="$(openssl rand -base64 24)" \
|
||||||
|
# --dry-run=client -o json \
|
||||||
|
# | kubeseal \
|
||||||
|
# --controller-name=sealed-secrets-controller \
|
||||||
|
# --controller-namespace=kube-system \
|
||||||
|
# --format yaml \
|
||||||
|
# > ../cloud-host-gitops/sealed-secrets/elasticsearch-credentials.yaml
|
||||||
|
#
|
||||||
|
# Backend must receive the same ELASTIC_* values via backend.env in values-abrban.yaml
|
||||||
|
# (or a separate SealedSecret referenced with envFrom).
|
||||||
|
#
|
||||||
|
# After deploy, verify:
|
||||||
|
# kubectl -n logging get secret elasticsearch-credentials
|
||||||
|
# curl -u elastic:$ELASTIC_PASSWORD https://elasticsearch.logging.svc.cluster.local:9200
|
||||||
@@ -6,13 +6,18 @@ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|||||||
NAMESPACE="${NAMESPACE:-cloudhost}"
|
NAMESPACE="${NAMESPACE:-cloudhost}"
|
||||||
RELEASE="${RELEASE:-cloudhost}"
|
RELEASE="${RELEASE:-cloudhost}"
|
||||||
# Production values now live in the cloud-host-gitops repo (platform/values-abrban.yaml).
|
# Production values now live in the cloud-host-gitops repo (platform/values-abrban.yaml).
|
||||||
|
# Fallback: example template in this repo for bootstrap / local helm.
|
||||||
VALUES="${VALUES:-${ROOT}/../cloud-host-gitops/platform/values-abrban.yaml}"
|
VALUES="${VALUES:-${ROOT}/../cloud-host-gitops/platform/values-abrban.yaml}"
|
||||||
|
if [[ ! -f "${VALUES}" ]]; then
|
||||||
|
VALUES="${ROOT}/gitops/platform/values-abrban.example.yaml"
|
||||||
|
fi
|
||||||
TAG="${TAG:-}"
|
TAG="${TAG:-}"
|
||||||
|
|
||||||
if [[ ! -f "${VALUES}" ]]; then
|
if [[ ! -f "${VALUES}" ]]; then
|
||||||
echo "ERROR: values file not found: ${VALUES}" >&2
|
echo "ERROR: values file not found." >&2
|
||||||
echo "Clone the GitOps repo next to this one, or pass VALUES=/path/to/values-abrban.yaml:" >&2
|
echo "Clone the GitOps repo next to this one, or pass VALUES=/path/to/values-abrban.yaml:" >&2
|
||||||
echo " git clone https://git.abrban.com/abrban/cloud-host-gitops.git" >&2
|
echo " git clone https://git.abrban.com/abrban/cloud-host-gitops.git" >&2
|
||||||
|
echo "Or copy gitops/platform/values-abrban.example.yaml to your gitops repo." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user