From 0115142fafe295c99379a207cffc9b6bc6a56adf Mon Sep 17 00:00:00 2001 From: masoodafar-web Date: Mon, 23 Feb 2026 22:13:15 +0330 Subject: [PATCH] =?UTF-8?q?docs:=20update=20TECH-03=20=E2=80=94=20K8s=20Se?= =?UTF-8?q?cret=20for=20persistent=20config,=20branch/appsettings=20separa?= =?UTF-8?q?tion,=20updated=20CI/CD=20flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- technical/TECH-03-DEPLOYMENT-INFRA.md | 120 ++++++++++++++++++++------ 1 file changed, 94 insertions(+), 26 deletions(-) diff --git a/technical/TECH-03-DEPLOYMENT-INFRA.md b/technical/TECH-03-DEPLOYMENT-INFRA.md index a0f3d90..887fea8 100644 --- a/technical/TECH-03-DEPLOYMENT-INFRA.md +++ b/technical/TECH-03-DEPLOYMENT-INFRA.md @@ -1,7 +1,7 @@ # 🚀 استقرار، CI/CD و زیرساخت > **منابع ادغام‌شده:** `CICD-PIPELINE-GUIDE.md`, `DEPLOYMENT-README.md`, `INFRASTRUCTURE-GUIDE.md`, `INGRESS-NGINX-WARNING.md`, `OFFLINE-DEPLOYMENT-GUIDE.md`, `SERVER-MIRRORS-CONFIG.md` -> **آخرین بروزرسانی:** اسفند ۱۴۰۴ (بروزرسانی: PersistentVolume برای آپلود فایل + اصلاح namespace + حذف secretRef) +> **آخرین بروزرسانی:** اسفند ۱۴۰۴ (بروزرسانی: PVC آپلود + K8s Secret برای config دائمی + جدا کردن appsettings هر برنچ) --- @@ -83,9 +83,11 @@ ENTRYPOINT ["dotnet", "CMSMicroservice.dll"] CMS/ k8s/ staging/ - cms-deployment.yaml ← PVC + Deployment + Service + Ingress + cms-config.yaml ← K8s Secret (appsettings.Staging.json) + cms-deployment.yaml ← PVC + Deployment + Service + Ingress production/ - cms-deployment.yaml ← PVC + Deployment + Service + Ingress + cms-config.yaml ← K8s Secret (appsettings.Production.json) + cms-deployment.yaml ← PVC + Deployment + Service + Ingress ``` > ⚠️ **هر دو محیط از namespace `default` استفاده می‌کنن.** @@ -131,30 +133,60 @@ volumes: > 💡 **نکته مهم:** چون `ReadWriteOnce` هست، فقط **1 replica** می‌تونه بنویسه. برای 2+ replica نیاز به NFS/CephFS با `ReadWriteMany` هست. -### ۳.۳ تنظیمات محیطی (Environment Variables) +### ۳.۳ تنظیمات محیطی (K8s Secret) -تنظیمات حساس (ConnectionString, Email, SMS, ZarinPal) **داخل `appsettings.{Environment}.json`** در ایمیج Docker قرار دارن. -**هیچ K8s Secret استفاده نمی‌شه** — .NET خودش فایل config مربوط به environment رو می‌خونه. +تنظیمات حساس (ConnectionString, Email, SMS, ZarinPal) **در K8s Secret** نگهداری می‌شن — نه داخل Docker image. +فایل `appsettings.{Environment}.json` از Secret به `/app/` مونت می‌شه و .NET اون رو override می‌خونه. -| محیط | `ASPNETCORE_ENVIRONMENT` | فایل Config | +```mermaid +flowchart LR + S["K8s Secret
cms-appsettings"] -->|volumeMount| F["/app/appsettings.*.json"] + F --> D[".NET reads config"] + I["Docker Image
appsettings.json (base)"] --> D +``` + +| محیط | `ASPNETCORE_ENVIRONMENT` | فایل Config (از Secret) | |------|---------------------------|-------------| | **Staging** | `Staging` | `appsettings.Staging.json` | | **Production** | `Production` | `appsettings.Production.json` | -env var‌های K8s manifest: +**Secret manifest** (`cms-config.yaml`): +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: cms-appsettings + namespace: default +type: Opaque +stringData: + appsettings.Staging.json: | # یا appsettings.Production.json + { "ConnectionStrings": { ... }, "ZarinPal": { ... }, ... } +``` +**Volume mount در Deployment:** +```yaml +volumeMounts: +- name: cms-config + mountPath: /app/appsettings.Staging.json + subPath: appsettings.Staging.json + readOnly: true +volumes: +- name: cms-config + secret: + secretName: cms-appsettings +``` + +env var‌های K8s manifest (فقط environment و URL): ```yaml env: - name: ASPNETCORE_ENVIRONMENT value: "Staging" # یا "Production" - name: ASPNETCORE_URLS value: "http://+:8080" -- name: Kestrel__EndpointDefaults__Protocols - value: "Http1AndHttp2" -- name: FileStorage__UploadPath - value: "/app/Uploads" ``` +> 💡 **تغییر config بدون deploy:** `kubectl edit secret cms-appsettings && kubectl rollout restart deployment/cms` + ### ۳.۴ مثال Deployment (واقعی) ```yaml @@ -179,11 +211,15 @@ spec: env: - name: ASPNETCORE_ENVIRONMENT value: "Staging" - - name: FileStorage__UploadPath - value: "/app/Uploads" + - name: ASPNETCORE_URLS + value: "http://+:8080" volumeMounts: - name: cms-uploads mountPath: /app/Uploads + - name: cms-config + mountPath: /app/appsettings.Staging.json + subPath: appsettings.Staging.json + readOnly: true resources: requests: { memory: "512Mi", cpu: "500m" } limits: { memory: "1Gi", cpu: "1000m" } @@ -191,6 +227,9 @@ spec: - name: cms-uploads persistentVolumeClaim: claimName: cms-uploads-pvc + - name: cms-config + secret: + secretName: cms-appsettings ``` ### ۳.۵ Ingress @@ -217,6 +256,28 @@ spec: > ⚠️ **هشدار:** از `spec.ingressClassName: nginx` استفاده کنید، نه `kubernetes.io/ingress.class` annotation (deprecated). +### ۳.۶ جداسازی appsettings در Git + +هر برنچ فقط فایل config مربوط به محیط خودش رو داره: + +| برنچ | `appsettings.json` | `appsettings.Staging.json` | `appsettings.Production.json` | +|------|---|---|---| +| `kub-stage` | ✅ | ✅ | ❌ حذف شده | +| `production` | ✅ | ❌ حذف شده | ✅ | + +**چرا؟** چون config اصلی از K8s Secret میاد (`cms-config.yaml`)، فایل‌های محیط دیگه داخل ایمیج اضافی و گمراه‌کننده‌ان. +همچنین وقتی merge/cherry-pick می‌کنید، فایل config محیط دیگه دیگه conflict ایجاد نمی‌کنه. + +> ⚠️ **کامیت‌های حذف فایل config رو هرگز cherry-pick نکنید به برنچ دیگه!** +> `e72673c` (حذف Production از staging) و `3ebe0f9` (حذف Staging از production) + +### ۳.۷ خلاصه: چه چیزهایی دائمی هستند (مستقل از ایمیج) + +| چه چیزی | مکانیزم K8s | محل Mount | +|---------|-------------|------------| +| **فایل‌های آپلود** (عکس، آواتار، ...) | `PersistentVolumeClaim` | `/app/Uploads` | +| **تنظیمات اپلیکیشن** (DB, SMS, IPG, ...) | `Secret` (`cms-appsettings`) | `/app/appsettings.{Env}.json` | + --- ## ۴. CI/CD Pipeline @@ -240,10 +301,11 @@ flowchart TD C --> D["Pack & Push Proto NuGet"] D --> E["Docker build → tag :latest"] E --> F["Push to 194.5.195.53:30080"] - F --> G["SCP manifest to server"] - G --> H["kubectl apply -f cms-deployment.yaml"] - H --> I["kubectl rollout restart"] - I --> J["✅ Deployed to Staging"] + F --> G["SCP cms-config.yaml + cms-deployment.yaml"] + G --> H["kubectl apply -f cms-config.yaml (Secret)"] + H --> I["kubectl apply -f cms-deployment.yaml"] + I --> J["kubectl rollout restart"] + J --> K["✅ Deployed to Staging"] ``` ### ۴.۳ فلوی Production (`prod-deploy.yml`) @@ -255,10 +317,11 @@ flowchart TD C --> D["Pack & Push Proto NuGet"] D --> E["Docker build → tag :sha + :prod"] E --> F["Push to 194.5.195.53:30080"] - F --> G["SCP manifest to server"] - G --> H["kubectl apply -f cms-deployment.yaml"] - H --> I["kubectl set image → sha"] - I --> J["✅ Deployed to Production"] + F --> G["SCP cms-config.yaml + cms-deployment.yaml"] + G --> H["kubectl apply -f cms-config.yaml (Secret)"] + H --> I["kubectl apply -f cms-deployment.yaml"] + I --> J["kubectl set image → sha"] + J --> K["✅ Deployed to Production"] ``` ### ۴.۴ شاخه‌ها و محیط‌ها @@ -271,10 +334,11 @@ flowchart TD ### ۴.۵ نکات مهم CI/CD - **Proto NuGet:** هر deploy ابتدا proto packages رو build و به Nexus push می‌کنه -- **Manifest apply:** پایپلاین مانیفست K8s رو SCP به سرور و `kubectl apply` می‌زنه - → PVC، Deployment، Service و Ingress هر بار اعمال می‌شه +- **Manifest apply:** پایپلاین ابتدا `cms-config.yaml` (Secret) رو apply می‌کنه، بعد `cms-deployment.yaml` + → Secret + PVC + Deployment + Service + Ingress هر بار اعمال می‌شه - **Image registry:** `194.5.195.53:30080` (داخلی Nexus) — نه `git.se.kbs1.ir` -- **appsettings حفاظت:** `.gitattributes` با `merge=ours` مانع overwrite شدن `appsettings.Production.json` موقع merge می‌شه +- **Config دائمی:** تنظیمات در K8s Secret هست، نه داخل Docker image — تغییر config بدون rebuild ایمیج ممکنه +- **جداسازی برنچ:** هر برنچ فقط appsettings محیط خودش رو داره (بخش ۳.۶) --- @@ -406,8 +470,12 @@ flowchart TD | `2d6c95e` | fix: use local registry 194.5.195.53:30080 instead of git.se.kbs1.ir | | `f8dc4ab` | fix: staging ASPNETCORE_ENVIRONMENT=Staging, remove secretKeyRef | | `de83c31` | fix: production uses namespace default + remove foursat namespace references | +| `9288d06` | feat: externalize appsettings to K8s Secret — config persists independently | +| `e72673c` | chore(staging): remove appsettings.Production.json (فقط kub-stage) | +| `3ebe0f9` | chore(production): remove appsettings.Staging.json (فقط production) | -> همه کامیت‌ها به هر دو شاخه `kub-stage` و `production` push شده‌اند. +> کامیت‌های PVC و Secret به هر دو شاخه push شده‌اند. +> ⚠️ کامیت‌های حذف appsettings فقط به برنچ مربوطه push شده — cherry-pick نکنید! **تنظیمات محیطی Production (`appsettings.Production.json`):**