Files
project-infra/docs/standards/infra/resources-probes-availability.md
T

10 KiB
Raw Blame History

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) 는 behavior block으로 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.

예외 (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). 예: Keycloak periodSeconds: 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: request 1Gi, limit 1.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, host ScheduleAnyway.

keycloak (JVM, slow boot, latency-sensitive)

  • QoS: Guaranteed (request == limit, memory 2Gi 고정).
  • CPU: request 1, limit 1 (Guaranteed 요구).
  • Probes: startup 5분 budget (periodSeconds: 10, failureThreshold: 30), readiness /health/ready on 9000, liveness /health/live on 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.