docs: document CI/CD bootstrap, seed images, and troubleshooting
Build and Deploy Platform / build-and-deploy (push) Successful in 8m9s

Add bootstrap checklist (seed-ci-images, Sealed Secrets, two-repo GitOps),
Kaniko BASE_IMAGE/insecure-pull details, platform secret requirements, and
updated gitops install order. Link RUNBOOK-CICD from README.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
keyhan
2026-07-02 19:21:48 +03:30
parent 38b4a67db1
commit 34c110be6a
4 changed files with 103 additions and 16 deletions
+2
View File
@@ -13,6 +13,8 @@ custom `wp-content` entrypoint).
> 🇮🇷 Production deployment on the `abrban.com` k3s cluster — including all the
> 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).
---
## Architecture Overview
+73 -2
View File
@@ -32,6 +32,55 @@ flowchart TD
> **جلوگیری از حلقهٔ 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 (دو ریپو)
@@ -105,7 +154,21 @@ kubectl -n cloudhost-builds create secret docker-registry kaniko-harbor-auth \
نمونهٔ 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 داخلی و بدون TLS است، فلگ‌های `--insecure --skip-tls-verify` لازم‌اند — این ترافیک از کلاستر خارج نمی‌شود.
ورک‌فلو این 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 استفاده کنید (بخش عیب‌یابی).
@@ -186,6 +249,9 @@ git push origin main
| `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** |
چارت Helm با `secrets.existingSecret: abrban-platform-secrets` در `platform/values-abrban.yaml` (ریپوی gitops) از Secret ازپیش‌ساخته استفاده می‌کند — Argo CD با `helm template` نمی‌تواند Secret تصادفی بسازد (lookup خالی است و هر sync مقادیر JWT را عوض می‌کند).
### ساخت/به‌روزرسانی یک SealedSecret
@@ -228,10 +294,15 @@ Secretهایی که هنوز دستی‌اند (خارج از چرخهٔ CI): `a
|-------|-------|
| Workflow اجرا نمی‌شود | `kubectl -n gitea logs deploy/gitea-act-runner` — ثبت Runner و لیبل `abrban-builder` |
| Build fail — clone | معتبربودن Secret `CI_TOKEN` در تنظیمات ریپوی `cloud-host` |
| Build fail — pull ایمیج پایه (403/timeout از docker.io) | Kaniko نباید مستقیم از docker.io بکشد؛ ایمیج `node:24-alpine` باید در Harbor پروژهٔ `abrban/` seed شده باشد (`gitops/jobs/seed-ci-images.yaml`) و Workflow `--build-arg=BASE_IMAGE=registry.abrban.com/abrban/node:24-alpine` را پاس می‌دهد |
| 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) |
| SealedSecret باز نمی‌شود | `kubectl get sealedsecrets -A` (ستون SYNCED) و لاگ `kubectl -n kube-system logs deploy/sealed-secrets-controller` |
+17 -13
View File
@@ -46,34 +46,38 @@ helm upgrade --install argocd argo/argo-cd -n argocd --create-namespace \
helm upgrade --install gitea gitea-charts/gitea -n gitea --create-namespace \
-f gitops/gitea/values.yaml --timeout 15m --wait
# 4. TLS + registry secrets در namespaceهای argocd/gitea/cloudhost-builds
# 4. TLS + registry + egress secrets در namespaceهای argocd/gitea/cloudhost-builds
for ns in argocd gitea cloudhost-builds; do
kubectl -n cloudhost get secret abrban-wildcard-tls -o yaml | sed "s/namespace: cloudhost/namespace: ${ns}/" | kubectl apply -f -
kubectl -n cloudhost get secret registry-pull-secret -o yaml | sed "s/namespace: cloudhost/namespace: ${ns}/" | kubectl apply -f -
done
# پروکسی egress برای act-runner (دانلود kubectl) و Kaniko (pull ایمیج پایه از docker.io)
for ns in gitea cloudhost-builds; do
kubectl -n cloudhost get secret registry-egress-proxy -o yaml | sed "s/namespace: cloudhost/namespace: ${ns}/" | kubectl apply -f -
done
# 5. git push سورس به Gitea (نه docker push)
# 5. Seed ایمیج‌های CI در Harbor abrban/ (یک‌بار — الزامی قبل از Pipeline)
kubectl apply -f gitops/jobs/seed-ci-images.yaml
kubectl -n cloudhost 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. git push سورس به Gitea
# git remote add gitea https://git.abrban.com/abrban/cloud-host.git
# git push gitea main
# 6. اولین build در کلاستر (Kaniko → abrban/)
./scripts/trigger-platform-build.sh
# 7. Deploy
./scripts/gitops-deploy.sh
# ریپوی GitOps: https://git.abrban.com/abrban/cloud-host-gitops (SealedSecretها + values)
# 8. Gitea Actions runner
# توکن ثبت: SealedSecret در ریپوی cloud-host-gitops (sealed-secrets/gitea-act-runner-token.yaml)
# یا دستی: kubectl -n gitea create secret generic gitea-act-runner-token --from-literal=token='<TOKEN>'
# توکن ثبت: SealedSecret در cloud-host-gitops/sealed-secrets/gitea-act-runner-token.yaml
kubectl apply -f gitops/gitea/act-runner.yaml
# 9. Argo CD Application
# 9. Argo CD Application (multi-source: chart از cloud-host، values از cloud-host-gitops)
kubectl apply -f gitops/argocd/application-platform.yaml
# 10. اولین deploy: push به main → Gitea Actions → Argo CD sync (جریان عادی)
# برای bootstrap دستی: TAG=... ./scripts/gitops-deploy.sh
```
## CI/CD
+11 -1
View File
@@ -1,4 +1,13 @@
# Bootstrap: copy act_runner + kaniko into abrban/ (kubelet cannot use proxy-cache reliably)
# Bootstrap: seed CI/build images into Harbor project abrban/
#
# kubelet and Kaniko cannot reliably use Harbor proxy-cache for all images.
# Run once after Harbor is up and registry-egress-proxy exists in cloudhost:
#
# kubectl apply -f gitops/jobs/seed-ci-images.yaml
# kubectl -n cloudhost wait --for=condition=complete job/seed-ci-images --timeout=15m
#
# Images copied (see RUNBOOK-CICD.fa.md):
# abrban/act-runner, abrban/alpine-git, abrban/node, abrban/kaniko-executor
apiVersion: batch/v1
kind: Job
metadata:
@@ -25,6 +34,7 @@ spec:
- |
set -eux
DEST="docker://harbor-registry.cloudhost.svc.cluster.local:5000/abrban"
# Must match harbor-core → REGISTRY_CREDENTIAL_PASSWORD (default: harbor_registry_password)
CREDS="harbor_registry_user:harbor_registry_password"
skopeo copy --dest-tls-verify=false --dest-creds="${CREDS}" \
docker://docker.io/gitea/act_runner:0.2.11 \