Files
project-infra/docs/standards/infra/workload-selection.md
T

325 lines
15 KiB
Markdown

# workload selection 기준
## 목적
이 문서는 각 컴포넌트를
- Deployment
- StatefulSet
- DaemonSet
- Job
- CronJob
중 무엇으로 배포할지 먼저 고정한다.
목표:
- 상태 저장 / 무상태 / 노드로컬 / 일회성 / 주기성 워크로드를 섞지 않는다
- PVC가 필요하다는 이유만으로 StatefulSet을 선택하는 실수를 막는다
- Ingress controller / CNI / CSI / 로그 shipper / node-exporter 같은 노드로컬 에이전트를 Deployment로 배포하는 실수를 막는다
- migration / bootstrap / 백업을 장기 실행 앱과 분리한다
- 1000+ 서비스 스케일에서 operator-managed 패턴이 기본인 영역(DB, Kafka, monitoring)은 operator를 기본 선택으로 문서화한다
## 공식 의미 (근거)
- **Deployment**: stateless 장기 실행. `spec.replicas` 기반 수평 확장. ReplicaSet으로 rolling update. `https://kubernetes.io/docs/concepts/workloads/controllers/deployment/`.
- **StatefulSet**: stable network identity, stable persistent storage, ordered deployment/scaling. 각 Pod는 `<name>-<ordinal>` 이름을 가지고 PVC가 `volumeClaimTemplates`로 자동 생성. `persistentVolumeClaimRetentionPolicy` (GA since 1.27) 필드: 기본값 `{whenDeleted: Retain, whenScaled: Retain}`. `podManagementPolicy` (OrderedReady / Parallel). `updateStrategy` (RollingUpdate / OnDelete).
- **DaemonSet**: 선택된 모든 노드에 정확히 한 Pod를 실행. 노드 추가/제거에 따라 자동 생성/삭제. `https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/`.
- **Job**: 한 번 실행되어 완료. `restartPolicy: Never | OnFailure`. `backoffLimit` / `activeDeadlineSeconds` / `ttlSecondsAfterFinished` / `parallelism` / `completions`.
- **CronJob**: 시간 기반 스케줄 Job. `concurrencyPolicy: Allow | Forbid | Replace`, `startingDeadlineSeconds`, `successfulJobsHistoryLimit` / `failedJobsHistoryLimit`.
- **Operator pattern**: CRD + controller. 1000-서비스 스케일에서 DB/Kafka/Redis/monitoring은 사실상 Deployment/StatefulSet을 직접 쓰지 않고 operator가 소유.
## 기본 규칙
### 1. 기본 선택 기준은 "상태 + 수명 + 배치 위치"
3축으로 먼저 분류:
1. **수명**: 장기 실행 / 일회성 / 주기성
2. **상태**: stable identity + persistent storage 필요 / 불필요
3. **배치**: 노드별 한 Pod 필요 / cluster-wide 자유 배치
매핑:
- 장기 + 무상태 + 자유 배치 → **Deployment**
- 장기 + stateful + 자유 배치 → **StatefulSet** (또는 operator)
- 장기 + 무상태 + 노드별 한 Pod → **DaemonSet**
- 일회성 → **Job**
- 주기성 → **CronJob**
### 2. Deployment는 stateless 장기 실행 기본값
조건:
- Pod identity가 교체 가능
- durable state가 외부 DB / 외부 storage / 외부 cache에 있음
- 수평 확장이 자연스러움
- Pod 이름 / 순서가 의미 없음
적용 후보:
- `auth-server`
- `test-server` (장기 실행 모드)
- 외부 DB 사용하는 `keycloak`
- 대부분의 stateless API / worker
**필수 동반 리소스 (replicas≥2인 prod 워크로드):**
- `PodDisruptionBudget` (minAvailable ≥ 50% 또는 SLO tier에 맞춘 값)
- `HorizontalPodAutoscaler` v2 (behavior 포함)
- `topologySpreadConstraints` (zone + hostname)
- `ServiceMonitor` 또는 Prometheus scrape annotation
### 3. StatefulSet은 "stable identity + storage" 모두 맞을 때만
조건 중 하나라도 강하면:
- stable network identity (DNS name per replica) 필요
- stable persistent storage per Pod 필요
- ordered rollout / termination 필요
- replica 간 peer discovery가 ordinal에 의존
적용 후보:
- `postgres` (operator 없을 때 — 있으면 CloudNativePG 같은 operator 우선)
- `minio` (MinIO Operator 가능)
- `vault` (Raft storage mode)
- `etcd` (외부)
- `kafka`, `zookeeper` (Strimzi operator 우선)
- `elasticsearch` (ECK operator 우선)
**필수 선언**:
- `serviceName` (headless Service 참조)
- `volumeClaimTemplates`
- `podManagementPolicy: OrderedReady` 기본. Parallel은 peer discovery가 순서를 요구하지 않을 때만.
- `updateStrategy: RollingUpdate` + `partition`으로 canary rollout
- `persistentVolumeClaimRetentionPolicy` 명시 (prod 기본 `{whenDeleted: Retain, whenScaled: Retain}`)
### 4. DaemonSet은 노드 전역 에이전트 전용
조건:
- 노드별 한 개만 떠야 함 (또는 특정 노드 group에 한 개)
- 노드 추가/제거에 자동 반응
- hostPath / hostNetwork / hostPID 필요한 경우 다수
적용 후보:
- 로그 shipper: `fluent-bit`, `fluentd`, `vector`
- metric exporter: `node-exporter`, `cadvisor`
- CNI agent: `calico-node`, `cilium-agent`
- CSI node plugin: `longhorn-manager`, `ceph-csi-node`
- security agent: `falco`, `tetragon`
- service mesh node proxy: `istio-cni-node`
- ingress-nginx **DaemonSet 모드** (edge 노드가 고정되고 모든 edge 노드가 80/443 HostPort / hostNetwork로 외부 노출해야 할 때)
**Ingress controller: DaemonSet vs Deployment 결정**:
- **Deployment** + `Service type=LoadBalancer` (MetalLB / 외부 LB): 기본 권장. 노드와 ingress replica 수가 분리됨. HPA 적용 가능.
- **DaemonSet** + `hostNetwork: true` / HostPort 80,443: bare-metal + 외부 LB 없이 모든 노드가 ingress가 되어야 할 때. 80/443 노드 포트 점유, HPA 불가, 노드 수 = replica 수.
**필수**:
- `tolerations`로 노드 taint (예: `node-role.kubernetes.io/control-plane`) 대응 결정
- `nodeSelector` 또는 `affinity`로 대상 노드 그룹 명시 (label 기반)
- `updateStrategy: RollingUpdate` + `maxUnavailable` 지정
- `priorityClassName: system-node-critical` (필수 인프라 에이전트)
### 5. PVC가 있다고 무조건 StatefulSet 아니다
체크리스트:
- Pod마다 고유한 storage identity 필요? 아니면 단일 PVC 공유?
- Pod 이름 / 순서가 의미 있나?
- peer discovery가 stable DNS name에 의존?
아니면:
- **단일 replica Deployment + PVC (ReadWriteOnce)** 도 유효
- **Deployment + ReadWriteMany PVC** (여러 replica가 동일 storage 공유) 도 유효 (shared cache 등)
### 6. Job은 migration / bootstrap / one-off 기본값
적용 후보:
- `flyway-migrate` / `liquibase-migrate`
- schema validation
- 초기 admin 사용자 bootstrap
- 데이터 리페어 / 정리
- 이미지 빌드 trigger
**필수**:
- `restartPolicy: Never` (실패 원인 디버깅 가능) 또는 `OnFailure` (transient 실패 재시도)
- `backoffLimit` 명시 (기본값 6은 prod에서 너무 관대할 수 있음)
- `activeDeadlineSeconds` (무한 실행 방지)
- `ttlSecondsAfterFinished` (완료 Job 자동 정리, 1000-서비스 스케일 필수)
- ServiceAccount 최소 권한
### 7. CronJob은 주기 실행 전용
적용 후보:
- etcd / DB 백업
- 정기 정리 (old PVC, old Snapshot, old Job)
- 정기 검증 / 리포트
- 비즈니스 배치 (야간 집계)
**필수**:
- `concurrencyPolicy: Forbid` 기본 (동시 실행 방지). 멱등하면 `Allow`.
- `startingDeadlineSeconds` (노드 장애로 miss 된 job 무한 누적 방지)
- `successfulJobsHistoryLimit: 3` / `failedJobsHistoryLimit: 5`
- schedule timezone 명시 (`spec.timeZone` v1.25+)
금지:
- 항상 떠 있어야 하는 서버를 CronJob으로 배포
- 본 서비스 온라인 처리를 CronJob에 의존
### 8. Stateful workload는 retention / scale-down 정책을 먼저 박는다
StatefulSet의 `persistentVolumeClaimRetentionPolicy`:
- `whenDeleted` (StatefulSet이 삭제될 때 PVC 처리): `Retain` (기본) / `Delete`
- `whenScaled` (replica 축소될 때 PVC 처리): `Retain` (기본) / `Delete`
**prod 기본**: `{whenDeleted: Retain, whenScaled: Retain}` (기본값). DB/Vault/MinIO 모두 여기서 이탈하지 않는다.
**dev/staging**: `{whenDeleted: Delete, whenScaled: Delete}` 허용 (클러스터 재생성 시 자동 정리).
### 9. 외부 DB를 쓰는 앱 서버는 stateless 우선
Pod에 durable state가 없으면 Deployment. Pod identity가 고정되어야 한다는 이유만으로 StatefulSet 선택 금지.
기준:
- `auth-server` → Deployment
- 외부 DB 사용 `keycloak` → Deployment (caching은 external Redis / Infinispan cluster)
- 외부 object store 사용 앱 → Deployment
### 10. Keycloak은 앱 레이어와 저장소를 분리
Keycloak 서버 자체는 stateless로 다룬다.
- **외부 DB (PostgreSQL)** 사용이 prod 기본
- session / cache는 Infinispan embedded 또는 remote 모드 결정 (remote 선호, replica 간 peer discovery는 Kubernetes DNS)
- **Deployment** + externalTrafficPolicy 고려
- HA replica ≥ 2 + PDB
### 11. Vault는 모드별로 다르다
- **dev mode**: 학습 전용. prod 절대 금지.
- **standalone (file storage)**: StatefulSet + PVC. replica=1. 단일 장애점.
- **HA Raft**: StatefulSet (integrated storage). replica 3 또는 5. `podManagementPolicy: Parallel` 허용.
- **HA Consul backend**: StatefulSet (Vault) + StatefulSet (Consul). operator 권장.
- **external Vault**: 클러스터 내부 서버 없음, ExternalSecrets로 참조만.
prod 권장: **HA Raft mode StatefulSet (replica 3)** 또는 **external Vault**.
### 12. MinIO는 Operator 기본 (StatefulSet은 fallback)
1000-서비스 스케일에서 MinIO는 MinIO Operator + `Tenant` CRD가 기본. tenant가 StatefulSet을 내부적으로 생성.
raw StatefulSet은 단일 node/dev 환경에서만 예외 허용.
### 13. Flyway는 Job 기본값
- 앱 startup 내부 migration 금지 (앱 부팅 실패와 migration 실패가 섞임)
- Flyway Job이 선행되고 Success 후에 Deployment rollout
- ArgoCD PostSync hook 또는 Argo Workflows로 순서 제어
- `migrate`, `validate`, `info`, `repair` 각각 독립 Job
### 14. Ingress controller 배치 결정
prod 권장:
- **ingress-nginx Deployment** + `Service type=LoadBalancer` (MetalLB L2 또는 BGP, 또는 외부 LB)
- 또는 **Envoy Gateway / Gateway API 기반 Deployment**
- HPA 가능, PDB 필수 (tier-1 로 취급)
- 복수 IngressClass (`nginx-public`, `nginx-internal`) 분리
DaemonSet 선택 조건:
- edge 노드가 고정되어 있고 hostNetwork 80/443이 필요
- 외부 LB가 없고 DNS round-robin으로 다수 노드 IP 노출
### 15. DB는 operator 기본, StatefulSet은 fallback
1000-서비스 스케일의 PostgreSQL:
- **CloudNativePG Operator** 기본 → `Cluster` CRD. operator가 StatefulSet/Service/Secret/ConfigMap/Backup 전부 관리.
- **Zalando Postgres Operator**도 대안
- raw StatefulSet은 dev / 특수 케이스에만
MySQL/MariaDB:
- **MariaDB Operator** / **mysql-operator**
Redis:
- **Spotahome redis-operator** / **Redis Enterprise Operator**
- cluster 모드면 StatefulSet, sentinel 모드면 Deployment(sentinel) + StatefulSet(redis)
### 16. Batch / 대량 병렬은 Job + `parallelism` + IndexedJob
단일 Job으로 수천 개 task 병렬 실행:
- `completionMode: Indexed` + `parallelism: N`
- 각 Pod가 `JOB_COMPLETION_INDEX` env로 자기 작업 식별
- 더 복잡한 DAG는 Argo Workflows / Tekton
### 17. workload 종류만 맞는다고 품질이 보장되지 않는다
최종 결정 전 동반 표준 확인:
- `storage-pvc.md` (StorageClass / volumeClaimTemplate / snapshot)
- `network-ingress-tls.md` (ingressClassName / TLS)
- `resources-probes-availability.md` (PDB / HPA / probe / resources)
- `backup-restore.md` (RPO / RTO / 절차)
- `security-podsecurity.md` (PSA / seccomp / capabilities)
- `observability.md` (ServiceMonitor / log shipping)
### 18. priority class / preemption 전략
- 플랫폼 에이전트 (CNI, CSI, log shipper, node-exporter): `system-node-critical`
- 클러스터 컨트롤러 (cert-manager, external-secrets, operator): `system-cluster-critical`
- 비즈니스 tier-1: 커스텀 `tier-1-critical` (value 1000000)
- 비즈니스 tier-2: `tier-2` (value 100000)
- 비즈니스 tier-3 / batch: `tier-3` (value 10000)
## 현재 스택 기본 권장안 (prod)
| 컴포넌트 | workload 종류 | operator | 비고 |
|----------------------|--------------------------------|-------------------|--------------------------------|
| `auth-server` | Deployment | — | tier-1 HPA+PDB |
| `test-server` (장기) | Deployment | — | |
| `test-server` (검증) | Job | — | ttlSecondsAfterFinished |
| `keycloak` | Deployment (외부 DB) | — | HA replicas≥2 |
| `postgres-identity` | StatefulSet (via CNPG) | CloudNativePG | replica 3 |
| `vault` | StatefulSet (Raft) | Vault Operator | replica 3 |
| `minio` | StatefulSet (via Tenant) | MinIO Operator | |
| `kafka` | StatefulSet (via Strimzi) | Strimzi | |
| `redis` | StatefulSet (via operator) | redis-operator | sentinel 또는 cluster 모드 |
| `flyway-migrate` | Job | — | ArgoCD PostSync hook |
| `postgres-backup` | CronJob | CNPG ScheduledBkp | operator가 소유 |
| `ingress-nginx` | Deployment + MetalLB | — | public / internal 분리 |
| `cert-manager` | Deployment | — | system-cluster-critical |
| `external-secrets` | Deployment | — | system-cluster-critical |
| `prometheus` | StatefulSet (via Prometheus) | Prometheus Op | |
| `fluent-bit` | DaemonSet | — | system-node-critical |
| `node-exporter` | DaemonSet | — | system-node-critical |
| `cilium-agent` | DaemonSet | Cilium Op (opt) | system-node-critical |
| `longhorn-manager` | DaemonSet | Longhorn | |
## 프로젝트 기준 요약
- stateless 장기 → Deployment (+PDB+HPA+topologySpread 필수)
- stateful + stable identity/storage → StatefulSet (또는 operator)
- 노드 전역 에이전트 → DaemonSet
- 일회성 → Job (ttlSecondsAfterFinished 필수)
- 주기성 → CronJob (concurrencyPolicy + startingDeadlineSeconds)
- PVC ≠ StatefulSet 신호 전부 아님
- DB/Kafka/Redis/Prometheus는 operator 기본
- `persistentVolumeClaimRetentionPolicy` 명시, prod는 Retain/Retain
- Flyway는 Job, 앱 startup 내부 migration 금지
- ingress controller는 Deployment+LB 기본, DaemonSet은 edge 조건에만