Azure Kubernetes에서 비용 효율적인 마이크로 서비스 플랫폼 구현
Source: Dev.to
Scope and Assumptions
이 게시물은 다음을 전제로 합니다:
- Kubernetes 기본 개념에 대한 친숙함
- Terraform 및 Helm을 읽는 데 익숙함
- 단순히 시스템을 배포하는 것이 아니라 운영 시스템에 대한 관심
플랫폼은 Azure Kubernetes Service에서 실행되며, Terraform으로 프로비저닝하고 Helm으로 배포합니다.
1. AKS 기본선: 작게 시작하고 필요에 따라 확장
가장 흔한 AKS 비용 실수는 피크 부하에 맞춰 프로비저닝하는 것입니다.
대신에 우리는:
- 최소 기본 용량으로 시작합니다
- 클러스터 자동 스케일러를 활성화합니다
- 수요에 따라 노드 수를 조정합니다
자동 스케일링이 적용된 AKS 클러스터
resource "azurerm_kubernetes_cluster" "aks" {
name = var.cluster_name
location = var.location
resource_group_name = var.resource_group_name
dns_prefix = var.cluster_name
default_node_pool {
name = "default"
vm_size = "Standard_D2s_v5"
auto_scaling_enabled = true
min_count = 1
max_count = 10
}
identity {
type = "SystemAssigned"
}
}
왜 이렇게 작동하는가
- 유휴 비용이 낮게 유지됩니다
- 노드는 파드가 대기 중일 때만 추가됩니다
- 용량이 추정이 아니라 실제 수요에 맞춰집니다
2. 예측 가능한 동작을 갖는 수평 포드 자동 스케일링
자동 스케일링 기본값은 공격적이며 종종 불안정합니다.
우리는 스케일 동작을 명시적으로 조정하여 churn 및 지연 시간 스파이크를 감소시킵니다.
안정화가 적용된 HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-service
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-service
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 300
핵심 결과
- 짧은 트래픽 감소 시 급격한 스케일‑다운 방지
- 꼬리 지연 시간 개선
- 불필요한 포드 재시작 감소
3. 내결함성 워크로드를 위한 Spot 노드 풀
Spot 용량은 가장 높은 비용 효율을 제공하는 최적화 중 하나입니다—올바르게 격리될 때.
Terraform: Spot Node Pool
resource "azurerm_kubernetes_cluster_node_pool" "spot" {
name = "spot"
kubernetes_cluster_id = azurerm_kubernetes_cluster.aks.id
vm_size = "Standard_D2as_v5"
priority = "Spot"
eviction_policy = "Delete"
spot_max_price = -1
auto_scaling_enabled = true
min_count = 0
max_count = 10
node_taints = [
"kubernetes.azure.com/scalesetpriority=spot:NoSchedule"
]
}
Spot 노드에서 워커 스케줄링
tolerations:
- key: "kubernetes.azure.com/scalesetpriority"
operator: "Equal"
value: "spot"
effect: "NoSchedule"
nodeSelector:
kubernetes.azure.com/scalesetpriority: spot
우리가 따른 규칙
- API는 절대 Spot에서 실행되지 않음
- 워커는
SIGTERM을 깔끔하게 처리함 - 모든 상태는 워커 외부에 존재함
이와 같이 사용하면 Spot 용량이 사용자에게 눈에 띄는 영향을 주지 않으면서도 상당한 비용 절감을 제공합니다.
4. 프라이빗 네트워킹을 이용한 관리형 PostgreSQL
데이터베이스는 비용 실험을 수행하는 장소가 아닙니다.
PostgreSQL은 다음과 같은 관리형 서비스로 실행됩니다:
- 서브넷 위임
- 프라이빗 DNS
- 공개 액세스 없음
PostgreSQL용 위임된 서브넷
resource "azurerm_subnet" "postgres" {
name = "postgres-subnet"
virtual_network_name = azurerm_virtual_network.main.name
resource_group_name = var.resource_group_name
address_prefixes = ["10.0.2.0/24"]
delegation {
name = "postgres"
service_delegation {
name = "Microsoft.DBforPostgreSQL/flexibleServers"
}
}
}
이것이 중요한 이유
- 데이터베이스가 공용 인터넷에서 접근할 수 없습니다
- 네트워크 계층에서 접근이 제한됩니다
- 운영 위험이 크게 감소합니다
5. Helm 기본값으로 안전한 배포
Helm 차트는 보안‑우선 기본값을 전제로 작성되었습니다.
Pod 보안 컨텍스트
securityContext:
runAsUser: 1000
runAsGroup: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
이 설정은 즉시:
- 공격 표면을 축소합니다
- 런타임 변조를 방지합니다
- 불안전한 이미지를 초기에 감지합니다
중요한 헬스 프로브
livenessProbe:
httpGet:
path: /health/db-cache
port: 8080
initialDelaySeconds: 60
우리는 프로세스 상태뿐만 아니라 의존성도 의도적으로 확인합니다.
6. 워크로드 아이덴티티: 쿠버네티스에 비밀 저장 금지
클라우드 자격 증명을 쿠버네티스 시크릿에 저장할 필요가 없습니다.
우리는 워크로드 아이덴티티를 사용하여 파드‑투‑Azure 인증을 수행합니다.
연합 아이덴티티 자격 증명
resource "azurerm_federated_identity_credential" "api" {
name = "api-federated"
parent_id = azurerm_user_assigned_identity.api.id
issuer = azurerm_kubernetes_cluster.aks.oidc_issuer_url
subject = "system:serviceaccount:default:api"
audiences = ["api://AzureADTokenExchange"]
}
7. Ingestion‑Based Pricing 없이 관측성
관리형 로그 수집 대신 다음을 사용합니다:
- Prometheus – 메트릭 수집
- Loki – 로그 수집
- Object storage – 보관
Loki Storage Configuration
storage_config:
azure:
container_name: logs
account_name: ${ACCOUNT_NAME}
access_tier: Cool
왜 이렇게 작동하는가
- 로그 조회 빈도가 낮음
- 스토리지는 저렴함
- 수집 비용이 관리형 관측성 가격을 좌우함
8. 인프라 접근 패턴 (보안 및 실용적)
클러스터 접근
Azure AD‑기반 kubectl
- 감사 가능
- 공유 자격 증명 없음
실제로 cluster-admin 접근은 소규모 부트스트랩 그룹으로 제한되며, 대부분의 팀은 네임스페이스 범위 역할을 사용합니다.
데이터베이스 접근 (가끔 관리자 작업)
kubectl run psql \
--image=postgres:16 \
--rm -it -- \
psql -h <private‑endpoint> -U admin
데이터베이스는 비공개 상태를 유지하며, 접근은 인증되고 감사 가능합니다.
관측 대시보드
Grafana는 Azure AD를 이용한 OAuth 뒤에서 보호됩니다.
OAuth2‑Proxy는 다중 복제본으로 실행되며 쿠키 비밀을 회전시켜 단일 장애 지점을 방지합니다.
- VPN 없음
- 베스천 호스트 없음
- 추가 관리형 서비스 불필요
운영 교훈 (실제)
-
기본값은 거의 프로덕션에 안전하지 않다
자동 스케일링, 프로브, 보안 컨텍스트는 명시적인 튜닝이 필요합니다. -
스팟 용량은 규율이 필요하다
매우 잘 작동하지만—단독으로 사용할 때만 가능합니다. -
Identity가 Secrets보다 확장성이 좋다
운영 부하와 보안 위험을 감소시킵니다. -
Kubernetes는 도구이며 목적지가 아니다
레버리지를 제공할 때만 사용하고—덤핑 그라운드로 사용하지 마세요.
마무리 생각
이 구현은 영리한 트릭에 관한 것이 아니라 의도적인 트레이드‑오프에 관한 것입니다.
다음과 같이:
- 필요할 때만 확장
- 스팟 용량을 책임감 있게 사용
- 중요한 상태를 관리된 상태로 유지
- 인제션 기반 가시성 비용을 회피
- 보안을 기본값으로 다룸
그 결과 다음과 같은 시스템이 탄생했습니다:
- 운영이 예측 가능함
- 유휴 시 비용 효율적
- 부하가 걸릴 때 회복력 있음
- 진화가 쉬움
아키텍처는 방향을 설정한다.
구현은 그것이 현실과 마주했을 때 살아남을 수 있는지를 결정한다.
