Skip to content

필사 모드: Kubernetes FinOps와 클라우드 비용 최적화 전략: 리소스 낭비를 잡는 실전 가이드

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

들어가며: 왜 Kubernetes 비용 관리가 중요한가

Kubernetes 도입이 가속화되면서 클라우드 비용 문제가 심각해지고 있습니다. CNCF의 2025 FinOps 리포트에 따르면, 기업들의 Kubernetes 관련 클라우드 지출 중 평균 30-35%가 낭비되고 있습니다. 이는 단순히 리소스를 과다하게 할당하는 문제를 넘어, 비용에 대한 가시성 부족, 팀 간 책임 소재 불분명, 최적화 프로세스의 부재라는 구조적 문제에서 비롯됩니다.

FinOps Foundation에서 정의하는 FinOps는 **"엔지니어링, 재무, 비즈니스 팀이 데이터 기반으로 협업하여 클라우드의 비즈니스 가치를 극대화하는 운영 프레임워크"**입니다. 전통적인 IT 인프라에서는 CapEx(자본 지출) 모델로 한 번 구매하면 끝이었지만, 클라우드는 OpEx(운영 지출) 모델로 매 시간, 매 분 비용이 발생합니다. 이런 환경에서 FinOps는 선택이 아닌 필수입니다.

이 글에서는 Kubernetes 환경에 특화된 FinOps 전략을 다룹니다. 비용 가시성 확보부터 리소스 최적화, 자동 스케일링, 그리고 팀 문화까지 실전에서 바로 적용할 수 있는 가이드를 제공합니다.

FinOps의 핵심 원칙과 Kubernetes 적용

FinOps의 세 가지 원칙

FinOps Foundation이 제시하는 핵심 원칙은 다음과 같습니다.

1. **팀은 자신의 클라우드 사용량에 대해 책임을 진다** - 엔지니어링 팀이 비용을 인식하고 최적화 결정을 내려야 합니다

2. **의사결정은 클라우드의 비즈니스 가치에 기반한다** - 단순 비용 절감이 아닌 비즈니스 가치 대비 비용 효율을 추구합니다

3. **FinOps는 중앙 집중형 팀이 주도한다** - 도구, 프로세스, 모범 사례를 중앙에서 관리하되, 실행은 각 팀이 합니다

Inform, Optimize, Operate 사이클

FinOps는 세 단계의 반복 사이클로 운영됩니다.

| 단계 | 목표 | Kubernetes 적용 |

| ------------ | ---------------- | ---------------------------------------------------- |

| **Inform** | 비용 가시성 확보 | Kubecost/OpenCost 도입, 네임스페이스별 비용 대시보드 |

| **Optimize** | 비용 최적화 실행 | Request/Limit 튜닝, Spot 인스턴스, 유휴 리소스 정리 |

| **Operate** | 지속적 거버넌스 | 비용 알림, 정기 리뷰, 팀별 예산 관리 |

Kubernetes 리소스 낭비의 주요 원인

비용 최적화를 시작하기 전에, 어디서 낭비가 발생하는지 정확히 이해해야 합니다.

1. 과도한 Request/Limit 설정

가장 흔한 원인입니다. 개발자들이 "안전하게" 높은 값을 설정하는 경향이 있습니다.

문제: 실제 사용량 대비 과도한 리소스 요청

apiVersion: v1

kind: Pod

metadata:

name: over-provisioned-app

spec:

containers:

- name: app

image: my-app:latest

resources:

requests:

cpu: '2' # 실제 사용량: 200m

memory: '4Gi' # 실제 사용량: 512Mi

limits:

cpu: '4'

memory: '8Gi'

위 예시에서 CPU는 실제 사용량의 10배, 메모리는 8배를 요청하고 있습니다. 이 Pod 하나만으로는 큰 문제가 아니지만, 100개의 Pod가 이런 식이라면 월 수천 달러의 낭비가 발생합니다.

2. 스케일링 정책 부재

트래픽이 감소해도 Pod 수가 줄어들지 않거나, 야간/주말에도 동일한 리소스가 유지되는 경우입니다.

3. 유휴 리소스 방치

더 이상 사용하지 않는 PersistentVolume, LoadBalancer Service, 테스트용 네임스페이스 등이 정리되지 않고 남아 있는 경우입니다.

4. 노드 단편화(Fragmentation)

작은 Pod들이 여러 노드에 분산되어 각 노드의 활용률이 낮아지는 현상입니다.

노드별 리소스 활용률 확인

kubectl top nodes

결과 예시

NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%

node-1 800m 20% 2Gi 25%

node-2 600m 15% 1.5Gi 18%

node-3 400m 10% 1Gi 12%

=> 3개 노드 모두 활용률 25% 이하 - 1개 노드로 통합 가능

비용 가시성 확보: Kubecost와 OpenCost

비용 최적화의 첫 단계는 **"지금 얼마를 쓰고 있는지"**를 아는 것입니다.

OpenCost 설치 및 설정

OpenCost는 CNCF 공식 프로젝트로, Kubernetes 비용 모니터링을 위한 오픈소스 표준입니다. Prometheus와 통합되어 실시간 비용 데이터를 수집합니다.

Helm으로 OpenCost 설치

helm repo add opencost https://opencost.github.io/opencost-helm-chart

helm repo update

helm install opencost opencost/opencost \

--namespace opencost-system \

--create-namespace \

--set opencost.prometheus.internal.enabled=true \

--set opencost.ui.enabled=true

OpenCost의 커스텀 가격 설정을 통해 실제 클라우드 요금을 반영할 수 있습니다.

opencost-custom-pricing.yaml

apiVersion: v1

kind: ConfigMap

metadata:

name: opencost-custom-pricing

namespace: opencost-system

data:

default.json: |

{

"provider": "custom",

"description": "Custom pricing for on-prem + cloud hybrid",

"CPU": "0.031611",

"spotCPU": "0.012644",

"RAM": "0.004237",

"spotRAM": "0.001694",

"storage": "0.000138888",

"GPU": "0.95"

}

kubectl apply -f opencost-custom-pricing.yaml

Kubecost 설치 및 설정

Kubecost는 OpenCost를 기반으로 더 풍부한 기능(알림, 추천, 거버넌스)을 제공하는 상용/오픈소스 하이브리드 도구입니다.

Kubecost 설치 (Free Tier)

helm repo add kubecost https://kubecost.github.io/cost-analyzer/

helm repo update

helm install kubecost kubecost/cost-analyzer \

--namespace kubecost \

--create-namespace \

--set kubecostToken="YOUR_TOKEN" \

--set prometheus.server.persistentVolume.enabled=true \

--set prometheus.server.persistentVolume.size=32Gi

Kubecost API를 통해 프로그래밍 방식으로 비용 데이터를 조회할 수 있습니다.

네임스페이스별 비용 조회 (최근 7일)

curl -s "http://kubecost.example.com/model/allocation?window=7d&aggregate=namespace" | \

python3 -m json.tool

출력 예시 (간소화)

{

"data": [{

"production": {

"cpuCost": 245.67,

"ramCost": 123.45,

"pvCost": 34.56,

"totalCost": 403.68

},

"staging": {

"cpuCost": 89.12,

"ramCost": 45.23,

"pvCost": 12.34,

"totalCost": 146.69

}

}]

}

비용 모니터링 도구 비교표

| 기능 | OpenCost | Kubecost Free | Kubecost Enterprise | CloudHealth | Spot.io (NetApp) |

| -------------------- | --------------------- | ------------------ | ------------------- | ----------- | ---------------- |

| 라이선스 | 오픈소스 (Apache 2.0) | 무료 (15일 데이터) | 상용 | 상용 | 상용 |

| CNCF 프로젝트 | O | X (기반) | X | X | X |

| 실시간 비용 모니터링 | O | O | O | O | O |

| 네임스페이스별 비용 | O | O | O | O | O |

| 비용 절감 추천 | X | 기본 | 고급 | 고급 | 고급 |

| 멀티클러스터 지원 | O (수동) | X | O | O | O |

| 알림/알람 | X | 기본 | 고급 | 고급 | 고급 |

| Spot 인스턴스 관리 | X | X | X | X | O |

| 데이터 보관 기간 | Prometheus 의존 | 15일 | 무제한 | 무제한 | 무제한 |

| 비용 할당 정확도 | 높음 | 높음 | 매우 높음 | 높음 | 높음 |

| 설치 난이도 | 낮음 | 낮음 | 중간 | 높음 | 중간 |

| 월 비용 | 무료 | 무료 | 클러스터당 | 협의 | 절감액 % |

> **권장 사항**: 소규모 팀은 OpenCost로 시작하고, 비용 규모가 커지면 Kubecost Enterprise나 Spot.io로 전환하는 것이 현실적입니다. FinOps Foundation의 멤버 기업 중 68%가 이 경로를 따랐습니다.

리소스 최적화 전략

Request/Limit 튜닝: VPA를 활용한 자동 적정화

Vertical Pod Autoscaler(VPA)는 실제 리소스 사용 패턴을 분석하여 적절한 Request/Limit 값을 추천합니다.

vpa-recommendation.yaml

apiVersion: autoscaling.k8s.io/v1

kind: VerticalPodAutoscaler

metadata:

name: app-vpa

namespace: production

spec:

targetRef:

apiVersion: apps/v1

kind: Deployment

name: my-app

updatePolicy:

updateMode: 'Off' # 추천만 받고 자동 적용하지 않음 (안전)

resourcePolicy:

containerPolicies:

- containerName: app

minAllowed:

cpu: '100m'

memory: '128Mi'

maxAllowed:

cpu: '2'

memory: '4Gi'

controlledResources: ['cpu', 'memory']

VPA 추천 값 확인

kubectl describe vpa app-vpa -n production

출력 예시

Recommendation:

Container Recommendations:

Container Name: app

Lower Bound:

Cpu: 150m

Memory: 256Mi

Target:

Cpu: 250m

Memory: 512Mi

Uncapped Target:

Cpu: 250m

Memory: 512Mi

Upper Bound:

Cpu: 800m

Memory: 1Gi

**주의사항**: VPA의 `updateMode: "Auto"`는 Pod를 재시작합니다. Kubernetes 1.33부터 지원되는 In-Place Resource Resize(KEP-1287)를 활용하면 재시작 없이 리소스를 조정할 수 있습니다. 프로덕션에서는 반드시 `"Off"` 모드로 시작하여 추천값을 검토한 후 점진적으로 적용해야 합니다.

네임스페이스별 ResourceQuota

팀별 리소스 사용량을 제한하여 비용 폭주를 방지합니다.

resourcequota-team-backend.yaml

apiVersion: v1

kind: ResourceQuota

metadata:

name: team-backend-quota

namespace: team-backend

spec:

hard:

requests.cpu: '20'

requests.memory: '40Gi'

limits.cpu: '40'

limits.memory: '80Gi'

persistentvolumeclaims: '10'

services.loadbalancers: '2'

pods: '50'

비용 관점: LoadBalancer 서비스 수 제한 (AWS에서 각각 약 월 $18)

ResourceQuota 사용 현황 확인

kubectl describe resourcequota team-backend-quota -n team-backend

출력 예시

Name: team-backend-quota

Namespace: team-backend

Resource Used Hard

-------- ---- ----

limits.cpu 12 40

limits.memory 24Gi 80Gi

persistentvolumeclaims 3 10

pods 15 50

requests.cpu 6 20

requests.memory 12Gi 40Gi

services.loadbalancers 1 2

LimitRange 설정

개별 Pod/Container 단위에서 기본 리소스 값과 범위를 강제합니다.

limitrange-default.yaml

apiVersion: v1

kind: LimitRange

metadata:

name: default-limits

namespace: team-backend

spec:

limits:

- type: Container

default: # Limit 기본값 (Request 미설정 시 자동 적용)

cpu: '500m'

memory: '512Mi'

defaultRequest: # Request 기본값

cpu: '100m'

memory: '128Mi'

min: # 최소값 (이하 불가)

cpu: '50m'

memory: '64Mi'

max: # 최대값 (이상 불가)

cpu: '4'

memory: '8Gi'

- type: PersistentVolumeClaim

min:

storage: '1Gi'

max:

storage: '100Gi' # PVC 최대 크기 제한

kubectl apply -f limitrange-default.yaml

Request/Limit 미설정 Pod 생성 시 자동으로 기본값 적용

kubectl run test-pod --image=nginx -n team-backend

kubectl describe pod test-pod -n team-backend | grep -A 5 "Limits\|Requests"

노드 풀 최적화: Spot/Preemptible 인스턴스 활용

Spot 인스턴스는 On-Demand 대비 60-90% 저렴하지만, 클라우드 제공자가 언제든 회수할 수 있습니다. 올바르게 활용하면 Kubernetes 비용을 획기적으로 줄일 수 있습니다.

spot-tolerant-deployment.yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: batch-processor

namespace: production

spec:

replicas: 5

selector:

matchLabels:

app: batch-processor

template:

metadata:

labels:

app: batch-processor

spec:

Spot 노드에 스케줄링

nodeSelector:

node.kubernetes.io/capacity-type: spot

tolerations:

- key: 'spot'

operator: 'Equal'

value: 'true'

effect: 'NoSchedule'

Graceful shutdown - Spot 인스턴스 회수 시 정상 종료

terminationGracePeriodSeconds: 120

containers:

- name: processor

image: batch-processor:latest

resources:

requests:

cpu: '500m'

memory: '1Gi'

limits:

cpu: '1'

memory: '2Gi'

Spot 인스턴스 회수 신호 처리

lifecycle:

preStop:

exec:

command: ['/bin/sh', '-c', 'kill -SIGTERM 1 && sleep 90']

Pod Disruption Budget 설정

topologySpreadConstraints:

- maxSkew: 1

topologyKey: topology.kubernetes.io/zone

whenUnsatisfiable: DoNotSchedule

labelSelector:

matchLabels:

app: batch-processor

**Spot 인스턴스 적합한 워크로드와 부적합한 워크로드 비교**

| 적합한 워크로드 | 부적합한 워크로드 |

| ---------------------------------- | -------------------------------- |

| Batch 작업/데이터 처리 | 단일 인스턴스 데이터베이스 |

| CI/CD 파이프라인 | 스테이트풀 서비스 (Kafka, Redis) |

| 스테이트리스 웹 서버 (여러 복제본) | 장시간 실행 트랜잭션 |

| 개발/테스트 환경 | 실시간 스트리밍 처리 |

| ML 학습 (체크포인트 지원) | 리더 선출 기반 서비스 |

자동 스케일링을 통한 비용 절감

Karpenter: 차세대 노드 프로비저닝

Karpenter는 AWS에서 시작하여 현재 멀티클라우드로 확장 중인 노드 프로비저너입니다. Cluster Autoscaler보다 빠르고 유연한 노드 관리를 제공합니다.

karpenter-nodepool.yaml

apiVersion: karpenter.sh/v1

kind: NodePool

metadata:

name: cost-optimized

spec:

template:

spec:

requirements:

- key: kubernetes.io/arch

operator: In

values: ['amd64']

- key: karpenter.sh/capacity-type

operator: In

values: ['spot', 'on-demand'] # Spot 우선, 실패 시 On-Demand

- key: karpenter.k8s.aws/instance-category

operator: In

values: ['c', 'm', 'r'] # 컴퓨팅/범용/메모리 최적화

- key: karpenter.k8s.aws/instance-generation

operator: Gt

values: ['5'] # 6세대 이상만 (가성비 우수)

nodeClassRef:

group: karpenter.k8s.aws

kind: EC2NodeClass

name: default

limits:

cpu: '100'

memory: '400Gi'

disruption:

consolidationPolicy: WhenEmptyOrUnderutilized

consolidateAfter: 30s

미활용 노드 30초 후 자동 통합

weight: 10 # 다른 NodePool보다 우선 사용

karpenter-ec2nodeclass.yaml

apiVersion: karpenter.k8s.aws/v1

kind: EC2NodeClass

metadata:

name: default

spec:

amiSelectorTerms:

- alias: 'al2023@latest'

subnetSelectorTerms:

- tags:

karpenter.sh/discovery: 'my-cluster'

securityGroupSelectorTerms:

- tags:

karpenter.sh/discovery: 'my-cluster'

blockDeviceMappings:

- deviceName: /dev/xvda

ebs:

volumeSize: 50Gi

volumeType: gp3

encrypted: true

Karpenter vs Cluster Autoscaler 비교

| 항목 | Karpenter | Cluster Autoscaler |

| ------------------ | ---------------------------- | --------------------- |

| 스케일업 속도 | 수 초 ~ 1분 | 2-5분 |

| 인스턴스 타입 선택 | 자동 최적 선택 (다양한 타입) | 노드 그룹별 고정 |

| 스케일다운 | 적극적 통합 (consolidation) | 보수적 (10분+ 대기) |

| Spot 처리 | 네이티브 지원, 자동 전환 | 별도 노드 그룹 필요 |

| 멀티 AZ 분산 | 자동 | 노드 그룹별 설정 |

| 빈 패킹 효율 | 높음 (Pod 크기 기반 선택) | 낮음 (고정 노드 크기) |

| 노드 파편화 해소 | 자동 (consolidation) | 수동 |

| 클라우드 지원 | AWS(GA), Azure(Preview) | AWS, GCP, Azure 모두 |

> **실전 팁**: Karpenter의 `consolidationPolicy: WhenEmptyOrUnderutilized`는 리소스 활용률이 낮은 노드의 Pod를 다른 노드로 자동 이동하고 해당 노드를 종료합니다. 이것만으로도 노드 비용을 20-30% 절감할 수 있습니다 (Karpenter 공식 문서의 best practices 참고).

야간/주말 스케일다운 자동화

비-프로덕션 환경의 워크로드를 업무 시간 외에 자동으로 축소하면 상당한 비용을 절감할 수 있습니다.

cronjob-scaledown.yaml

apiVersion: batch/v1

kind: CronJob

metadata:

name: nighttime-scaledown

namespace: kube-system

spec:

schedule: '0 22 * * 1-5' # 평일 22시 (KST 기준)

jobTemplate:

spec:

template:

spec:

serviceAccountName: scaler-sa

containers:

- name: scaler

image: bitnami/kubectl:latest

command:

- /bin/sh

- -c

- |

staging 네임스페이스의 모든 Deployment를 0으로 축소

for deploy in $(kubectl get deploy -n staging -o name); do

kubectl scale $deploy --replicas=0 -n staging

done

echo "Scaled down staging at $(date)"

restartPolicy: OnFailure

apiVersion: batch/v1

kind: CronJob

metadata:

name: morning-scaleup

namespace: kube-system

spec:

schedule: '0 8 * * 1-5' # 평일 08시 (KST 기준)

jobTemplate:

spec:

template:

spec:

serviceAccountName: scaler-sa

containers:

- name: scaler

image: bitnami/kubectl:latest

command:

- /bin/sh

- -c

- |

staging 네임스페이스의 Deployment를 원래 상태로 복구

kubectl scale deploy/api-server --replicas=3 -n staging

kubectl scale deploy/web-frontend --replicas=2 -n staging

kubectl scale deploy/worker --replicas=2 -n staging

echo "Scaled up staging at $(date)"

restartPolicy: OnFailure

이미지 최적화와 스토리지 비용 절감

컨테이너 이미지 최적화

컨테이너 이미지 크기가 크면 레지스트리 저장 비용, 이미지 풀 시간, 네트워크 전송 비용이 모두 증가합니다.

Bad: 거대한 이미지 (1.2GB+)

FROM node:20

WORKDIR /app

COPY . .

RUN npm install

CMD ["node", "server.js"]

Good: 멀티스테이지 빌드로 최적화 (150MB 이하)

FROM node:20-slim AS builder

WORKDIR /app

COPY package*.json ./

RUN npm ci --only=production

COPY . .

RUN npm run build

FROM gcr.io/distroless/nodejs20-debian12

WORKDIR /app

COPY --from=builder /app/dist ./dist

COPY --from=builder /app/node_modules ./node_modules

CMD ["dist/server.js"]

미사용 PersistentVolume 정리

방치된 PV/PVC는 클라우드 스토리지 비용을 지속적으로 발생시킵니다.

Released 상태의 PV 확인 (바인딩 해제되었지만 삭제되지 않은 PV)

kubectl get pv --field-selector=status.phase=Released

사용 중이지 않은 PVC 찾기 (어떤 Pod에도 마운트되지 않은 PVC)

kubectl get pvc --all-namespaces -o json | \

python3 -c "

data = json.load(sys.stdin)

for pvc in data['items']:

ns = pvc['metadata']['namespace']

name = pvc['metadata']['name']

phase = pvc['status'].get('phase', 'Unknown')

if phase == 'Bound':

print(f'{ns}/{name} - Bound but check if any pod uses it')

"

특정 PVC를 사용하는 Pod가 있는지 확인

kubectl get pods --all-namespaces -o json | \

python3 -c "

data = json.load(sys.stdin)

used_pvcs = set()

for pod in data['items']:

volumes = pod['spec'].get('volumes', [])

for vol in volumes:

if 'persistentVolumeClaim' in vol:

ns = pod['metadata']['namespace']

pvc_name = vol['persistentVolumeClaim']['claimName']

used_pvcs.add(f'{ns}/{pvc_name}')

for pvc in sorted(used_pvcs):

print(f'IN USE: {pvc}')

"

idle 리소스 탐지 스크립트

#!/bin/bash

idle-resource-detector.sh

유휴 리소스를 탐지하여 비용 절감 기회를 찾는 스크립트

echo "=== 유휴 리소스 탐지 리포트 ==="

echo "날짜: $(date)"

echo ""

1. 0 레플리카인데 삭제되지 않은 Deployment

echo "--- 레플리카 0인 Deployment ---"

kubectl get deploy --all-namespaces -o json | \

python3 -c "

data = json.load(sys.stdin)

for d in data['items']:

if d['spec'].get('replicas', 1) == 0:

print(f\" {d['metadata']['namespace']}/{d['metadata']['name']}\")

"

2. 7일 이상 Completed 상태인 Job

echo ""

echo "--- 완료 후 7일 이상 경과한 Job ---"

kubectl get jobs --all-namespaces --field-selector=status.successful=1 \

-o custom-columns="NAMESPACE:.metadata.namespace,NAME:.metadata.name,COMPLETED:.status.completionTime"

3. 외부 트래픽 없는 LoadBalancer Service

echo ""

echo "--- LoadBalancer 타입 Service (비용 발생 중) ---"

kubectl get svc --all-namespaces --field-selector=spec.type=LoadBalancer \

-o custom-columns="NAMESPACE:.metadata.namespace,NAME:.metadata.name,EXTERNAL-IP:.status.loadBalancer.ingress[0].hostname"

Prometheus를 활용한 비용 메트릭 모니터링

OpenCost와 Prometheus를 연동하면 Grafana 대시보드에서 실시간 비용 추이를 모니터링할 수 있습니다.

prometheus-cost-alerts.yaml

apiVersion: monitoring.coreos.com/v1

kind: PrometheusRule

metadata:

name: cost-alerts

namespace: monitoring

spec:

groups:

- name: cost-optimization

rules:

CPU 요청 대비 실제 사용률이 20% 이하인 컨테이너 탐지

- alert: LowCPUUtilization

expr: |

(

sum(rate(container_cpu_usage_seconds_total[5m])) by (namespace, pod, container)

/

sum(kube_pod_container_resource_requests{resource="cpu"}) by (namespace, pod, container)

) < 0.2

for: 24h

labels:

severity: warning

category: cost

annotations:

summary: 'CPU 사용률이 Request의 20% 미만'

description: '{{ `{{ $labels.namespace }}` }}/{{ `{{ $labels.pod }}` }}의 CPU 사용률이 24시간 동안 Request의 20% 미만입니다. Request 값 하향 조정을 권장합니다.'

메모리 요청 대비 실제 사용률이 30% 이하인 컨테이너 탐지

- alert: LowMemoryUtilization

expr: |

(

sum(container_memory_working_set_bytes) by (namespace, pod, container)

/

sum(kube_pod_container_resource_requests{resource="memory"}) by (namespace, pod, container)

) < 0.3

for: 24h

labels:

severity: warning

category: cost

annotations:

summary: '메모리 사용률이 Request의 30% 미만'

description: '{{ `{{ $labels.namespace }}` }}/{{ `{{ $labels.pod }}` }}의 메모리 사용률이 24시간 동안 Request의 30% 미만입니다.'

네임스페이스별 일일 비용 임계값 초과

- alert: NamespaceCostThresholdExceeded

expr: |

sum(

sum_over_time(opencost_container_cost_cpu_hourly[24h]) +

sum_over_time(opencost_container_cost_memory_hourly[24h])

) by (namespace) > 100

labels:

severity: critical

category: cost

annotations:

summary: '네임스페이스 일일 비용 임계값 초과'

description: '{{ `{{ $labels.namespace }}` }} 네임스페이스의 일일 비용이 100 USD를 초과했습니다.'

클라우드별 비용 최적화 전략

AWS EKS 비용 최적화

AWS Savings Plans 추천 조회

aws ce get-savings-plans-purchase-recommendation \

--savings-plans-type COMPUTE_SP \

--term-in-years ONE_YEAR \

--payment-option NO_UPFRONT \

--lookback-period-in-days SIXTY_DAYS

EKS 노드에 대한 Reserved Instance 추천

aws ce get-reservation-purchase-recommendation \

--service "Amazon Elastic Compute Cloud - Compute" \

--lookback-period-in-days SIXTY_DAYS

GCP GKE 비용 최적화

GKE 비용 추천 확인

gcloud recommender recommendations list \

--recommender=google.compute.instance.MachineTypeRecommender \

--project=my-project \

--location=asia-northeast3-a \

--format="table(content.overview.resourceName, content.overview.recommendedMachineType.name, primaryImpact.costProjection.cost.units)"

Committed Use Discounts 확인

gcloud compute commitments list --project=my-project

멀티클라우드 비용 비교

| 항목 | AWS EKS | GCP GKE | Azure AKS |

| ------------------- | --------------------- | ------------------------------------- | ---------------------------------- |

| 컨트롤 플레인 비용 | 월 $73 (클러스터당) | 무료 (Standard) / 월 $73 (Enterprise) | 무료 (Standard) / 월 $73 (Premium) |

| Spot 할인율 | 60-90% | 60-91% | 60-90% |

| Spot 최소 보장 | 2분 경고 | 30초 경고 | 30초 경고 |

| Savings Plan/CUD | Compute Savings Plans | Committed Use Discounts | Azure Reservations |

| 최대 약정 할인 | 72% (3년 전액 선결제) | 70% (3년 약정) | 72% (3년 예약) |

| 오토파일럿/서버리스 | Fargate | GKE Autopilot | AKS Virtual Nodes |

실패 사례: 과도한 비용 절감으로 인한 서비스 장애

사례 1: Spot 인스턴스 100% 운영으로 인한 대규모 장애

한 스타트업에서 비용 절감을 극대화하기 위해 프로덕션 클러스터의 모든 노드를 Spot 인스턴스로 운영했습니다.

**발생 상황**:

- AWS가 특정 리전에서 Spot 용량을 대규모로 회수

- 전체 노드의 70%가 동시에 종료됨

- Pod가 스케줄링될 노드가 없어 서비스 전면 장애 발생

- On-Demand 노드 프로비저닝까지 8분 소요

**교훈**:

- 프로덕션 핵심 서비스는 반드시 On-Demand 노드에 배치

- Spot 비율은 전체의 70%를 넘기지 않을 것

- PodDisruptionBudget으로 최소 가용 Pod 수를 보장할 것

필수: PodDisruptionBudget 설정

apiVersion: policy/v1

kind: PodDisruptionBudget

metadata:

name: api-server-pdb

namespace: production

spec:

minAvailable: 2 # 최소 2개 Pod는 항상 유지

selector:

matchLabels:

app: api-server

사례 2: 리소스 Request를 너무 낮게 설정하여 OOM 빈발

비용 최적화 과정에서 모든 Pod의 메모리 Request를 실사용량 기준으로 타이트하게 설정한 결과, 트래픽 피크 시간에 OOMKilled가 빈발했습니다.

**교훈**:

- Request는 P99 사용량 기준이 아닌 P95 사용량 + 20% 버퍼로 설정

- Limit은 Request의 1.5-2배로 설정하여 버스트 여유를 줌

- VPA의 추천값을 무조건 따르지 말고, 트래픽 패턴을 고려하여 조정

사례 3: 테스트 환경 삭제로 인한 개발 생산성 저하

비용 절감을 위해 야간에 모든 개발/테스트 환경을 삭제하는 정책을 시행했으나, 해외 팀과의 시차로 인해 협업에 심각한 문제가 발생했습니다.

**교훈**:

- 비용 절감과 개발자 경험(DX)의 균형이 필요

- 완전 삭제가 아닌 스케일다운(replicas=0) 방식이 더 안전

- 글로벌 팀의 경우 각 시간대를 고려한 스케줄 설정

FinOps 운영 체크리스트

주간 비용 리뷰 체크리스트

| 체크 항목 | 담당 | 도구 |

| -------------------------------------- | --------- | ------------------ |

| 네임스페이스별 비용 추이 확인 | FinOps 팀 | Kubecost/OpenCost |

| CPU/메모리 활용률 20% 이하 Pod 리스트 | SRE | Prometheus/Grafana |

| Spot 인스턴스 비율 확인 (목표: 50-70%) | Infra | 클라우드 콘솔 |

| 미사용 PV/PVC 정리 | 개발팀 | kubectl 스크립트 |

| LoadBalancer Service 수 확인 | Infra | kubectl |

| 이미지 레지스트리 오래된 태그 정리 | DevOps | 레지스트리 API |

월간 비용 리뷰 체크리스트

| 체크 항목 | 담당 | 도구 |

| -------------------------------------- | --------- | ---------------------- |

| 전월 대비 비용 증감 분석 | FinOps 팀 | 클라우드 Billing |

| Savings Plan/CUD 커버리지 확인 | FinOps 팀 | 클라우드 콘솔 |

| VPA 추천값 기반 Request/Limit 업데이트 | 개발팀 | VPA Recommender |

| 노드 인스턴스 타입 최적화 검토 | Infra | Karpenter 로그 |

| 팀별 비용 할당 리포트 공유 | FinOps 팀 | Kubecost |

| 비용 이상치(anomaly) 원인 분석 | SRE | 클라우드 Cost Explorer |

분기별 비용 리뷰 체크리스트

| 체크 항목 | 담당 | 도구 |

| -------------------------------------------------- | --------- | ---------------------------- |

| Reserved Instance/CUD 갱신 검토 | FinOps 팀 | 클라우드 콘솔 |

| 아키텍처 수준 비용 최적화 (마이크로서비스 통합 등) | 아키텍트 | 설계 리뷰 |

| 비용 예측 모델 업데이트 | FinOps 팀 | 스프레드시트/BI |

| FinOps 성숙도 자체 평가 | FinOps 팀 | FinOps Foundation 프레임워크 |

FinOps 팀 문화와 비용 인식

비용 태그 전략

모든 Kubernetes 리소스에 일관된 태그(label)를 부여하여 비용을 정확히 추적할 수 있어야 합니다.

비용 추적을 위한 표준 Label 정의

apiVersion: apps/v1

kind: Deployment

metadata:

name: api-server

namespace: production

labels:

app.kubernetes.io/name: api-server

app.kubernetes.io/part-of: payment-platform

FinOps 비용 태그

cost-center: 'engineering'

team: 'backend'

env: 'production'

project: 'payment-v2'

spec:

replicas: 3

selector:

matchLabels:

app.kubernetes.io/name: api-server

template:

metadata:

labels:

app.kubernetes.io/name: api-server

cost-center: 'engineering'

team: 'backend'

env: 'production'

project: 'payment-v2'

spec:

containers:

- name: api-server

image: api-server:v2.1.0

resources:

requests:

cpu: '500m'

memory: '1Gi'

limits:

cpu: '1'

memory: '2Gi'

OPA/Gatekeeper로 비용 태그 강제

cost-label-constraint.yaml

apiVersion: templates.gatekeeper.sh/v1

kind: ConstraintTemplate

metadata:

name: k8srequiredcostlabels

spec:

crd:

spec:

names:

kind: K8sRequiredCostLabels

targets:

- target: admission.k8s.gatekeeper.sh

rego: |

package k8srequiredcostlabels

required_labels := {"cost-center", "team", "env", "project"}

violation[{"msg": msg}] {

provided := {label | input.review.object.metadata.labels[label]}

missing := required_labels - provided

count(missing) > 0

msg := sprintf("필수 비용 태그가 누락되었습니다: %v", [missing])

}

apiVersion: constraints.gatekeeper.sh/v1beta1

kind: K8sRequiredCostLabels

metadata:

name: require-cost-labels

spec:

match:

kinds:

- apiGroups: ['apps']

kinds: ['Deployment', 'StatefulSet', 'DaemonSet']

namespaces: ['production', 'staging']

비용 인식 문화 구축

FinOps는 도구만으로는 성공할 수 없습니다. 팀 전체가 비용을 인식하고 최적화에 참여하는 문화가 필요합니다.

**비용 인식 문화의 핵심 요소**:

1. **비용 가시성**: 매주 팀별 비용 대시보드를 공유합니다

2. **비용 책임**: 각 팀이 자신의 네임스페이스 비용에 대해 책임집니다

3. **인센티브**: 비용 절감 사례를 공유하고 인정하는 문화를 만듭니다

4. **교육**: 개발자가 리소스 Request/Limit의 의미와 영향을 이해해야 합니다

5. **자동화**: 수동 작업을 줄이고, 정책 기반 자동 최적화를 추구합니다

FinOps 성숙도 모델

FinOps Foundation은 조직의 FinOps 성숙도를 세 단계로 정의합니다.

| 단계 | 특징 | Kubernetes 지표 |

| ---------------- | ----------------------------------- | --------------------------------------------------- |

| **Crawl** (시작) | 기본 비용 가시성 확보, 수동 최적화 | OpenCost 도입, 월간 비용 리뷰 시작 |

| **Walk** (발전) | 팀별 비용 할당, 자동화 시작 | Kubecost 알림, VPA 추천 적용, Spot 50%+ |

| **Run** (성숙) | 실시간 최적화, 비용 예측, 문화 정착 | Karpenter 자동 통합, 비용 예측 모델, FinOps 팀 운영 |

정리 및 권장 사항

즉시 실행 가능한 Quick Win

1. **OpenCost 설치** (1시간): 비용 가시성 즉시 확보

2. **VPA 추천 모드 배포** (30분): 리소스 적정화 데이터 수집 시작

3. **미사용 리소스 정리** (2시간): Released PV, 빈 네임스페이스, 오래된 Job 삭제

4. **LimitRange 적용** (1시간): Request 미설정 Pod에 기본값 강제

중기 최적화 (1-3개월)

1. Kubecost 또는 상용 도구 도입하여 팀별 비용 할당 시작

2. Spot 인스턴스 도입 (비-프로덕션부터 시작, 점진적으로 프로덕션 확대)

3. Karpenter 도입하여 노드 자동 통합 활성화

4. 야간/주말 스케일다운 CronJob 배포

장기 전략 (3-12개월)

1. FinOps 전담 팀 또는 역할 구성

2. Savings Plan/CUD 최적화

3. 비용 예측 모델 구축 (과거 데이터 기반)

4. 비용 인식 문화 정착 (교육, 대시보드, 인센티브)

Kubernetes 비용 최적화는 한 번의 프로젝트가 아니라 지속적인 프로세스입니다. FinOps의 Inform-Optimize-Operate 사이클을 반복하면서 점진적으로 성숙도를 높여가는 것이 핵심입니다. 가장 중요한 첫 단계는 **"지금 얼마를 쓰고 있는지 아는 것"**입니다. OpenCost를 설치하고, 이 글의 체크리스트를 따라 첫 번째 비용 리뷰를 시작해 보시기 바랍니다.

참고 자료

- [FinOps Foundation - Kubernetes Best Practices](https://www.finops.org/wg/kubernetes-finops/) - FinOps의 Kubernetes 적용 가이드라인

- [Kubecost Documentation](https://docs.kubecost.com/) - Kubecost 설치, 설정, API 활용법

- [OpenCost - CNCF Project](https://www.opencost.io/docs) - OpenCost 아키텍처와 설치 가이드

- [AWS EKS Best Practices - Cost Optimization](https://aws.github.io/aws-eks-best-practices/cost_optimization/) - AWS EKS 비용 최적화 공식 가이드

- [Karpenter Documentation - Best Practices](https://karpenter.sh/docs/) - Karpenter 노드 프로비저닝과 통합 전략

- [GCP GKE Cost Optimization](https://cloud.google.com/kubernetes-engine/docs/best-practices/cost-optimization) - GKE 비용 최적화 공식 문서

- [Azure AKS Cost Optimization](https://learn.microsoft.com/en-us/azure/aks/best-practices-cost) - AKS 비용 관리 모범 사례

현재 단락 (1/608)

Kubernetes 도입이 가속화되면서 클라우드 비용 문제가 심각해지고 있습니다. CNCF의 2025 FinOps 리포트에 따르면, 기업들의 Kubernetes 관련 클라우드 지출...

작성 글자: 0원문 글자: 19,923작성 단락: 0/608