Files
cloud-host/RUNBOOK-CICD.fa.md
keyhan 6d9cd89cc5 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>
2026-07-03 12:17:55 +03:30

322 lines
23 KiB
Markdown

# RUNBOOK — خط CI/CD (Gitea Actions → Kaniko → Harbor → Argo CD)
این مستند جریان کامل Build و Deploy پلتفرم را توضیح می‌دهد: از Push شدن کد روی `main` تا استقرار خودکار روی Kubernetes.
> **استقرار از صفر روی سرور جدید:** [`RUNBOOK-DEPLOY.fa.md`](RUNBOOK-DEPLOY.fa.md) — شامل جدول متغیرها، seal کردن Secretها، logging stack، greenfield reset، و چک‌لیست سلامت.
---
## معماری و جریان کلی
```mermaid
flowchart TD
Dev[Developer] -->|git push main| AppRepo["Gitea: abrban/cloud-host (کد + چارت)"]
AppRepo -->|trigger workflow| Runner["Act Runner (namespace: gitea)"]
Runner -->|"checkout با CI_TOKEN"| AppRepo
Runner -->|kubectl apply Job| Kaniko["Kaniko Job (namespace: cloudhost-builds)"]
Kaniko -->|"push با harbor_registry_user"| Harbor["Harbor (harbor-registry:5000)"]
Runner -->|"آپدیت image.tag + commit/push"| GitOpsRepo["Gitea: abrban/cloud-host-gitops (state)"]
GitOpsRepo -->|"poll (پیش‌فرض هر ۳ دقیقه)"| Argo["Argo CD (automated sync)"]
AppRepo -->|"Helm Chart (source دوم)"| Argo
Argo -->|"helm render + apply"| K8s["Kubernetes (namespace: cloudhost)"]
Harbor -->|"pull از طریق mirror در k3s"| K8s
Rollback["Rollback: git revert در cloud-host-gitops"] -.-> GitOpsRepo
```
مراحل به ترتیب:
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` اجرا می‌شود.
3. Runner کد را با توکن CI کلون می‌کند و تگ ایمیج (`YYYYMMDD-HHMM-<sha>`) را می‌سازد.
4. **Job تست بک‌اند** در namespace `cloudhost-builds` اجرا می‌شود (`npm ci` + `jest --ci`) — در صورت fail، بیلد ایمیج شروع نمی‌شود.
5. برای هر ایمیج (backend و frontend) یک Kaniko Job در namespace `cloudhost-builds` ساخته می‌شود که کد را کلون، ایمیج را build و به Harbor push می‌کند.
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 نمی‌شود.
**زمان تقریبی یک Pipeline کامل:** ۱۵–۲۵ دقیقه (backend سنگین‌تر است؛ شامل دانلود npm، helm و kubectl داخل Dockerfile).
---
## Bootstrap — پیش‌نیازهای یک‌بار (کلاستر تازه)
قبل از اولین push به `main`، این موارد باید در کلاستر آماده باشند:
| # | کار | دستور / فایل |
|---|-----|--------------|
| 1 | Mirror k3s → Harbor | `./scripts/apply-k3s-registries.sh` |
| 2 | Secretهای TLS و registry در nsهای `gitea`, `cloudhost-builds`, `argocd` | [`gitops/README.md`](gitops/README.md) گام ۴ |
| 3 | پروکسی egress در `gitea` و `cloudhost-builds` | همان گام ۴ — برای npm/helm/kubectl داخل build و دانلود kubectl توسط Runner |
| 4 | **Seed ایمیج‌های CI** در Harbor `abrban/` | [`gitops/jobs/seed-ci-images.yaml`](gitops/jobs/seed-ci-images.yaml) |
| 5 | Sealed Secrets controller | `helm upgrade --install sealed-secrets ... -f gitops/sealed-secrets/values.yaml` |
| 6 | SealedSecretها از ریپوی gitops | `kubectl apply -f` روی `cloud-host-gitops/sealed-secrets/` |
| 7 | Gitea Runner + Secret `CI_TOKEN` در ریپو | [`gitops/gitea/act-runner.yaml`](gitops/gitea/act-runner.yaml) |
| 8 | ریپوی `cloud-host-gitops` + Argo Application | [`gitops/argocd/application-platform.yaml`](gitops/argocd/application-platform.yaml) |
### Seed ایمیج‌های CI (الزامی)
kubelet و Kaniko نمی‌توانند reliably از proxy-cache هاربر برای همهٔ ایمیج‌ها استفاده کنند. این ایمیج‌ها باید **یک‌بار** با skopeo در پروژهٔ `abrban/` کپی شوند:
| ایمیج در Harbor | منبع upstream | مصرف |
|-----------------|---------------|------|
| `abrban/act-runner:0.2.11` | docker.io/gitea/act_runner | Gitea Actions runner |
| `abrban/alpine-git:2.43.0` | docker.io/alpine/git | initContainer کلون در Kaniko Job |
| `abrban/node:24-alpine` | docker.io/library/node | **BASE_IMAGE** در Dockerfile (هر stage) |
| `abrban/kaniko-executor:v1.27.6-debug` | gcr.io/kaniko-project/executor | Kaniko Job |
```bash
# پیش‌نیاز: secret registry-egress-proxy و registry-pull-secret در ns cloudhost
kubectl apply -f gitops/jobs/seed-ci-images.yaml
kubectl -n cloudhost wait --for=condition=complete job/seed-ci-images --timeout=15m
kubectl -n cloudhost logs job/seed-ci-images --tail=5
# انتظار: SEED_OK
```
بررسی:
```bash
kubectl -n cloudhost run tags --rm -i --restart=Never \
--image=registry.abrban.com/abrban/alpine:3 \
--overrides='{"spec":{"imagePullSecrets":[{"name":"registry-pull-secret"}]}}' \
-- sh -c 'H="harbor_registry_user:$(kubectl -n cloudhost get secret harbor-core -o jsonpath="{.data.REGISTRY_CREDENTIAL_PASSWORD}" | base64 -d)@harbor-registry.cloudhost.svc.cluster.local:5000"; for r in act-runner kaniko-executor alpine-git node; do wget -qO- "http://${H}/v2/abrban/${r}/tags/list"; echo; done'
```
> بعد از bootstrap، Pipeline با push به `main` خودکار اجرا می‌شود؛ نیازی به `./scripts/trigger-platform-build.sh` برای جریان عادی نیست (فقط برای دیباگ دستی).
---
## ساختار Repository (دو ریپو)
### `abrban/cloud-host` — Application Repo
| مسیر | نقش |
|------|-----|
| `backend/`, `frontend/` | کد اپلیکیشن + Dockerfile |
| `backend/helm/cloudhost-platform/` | Helm Chart پلتفرم |
| `.gitea/workflows/build-deploy.yaml` | Pipeline (Build + آپدیت GitOps) |
| `gitops/` | نصب زیرساخت (Argo CD، Gitea، Sealed Secrets، k3s و…) |
### `abrban/cloud-host-gitops` — GitOps Repo (منبع حقیقت Argo CD)
| مسیر | نقش |
|------|-----|
| `platform/values-abrban.yaml` | مقادیر Production — تنها فایلی که CI آپدیت می‌کند |
| `argocd/application-platform.yaml` | تعریف Application (نسخهٔ mirror آن در `gitops/argocd/` ریپوی اپ هم هست) |
| `sealed-secrets/*.yaml` | SealedSecretهای CI — رمزشده و قابل کامیت |
Application در Argo CD به‌صورت **multi-source** تعریف شده: چارت از `cloud-host` و values از `cloud-host-gitops`:
```yaml
sources:
- repoURL: https://git.abrban.com/abrban/cloud-host.git
path: backend/helm/cloudhost-platform
helm:
valueFiles:
- $values/platform/values-abrban.yaml
- repoURL: https://git.abrban.com/abrban/cloud-host-gitops.git
ref: values
```
مزیت این جداسازی: history تمیز، دسترسی نوشتن CI محدود به ریپوی state، و امکان دیدن کل تاریخچهٔ Deployها با `git log` یک ریپوی کوچک.
---
## احراز هویت‌ها (چه کسی با چه چیزی به کجا وصل می‌شود)
| مسیر | مکانیزم | محل نگهداری |
|------|---------|--------------|
| Runner → Gitea (ثبت) | Registration Token | SealedSecret `gitea-act-runner-token` (ns `gitea`) در ریپوی gitops |
| Workflow → Gitea (clone/push هر دو ریپو) | PAT کاربر `ci` | Secret ریپوی `cloud-host` در Gitea با نام **`CI_TOKEN`** (نام‌های `GITEA_*` رزرو هستند) |
| Kaniko → Harbor (push) | `harbor_registry_user` | SealedSecret `kaniko-harbor-auth` (ns `cloudhost-builds`) در ریپوی gitops |
| kubelet → Harbor (pull) | user `cloudhost` | Secret `registry-pull-secret` + mirror در `gitops/k3s/registries.yaml` |
| Argo CD → `cloud-host` (read) | repo credential | Secret `gitea-repo-creds` (ns `argocd`) |
| Argo CD → `cloud-host-gitops` (read) | PAT کاربر `ci` | SealedSecret `gitea-gitops-repo-creds` (ns `argocd`) در ریپوی gitops |
### توکن CI برای Gitea (`CI_TOKEN`)
کاربر `ci` در Gitea ساخته شده و روی هر دو ریپو دسترسی write دارد. PAT آن با scope `read:repository, write:repository` به‌عنوان Secret با نام `CI_TOKEN` در **Settings → Actions → Secrets** ریپوی `cloud-host` ثبت شده است.
برای rotate: در Gitea با کاربر `ci` توکن جدید بسازید (یا از API ادمین: `POST /api/v1/users/ci/tokens`)، مقدار Secret را در تنظیمات ریپو آپدیت کنید و SealedSecret `gitea-gitops-repo-creds` را هم دوباره seal کنید.
### احراز هویت Kaniko به Harbor
Kaniko به endpoint داخلی `harbor-registry.cloudhost.svc.cluster.local:5000` push می‌کند که **مستقیم به کامپوننت registry** می‌رود و harbor-core را دور می‌زند. نکتهٔ مهم:
- **Robot Accountهای Harbor اینجا کار نمی‌کنند** — توکن آن‌ها را harbor-core صادر می‌کند و endpoint داخلی به سرویس توکن دسترسی ندارد.
- credential درست، کاربر داخلی `harbor_registry_user` است با پسورد `REGISTRY_CREDENTIAL_PASSWORD` از Secret `harbor-core`:
```bash
REG_PASS="$(kubectl -n cloudhost get secret harbor-core \
-o jsonpath='{.data.REGISTRY_CREDENTIAL_PASSWORD}' | base64 -d)"
kubectl -n cloudhost-builds create secret docker-registry kaniko-harbor-auth \
--docker-server=harbor-registry.cloudhost.svc.cluster.local:5000 \
--docker-username=harbor_registry_user \
--docker-password="${REG_PASS}"
```
نمونهٔ manifest: [`gitops/jobs/kaniko-harbor-auth.example.yaml`](gitops/jobs/kaniko-harbor-auth.example.yaml) — نسخهٔ واقعی به‌صورت SealedSecret در ریپوی gitops است.
ورک‌فلو این Secret را در مسیر `/kaniko/.docker/config.json` هر دو Kaniko Job مانت می‌کند. چون push/pull داخلی و بدون TLS است، این فلگ‌ها لازم‌اند:
- `--insecure` / `--skip-tls-verify` — push
- `--insecure-pull` / `--insecure-registry=${PUSH_REGISTRY}` — pull ایمیج پایه از `harbor-registry:5000`
ایمیج پایه (`node:24-alpine`) از endpoint داخلی کشیده می‌شود، نه از `registry.abrban.com`:
```yaml
# در .gitea/workflows/build-deploy.yaml
--build-arg=BASE_IMAGE=harbor-registry.cloudhost.svc.cluster.local:5000/abrban/node:24-alpine
```
Dockerfileها از `ARG BASE_IMAGE=node:24-alpine` استفاده می‌کنند (build محلی بدون تغییر).
> **پروکسی egress** (`registry-egress-proxy` در ns `cloudhost-builds`) هنوز لازم است برای `npm ci` و دانلود helm/kubectl **داخل** مراحل RUN در Dockerfile — فقط pull ایمیج پایه از docker.io حذف شده است.
> **عارضهٔ جانبی push مستقیم به :5000** — Harbor DB از این ایمیج‌ها بی‌خبر می‌ماند؛ در UI هاربر دیده نمی‌شوند ولی pull به‌درستی کار می‌کند. برای دیدن تگ‌ها از registry API استفاده کنید (بخش عیب‌یابی).
### ارتباط Runner با Harbor
Runner خودش با Harbor حرف نمی‌زند؛ فقط Job می‌سازد. دو مسیر Harbor:
- **Push (داخلی):** `harbor-registry.cloudhost.svc.cluster.local:5000` — بدون عبور از Traefik.
- **Pull (kubelet):** `registry.abrban.com` — از طریق mirror در k3s (`scripts/apply-k3s-registries.sh`) به harbor-core route می‌شود.
---
## Versioning ایمیج‌ها
**استاندارد فعلی:** `YYYYMMDD-HHMM-<git-sha-short>` (مثلاً `20260702-1230-a1b2c3d`)
- **Immutable** است — هیچ‌وقت یک تگ بازنویسی نمی‌شود (برخلاف `latest`).
- **قابل ردیابی** است — از روی تگ ایمیجِ در حال اجرا مستقیماً به کامیت می‌رسید.
- **مرتب‌شونده** است — به‌ترتیب زمانی دیده می‌شود.
از `latest` هرگز برای Deploy استفاده نکنید؛ هم قابلیت Rollback را از بین می‌برد و هم Argo CD تغییری برای sync نمی‌بیند.
**SemVer برای Releaseها (اختیاری):** روی کامیت release یک Git Tag مثل `v1.4.0` بزنید و همان ایمیج را با `skopeo copy` تگ اضافه بزنید (rebuild لازم نیست). تگ SemVer برای انسان‌هاست؛ منبع حقیقتِ Deploy همان تگ SHA-دار در values است.
---
## آپدیت خودکار Helm Values
مرحلهٔ آخر Workflow ریپوی `cloud-host-gitops` را کلون می‌کند و فقط دو مقدار را در `platform/values-abrban.yaml` عوض می‌کند:
```yaml
images:
backend:
repository: registry.abrban.com/abrban/cloudhost-backend
tag: "20260702-1230-a1b2c3d" # ← CI این را آپدیت می‌کند
frontend:
repository: registry.abrban.com/abrban/cloudhost-frontend
tag: "20260702-1230-a1b2c3d" # ← CI این را آپدیت می‌کند
```
اگر `yq` روی Runner موجود باشد از آن استفاده می‌شود، وگرنه `sed` هدفمند (فقط خطِ `tag:` بلافاصله بعد از `repository: ...cloudhost-*`) اجرا می‌شود.
جایگزین بررسی‌شده و کنارگذاشته‌شده: **Argo CD Image Updater** — با روش فعلی هم‌پوشانی دارد و شفافیت کامیتِ صریح از CI را ندارد.
---
## Rollback
چون Deploy فقط از Git انجام می‌شود، Rollback هم یک عملیات Git است — این بار در ریپوی `cloud-host-gitops`:
```bash
git clone https://git.abrban.com/abrban/cloud-host-gitops.git && cd cloud-host-gitops
# 1. پیدا کردن کامیت deploy مشکل‌دار
git log --oneline -- platform/values-abrban.yaml
# 2. برگرداندن آن (تگ ایمیج به نسخهٔ قبلی برمی‌گردد)
git revert <commit-sha>
git push origin main
# 3. Argo CD به‌صورت خودکار به نسخهٔ قبلی sync می‌کند (ایمیج قبلی هنوز در Harbor هست)
```
نکته‌ها:
- `git revert` (نه `reset --force`) — history حفظ می‌شود و مشخص است چه چیزی چرا برگشت.
- **Rollback اضطراری** (وقتی Git در دسترس نیست): `argocd app rollback abrban-platform` یا Sync به revision قبلی در UI. **هشدار:** چون `selfHeal: true` فعال است، Argo در sync بعدی دوباره به HEAD گیت برمی‌گردد — rollback اضطراری موقتی است و باید بلافاصله با `git revert` دائمی شود.
- اگر Deployment جدید خراب باشد (CrashLoopBackOff)، به‌خاطر `RollingUpdate` نسخهٔ قبلی تا آماده‌شدن نسخهٔ جدید بالا می‌ماند.
---
## مدیریت Secretها (Sealed Secrets)
کنترلر **Sealed Secrets** در `kube-system` نصب است (values در [`gitops/sealed-secrets/values.yaml`](gitops/sealed-secrets/values.yaml)؛ ایمیج آن از `ghcr.io/bitnami` به پروژهٔ `abrban/` هاربر seed شده). Secretهای CI به‌صورت **SealedSecret** در ریپوی `cloud-host-gitops` (پوشهٔ `sealed-secrets/`) نگهداری می‌شوند — رمزشده با کلید عمومی کلاستر؛ فقط کنترلرِ داخل کلاستر می‌تواند رمزگشایی کند، پس کامیت‌کردنشان امن است.
| SealedSecret | Namespace | محتوا |
|--------------|-----------|-------|
| `gitea-act-runner-token` | `gitea` | توکن ثبت Runner |
| `kaniko-harbor-auth` | `cloudhost-builds` | dockerconfig کاربر `harbor_registry_user` |
| `gitea-gitops-repo-creds` | `argocd` | repo credential ریپوی gitops (کاربر `ci`) |
| `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/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
```bash
brew install kubeseal # فقط بار اول
kubectl -n <ns> create secret generic <name> --from-literal=key=value --dry-run=client -o json \
| kubeseal --controller-name=sealed-secrets-controller --controller-namespace=kube-system --format yaml \
> sealed-secrets/<name>.yaml
# سپس commit/push در ریپوی cloud-host-gitops و kubectl apply (یا sync توسط Argo در آینده)
```
> اگر Secret از قبل در کلاستر وجود دارد و می‌خواهید کنترلر آن را تصاحب کند، اول annotate کنید:
> `kubectl -n <ns> annotate secret <name> sealedsecrets.bitnami.com/managed="true"`
Secretهایی که هنوز دستی‌اند (خارج از چرخهٔ CI): `abrban-wildcard-tls`، `registry-pull-secret`، `registry-egress-proxy`، `harbor-core` (ساختهٔ Helm) — می‌توانند به‌تدریج seal شوند.
> **نکتهٔ امنیتی:** توکن ثبت Runner و پسورد پروکسی که قبلاً در history گیت افشا شده بودند rotate شده‌اند (توکن Runner جدید صادر و Runner دوباره ثبت شد). پسورد کاربر پروکسی (`builder`) روی سرور پروکسی هنوز باید توسط ادمین عوض شود؛ بعد از تغییر، Secret `registry-egress-proxy` را در namespaceهای `cloudhost` و `gitea` آپدیت کنید.
---
## Best Practiceهای GitOps در این استک (چک‌لیست)
- [x] **Git تنها منبع حقیقت** — Argo CD با `automated + prune + selfHeal`؛ تغییر دستی با `kubectl edit` برگردانده می‌شود.
- [x] **جداسازی App Repo از GitOps Repo** — history تمیز و دسترسی حداقلی CI.
- [x] **تگ Immutable به‌جای `latest`** — هر Build تگ یکتا دارد.
- [x] **جلوگیری از CI Loop** — کامیت CI به ریپوی جدا می‌رود که Workflow ندارد.
- [x] **Build بدون Docker Daemon** — Kaniko داخل Job، بدون `docker.sock` و بدون privileged.
- [x] **جداسازی push/pull هاربر** — push داخلی بدون عبور از Ingress؛ pull از طریق mirror k3s.
- [x] **Concurrency در Workflow** — دو push پشت‌سرهم روی آپدیت values با هم race نمی‌کنند.
- [x] **Secretهای GitOps-شده** — Sealed Secrets نصب و secretهای CI رمزشده در Git.
- [ ] **محیط Staging** — با `platform/values-staging.yaml` و Application دوم قابل اضافه‌شدن است.
- [ ] **Notification** — Argo CD Notifications برای اطلاع از Sync موفق/ناموفق.
---
## عیب‌یابی سریع
| علامت | بررسی |
|-------|-------|
| Workflow اجرا نمی‌شود | `kubectl -n gitea logs deploy/gitea-act-runner` — ثبت Runner و لیبل `abrban-builder` |
| Build fail — clone | معتبربودن Secret `CI_TOKEN` در تنظیمات ریپوی `cloud-host` |
| Build fail — pull ایمیج پایه | `node:24-alpine` باید seed شده باشد؛ Workflow باید `BASE_IMAGE=harbor-registry.../abrban/node:24-alpine` + `--insecure-pull` داشته باشد؛ نه pull مستقیم از docker.io |
| Build fail — UNAUTHORIZED روی registry.abrban.com | BASE_IMAGE نباید `registry.abrban.com/...` باشد — credential کانیکو فقط برای `harbor-registry:5000` است |
| Build fail — HTTP response to HTTPS client | `--insecure-pull` و `--insecure-registry=harbor-registry.cloudhost.svc.cluster.local:5000` در Kaniko args |
| Build fail — timeout npm/helm/kubectl | `registry-egress-proxy` در ns `cloudhost-builds` و سلامت پروکسی egress |
| Build fail — push به Harbor | `kubectl -n cloudhost-builds get secret kaniko-harbor-auth`؛ پسورد باید با `REGISTRY_CREDENTIAL_PASSWORD` هاربر یکی باشد |
| کامیت values push نمی‌شود | دسترسی write کاربر `ci` روی `cloud-host-gitops` |
| Argo sync نمی‌کند | `kubectl -n argocd get app abrban-platform`؛ هر دو repo credential (`gitea-repo-creds` و `gitea-gitops-repo-creds`) |
| Pod ایمیج را pull نمی‌کند | `registry-pull-secret` در ns `cloudhost` و mirror k3s (`scripts/apply-k3s-registries.sh`) |
| دیدن تگ‌های موجود در 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 — 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` |