cmod.ify

[Optimization] Wattup #5 K8s 클러스터 메모리 최적화 본문

Project

[Optimization] Wattup #5 K8s 클러스터 메모리 최적화

modifyC 2026. 3. 12. 11:25
728x90
반응형
[Optimization] Wattup #5 K8s 클러스터 메모리 최적화 — 95% → 76%

 

OPTIMIZATION GUIDE

WattUp K8s 메모리 최적화

Kafka 단일화 · JVM 힙 제한 · 리소스 튜닝 · 95% → 76%

최적화 흐름 요약
단계작업메모리 변화
1Kafka 3→1 KRaft 3-node → 단일 노드로 축소 95% → 83%
2Worker 축소 시도 worker3 제거 시도 → PVC 분산으로 포기 변화 없음
3리소스 limits 설정 backend / importer livenessProbe + resources 추가 83% → 84% (역효과)
4JVM 힙 제한 Kafka HEAP_OPTS, PostgreSQL shared_buffers 제한 84% → 76%
메모리 사용량 변화
최적화 전 — 95%
95%
Kafka 단일화 후 — 83%
83%
리소스 limits 추가 후 — 84% (소폭 증가)
84%
JVM 힙 제한 후 — 76% ✅
76%
최적화 1 — Kafka 3-node → 1-node
1
Kafka 단일 노드로 축소 (03-kafka.yaml)

개발/테스트 환경에서 Kafka 3-node KRaft 클러스터는 리소스 낭비가 크다. 단일 노드로 줄이면 broker+controller를 겸임할 수 있어 구조가 오히려 단순해진다.

yaml — 03-kafka.yaml : StatefulSet replicas
# 찾아서 replicas: 3 # 이렇게 replicas: 1
yaml — QUORUM_VOTERS 단일 노드로 변경
# 찾아서 (3개 나열된 부분) 1@kafka-0.kafka-headless.ev-prod.svc.cluster.local:9093,2@kafka-1...:9093,3@kafka-2...:9093 # 이렇게 (kafka-0만 남기기) 1@kafka-0.kafka-headless.ev-prod.svc.cluster.local:9093
yaml — 04-debezium.yaml, 05-apps.yaml : BOOTSTRAP_SERVERS
# 찾아서 kafka-0...9092,kafka-1...9092,kafka-2...9092 # 이렇게 kafka-0.kafka-headless.ev-prod.svc.cluster.local:9092
bash — 기존 Kafka PVC까지 완전히 제거 후 재배포
# 설정 데이터가 남아있으면 충돌하므로 PVC까지 반드시 삭제 kubectl delete statefulset kafka -n ev-prod kubectl delete pvc -l app=kafka -n ev-prod # 재배포 kubectl apply -f 03-kafka.yaml kubectl wait --for=condition=Ready pod -l app=kafka -n ev-prod --timeout=120s # Debezium, apps 재배포 kubectl apply -f 04-debezium.yaml kubectl apply -f 05-apps.yaml # 확인 — kafka-0 1/1 Running 이면 성공 kubectl get pods -n ev-prod
✅ RESULT
메모리 사용량 95% → 83% 감소. Kafka 3개 프로세스가 차지하던 JVM 힙이 사라지면서 12% 확보.
최적화 2 — Worker 노드 축소 시도 (실패)
2
worker3 제거 시도 → PVC 분산으로 포기

VM 1대를 줄이면 호스트 머신 메모리도 직접 확보할 수 있다. 하지만 PVC는 노드에 묶여있어서 PVC가 있는 노드는 절대 제거할 수 없다.

bash — 어느 노드에 PVC pod이 있는지 먼저 확인
kubectl get nodes kubectl get pods -n ev-prod -o wide kubectl get pvc -n ev-prod -o wide
🔥 트러블슈팅 — Worker 제거 불가 (PVC 분산 문제)
문제
PVC를 가진 pod(postgres, mongo, kafka)가 worker1, worker2, worker3에 각각 흩어져 있었다. 어느 노드도 안전하게 지울 수 없는 상태.
원인
local-path-provisioner의 PV는 특정 노드에 고정(node affinity)된다. pod 분포 결과 — postgres: worker1, debezium: worker2, kafka+mongo+nginx: worker3. 모든 노드에 PVC 관련 pod이 분산.
결론
worker 노드 축소는 이번 구조에선 불가. 처음부터 StatefulSet pod을 특정 노드에 몰아두는 nodeSelector 설계가 필요하다.
교훈
local-path PVC를 사용하면 pod이 어느 노드에 스케줄되느냐에 따라 PV 위치가 결정된다. 나중에 노드를 줄일 계획이 있다면 처음부터 nodeSelectornodeName으로 StatefulSet의 위치를 고정해야 한다.
결과
포기. 메모리 변화 없음. 하지만 local-path PVC + nodeSelector 설계라는 중요한 교훈을 얻었다.
최적화 3 — 리소스 limits + livenessProbe (역효과)
3
backend / importer resources 설정 → 오히려 증가

리소스 낭비를 막고 가용성도 높이기 위해 livenessProberesources를 함께 추가했다.

yaml — 05-apps.yaml : backend
livenessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 20 periodSeconds: 20 timeoutSeconds: 3 failureThreshold: 3 resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m"
yaml — 05-apps.yaml : importer (백엔드보다 가볍게)
livenessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 20 periodSeconds: 20 timeoutSeconds: 3 failureThreshold: 3 resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "256Mi" cpu: "250m"
🔥 트러블슈팅 — resources 설정이 오히려 메모리를 늘린 이유
문제
83% → 84%로 오히려 소폭 증가했다.
원인
resources.requests는 실제 사용량이 아니라 K8s 스케줄러가 미리 예약(reserve)하는 값이다. 실제 프로세스가 그만큼 쓰지 않아도, 노드 입장에서는 해당 메모리가 "사용 중"으로 잡힌다. 즉, requests를 잡는 순간 K8s가 그 양을 미리 점유해버린다.
시도
limits를 더 타이트하게 잡아보거나 requests 값을 실제 사용량에 맞게 내려야 함. 하지만 이 환경에서 실질적인 효과는 다음 단계(JVM 힙 제한)에서 나왔다.
교훈
requests는 예약이고 limits는 상한선이다. 메모리를 실제로 줄이려면 애플리케이션이 쓰는 양 자체를 줄여야 한다. requests만 높게 잡으면 실제 사용량과 무관하게 노드 메모리가 꽉 찬 것처럼 보인다.
결과
메모리 83% → 84%. 역효과. livenessProbe는 가용성 측면에선 유효하지만, 메모리 최적화 수단으로는 requests/limits 설정 자체가 아닌 실제 사용량 감소가 핵심임을 확인.
최적화 4 — JVM 힙 제한 (핵심)
4
Kafka HEAP_OPTS + PostgreSQL shared_buffers 제한

JVM 기반 프로세스(Kafka)와 메모리 공격적인 프로세스(PostgreSQL)는 제한을 안 걸면 가용 메모리를 최대한 먹으려 한다. 명시적으로 상한을 지정하면 실제 사용량이 바로 줄어든다.

yaml — 03-kafka.yaml : Kafka 컨테이너 env에 추가
- name: KAFKA_HEAP_OPTS value: "-Xmx512m -Xms512m"
yaml — 02-databases.yaml : PostgreSQL env에 추가
- name: POSTGRES_SHARED_BUFFERS value: "128MB"
bash — 재배포
# Kafka는 PVC까지 지워야 힙 설정이 제대로 적용됨 kubectl delete statefulset kafka -n ev-prod kubectl delete pvc -l app=kafka -n ev-prod kubectl apply -f 03-kafka.yaml # PostgreSQL은 PVC 유지하고 재시작 kubectl rollout restart statefulset/postgres -n ev-prod # 확인 kubectl get pods -n ev-prod kubectl top nodes # metrics-server 있으면 메모리 확인 가능
🔥 트러블슈팅 — JVM이 기본적으로 메모리를 과하게 잡는 이유
원인
JVM은 기본적으로 시스템 메모리의 1/4을 최대 힙(-Xmx)으로 자동 설정한다. 8GB RAM 노드라면 Kafka 인스턴스 하나가 2GB를 잡아먹는다. 컨테이너 환경에서 limits.memory가 없으면 노드 전체 RAM을 기준으로 계산해버린다.
해결
KAFKA_HEAP_OPTS="-Xmx512m -Xms512m"으로 개발 환경에서는 512MB로 고정. PostgreSQL도 shared_buffers를 명시적으로 제한.
교훈
K8s의 limits.memory는 OOM killer를 위한 상한이고, JVM의 -Xmx는 힙 자체의 크기다. 두 개는 별개로 동작한다. JVM 프로세스를 컨테이너에 올릴 때는 반드시 HEAP_OPTS와 limits.memory를 함께 설정해야 한다.
✅ RESULT
메모리 사용량 84% → 76%. JVM 힙 제한이 가장 직접적이고 효과적인 메모리 최적화 수단임을 확인.
최종 정리 — 교훈 모음
최적화 항목핵심 교훈효과
Kafka 3→1 개발 환경에서 3-node KRaft는 과잉. 단일 노드로 충분. -12%
Worker 축소 시도 local-path PVC는 노드에 고정됨. 처음 설계 시 nodeSelector 필수. 0%
resources limits requests는 예약이라 오히려 메모리가 늘어 보임. 실제 사용량 감소가 핵심. +1% (역효과)
JVM 힙 제한 JVM은 자동으로 RAM의 1/4을 힙으로 잡음. HEAP_OPTS 명시 필수. -8%
📌 전체 메모리 절감 결과
최적화 전 95% → 최적화 후 76%. 약 19% 감소. VM 4대(master+worker3) 기준 총 32GB RAM 중 약 6GB 확보.
728x90
반응형