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

200 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 두 축** 모두 제약한다.
```yaml
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.