From 34c110be6aab21479497af48c09f15856269d0ce Mon Sep 17 00:00:00 2001 From: keyhan Date: Thu, 2 Jul 2026 19:21:48 +0330 Subject: [PATCH] docs: document CI/CD bootstrap, seed images, and troubleshooting 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 --- README.md | 2 + RUNBOOK-CICD.fa.md | 75 ++++++++++++++++++++++++++++++++- gitops/README.md | 30 +++++++------ gitops/jobs/seed-ci-images.yaml | 12 +++++- 4 files changed, 103 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 4b60d5e..868fdef 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/RUNBOOK-CICD.fa.md b/RUNBOOK-CICD.fa.md index 02f10b9..a5ba949 100644 --- a/RUNBOOK-CICD.fa.md +++ b/RUNBOOK-CICD.fa.md @@ -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:@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` | diff --git a/gitops/README.md b/gitops/README.md index 4657a97..19ab905 100644 --- a/gitops/README.md +++ b/gitops/README.md @@ -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='' +# توکن ثبت: 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 diff --git a/gitops/jobs/seed-ci-images.yaml b/gitops/jobs/seed-ci-images.yaml index 8eaef2c..569cfb0 100644 --- a/gitops/jobs/seed-ci-images.yaml +++ b/gitops/jobs/seed-ci-images.yaml @@ -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 \