Şırnak merkezli, banka yan kuruluşu olarak POS + ödeme + esnaf finansman uygulamaları geliştiren bir fintech, 28 microservice’ini eski VM tabanlı deployment’tan AKS’e taşıdı. 8 aylık programın sahadaki teknik notları aşağıda.
Kubernetes Nedir? Neden?
| Konu | Docker standalone | Kubernetes (K8s) |
|---|---|---|
| Multi-host orchestration | Yok (Swarm sınırlı) | Var (cluster) |
| Self-healing | Manuel | Otomatik (pod restart, reschedule) |
| Scaling | Manuel | HPA + cluster autoscaler |
| Service discovery | Manuel | Built-in (DNS + Service) |
| Rollout / rollback | Manuel | Built-in (Deployment) |
| Karmaşıklık | Düşük | Yüksek (öğrenme eğrisi) |
AKS vs Self-Hosted K8s
Self-hosted (kubeadm, RKE2) tam kontrol verir ama operasyon yükü yüksek (control plane backup, etcd, API server upgrade). AKS managed control plane, Azure-native entegrasyon (Entra ID, Key Vault, ACR, Defender for Containers). Şirket AKS Standard tier seçti ($73/ay control plane SLA).
Cluster Topology
AKS Cluster: aks-prod-tr
Region: Northeurope (TR'ye yakın, ExpressRoute)
Kubernetes versiyon: 1.30 (auto upgrade enabled, channel: stable)
Networking:
- Network plugin: Azure CNI Overlay
- Network policy: Cilium (BPF-based, advanced)
- Service CIDR: 10.0.0.0/16
- Pod CIDR: 10.244.0.0/16 (overlay, VNet IP yemiyor)
API Server: Private (no public)
Ingress: Application Gateway Ingress Controller (AGIC) + WAF
Node Pool Tasarımı
| Pool | SKU | Sayı | Workload |
|---|---|---|---|
| system | D4s_v5 | 3 (zone-spread) | kube-system, ingress, monitoring |
| app-default | D8s_v5 | 4-15 (autoscale) | Standart microservice |
| app-memory | E16s_v5 (memory-opt) | 2-6 | In-memory cache, ML inference |
| spot-batch | D8s_v5 Spot | 0-10 (autoscale) | Non-critical batch, ETL |
Pod Resource Management
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-api
namespace: payment
spec:
replicas: 3
selector:
matchLabels: { app: payment-api }
template:
metadata:
labels: { app: payment-api }
spec:
nodeSelector:
agentpool: app-default
containers:
- name: api
image: acrprod.azurecr.io/payment-api:1.4.2
ports: [{ containerPort: 8080 }]
resources:
requests: { cpu: "200m", memory: "256Mi" }
limits: { cpu: "1000m", memory: "1Gi" }
readinessProbe:
httpGet: { path: /health/ready, port: 8080 }
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet: { path: /health/live, port: 8080 }
initialDelaySeconds: 30
periodSeconds: 10
env:
- name: ConnectionStrings__Db
valueFrom:
secretKeyRef: { name: payment-secrets, key: db-conn }
Horizontal Pod Autoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: payment-api-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: payment-api
minReplicas: 3
maxReplicas: 30
metrics:
- type: Resource
resource:
name: cpu
target: { type: Utilization, averageUtilization: 70 }
- type: Resource
resource:
name: memory
target: { type: Utilization, averageUtilization: 80 }
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
Ingress (AGIC + WAF)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: payment-ingress
annotations:
kubernetes.io/ingress.class: azure/application-gateway
appgw.ingress.kubernetes.io/ssl-redirect: "true"
appgw.ingress.kubernetes.io/waf-policy-for-path: "/subscriptions/.../wafPolicy"
appgw.ingress.kubernetes.io/health-probe-path: "/health/live"
spec:
tls:
- hosts: [api.fintech.com.tr]
secretName: tls-api
rules:
- host: api.fintech.com.tr
http:
paths:
- path: /payment
pathType: Prefix
backend:
service:
name: payment-api
port: { number: 80 }
Secret Management (Key Vault CSI)
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: payment-kv
spec:
provider: azure
parameters:
usePodIdentity: "false"
useVMManagedIdentity: "true"
userAssignedIdentityID: ""
keyvaultName: "kv-prod-fintech"
objects: |
array:
- |
objectName: db-connection-string
objectType: secret
- |
objectName: payment-api-key
objectType: secret
tenantId: ""
secretObjects:
- secretName: payment-secrets
type: Opaque
data:
- objectName: db-connection-string
key: db-conn
- objectName: payment-api-key
key: api-key
Avantaj: K8s Secret olarak direkt yazmaktan daha güvenli. Key Vault’tan canlı çekiliyor, döndürmeler otomatik.
RBAC + Entra ID
AKS Entra ID integration:
- Cluster admin: Entra group "aks-cluster-admin" (3 kişi)
- Namespace admin (payment): Entra group "team-payment"
- Read-only (auditor): Entra group "aks-readonly"
kubectl auth check:
kubectl get pods -n payment # team-payment user → izin var
kubectl delete deploy/x -n trading # team-payment user → izin yok
Observability
| Metric / log | Araç |
|---|---|
| Container log | Container Insights → Log Analytics |
| Metric (Prometheus) | Azure Managed Prometheus + Grafana |
| Tracing | OpenTelemetry → App Insights |
| Alert | Azure Monitor alert + Teams webhook |
Security: Defender for Containers
- Image scan (CVE) on push to ACR
- Runtime threat detection (suspicious pod, lateral movement)
- Compliance benchmark (CIS, PCI DSS)
- Network policy gap analizi
Sonuçlar (8 Ay)
| Metrik | Önce (VM) | Sonra (AKS) |
|---|---|---|
| Deployment frequency | ~~haftada 1 | ~~günde 4-6 |
| Mean Time to Recovery | ~~45 dk | ~~3 dk (rollback) |
| Resource utilization (avg CPU) | ~~%18 | ~~%52 (bin packing) |
| VM sayısı | ~~28 | ~~12 (node pool) |
| Aylık compute maliyet | ~~$8.500 | ~~$5.200 |
| Black Friday peak handling | Manuel scale 2 saat | Auto-scale 3 dk |
Sahada Düşülen Üç Tuzak
- Resource request/limit eksik bırakmak: Pod’lar çakışır, OOMKill olur, scheduler kötü kararlar verir. Her container’a request + limit zorunlu.
- Probe (readiness/liveness) eklememek: Pod up sanılır ama trafik kabul etmiyor, ya da app dead ama K8s restart etmiyor. Probe’lar Day 1.
- Tek cluster’a her şeyi sıkıştırmak: Dev + test + prod aynı cluster’da, namespace ile ayrım yetmez. Cluster ayrımı (en az prod ayrı) güvenlik + blast radius.
CloudSpark olarak AKS cluster tasarımı, node pool stratejisi, Azure CNI Overlay + Cilium, ingress + WAF, Key Vault CSI integration, RBAC ve observability stack için danışmanlık veriyoruz.



