10 KiB
resources / probes / availability 기준
목적
이 문서는 1000+ 서비스를 운영하는 Kubernetes 플랫폼에서 AI coding agent가 생성하는 모든 워크로드 매니페스트의 ground truth다. 모든 rule은 Google SRE / Netflix / Shopify의 실제 프로덕션 합의를 기반으로 한다.
정하는 것:
- resource requests/limits를 어떤 값으로, 어떤 QoS class로 줄지
- probe (startup / readiness / liveness) 세 축을 어떻게 분리할지
- 가용성(PDB / topologySpread / HPA)을 어떤 조합으로 구성할지
- K3s 환경에서 metrics-server 전제를 어떻게 다룰지
공식 / 업계 근거
- Kubernetes QoS class는 resources 값에 의해 자동 결정된다 (
Guaranteed,Burstable,BestEffort). - CPU는 compressible resource로 limit 초과 시 throttle된다. memory는 incompressible로 OOM kill된다.
- Tim Hockin (Google, Kubernetes co-founder) 및 다수 SRE 컨퍼런스 토크: CPU limit는 CFS throttling을 quota 미만에서도 유발하므로 대부분의 프로덕션 워크로드에서 제거한다. CPU request만 설정하여 노드 capacity를 공정 공유한다.
- memory limit는 OOM kill의 유일한 제어 수단이므로 반드시 설정한다.
topologySpreadConstraints는 1.19+ stable. zone과 host 두 축으로 skew를 제한하는 것이 표준이다.podAntiAffinity는 legacy 대안, 현대 가이드는 topologySpreadConstraints 우선.- HPA v2 (
autoscaling/v2) 는behaviorblock으로 scale up/down stabilizationWindow와 policy를 분리 제어한다. - PodDisruptionBudget은
maxUnavailable또는minAvailable. 대규모 fleet에서는maxUnavailable권장 (replica scale 변화 추종). - startup probe는 성공 전까지 liveness/readiness를 차단한다. slow boot 서비스에 필수.
- Kubernetes 1.29+ native sidecar: init container에
restartPolicy: Always명시.
기본 규칙
1. QoS class는 의도적으로 선택한다
QoS class는 resources 값의 결과물이 아니라 선택이다.
- Guaranteed: 모든 컨테이너의 request == limit. 가장 높은 eviction 우선순위 보호.
- 적용: latency-sensitive JVM (Keycloak, auth-server critical tier), stateful 단일 인스턴스 (vault active), 단일 ReplicaSet critical path.
- Burstable: request < limit 또는 일부만 설정. 탄력적 CPU burst 허용.
- 적용: stateless HTTP API, worker, generic service — 기본값.
- BestEffort: request/limit 모두 없음. 가장 먼저 evict됨.
- 적용: 일시적 debugging pod, 무영향 experiment. 프로덕션 금지.
2. CPU limit anti-pattern — 기본은 CPU request only
Google SRE 및 Tim Hockin의 공식 stance는 "대부분의 워크로드에서 CPU limit를 설정하지 말 것"이다. Linux CFS의 quota 회계가 sub-period burst에서도 throttle을 유발하기 때문이다.
기본:
- CPU: request만 설정, limit 생략.
- Memory: limit 반드시 설정.
- Guaranteed를 원하면:
limits.memory == requests.memory. - Burstable 기본값:
limits.memory = 1.1 ~ 1.5 × requests.memory.
- Guaranteed를 원하면:
예외 (CPU limit를 설정해야 하는 경우):
- multi-tenant 노드에서 noisy neighbor가 측정 가능한 손해를 유발.
- batch/cron Job에서 예산 통제가 필요.
- billing-backed 측정으로 인한 compliance 요구.
3. requests 값은 측정 기반으로 잡는다
- p95 cpu usage × 1.2 가 request 시작점.
- p99 memory (steady state) × 1.3 이 memory request 시작점.
- 최초 배포는 overprovision 으로 시작 → 1~2주 관측 후 right-sizing.
- VPA recommendation을 참고하되 자동 적용은 하지 않는다 (review 필요).
4. limit만 있고 request가 없는 구성 금지
Kubernetes는 request 미설정 시 limit를 request로 복사한다. 이는 암묵적 Guaranteed QoS로 귀결되며 의도와 다를 수 있다. 반드시 둘 다 명시한다.
5. Probe는 세 축으로 분리한다
- startup probe: "부팅이 끝났는가". 성공 전까지 readiness/liveness는 실행되지 않는다.
- 필수: Keycloak, Vault, JVM warm-up이 긴 서비스.
- 타이밍 규칙:
failureThreshold × periodSeconds ≥ 최악의 cold start (p99). 예: KeycloakperiodSeconds: 10, failureThreshold: 30= 300s.
- readiness probe: "지금 트래픽을 받아도 되는가". 실패 시 Service endpoint에서 제외.
- 모든 traffic-facing 서비스 필수.
- 외부 의존성 전체 가용성을 묶지 않는다 (동시 탈락 방지).
- liveness probe: "재시작이 치료인가" (deadlock only).
- Default = 설정하지 않거나 readiness와 다른 가벼운 self-check.
- 잘못 설정하면 cascading restart 유발. Kubernetes 공식 문서 명시.
6. readiness는 shallow, liveness는 더 shallow
readiness는 "app loop이 요청을 처리 가능한가"까지만 검사한다. DB connection pool 초기화처럼 intra-pod 조건은 OK. 외부 DB SELECT 1 전체 가용성 체크는 금지.
liveness는 process deadlock 감지 전용. HTTP endpoint면 /livez 같은 매우 가벼운 200 응답.
7. topologySpreadConstraints를 기본 가용성 primitive로
production multi-zone cluster에서는 zone + host 두 축 모두 제약한다.
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app.kubernetes.io/name: auth-server
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/name: auth-server
- zone
DoNotSchedule: 프로덕션에서 zone 장애 격리에 필수. - host
ScheduleAnyway: 노드 부족 시 배포 불가 방지.
8. podAntiAffinity는 legacy로 본다
topologySpreadConstraints가 등장한 뒤 podAntiAffinity는 대부분의 use case에서 대체되었다. 신규 매니페스트는 topologySpreadConstraints를 우선 적용한다.
예외: 단순 "한 노드에 두 개 이상 금지" 규칙만 필요하고 spread 회계가 불필요한 경우.
9. HPA는 autoscaling/v2, behavior block 필수
autoscaling/v1은 더 이상 사용하지 않는다. autoscaling/v2를 기본으로 한다.
metrics:유형:Resource(cpu/memory),Pods,Object,External,ContainerResource.behavior.scaleUp.stabilizationWindowSeconds: 0(트래픽 급증에 빠르게 반응).behavior.scaleDown.stabilizationWindowSeconds: 300(flapping 방지).policies조합:type: Percent(현 replica의 X%)와type: Pods(절대 수) 동시 지정,selectPolicy: Max또는Min.
10. HPA 전제 조건
- resource requests가 먼저 잡혀 있어야 한다 (utilization target이 request 기준).
- startup probe가 안정화되어 있어야 한다 (scale-up 중 flapping 방지).
- 해당 워크로드가 horizontal scale로 효과가 있는 성격이어야 한다. stateful / DB / quorum 기반은 HPA 비대상.
- K3s metrics-server가 packaged로 배포되어 있음을 전제로 하되, availability를 runbook에서 점검한다.
11. PDB는 fleet 규모에 맞춰 maxUnavailable 우선
- replica ≥ 3:
maxUnavailable: 1또는maxUnavailable: 25%. - replica 대규모 (10+):
maxUnavailable: 10%권장 (유연성). - replica 2:
maxUnavailable: 1. - replica 1: PDB 금지 (node drain을 막는다).
- quorum 기반 (etcd, vault raft, DB cluster):
minAvailable로 quorum 수 명시.
12. PDB zero disruption 금지
maxUnavailable: 0 또는 minAvailable: 100% 는 node drain / maintenance를 완전 차단한다. Kubernetes 업그레이드 자체가 불가능해진다. 명시적 예외 승인 없이 사용 금지.
13. init container 와 sidecar 순서 (1.29+)
- init container: main 전에 실행, 완료 후 종료. schema migration, secret preparation 용.
- native sidecar (1.29+): init container에
restartPolicy: Always명시. main과 병렬 실행, main 종료 후 종료.- 사용: log forwarder, metrics exporter, service mesh proxy.
initContainers배열 순서가 실행 순서다.
14. 워크로드별 기본 권장안
auth-server (stateless Spring Boot)
- QoS: Burstable.
- CPU: request only (
500m). Memory: request1Gi, limit1.5Gi. - Probes: startup
/actuator/health/started(60s), readiness/actuator/health/readiness, liveness/actuator/health/liveness. - HPA: CPU 70%, min 3, max 20, scale-down 300s.
- PDB:
maxUnavailable: 1. - topologySpread: zone
DoNotSchedule, hostScheduleAnyway.
keycloak (JVM, slow boot, latency-sensitive)
- QoS: Guaranteed (request == limit, memory 2Gi 고정).
- CPU: request
1, limit1(Guaranteed 요구). - Probes: startup 5분 budget (
periodSeconds: 10, failureThreshold: 30), readiness/health/readyon 9000, liveness/health/liveon 9000. - HPA: 보통 비대상. 고정 replica (3)로 시작, 측정 후 검토.
- PDB:
maxUnavailable: 1.
vault (raft quorum)
- QoS: Guaranteed.
- Probes: readiness/liveness는 raft sealed/active 상태 구분.
- HPA: 비대상.
- PDB:
minAvailable: 2(3-node raft 기준 quorum 보존).
minio (erasure coded storage)
- QoS: Guaranteed.
- PDB:
minAvailable: N-1(erasure set 기준). - HPA: 비대상.
migration-flyway (Job)
- probe 없음 (Job은 probe 무의미).
- requests 명시, limit는 memory만.
- activeDeadlineSeconds 설정.
- HPA/PDB 비대상.
ingress-controller
- QoS: Burstable 또는 Guaranteed (tier에 따라).
- HPA 후보 (traffic 기반).
- PDB:
maxUnavailable: 1.
프로젝트 기준 요약
- QoS는 의도적으로 선택. Guaranteed는 latency-sensitive JVM, Burstable은 stateless 기본.
- CPU limit 기본 제거 (throttling 회피). Memory limit 필수.
- requests/limits 함께 명시. limit만 단독 금지.
- probe 세 축 분리. startup 타이밍은 worst-case cold start 기준.
- topologySpreadConstraints zone + host 두 축으로 기본 구성.
- HPA v2 + behavior block. resource requests / startup 안정화 후 적용.
- PDB는
maxUnavailable우선, replica 전략과 함께 결정. - 1.29+ native sidecar는 init container
restartPolicy: Always. - K3s metrics-server는 HPA 전제로만 신뢰, full metrics는 별도 stack.