워크로드 및 연동 솔루션 별 고려 사항
Skuber+ Optimize 워크로드 및 연동 솔루션 별 고려 사항
목차
- Skuber+ Optimize 동작 원리
- 워크로드별 호환성 매트릭스
- Stateful 워크로드
- 데이터베이스 워크로드
- 메시지 큐 / 스트리밍
- JVM 기반 애플리케이션
- GPU/ML 워크로드
- GitOps 연동
- Service Mesh 연동
- 모니터링/Observability 연동
- Operator 기반 워크로드
- Batch/CronJob 워크로드
- 고객 사전 점검 체크리스트
- 권장 설정 템플릿
개요
본 문서는 Skuber+ Optimize(VPA 기반 리소스 최적화 솔루션)를 Kubernetes 환경에 적용할 때 고려해야 하는 워크로드 유형과 연동 솔루션들에 대한 종합 가이드입니다.
1. Skuber+ Optimize 동작 원리
1.1 목표: 클라우드 비용 최적화
Kubernetes 환경에서 리소스 오버프로비저닝은 클라우드 비용 낭비의 주요 원인입니다. 많은 조직이 "안전을 위해" 실제 사용량보다 2~3배 높은 리소스를 할당하며, 이는 불필요한 비용 지출로 이어집니다.
Skuber+ Optimize의 핵심 가치:
- 실제 사용량 기반의 적정 리소스 할당으로 비용 절감
- 안전 마진과 OOM 방지 메커니즘으로 안정성 보장
- 자동화된 리소스 관리로 운영 부담 감소
1.2 적용 대상: Deployment만 지원
Skuber+ Optimize는 Deployment 워크로드에만 적용됩니다.
| 워크로드 유형 | Optimize 적용 | 설명 |
|---|---|---|
| Deployment | 자동 적용 | Stateless 워크로드, 안전한 롤링 업데이트 가능 |
| StatefulSet | 추천값만 제공 | 순차적 재시작 필요, 수동 적용 권장 |
| DaemonSet | 추천값만 제공 | 노드별 실행, 수동 검토 필요 |
| Job/CronJob | 추천값만 제공 | 실행 시간 짧음, 참고용 |
Deployment만 지원하는 이유:
- Deployment는 롤링 업데이트를 통해 서비스 중단 없이 리소스 변경 가능
- StatefulSet, DaemonSet 등은 재시작 시 서비스 영향이 크거나 데이터 정합성 이슈 발생 가능
- 안전한 비용 최적화를 위해 검증된 워크로드 유형에만 자동 적용
1.3 동작 방식
Skuber+ Optimize는 Kubernetes VPA(Vertical Pod Autoscaler)의 Recommender가 제공하는 리소스 추천값을 기반으로 Deployment 워크로드의 CPU/Memory 리소스를 자동으로 최적화합니다.
1.4 VPA 추천값의 한계
VPA Recommender의 추천값은 과거 사용량 데이터를 기반으로 계산됩니다. 그러나 다음과 같은 상황에서는 추천값만으로 충분하지 않을 수 있습니다:
| 상황 | 원인 | 영향 |
|---|---|---|
| 트래픽 급증 | 예측 불가능한 스파이크 | 순간적 메모리 부족 |
| JVM Warmup | Heap 확장, JIT 컴파일 | 초기 메모리 급증 |
| 배치 작업 | 대량 데이터 로딩 | 일시적 메모리 스파이크 |
| 캐시 프리로딩 | 애플리케이션 시작 시 캐시 적재 | 초기 메모리 사용량 높음 |
| Agent 최초 설치 | 리소스 프로파일 변경 | Pod 재기동 시 Crash |
이러한 한계를 보완하기 위해 Skuber+ Optimize는 안전 마진과 OOM 방지 메커니즘을 제공합니다.
1.5 OOM 방지 메커니즘
Skuber+ Optimize는 VPA 추천값에 안전 마진을 적용하고, OOM 발생 시 자동으로 Bump-up하여 안정성을 보장합니다.
1.6 Bump-up 단계별 상세
| 단계 | 마진 | 메모리 계산 예시 (추천값 1Gi 기준) | 트리거 조건 |
|---|---|---|---|
| 기본 | +30% | 1Gi × 1.3 = 1.3Gi | 초기 적용 시 |
| 1차 Bump-up | +50% | 1Gi × 1.5 = 1.5Gi | OOM 1회 발생 |
| 2차 Bump-up | +100% | 1Gi × 2.0 = 2.0Gi | OOM 2회 발생 |
| 3차 Bump-up | +200% | 1Gi × 3.0 = 3.0Gi | OOM 3회 발생 |
| 4차 롤백 | 원복 | 원래 설정으로 복원 | OOM 4회 발생 |
구현 세부사항
OOM 감지 방식:
# Pod 상태 모니터링
status:
containerStatuses:
- name: my-app
lastState:
terminated:
reason: OOMKilled # OOM 감지 트리거
exitCode: 137Bump-up 적용 로직:
# 의사 코드
def calculate_memory_with_margin(recommended_memory, crash_count):
margins = {
0: 1.30, # 기본: +30%
1: 1.50, # 1차: +50%
2: 2.00, # 2차: +100%
3: 3.00, # 3차: +200%
}
if crash_count >= 4:
return rollback_to_original()
return recommended_memory * margins.get(crash_count, 1.30)안정화 판단 기준:
- Bump-up 적용 후 24시간 동안 OOM 미발생 시 안정화로 판단
- 안정화 후에도 추천값이 변경되면 새로운 추천값 + 30% 마진으로 재조정
롤백 시 처리
4차 연속 OOM 발생 시:
- 즉시 롤백: Skuber+ Optimize 적용 전 원래 리소스 설정으로 복원
- 알림 발송: 관리자에게 Slack/Email 알림
- 자동 제외: 해당 워크로드는 Skuber+ Optimize 적용 대상에서 자동 제외
- 분석 요청: 수동 분석 후 재적용 필요
OOM 방지 설정 예시
# Skuber+ Optimize CRD 설정
apiVersion: skuber.io/v1
kind: OptimizePolicy
metadata:
name: my-app-policy
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
oomProtection:
enabled: true
baseMargin: 30 # 기본 마진 30%
bumpUpMargins:
- 50 # 1차: 50%
- 100 # 2차: 100%
- 200 # 3차: 200%
maxBumpUpCount: 3 # 최대 Bump-up 횟수
stabilizationPeriod: 24h # 안정화 판단 기간
rollbackOnMaxRetry: true # 최대 재시도 후 롤백
notifyOnRollback:
slack: "#alerts"
email: "ops@company.com"워크로드별 OOM 방지 권장 설정
| 워크로드 유형 | 기본 마진 | Bump-up 활성화 | 특별 고려사항 |
|---|---|---|---|
| Stateless 웹앱 | 30% | 권장 | 표준 설정 적용 |
| JVM 애플리케이션 | 50% | 필수 | Heap 확장 고려 |
| 메모리 캐시 (Redis Client) | 40% | 권장 | 캐시 크기 변동 |
| Batch/CronJob | 50% | 선택적 | 실행 시간 짧음 |
| ML Inference | 40% | 권장 | 모델 로딩 시 스파이크 |
2. 워크로드별 호환성 매트릭스
Skuber+ Optimize는 Deployment에만 자동 적용됩니다. 다른 워크로드 유형은 추천값을 참고하여 수동으로 적용해야 합니다.
2.1 종합 호환성 요약
| 워크로드 유형 | Optimize 자동 적용 | 위험도 | 권장 OOM 마진 | 비고 |
|---|---|---|---|---|
| Deployment (Stateless) | 자동 적용 | 낮음 | 30% | 비용 최적화 대상 |
| Deployment (JVM) | 자동 적용 | 중간 | 50% | Heap 메모리 고려 |
| Deployment (ML Inference) | 자동 적용 | 중간 | 40% | 모델 로딩 고려 |
| StatefulSet (일반) | 추천값만 | 중간 | 40% | 수동 적용 권장 |
| StatefulSet (Database) | 추천값만 | 높음 | 50% | Operator 연동 권장 |
| StatefulSet (Kafka Broker) | 제외 | 매우 높음 | N/A | 적용 금지 |
| DaemonSet | 추천값만 | 중간 | 40% | 노드별 검토 필요 |
| Job/CronJob | 추천값만 | 낮음 | 50% | 실행 시간 짧음 |
2.2 Deployment 세부 분류
Deployment라도 애플리케이션 특성에 따라 권장 설정이 다릅니다:
| Deployment 유형 | 권장 OOM 마진 | 특별 고려사항 |
|---|---|---|
| 일반 웹앱 (Node.js, Python, Go) | 30% | 표준 설정 |
| JVM 애플리케이션 (Java, Kotlin) | 50% | Heap 확장, GC 고려 |
| ML Inference | 40% | 모델 로딩 시 스파이크 |
| 메모리 캐시 클라이언트 | 40% | 캐시 크기 변동 |
| API Gateway | 30% | 트래픽 패턴 모니터링 |
2.3 연동 솔루션별 호환성
| 연동 솔루션 | 호환성 | 설정 필요 | 주요 이슈 |
|---|---|---|---|
| ArgoCD | 조건부 | ignoreDifferences 필수 |
Sync Loop 방지 |
| FluxCD | 조건부 | 유사한 설정 필요 | Drift Detection |
| Helm Operator | 호환 | values 충돌 주의 | 릴리스 관리 |
| KEDA | 조건부 | 메트릭 분리 필요 | HPA 충돌 가능 |
| Istio | 호환 | Sidecar 리소스 고려 | CPU 오버헤드 |
| Linkerd | 호환 | Sidecar 리소스 설정 | 경량화된 오버헤드 |
| Prometheus | 호환 | - | 메트릭 수집 |
| Datadog | 호환 | - | 메트릭 연동 |
| HPA | 주의 | 메트릭 분리 필수 | 충돌 위험 높음 |
3. Stateful 워크로드
Skuber+ Optimize 정책: StatefulSet에는 자동 최적화가 적용되지 않습니다. 추천값만 제공되며, 수동으로 검토 후 적용해야 합니다.
3.1 StatefulSet에 자동 적용하지 않는 이유
핵심 문제:
- StatefulSet의 Pod는 순차적으로 관리되며, 임의 재시작이 어려움
- VPA Auto 모드는 Pod를 evict하여 리소스 변경을 적용하므로 서비스 중단 위험
- Master/Primary Pod가 먼저 재시작되면 데이터 정합성 문제 발생 가능
기술적 근거 (Kubernetes VPA 공식 문서):
"VPA does not support StatefulSets yet. The problem is scaling pods in StatefulSet is not simple. Neither starting nor restarting can be done the way it's done for a Deployment or ReplicaSet."
3.2 추천값 활용 방법
Skuber+ Optimize는 StatefulSet에 대해서도 추천값을 제공합니다. 이 추천값을 참고하여 수동으로 리소스를 조정할 수 있습니다.
# Skuber+가 제공하는 추천값 조회 (VPA Off 모드)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: stateful-app-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: StatefulSet
name: my-stateful-app
updatePolicy:
updateMode: "Off" # 추천값만 수집, 자동 적용 안 함
resourcePolicy:
containerPolicies:
- containerName: '*'
minAllowed:
cpu: 100m
memory: 256Mi
maxAllowed:
cpu: 4
memory: 8Gi3.3 수동 적용 전략
| 단계 | 모드 | 목적 |
|---|---|---|
| 1. 모니터링 | Off |
7-14일간 리소스 패턴 분석 |
| 2. 검증 | Off |
추천값 검토 및 테스트 환경 적용 |
| 3. 점진적 적용 | Initial |
신규 Pod 생성 시에만 적용 |
| 4. 유지보수 시 적용 | Manual | 계획된 유지보수 시간에 수동 적용 |
4. 데이터베이스 워크로드
Skuber+ Optimize 정책: 데이터베이스 워크로드(StatefulSet)에는 자동 최적화가 적용되지 않습니다. 추천값만 제공되며, Operator와 연동하거나 수동으로 적용해야 합니다.
4.1 MySQL / PostgreSQL
위험 요소:
- Primary/Replica 구조에서 Primary Pod 재시작 시 Failover 발생
- 대용량 데이터베이스의 경우 재시작 시간이 길어 서비스 영향
- Connection Pool이 끊기며 애플리케이션 에러 발생 가능
권장 구성:
# MySQL VPA - Recommend Only
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: mysql-vpa
namespace: database
spec:
targetRef:
apiVersion: "apps/v1"
kind: StatefulSet
name: mysql
updatePolicy:
updateMode: "Off"
resourcePolicy:
containerPolicies:
- containerName: mysql
minAllowed:
cpu: 500m
memory: 1Gi
maxAllowed:
cpu: 8
memory: 32Gi
controlledResources: ["cpu", "memory"]Database Operator 연동 시:
- Percona Operator / Vitess: Operator의 스케일링 기능 사용 권장
- CloudNativePG: Operator가 리소스 관리 담당
- VPA는
Off모드로 추천값만 참고
4.2 MongoDB
특수 고려사항:
- Sharded Cluster의 경우 각 Shard가 별도 StatefulSet으로 운영
- Config Server, Mongos, Shard 모두 다른 리소스 요구사항
- 리밸런싱 중 스케일링 시 데이터 정합성 위험
MongoDB Operator (Percona/MongoDB Enterprise)와 연동:
# MongoDB VPA - 각 컴포넌트별 분리
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: mongodb-shard-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: StatefulSet
name: mongodb-shard0
updatePolicy:
updateMode: "Off"4.3 Redis
Standalone Redis:
- 메모리 기반 데이터 저장소로 메모리 설정이 핵심
maxmemory설정과 Pod memory limit 동기화 필수
Redis Cluster:
- Cluster 모드에서 Pod 재시작 시 Slot 재분배 발생
- Failover 시간 동안 일부 키 접근 불가
권장 설정:
# Redis Cluster - VPA 제외 권장
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: redis-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: StatefulSet
name: redis-cluster
updatePolicy:
updateMode: "Off" # 클러스터 안정성을 위해 Off 필수4.4 Elasticsearch
핵심 이슈:
- Shard 재분배 시 클러스터 부하 급증
- Master 노드 재시작 시 클러스터 불안정
- 메모리 설정(Heap)과 Pod 메모리 간 관계 복잡
ECK (Elastic Cloud on Kubernetes) 연동:
# Elasticsearch - Data 노드만 VPA 참고용 적용
# Master 노드는 절대 VPA 적용 금지
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: elasticsearch-data-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: StatefulSet
name: elasticsearch-data
updatePolicy:
updateMode: "Off"
resourcePolicy:
containerPolicies:
- containerName: elasticsearch
minAllowed:
memory: 4Gi # ES는 최소 메모리 요구량 높음
controlledResources: ["memory"] # CPU만 제외하고 메모리만 추천5. 메시지 큐 / 스트리밍
Skuber+ Optimize 정책: Kafka Broker(StatefulSet)는 적용 제외 대상입니다. Kafka Consumer가 Deployment인 경우에만 자동 최적화가 적용됩니다.
5.1 Apache Kafka
Broker (반드시 제외):
| 위험 시나리오 | 영향도 | 발생 조건 |
|---|---|---|
| Broker Pod 재시작 | 치명적 | VPA Auto 모드 |
| 파티션 Under-replicated | 높음 | 동시 다중 Broker 재시작 |
| 데이터 손실 | 치명적 | PV 바인딩 해제 |
# Kafka Broker - 반드시 제외 (StatefulSet이므로 Skuber+ 자동 적용 안 됨)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: kafka-broker-vpa-exclude
spec:
targetRef:
apiVersion: "apps/v1"
kind: StatefulSet
name: kafka
updatePolicy:
updateMode: "Off" # 추천값 수집만 (적용 절대 금지)Consumer (Deployment인 경우 자동 적용):
Kafka Consumer가 Deployment로 배포된 경우 Skuber+ Optimize가 자동 적용됩니다. 단, Consumer 리밸런싱 문제를 완화하기 위한 설정이 필요합니다.
# Consumer 애플리케이션 설정
spec:
containers:
- name: kafka-consumer
env:
- name: KAFKA_GROUP_INSTANCE_ID
valueFrom:
fieldRef:
fieldPath: metadata.name # Static Membership
- name: KAFKA_SESSION_TIMEOUT_MS
value: "300000" # 5분 - 재시작 시간 확보
- name: KAFKA_PARTITION_ASSIGNMENT_STRATEGY
value: "org.apache.kafka.clients.consumer.CooperativeStickyAssignor"VPA 대신 KEDA 권장:
# KEDA ScaledObject - Kafka Lag 기반 스케일링
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: kafka-consumer-scaler
spec:
scaleTargetRef:
name: kafka-consumer
minReplicaCount: 1
maxReplicaCount: 10 # 파티션 수 이하로 제한
triggers:
- type: kafka
metadata:
bootstrapServers: kafka:9092
consumerGroup: my-consumer-group
topic: my-topic
lagThreshold: "100"
advanced:
horizontalPodAutoscalerConfig:
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # 리밸런싱 최소화5.2 RabbitMQ
위험 요소:
- Quorum Queue의 경우 노드 재시작 시 리더 선출 발생
- 메시지 손실 방지를 위해 graceful shutdown 필수
권장 설정:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: rabbitmq-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: StatefulSet
name: rabbitmq
updatePolicy:
updateMode: "Off"5.3 Apache Pulsar
- Broker와 BookKeeper 모두 StatefulSet으로 운영
- ZooKeeper 의존성으로 인해 스케일링 복잡
- 권장: VPA Off 모드, Pulsar 자체 autoscaling 기능 활용
6. JVM 기반 애플리케이션
Skuber+ Optimize 정책: JVM 애플리케이션(Deployment)에는 자동 최적화가 적용되지만, Memory는 제외하고 CPU만 최적화합니다. 기본 OOM 마진은 **50%**로 적용됩니다.
6.1 핵심 문제점
GKE 공식 문서 경고:
"Vertical Pod autoscaling is not ready for use with JVM-based workloads due to limited visibility into actual memory usage of the workload."
기술적 이유:
- Heap 메모리 특성: JVM은 할당된 메모리를 즉시 반환하지 않음
- GC 영향: Garbage Collection 활동이 CPU 사용률에 영향
- JIT 컴파일: 시작 시 CPU 버스트 발생으로 잘못된 스케일링 트리거
6.2 JVM 메모리 구성 이해
┌─────────────────────────────────────────┐
│ Container Memory Limit │
│ ┌─────────────────────────────────┐ │
│ │ JVM Heap (-Xmx) │ │
│ │ (Application Objects) │ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ Metaspace / PermGen │ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ Native Memory (Threads, JNI) │ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ Code Cache / Buffers │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘6.3 권장 설정
# JVM 애플리케이션 - CPU만 VPA 적용, Memory는 수동 관리
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: java-app-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: java-application
updatePolicy:
updateMode: "Off" # 또는 "Initial"
resourcePolicy:
containerPolicies:
- containerName: java-app
controlledResources: ["cpu"] # Memory 제외!
minAllowed:
cpu: 200m
maxAllowed:
cpu: 46.4 JVM용 Skuber+ OOM 방지 설정
JVM 애플리케이션은 메모리 특성상 **기본 마진 50%**를 권장합니다:
# Skuber+ Optimize - JVM 전용 설정
apiVersion: skuber.io/v1
kind: OptimizePolicy
metadata:
name: java-app-policy
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: java-application
resourcePolicy:
controlledResources: ["cpu"] # Memory는 JVM이 직접 관리
oomProtection:
enabled: true
baseMargin: 50 # JVM은 기본 50% 마진 권장
bumpUpMargins:
- 75 # 1차: 75%
- 100 # 2차: 100%
- 150 # 3차: 150%
stabilizationPeriod: 48h # JVM은 안정화 기간 늘림JVM OOM 방지를 위한 추가 권장사항:
-XX:+ExitOnOutOfMemoryError: OOM 발생 시 즉시 종료하여 Skuber+가 감지할 수 있도록 함-XX:+HeapDumpOnOutOfMemoryError: 분석용 힙 덤프 생성
JVM 설정 권장사항:
env:
- name: JAVA_OPTS
value: >-
-XX:+UseContainerSupport
-XX:MaxRAMPercentage=75.0
-XX:InitialRAMPercentage=50.0
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1PeriodicGCInterval=100006.5 HPA와 JVM 조합 시 주의사항
# JVM용 HPA - readinessProbe 활용 필수
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: java-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: java-application
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # JIT 컴파일 고려하여 여유 있게
behavior:
scaleUp:
stabilizationWindowSeconds: 120 # JVM Warmup 시간 고려
scaleDown:
stabilizationWindowSeconds: 3007. GPU/ML 워크로드
7.1 GPU 워크로드의 특수성
VPA의 한계:
- VPA는 CPU/Memory만 관리, GPU 리소스는 관리 불가
- GPU 워크로드는
nvidia.com/gpu같은 extended resource 사용 - GPU Pod 재시작 시 비용이 매우 높음 (GPU 인스턴스 시간 손실)
7.2 ML Training 워크로드
권장 도구:
- Kueue: Kubernetes-native job queueing
- Volcano: Gang scheduling for distributed training
- NVIDIA Run:ai: GPU 오케스트레이션
# GPU 워크로드 - VPA 적용 제한
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: ml-training-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: ml-training
updatePolicy:
updateMode: "Off" # 추천값 참고만
resourcePolicy:
containerPolicies:
- containerName: trainer
controlledResources: ["cpu", "memory"] # GPU는 VPA 관리 불가7.3 ML Inference 워크로드
KEDA를 활용한 Queue 기반 스케일링:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: inference-scaler
spec:
scaleTargetRef:
name: inference-server
minReplicaCount: 1
maxReplicaCount: 10
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus:9090
metricName: inference_queue_size
threshold: "10"
query: sum(inference_pending_requests)8. GitOps 연동
8.1 ArgoCD 연동
핵심 문제:
- ArgoCD는 Git을 Source of Truth로 사용
- VPA/Skuber+가 리소스 값을 변경하면 "OutOfSync" 상태 발생
- Auto-Sync 설정 시 무한 루프 위험
필수 설정 요약
| 설정 | 역할 |
|---|---|
ignoreDifferences |
차이 감지에서 제외 (drift 무시) |
RespectIgnoreDifferences=true |
Sync 시에도 해당 필드를 변경하지 않음 |
중요: 두 설정을 함께 사용해야 Skuber+가 변경한 값이 완전히 보존됩니다.
설정별 동작 차이
ignoreDifferences만 설정한 경우:
kubectl edit → 값 변경
↓
ArgoCD: "차이 있지만 ignoreDifferences니까 무시" (selfHeal 안 함)
↓
Sync 발생 (Git 변경 또는 수동 Sync)
↓
ArgoCD: "Sync니까 Git 기준으로 적용!" → 원래 값으로 원복 ❌ignoreDifferences + RespectIgnoreDifferences 설정한 경우:
kubectl edit → 값 변경
↓
ArgoCD: "차이 있지만 ignoreDifferences니까 무시"
↓
Sync 발생
↓
ArgoCD: "RespectIgnoreDifferences니까 이 필드는 건드리지 않음" → 변경값 유지 ✅기본 설정 구조
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
# ... source, destination 등
ignoreDifferences:
- group: <API_GROUP>
kind: <RESOURCE_KIND>
name: <RESOURCE_NAME> # 선택: 특정 리소스만 지정
namespace: <NAMESPACE> # 선택: 특정 네임스페이스만 지정
jsonPointers:
- <JSON_POINTER_PATH>
# 또는
jqPathExpressions:
- <JQ_EXPRESSION>
syncPolicy:
syncOptions:
- RespectIgnoreDifferences=true # 필수!Skuber+ 권장 설정 예시
# ArgoCD Application에 ignoreDifferences 추가
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-application
project: default
source:
repoURL: https://github.com/org/repo
path: manifests
destination:
server: https://kubernetes.default.svc
namespace: production
# Skuber+ Optimize 연동을 위한 핵심 설정
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/template/spec/containers/0/resources/requests
- /spec/template/spec/containers/0/resources/limits
- group: apps
kind: StatefulSet
jsonPointers:
- /spec/template/spec/containers/0/resources/requests
- /spec/template/spec/containers/0/resources/limits
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- RespectIgnoreDifferences=true # 필수!System-Level 설정 (argocd-cm)
여러 Application에 동일한 설정을 적용하려면 ArgoCD ConfigMap에서 전역 설정을 사용합니다.
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cm
namespace: argocd
data:
# 모든 Deployment의 resources 필드 무시
resource.customizations.ignoreDifferences.apps_Deployment: |
jqPathExpressions:
- .spec.template.spec.containers[].resources
# 모든 StatefulSet의 resources 필드 무시
resource.customizations.ignoreDifferences.apps_StatefulSet: |
jqPathExpressions:
- .spec.template.spec.containers[].resources
# 모든 DaemonSet의 resources 필드 무시
resource.customizations.ignoreDifferences.apps_DaemonSet: |
jqPathExpressions:
- .spec.template.spec.containers[].resources참고: System-Level 설정을 사용해도 각 Application에
RespectIgnoreDifferences=true는 개별 설정해야 합니다.
8.2 FluxCD 연동
Kustomization에서 리소스 패치 제외:
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: my-app
namespace: flux-system
spec:
interval: 10m
path: ./manifests
prune: true
sourceRef:
kind: GitRepository
name: my-repo
patches:
- patch: |
- op: remove
path: /spec/template/spec/containers/0/resources
target:
kind: Deployment8.3 Helm과 VPA 연동
values.yaml에서 리소스 템플릿화:
# values.yaml
resources:
requests:
cpu: "100m" # VPA에 의해 오버라이드될 수 있음
memory: "256Mi"
limits:
cpu: "1"
memory: "1Gi"
# VPA가 관리하는 경우 빈 값으로 설정 가능
# resources: {}9. Service Mesh 연동
9.1 Istio 연동
Sidecar 리소스 오버헤드:
- Envoy Proxy: 약 100-150m CPU, 50-100MB Memory per Pod
- VPA 추천값 계산 시 Sidecar 리소스 포함됨
Sidecar 리소스 설정:
# Istio Sidecar 리소스 명시적 설정
apiVersion: v1
kind: ConfigMap
metadata:
name: istio-sidecar-injector
namespace: istio-system
data:
values: |
global:
proxy:
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 256MiVPA에서 Sidecar 제외:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: my-app
resourcePolicy:
containerPolicies:
- containerName: istio-proxy
mode: "Off" # Sidecar는 VPA 관리에서 제외
- containerName: my-app
mode: "Auto"Istio 메트릭 기반 HPA (VPA와 함께 사용):
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
metrics:
- type: External
external:
metric:
name: istio_requests_per_second
selector:
matchLabels:
destination_workload: my-app
target:
type: AverageValue
averageValue: "100"9.2 Linkerd 연동
Linkerd 장점:
- 경량 Rust 기반 Proxy (약 10m CPU, 20MB Memory)
- Istio 대비 50% 이상 낮은 리소스 사용
HPA와 Linkerd 연동 시 주의사항:
Linkerd Sidecar에 리소스 request가 없으면 HPA가 동작하지 않음:
# Linkerd Proxy 리소스 명시적 설정
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
annotations:
linkerd.io/inject: enabled
config.linkerd.io/proxy-cpu-request: "10m"
config.linkerd.io/proxy-memory-request: "20Mi"
config.linkerd.io/proxy-cpu-limit: "100m"
config.linkerd.io/proxy-memory-limit: "50Mi"9.3 Service Mesh 리소스 오버헤드 비교
| Service Mesh | CPU (request) | Memory (request) | VPA 호환성 |
|---|---|---|---|
| Istio (Envoy) | 100m | 128Mi | Sidecar 제외 필요 |
| Istio Ambient | Node 레벨 | Node 레벨 | 양호 |
| Linkerd | 10m | 20Mi | 양호 |
| Cilium | Node 레벨 | Node 레벨 | 양호 |
10. 모니터링/Observability 연동
10.1 Prometheus 연동
VPA 메트릭 수집:
# ServiceMonitor for VPA
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: vpa-recommender
namespace: monitoring
spec:
selector:
matchLabels:
app: vpa-recommender
endpoints:
- port: metrics
interval: 30s유용한 VPA 메트릭:
# VPA 추천값 vs 실제 사용량
vpa_recommender_recommendation_cpu{container="my-app"}
vpa_recommender_recommendation_memory{container="my-app"}
# 실제 리소스 사용량
container_cpu_usage_seconds_total
container_memory_working_set_bytes10.2 Datadog 연동
# Datadog Agent에서 VPA 메트릭 수집
apiVersion: v1
kind: ConfigMap
metadata:
name: datadog-cluster-agent
data:
kubernetes_vpa.yaml: |
ad_identifiers:
- vpa-recommender
init_config:
instances:
- prometheus_url: http://%%host%%:8942/metrics10.3 Grafana 대시보드
VPA 모니터링을 위한 주요 패널:
- 추천값 vs 현재 설정: 리소스 낭비/부족 식별
- 추천값 변화 추이: 시간에 따른 패턴 분석
- Pod 재시작 횟수: VPA Auto 모드 영향 추적
- OOM Kill 이벤트: 메모리 추천값 적정성 확인
11. Operator 기반 워크로드
11.1 Database Operators
| Operator | VPA 연동 | 권장 설정 |
|---|---|---|
| CloudNativePG | 제한적 | Off 모드, Operator 스케일링 사용 |
| Percona Operator (MySQL/MongoDB/PostgreSQL) | 지원 | Operator의 VPA 연동 기능 활용 |
| Strimzi (Kafka) | 비호환 | Cruise Control 사용 권장 |
| Redis Operator | 제한적 | Off 모드 |
| ECK (Elasticsearch) | 제한적 | Off 모드 |
Percona Operator의 VPA 통합 예시:
apiVersion: psmdb.percona.com/v1
kind: PerconaServerMongoDB
metadata:
name: my-cluster
spec:
replsets:
- name: rs0
size: 3
resources:
requests:
cpu: "300m"
memory: "0.5G"
limits:
cpu: "2"
memory: "2G"
# Percona Operator는 자체 VPA 연동 지원
verticalAutoscaler:
enabled: true
updateMode: "Off"11.2 Flink Kubernetes Operator
Flink 자체 Autoscaler 사용 권장:
apiVersion: flink.apache.org/v1beta1
kind: FlinkDeployment
metadata:
name: flink-job
spec:
flinkConfiguration:
# Flink Autoscaler 활성화
job.autoscaler.enabled: "true"
job.autoscaler.scaling.enabled: "true"
job.autoscaler.target.utilization: "0.7"
job.autoscaler.stabilization.interval: "5m"VPA와의 관계:
- Flink Autoscaler는 Task parallelism 조정
- VPA는 TaskManager/JobManager Pod 리소스 조정 가능
- 두 시스템 간 충돌 방지를 위해 VPA는 Off 모드 권장
11.3 Spark Operator
apiVersion: sparkoperator.k8s.io/v1beta2
kind: SparkApplication
metadata:
name: spark-job
spec:
driver:
cores: 1
memory: "1g"
executor:
cores: 2
instances: 3
memory: "2g"
dynamicAllocation:
enabled: true
initialExecutors: 2
minExecutors: 1
maxExecutors: 10참고: Spark의 Dynamic Allocation은 VPA와 별개로 동작하며, VPA 적용 시 충돌 가능
12. Batch/CronJob 워크로드
12.1 CronJob에서 VPA 활용
제한사항:
- CronJob은 주기적으로 새 Pod 생성
- VPA는 8일간의 히스토리 데이터 필요
- 짧은 실행 시간의 Job은 데이터 수집 어려움
권장 접근법:
# CronJob - VPA Initial 모드로 초기 리소스만 설정
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: batch-job-vpa
spec:
targetRef:
apiVersion: "batch/v1"
kind: CronJob
name: daily-report
updatePolicy:
updateMode: "Initial" # 새 Job 생성 시에만 적용11.2 장기 실행 Batch Job
# 장기 실행 Job - Off 모드로 모니터링 후 수동 적용
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: long-running-job-vpa
spec:
targetRef:
apiVersion: "batch/v1"
kind: Job
name: data-migration
updatePolicy:
updateMode: "Off"13. 고객 사전 점검 체크리스트
13.1 환경 정보 수집
인프라 정보
- Kubernetes 버전: ________________
- 클라우드 제공자: ________________ (EKS/GKE/AKS/On-Premise)
- 노드 수: ________________
- 노드 타입/크기: ________________
기존 Autoscaling 설정
- HPA 사용 여부: Yes / No
- VPA 사용 여부: Yes / No
- Cluster Autoscaler 사용 여부: Yes / No
- KEDA 사용 여부: Yes / No
GitOps 도구
- ArgoCD 사용: Yes / No
- Auto-Sync 활성화: Yes / No
- Self-Heal 활성화: Yes / No
- FluxCD 사용: Yes / No
- 기타: ________________
13.2 워크로드 점검
Stateful 워크로드
| 워크로드 이름 | 유형 | Namespace | 현재 리소스 | VPA 적용 가능 |
|---|---|---|---|---|
데이터베이스 워크로드
| DB 종류 | Operator 사용 | StatefulSet 이름 | 권장 모드 |
|---|---|---|---|
Kafka 관련 워크로드
- Kafka Broker:
- Operator: Strimzi / Confluent / Bitnami / 기타
- 브로커 수: ________________
- Kafka Consumer:
- Static Membership 설정: Yes / No
- Consumer Group 수: ________________
JVM 애플리케이션
| 애플리케이션 | JVM 버전 | GC 종류 | Heap 설정 |
|---|---|---|---|
13.3 연동 솔루션 점검
Service Mesh
- Istio 사용: Yes / No
- mTLS 모드: STRICT / PERMISSIVE
- Sidecar 리소스 설정: Yes / No
- Linkerd 사용: Yes / No
- 기타: ________________
모니터링
- Prometheus 사용: Yes / No
- Datadog 사용: Yes / No
- 기타: ________________
14. 권장 설정 템플릿
13.1 Namespace 레벨 기본 설정
# 일반 워크로드용 기본 VPA 설정
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: default-vpa
namespace: my-namespace
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: my-deployment
updatePolicy:
updateMode: "Auto" # Stateless 워크로드용
resourcePolicy:
containerPolicies:
- containerName: '*'
minAllowed:
cpu: 50m
memory: 64Mi
maxAllowed:
cpu: 4
memory: 8Gi
controlledValues: RequestsAndLimits14.2 제외 워크로드 레이블 기반 관리
# 제외할 워크로드에 레이블 추가
metadata:
labels:
skuber.io/vpa-exclude: "true" # VPA 적용 제외14.3 ArgoCD + Skuber+ 통합 설정
# argocd-cm ConfigMap에 추가
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cm
namespace: argocd
data:
# Skuber+ 호환을 위한 전역 설정
resource.customizations.ignoreDifferences.apps_Deployment: |
jqPathExpressions:
- .spec.template.spec.containers[].resources
resource.customizations.ignoreDifferences.apps_StatefulSet: |
jqPathExpressions:
- .spec.template.spec.containers[].resources
resource.customizations.ignoreDifferences.apps_DaemonSet: |
jqPathExpressions:
- .spec.template.spec.containers[].resources14.4 KEDA + VPA 공존 설정
VPA와 KEDA/HPA가 동일한 메트릭을 관리하면 충돌이 발생합니다. 메트릭을 분리하여 공존해야 합니다.
메트릭 분리 원칙:
- VPA: Memory 수직 스케일링
- KEDA/HPA: CPU 또는 Custom Metric 기반 수평 스케일링
# VPA - Memory만 관리
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Auto"
resourcePolicy:
containerPolicies:
- containerName: my-app
controlledResources: ["memory"] # CPU는 제외 (KEDA가 관리)
---
# KEDA - CPU/Custom Metric 기반 HPA
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: my-app-keda
spec:
scaleTargetRef:
name: my-app
triggers:
- type: cpu
metricType: Utilization
metadata:
value: "70"부록: 리스크 매트릭스
전체 리스크 요약
| 카테고리 | 항목 | 발생 확률 | 영향도 | 완화 방안 |
|---|---|---|---|---|
| OOM 방지 | 추천값 부족으로 인한 Crash | 중간 | 높음 | 기본 30% 마진 적용 |
| OOM 방지 | 반복 OOM 발생 | 낮음 | 높음 | Bump-up 메커니즘 (50%→100%→200%) |
| OOM 방지 | Bump-up 실패 후 서비스 불안정 | 매우 낮음 | 치명적 | 4차 시 자동 롤백 |
| StatefulSet | Pod 순서 무시 재시작 | 높음 | 높음 | Off/Initial 모드 |
| Database | Primary 노드 재시작 | 중간 | 치명적 | VPA 제외 또는 Off |
| Kafka Broker | Partition Under-replicated | 높음 | 치명적 | 반드시 제외 |
| Kafka Consumer | Rebalancing Storm | 중간 | 높음 | Static Membership |
| JVM | 잘못된 메모리 추천 | 높음 | 높음 | CPU만 VPA 적용 + 50% 마진 |
| GPU | 리소스 낭비 | 낮음 | 중간 | Kueue/Volcano 사용 |
| ArgoCD | Sync Loop | 높음 | 높음 | ignoreDifferences |
| HPA+VPA | 충돌 | 높음 | 높음 | 메트릭 분리 |
| Service Mesh | Sidecar 리소스 누락 | 중간 | 중간 | 명시적 리소스 설정 |
OOM 방지 효과 지표
| 지표 | 설명 | 목표값 |
|---|---|---|
| OOM 발생률 감소 | Skuber+ 적용 전후 OOM Kill 비율 | 90% 이상 감소 |
| 1차 Bump-up 해결률 | 1차 Bump-up으로 안정화되는 비율 | 80% 이상 |
| 롤백 발생률 | 4차까지 도달하여 롤백되는 비율 | 5% 미만 |
| 평균 안정화 시간 | Bump-up 후 안정화까지 소요 시간 | 24시간 이내 |
참고 문서
- Kubernetes VPA 공식 문서
- KEDA 공식 문서
- ArgoCD Resource Customization
- Istio Performance Best Practices
- Flink Kubernetes Operator Autoscaler
문서 작성: Skuber+ Technical Team