Initial Guide — AKS: จาก cluster เปล่า ถึง state ปัจจุบัน
จุดประสงค์: หลัง provision AKS cluster ขึ้นมาแล้ว ต้อง config อะไรต่อ เพื่อให้ SuperApp รันได้เหมือน state ปัจจุบัน ตรวจสอบกับของจริงเมื่อ: 2026-09-21 — cluster
aks-SupperApp-dev(RGsua-azure-nonprd, subscription501c116b-493a-452e-a823-c7c0f443d01a, region southeastasia) GitOps repo:https://dev.azure.com/eximth/SuperApp/_git/Backend_Iacbranchdevelopment
การอ่าน tag ท้าย bullet:
[live]— ตรวจจาก cluster/Azure จริงวันที่ 2026-09-21[repo]— อ่านจาก manifest ในBackend_Iac[assume]— ยังไม่ได้ verify ต้องเช็กเองก่อนใช้
⚠️ ข้อจำกัดของข้อมูล
[repo]: อ่านจาก local checkoutfdd50179ซึ่ง ตามหลังorigin/developmentอยู่ 1042 commit จุดที่รู้ว่าต่างถูกระบุไว้แล้ว แต่ก่อนใช้งานจริงให้git fetch origin && git checkout origin/developmentก่อนเสมอไม่พบ bicep/terraform ที่ provision cluster นี้ใน repo ที่มีในเครื่อง (
SuperApp/IaCเป็นชุด manifest รุ่นเก่า ไม่มีMicrosoft.ContainerService) → ค่าใน §2–§3 ได้มาจากaz aks showของ cluster ที่รันอยู่ ไม่ใช่จาก source of truth
0. ขอบเขต
- ครอบคลุม: ตั้งแต่ cluster provision เสร็จ → addon/identity/ingress/GitOps → app รันครบ
[repo] - ไม่ครอบคลุม: การสร้าง VNet/subnet/firewall/APIM/PostgreSQL/Redis/Service Bus เอง (เป็น prerequisite ดู §1)
- cluster เดียวรันหลาย env:
superappdev,superappuat(+superappsitที่มี appset แต่ ไม่มี namespace จริง)[live]
1. Prerequisite นอก cluster (ต้องมีก่อน ไม่งั้น node pull image ไม่ได้)
- Networking — cluster เป็น private cluster (
enablePrivateCluster: true,privateDnsZone: System),enablePrivateClusterPublicFqdn: false→ kubectl ต้องมาจากใน VNet / VPN /az aks command invoke[live] - Egress =
outboundType: userDefinedRouting→ ต้องมี route table + firewall rule ปล่อย FQDN ที่ AKS ต้องใช้ (MCR, ACR, Azure Monitor, Key Vault,dev.azure.com,quay.ioสำหรับ ArgoCD image) ไม่งั้น node join ได้แต่ pull image ไม่ได้[live] - Subnets ที่ใช้จริง
[live]sua-az-dev-aks-npsys-snet— system node poolsua-az-dev-aks-np-snet— app node poolsua-az-dev-aks-np-ingress— subnet แยกสำหรับ internal LoadBalancer (ถูกอ้างใน annotation ของ ingress controller)
- ACR
suaazcrdev→ login serversuaazcrdev-a2aqcveegedbdbbk.azurecr.io[live] - Key Vault
sua-azure-nonprd-kv[live] - User-assigned Managed Identity
id-aks-SupperApp-dev-cp— control plane identity ของ cluster[live]centralize-aks-mi-dev(client-id8bc68e37-1d90-49de-b85f-8a386502332f, tenant6ff02f6c-...) — identity ที่ workload ใช้[live]
- Log Analytics
log-SupperApp-dev(ใช้ทั้ง Container Insights และ Defender)[live] - Data services — Redis Enterprise
sua-redis-mi-dev, Storagesuastoragedev+devwebfxsuperapp[live]; PostgreSQL Flexiblesua-az-dev-sql-prostgresและ Azure Service Bus[repo](ชื่อมาจาก connection string ใน manifest/KV ไม่ได้ query Azure โดยตรง) - Azure Policy initiative assignment (Kubernetes baseline) ที่ระดับ subscription/RG — เป็นตัวส่ง constraint ลงมาใน cluster ดู §4
[live]
2. ค่า cluster ปัจจุบัน (ใช้เป็น target ตอน provision)
| หัวข้อ | ค่าจริง | tag |
|---|---|---|
| Kubernetes | 1.34.10, SKU Base / tier Standard |
[live] |
| Auto-upgrade | channel patch, node OS NodeImage |
[live] |
| Network plugin | azure + overlay (networkPluginMode: overlay), dataplane azure, policy azure |
[live] |
| Pod CIDR / Service CIDR / DNS IP | 100.64.0.0/16 / 10.100.0.0/16 / 10.100.0.10 |
[live] |
| Identity | UserAssigned (id-aks-SupperApp-dev-cp) + kubelet identity aks-SupperApp-dev-agentpool |
[live] |
| AAD | managed AAD + Azure RBAC เปิด (enableAzureRbac: true) |
[live] |
| Node RG | MC_sua-azure-nonprd_aks-SupperApp-dev_southeastasia |
[live] |
Node pools [live]
sysnpz1— System,Standard_D2s_v4, AzureLinux, zone 1, autoscale 1–1, maxPods 110, taintCriticalAddonsOnly=true:NoScheduleappnpz1— User,Standard_D4s_v4, AzureLinux, zone 1, autoscale 1–3, maxPods 110, labelworkload-type=app- ผลที่ตามมา: workload ทั่วไปลง system pool ไม่ได้ → ถ้า app pool เต็ม pod จะ Pending (ไม่ใช่ล้นไป system pool)
3. เปิด addon / feature ของ cluster (ลำดับนี้ก่อน deploy อะไรทั้งสิ้น)
- OIDC Issuer — เปิด (
oidcIssuerProfile.enabled: true) issuer =https://southeastasia.oic.prod-aks.azure.com/<tenant>/<cluster-guid>/[live] - Workload Identity —
securityProfile.workloadIdentity.enabled: true→ ได้azure-wi-webhook-controller-managerใน kube-system[live] - Azure Key Vault Secrets Provider (CSI) — เปิด พร้อม
enableSecretRotation: true,rotationPollInterval: 1h[live] - Azure Policy add-on v2 — ได้ gatekeeper-system (audit + controller)
[live] - Microsoft Defender for Containers —
securityMonitoring.enabled: trueชี้ Log Analyticslog-SupperApp-dev[live] - Container Insights (omsagent) —
useAADAuth: trueชี้ workspace เดียวกัน[live] - Azure Monitor managed Prometheus —
azureMonitorProfile.metrics.enabled: true→ ได้ama-metrics*ใน kube-system[live] - App Routing (managed NGINX) —
ingressProfile.webAppRouting.enabled: true,defaultIngressControllerType: AnnotationControlled, identitywebapprouting-aks-supperapp-dev[live] - CSI drivers — azuredisk + azurefile + snapshot controller เปิด (blob CSI ปิด)
[live]
คำสั่งอ้างอิง — แยกคนละคำสั่ง (--enable-addons ไม่ใช่ flag ของ az aks update):
RG=sua-azure-nonprd; AKS=aks-SupperApp-dev
# identity
az aks update -g $RG -n $AKS --enable-oidc-issuer --enable-workload-identity
# key vault CSI + rotation
az aks enable-addons -g $RG -n $AKS -a azure-keyvault-secrets-provider \
--enable-secret-rotation --rotation-poll-interval 1h
# policy
az aks enable-addons -g $RG -n $AKS -a azure-policy
# container insights
az aks enable-addons -g $RG -n $AKS -a monitoring \
--workspace-resource-id /subscriptions/.../workspaces/log-SupperApp-dev
# managed prometheus + defender
az aks update -g $RG -n $AKS --enable-azure-monitor-metrics
az aks update -g $RG -n $AKS --enable-defender \
--defender-config <path-or-workspace-id>
# app routing (คำสั่งแยกกลุ่ม)
az aks approuting enable -g $RG -n $AKS
ถ้าทีมมี bicep/terraform สำหรับ provision cluster ให้ยึดไฟล์นั้นเป็นหลักแทนคำสั่งชุดนี้ (ตอนตรวจ 2026-09-21 หาไม่เจอในเครื่อง)
4. Azure Policy / Gatekeeper — รู้ก่อนว่ามันไม่ block
- constraint ทั้ง 16 ตัวที่ลงมาอยู่ใน
enforcementAction: dryrunทุกตัว[live] - แปลว่า deployment ปัจจุบัน (ซึ่ง
securityContext: {}, ไม่มี readOnlyRootFilesystem, ไม่ set runAsNonRoot) ผ่านได้เพราะ policy ยังไม่บังคับ[live] - ⚠️ ถ้า cluster ใหม่ได้ initiative แบบ
deny→ manifest ชุดเดิมจะถูกปฏิเสธทันที ต้องแก้ securityContext ก่อน[assume] - ตรวจ:
kubectl get constraints -o custom-columns='KIND:.kind,ACTION:.spec.enforcementAction'
5. ผูก identity / RBAC (ทำก่อน deploy app)
kubelet identity → AcrPull บน ACR — role assignment เดียวที่ทำให้ pull image ได้ (ไม่มี
imagePullSecretsใน deployment เลย)[live]- principal
f00374e5-90ac-42fb-8958-64203b95d3e7→AcrPullscope.../registries/suaazcrdev
- principal
centralize-aks-mi-devrole assignments ที่ต้องมีครบ[live]Key Vault Secrets User+Key Vault Crypto User@sua-azure-nonprd-kvStorage Blob Data Contributor@suastoragedev(+ containertempo-traces-dev,tempo-traces-sit,fx-file-pdf) และ @devwebfxsuperappRedis Cache Contributor@sua-redis-mi-dev
Federated credential — 1 อันต่อ namespace (subject =
system:serviceaccount:<ns>:<sa>, audienceapi://AzureADTokenExchange, issuer = OIDC issuer ของ cluster)[live]subject ที่มีอยู่จริง system:serviceaccount:superappdev:centralize-aks-sasystem:serviceaccount:superappuat:centralize-aks-sasystem:serviceaccount:superappsit:centralize-aks-sasystem:serviceaccount:observability-dev:centralize-aks-sasystem:serviceaccount:observability-uat:centralize-aks-sasystem:serviceaccount:observability-sit:centralize-aks-sasystem:serviceaccount:security:security-aks-sa(คนละ SA)⚠️ สร้าง namespace ใหม่ = ต้องเพิ่ม federated credential ใหม่เสมอ ไม่งั้น pod ขึ้นแต่ดึง KV ไม่ได้
[live]Azure RBAC สำหรับคนเข้า cluster — เปิด
enableAzureRbacแต่adminGroupObjectIDs: null→ สิทธิ์มาจาก role assignment (Azure Kubernetes Service RBAC Cluster Admin/Cluster User) ต้องมอบเองหลัง provision[assume]
6. Ingress layer
DEV — managed NGINX ผ่าน App Routing CRD [repo] + [live]
- apply
src/yamls/dev/nginx-internal-controller.yaml→NginxIngressControllerชื่อnginx-internalingressClassName: nginx-internalloadBalancerAnnotations:azure-load-balancer-internal: "true"+azure-load-balancer-internal-subnet: sua-az-dev-aks-np-ingress- ได้ LB internal IP
172.29.88.5(servicenginx-internal-0ใน nsapp-routing-system)
- อีกตัวสำหรับ ArgoCD:
argocd-nginx-internal→ IP172.29.88.6 - addon สร้าง
nginx(public, EXTERNAL-IP20.198.139.25) มาให้ default — ไม่มี ingress ตัวไหนใช้เลย ปิดได้ถ้าไม่ต้องการ public exposure[live]
UAT — Kong (Helm) [live]
- release
kong-uatnskong-system, chartkong-3.4.0(app 3.9) - values สำคัญ:
env.database: "off"(DB-less),router_flavor: expressions,admin/manager/portal: disabled,ingressController.ingressClass: kong-uat,installCRDs: false(ต้อง apply CRD แยก), proxy = internal LB subnet เดียวกัน,replicaCount: 2 - ได้ IP
172.29.88.7
Ingress ของ service [repo]
- host:
api.sua-az-dev.internal(dev) /api.sua-az-uat.internal(uat) - path pattern:
/api/<service>(/|$)(.*)+rewrite-target: /api/<service>/$2+use-regex: "true" - backend ชี้ container port ตรง ๆ (เช่น user → 5111) ไม่ใช่ 80
- ⚠️
api.sua-az-dev.internalไม่มี Private DNS zone ใน subscription นี้ และ resolve จากเครื่อง corp ไม่ได้ → ชื่อนี้ทำงานได้เพราะ upstream (APIM/gateway) ยิงเข้า LB IP พร้อม Host header[assume — ต้อง verify ที่ APIM backend config]
7. Namespace + ServiceAccount
- สร้าง namespace เองล่วงหน้า — ทุก ArgoCD app ตั้ง
CreateNamespace=false[repo]superappdev,superappuat,observability-dev,observability-uat,security,argocd,kong-system
- apply
src/yamls/<env>/service-account.yaml→ SAcentralize-aks-saพร้อม annotationazure.workload.identity/client-id+tenant-id[repo] - ns
securityใช้ SA แยก (security-aks-sa)[live] - pod ต้องมี label
azure.workload.identity/use: "true"ถึงจะได้ token projection[live]
8. Seed Key Vault (ขั้นนี้ห้ามข้าม — ทำก่อน ArgoCD sync)
- SecretProviderClass อ้าง secret ใน KV ด้วยชื่อตาม convention
{env}--{service}--{Section}--{Key}แล้ว map เป็น aliasSection__Key[live]- ตัวอย่างจริง:
dev--user-service--UserService--Database--Primary--ConnectionString→ aliasUserService__Database__Primary__ConnectionString - บางตัวเป็น global ไม่มี prefix service:
dev--ServiceBusSettings--ConnectionString,AtRestEncryption--Keys--kvdev20260830
- ตัวอย่างจริง:
- ถ้า objectName ใดใน SPC ไม่มีใน KV → pod ค้าง
ContainerCreatingทั้ง pod (CSI mount fail ทั้งก้อน ไม่ใช่แค่ key เดียว)[assume — พฤติกรรมมาตรฐานของ CSI driver] - K8s Secret (
<service>-secrets) ไม่ได้ถูก apply จาก repo — CSI driver สร้างให้ตอน pod mount volume สำเร็จเท่านั้น → ลำดับคือ KV ต้องมีของก่อน[live] - สคริปต์ช่วย:
scripts/migrate-kv-secrets-dev.ps1,scripts/cleanup-kv-secrets-dev.ps1,scripts/provision-servicebus-topics.ps1[repo]
9. ติดตั้ง ArgoCD
- เวอร์ชันจริง
quay.io/argoproj/argocd:v3.3.9[live]ติดตั้งด้วยkubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml(ระบุในdocs/knowledge/CICD_AZUREDEVOPS_ARGOCD_GUIDE.md:380และตรงกับ cluster — deployment มีlast-applied-configurationไม่มี helm label, ไม่มี Helm release)[repo]+[live]- ⚠️
stableชี้เวอร์ชันล่าสุดเสมอ → cluster ใหม่จะไม่ได้ 3.3.9 ถ้าต้องการ pin ให้ใช้ tagv3.3.9ใน URL
- ⚠️
argocd-cmd-params-cmตั้งserver.insecure: "true"เพราะ ingress วิ่ง HTTP port 80 (TLS terminate ที่ชั้นหน้า)[live]- สร้าง repo credential — Secret
argocd-repo-iacnsargocd(argocd.argoproj.io/secret-type=repository, url = Backend_Iac, usernameargo…+ ADO PAT)[live] - apply
src/argocd/superapp-project.yaml— AppProjectsuperappsourceRepos= Backend_Iac เท่านั้นdestinations=superappdev,superappsit,superappuat,observability-sit,observability-uat,securityclusterResourceWhitelist= Namespace อย่างเดียว
- apply ingress (
src/argocd/argocd-ingress.yaml) →argocd.sua-az-dev.internal(classnginx-internal) +argocd-nonprod.exim.go.th(classargocd-nginx-internal)[live] - apply notifications (
src/argocd/notifications/*) — MS Teams subscription บน sync/health events[repo] - เลือกอย่างใดอย่างหนึ่ง (สำคัญ — ดู §14)
src/argocd/apps/superapp-dev.yaml— umbrella Application 1 ตัว sync ทั้งsrc/yamls/dev/superappแบบ recursesrc/argocd/applicationsets/superapp-dev-appset.yaml— ApplicationSet แตกเป็น app ต่อ service ใช้directory.include: "*/{{service}}-*"
- ⚠️
src/argocd/**ไม่ได้ถูก GitOps manage — ไม่มี app-of-apps ทุกอย่าง (AppProject / Application / ApplicationSet / notifications / ingress) ต้องkubectl applyด้วยมือ และ drift ได้จริง: ApplicationSet ใน cluster มี 15 element ขณะที่origin/developmentมี 21[live] - sync policy ทั้งคู่:
automated{prune, selfHeal},ServerSideApply=true,ApplyOutOfSyncOnly=true,CreateNamespace=false, retry 3 ครั้ง backoff 5s×2 max 3m[repo]
10. Manifest ของ service (ArgoCD sync ให้เอง)
โครงต่อ 1 service ใน src/yamls/<env>/superapp/ [repo]
deployments/<svc>-deployment.yaml— imagesuaazcrdev-….azurecr.io/<svc>:<env>-<sha>,serviceAccountName: centralize-aks-sa, labelazure.workload.identity/use: "true",envFrom.secretRef: <svc>-secrets, CSI volume mount/mnt/secrets-store, probes/health, podAntiAffinity ต่อ hostname, annotationprometheus.io/scrape|port|pathservices/<svc>-service.yaml— ClusterIP,port == targetPort == containerPortingresses/<svc>-service-ingress.yamlhpas/<svc>-hpa.yaml—autoscaling/v2CPU 70%secret-providers/<svc>-secret-provider.yaml— SecretProviderClass ชื่อ<svc>-kv-secrets(คนละชื่อกับ K8s Secret<svc>-secrets)cronjobs/sync-employee-cronjob.yaml+secrets/sync-employee-cronjob-secret.yaml- จำนวนที่รันจริงตอนนี้: 25 deployment ใน
superappdev, 24 ในsuperappuat[live] origin/developmentมี deployment manifest 25 ไฟล์สำหรับ dev (รวมcompanies-migration,report,fx-report) ตรงกับ cluster — local checkout เก่ามีแค่ 23[live]
11. Observability
- ns
observability-dev/observability-uat[live] - ⚠️
observability-devไม่อยู่ในdestinationsของ AppProjectsuperappและไม่มี Application ใน ArgoCD → ถูก apply ด้วยมือทั้งหมด[live] otel-collector— OTLP gRPC 4317 / HTTP 4318 → export เข้าtempo.<ns>.svc.cluster.local:4317[repo]tempo— เก็บ trace ลง blob containertempo-traces-<env>ผ่าน workload identity (ต้องมี role assignment §5)[repo]- service ชี้ OTLP endpoint ผ่าน secret
dev--OpenTelemetry--OtlpEndpointใน KV[live] - managed Prometheus: apply
ama-metrics-prometheus-config.yaml(ConfigMap ใน kube-system) +podmonitor-superapp.yaml[repo] - Grafana dashboard นำเข้าด้วย
scripts/Import-GrafanaDashboard.ps1[repo] - ingress ของ Tempo: dev ใช้
nginx-internal, uat ใช้kong-uat[live]
12. Namespace security (optional แต่ของจริงมี)
src/yamls/security/—namespace.yaml,service-account.yaml(security-aks-sa),sonarqube.yaml,dependency-track.yaml[repo]- รันจริง:
sonarqube,dependency-track-apiserver,dependency-track-frontend— expose ผ่าน ingress classkong-uat[live]
13. CI/CD ที่ทำให้ image เปลี่ยน
- Azure DevOps pipeline (
pipelines/templates/):run-tests.yml→docker-build-push.yml(push ACR) →image-scan.yml→update-manifest.yml(แก้ image tag ใน Backend_Iac แล้ว push[skip ci])[repo] - image tag =
{env}-{commit-sha}เช่นdev-686993dไม่ใช่ semver[live] - ArgoCD auto-sync + selfHeal หยิบ commit ไป apply เอง → ห้ามแก้ image ด้วย
kubectl set imageจะถูก selfHeal ย้อนกลับ[repo] - rollback:
docs/operation/ROLLBACK_PROCEDURE.md[repo]
14. ⚠️ Gotcha ที่เจอจริงใน cluster ปัจจุบัน
- umbrella app ชนกับ appset — resource ทุกตัวใน
superappdevมี tracking-idsuperapp-dev:…(umbrella เป็นเจ้าของ) ทำให้ app ราย service (user-dev,codex-dev, …) ขึ้น OutOfSync ถาวร ทั้งที่ Healthy — เป็นผลจากการ apply ทั้งสองแบบพร้อมกัน cluster ใหม่ให้เลือกแบบเดียว[live] superapp-dev(umbrella) status = Synced / Degraded[live]superapp-uat-observabilityชี้targetRevision: uat→ status Missing (branch/pathไม่ตรง)[live]superapp-securityมี manifest ใน repo แต่ ไม่มี Application ใน cluster → nssecurityถูก apply มือ[live]- ingressClass
uat-nginx-internalไม่มีอยู่จริง แต่fx-service-ingressและredisinsight-ingressในsuperappuatยังอ้างอยู่ → 2 ingress นี้ไม่ถูก route[live] - deployment ที่ตั้งใจ scale 0:
fx-service,fx400fx-service(ทั้ง dev+uat),redisinsight+companies-migration-service(uat)[live] - secret plaintext ใน repo —
src/yamls/dev/superapp/secrets/sync-employee-cronjob-secret.yamlcommitPOSTGRES_CONNECTION_STRINGพร้อมรหัสผ่านจริงของsua-dev-adminลงใน git (ไม่ผ่าน KV/CSI เหมือนตัวอื่น) — cluster ใหม่ควรย้ายเข้า Key Vault + SPC และ rotate รหัสผ่านตัวนี้[live] - local clone ของ Backend_Iac ในเครื่อง (
fdd50179) ตามหลังorigin/development1042 commit — อย่าใช้ไฟล์ในเครื่องเป็นฐานตอน rebuild[live] README.mdของ Backend_Iac ยังเขียนว่า env = DEV/SIT และ 16 service — ของจริงคือ DEV/UAT และ 23 deployment;docs/iac/grill-iac.mdใหม่กว่าและ verify แล้ว ให้ยึดตัวนั้น[live]- appset
superapp-sit-appset.yamlยังอยู่ใน repo แต่ ไม่มี namespacesuperappsitใน cluster[live] - ns
aks-commandมีอยู่ → ทีมใช้az aks command invokeเข้าถึง private cluster[live] - ApplicationSet drift — CR ใน cluster มี 15 service,
origin/developmentมี 21 → app ของfxcodex,orchestrator,fxrate,fxfile,fx-report,companies-migrationฯลฯ ไม่ได้ถูก generate แต่ deployment ยังรันอยู่ (umbrella app เป็นคนดูแล)[live] observability-devไม่มีใน AppProject destinations → apply มือ (ดู §11)[live]
15. Verification checklist
# 1. node + pool
kubectl get nodes -o wide
kubectl get nodes -L workload-type
# 2. addon พร้อม
kubectl get pods -n kube-system | grep -E 'wi-webhook|secrets-store|ama-|azure-policy'
# 3. workload identity ใช้ได้จริง (ต้องเห็น K8s Secret ถูกสร้าง)
kubectl get secretproviderclass -n superappdev
kubectl get secret -n superappdev | head
# 4. ingress ชั้น network
kubectl get ingressclass
kubectl get svc -A | grep LoadBalancer
kubectl get ingress -A
# 5. GitOps
kubectl get applications -n argocd \
-o custom-columns='NAME:.metadata.name,SYNC:.status.sync.status,HEALTH:.status.health.status'
# 6. workload
kubectl get deploy -n superappdev
kubectl get pods -n superappdev --field-selector=status.phase!=Running
# 7. policy ไม่ block
kubectl get constraints -o custom-columns='KIND:.kind,ACTION:.spec.enforcementAction'
# 8. end-to-end ผ่าน ingress (รันจากใน VNet)
curl -H 'Host: api.sua-az-dev.internal' http://172.29.88.5/api/user-service/health
16. ลำดับสรุป (TL;DR)
- Networking + firewall (UDR egress) + subnet 3 ตัว → ACR / KV / MI / Log Analytics
- Provision cluster: private, azure CNI overlay, Azure RBAC, 2 node pool (taint/label ตาม §2)
- เปิด addon: OIDC + Workload Identity + KV CSI (rotation 1h) + azure-policy + Defender + monitoring + managed Prometheus + app-routing
- Role assignment: kubelet→AcrPull, centralize MI→KV/Storage/Redis
- สร้าง namespace ทั้งหมด → apply ServiceAccount → สร้าง federated credential ต่อ namespace
- Seed Key Vault ให้ครบตาม objectName ใน SecretProviderClass
- Apply NginxIngressController (dev) / ติดตั้ง Kong chart (uat)
- ติดตั้ง ArgoCD v3.3.9 (
server.insecure=true) → repo secret → AppProject → ingress → notifications - Apply umbrella Application หรือ ApplicationSet (เลือกอย่างเดียว) → รอ auto-sync
- Observability + security namespace
- เดิน checklist §15