Files
cloud-host/RUNBOOK-DEPLOY.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

18 KiB
Raw Permalink Blame History

RUNBOOK — استقرار پلتفرم CloudHost از صفر

این سند کارهایی را که روی سرور/کلاستر باید انجام دهید مرحله‌به‌مرحله توضیح می‌دهد — از bootstrap زیرساخت تا اولین deploy موفق پس از hardening.

برای چه کسی است: هر کسی که می‌خواهد CloudHost را روی یک کلاستر Kubernetes تازه (یا کلاستر دیگری غیر از abrban) بالا بیاورد.

چه چیزی اینجا نیست: جزئیات معماری اپ → RUNBOOK.fa.md؛ جزئیات pipeline CI → 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
# نمونه — قبل از اجرای دستورات 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 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 هم هست؛ خلاصه:

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

بررسی فاز ۱:

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)

# کلون ریپوی 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 }]
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)
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

بررسی:

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 بسازید.


فاز ۴ — Logging stack + Secret Elasticsearch

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.

cd cloud-host
git push origin main   # یا push به Gitea remote
# پیگیری: Gitea Actions UI یا kubectl -n $BUILD_NS get jobs -w

روش ۲ — دستی (bootstrap / بدون CI):

# 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:

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 بگیرید.

# 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

فاز ۷ — چک‌لیست تأیید سلامت

# 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 عیب‌یابی
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:

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 Template values — کپی و rename برای محیط جدید
gitops/sealed-secrets/abrban-platform-secrets.example.yaml دستور seal Secret پلتفرم
gitops/sealed-secrets/elasticsearch-credentials.example.yaml دستور seal Secret logging
backend/helm/cloudhost-platform/values-production.example.yaml Template برای مسیر B
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