Part 9/26: StatefulSet과 DaemonSet
1. StatefulSet
1.1 StatefulSet이란?
원문 (kubernetes.io - StatefulSets): StatefulSet is the workload API object used to manage stateful applications. It manages the deployment and scaling of a set of Pods, and provides guarantees about the ordering and uniqueness of these Pods.
번역: StatefulSet은 상태 저장 애플리케이션을 관리하는 데 사용되는 워크로드 API 객체이다. Pod 집합의 배포와 스케일링을 관리하고 이러한 Pod의 순서와 고유성에 대한 보장을 제공한다.
Deployment vs StatefulSet:
| 특성 | Deployment | StatefulSet |
|---|---|---|
| Pod 이름 | 랜덤 (nginx-6b9f7c8d5-abc12) | 순차적 (mysql-0, mysql-1) |
| 생성/삭제 순서 | 무작위 | 순차적 |
| 네트워크 ID | 변경됨 | 고정 (Headless Service) |
| 스토리지 | 공유 가능 | Pod별 독립 PVC |
| 적합한 워크로드 | 웹 서버, API | 데이터베이스, 분산 시스템 |
1.2 StatefulSet의 핵심 특징
원문 (kubernetes.io - StatefulSets): StatefulSet provides guarantees about ordering and uniqueness. For a StatefulSet with N replicas, when Pods are being deployed, they are created sequentially, in order from {0..N-1}. When Pods are being deleted, they are terminated in reverse order, from {N-1..0}. Before a scaling operation is applied to a Pod, all of its predecessors must be Running and Ready.
번역: StatefulSet은 순서와 고유성에 대한 보장을 제공한다. N개의 복제본이 있는 StatefulSet의 경우 Pod가 배포될 때 {0..N-1} 순서대로 순차적으로 생성된다. Pod가 삭제될 때는 {N-1..0}에서 역순으로 종료된다. 스케일링 작업이 Pod에 적용되기 전에 모든 선행 Pod가 Running 및 Ready 상태여야 한다.
역할/설명:
1. 안정적인 네트워크 ID:
1
2
3
4
5
6
7
Pod 이름: <statefulset-name>-<ordinal>
DNS: <pod-name>.<headless-service>.<namespace>.svc.cluster.local
예시:
- mysql-0.mysql-headless.default.svc.cluster.local
- mysql-1.mysql-headless.default.svc.cluster.local
- mysql-2.mysql-headless.default.svc.cluster.local
2. 순서 보장:
- 생성: 0 → 1 → 2 순서로 생성
- 삭제: 2 → 1 → 0 역순으로 삭제
- 스케일 업: 이전 Pod이 Ready 상태여야 다음 Pod 생성
- 스케일 다운: 역순으로 하나씩 삭제
3. 영구적인 스토리지:
- 각 Pod에 독립적인 PVC 자동 생성
- Pod이 삭제되어도 PVC는 유지
- Pod이 다시 생성되면 같은 PVC에 연결
1.3 StatefulSet 정의
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-headless # Headless Service 이름 (필수)
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
volumeClaimTemplates: # PVC 템플릿 (자동 생성)
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: standard
resources:
requests:
storage: 10Gi
---
# Headless Service (ClusterIP: None)
apiVersion: v1
kind: Service
metadata:
name: mysql-headless
spec:
clusterIP: None # Headless Service
selector:
app: mysql
ports:
- port: 3306
1.4 Headless Service
원문 (kubernetes.io - StatefulSets): The StatefulSet can use a Headless Service to control the domain of its Pods. The domain managed by this Service takes the form: $(service name).$(namespace).svc.cluster.local, where “cluster.local” is the cluster domain. As each Pod is created, it gets a matching DNS subdomain, taking the form: $(podname).$(governing service domain), where the governing service is defined by the serviceName field on the StatefulSet.
번역: StatefulSet은 Headless Service를 사용하여 Pod의 도메인을 제어할 수 있다. 이 Service가 관리하는 도메인은 $(service name).$(namespace).svc.cluster.local 형식을 취하며 “cluster.local”은 클러스터 도메인이다. 각 Pod가 생성되면 일치하는 DNS 하위 도메인을 얻으며 $(podname).$(governing service domain) 형식을 취한다. governing service는 StatefulSet의 serviceName 필드로 정의된다.
역할/설명:
StatefulSet은 Headless Service를 사용하여 각 Pod에 고유한 DNS 레코드를 생성한다.
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Service
metadata:
name: mysql-headless
spec:
clusterIP: None # Headless Service 핵심
selector:
app: mysql
ports:
- port: 3306
일반 Service vs Headless Service:
| 구분 | 일반 Service | Headless Service |
|---|---|---|
| ClusterIP | 할당됨 | None |
| 로드 밸런싱 | Service IP로 분산 | 직접 Pod IP 반환 |
| DNS 응답 | Service IP | 모든 Pod IP |
| 용도 | 일반 애플리케이션 | StatefulSet, 개별 Pod 접근 |
1.5 Pod 관리 정책
1
2
3
4
spec:
podManagementPolicy: OrderedReady # 기본값
# OrderedReady: 순차적 생성/삭제
# Parallel: 동시 생성/삭제 (순서 불필요 시)
1.6 업데이트 전략
1
2
3
4
5
spec:
updateStrategy:
type: RollingUpdate # 기본값
rollingUpdate:
partition: 2 # 이 값 이상의 ordinal만 업데이트
partition 활용 (카나리 배포):
partition: 2로 설정하면 mysql-2만 업데이트- 검증 후
partition: 0으로 변경하여 전체 업데이트
1.7 StatefulSet 사용 사례
- 데이터베이스: MySQL, PostgreSQL, MongoDB
- 분산 시스템: Kafka, Zookeeper, Elasticsearch
- 캐시 클러스터: Redis Cluster
- 메시지 큐: RabbitMQ
2. DaemonSet
2.1 DaemonSet이란?
원문 (kubernetes.io - DaemonSet): A DaemonSet ensures that all (or some) Nodes run a copy of a Pod. As nodes are added to the cluster, Pods are added to them. As nodes are removed from the cluster, those Pods are garbage collected.
번역: DaemonSet은 모든(또는 일부) 노드가 Pod의 복사본을 실행하도록 보장한다. 노드가 클러스터에 추가되면 Pod가 추가된다. 노드가 클러스터에서 제거되면 해당 Pod는 가비지 수집된다.
flowchart TB
subgraph cluster["Kubernetes Cluster"]
subgraph node1["Node 1"]
ds1["DaemonSet Pod<br/>(log collector)"]
end
subgraph node2["Node 2"]
ds2["DaemonSet Pod<br/>(log collector)"]
end
subgraph node3["Node 3"]
ds3["DaemonSet Pod<br/>(log collector)"]
end
subgraph node4["Node 4"]
ds4["DaemonSet Pod<br/>(log collector)"]
end
end
note["새 노드 추가 → 자동으로 DaemonSet Pod 생성<br/>노드 제거 → 해당 노드의 DaemonSet Pod 삭제"]
2.2 DaemonSet 사용 사례
- 로그 수집: Fluentd, Filebeat
- 모니터링 에이전트: Prometheus Node Exporter, Datadog Agent
- 네트워킹: CNI 플러그인 (Calico, Flannel)
- 스토리지: CSI 드라이버
- 보안: Falco, Sysdig
2.3 DaemonSet 정의
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace: kube-system
spec:
selector:
matchLabels:
name: fluentd
template:
metadata:
labels:
name: fluentd
spec:
containers:
- name: fluentd
image: fluentd:latest
resources:
limits:
cpu: 200m
memory: 200Mi
requests:
cpu: 100m
memory: 100Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
2.4 특정 노드에만 배포
nodeSelector 사용:
1
2
3
4
5
spec:
template:
spec:
nodeSelector:
disktype: ssd
nodeAffinity 사용:
1
2
3
4
5
6
7
8
9
10
11
12
spec:
template:
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-type
operator: In
values:
- worker
2.5 Taint Toleration
Control Plane 노드에도 DaemonSet Pod을 실행하려면:
1
2
3
4
5
6
7
8
9
10
spec:
template:
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
2.6 업데이트 전략
1
2
3
4
5
spec:
updateStrategy:
type: RollingUpdate # 기본값
rollingUpdate:
maxUnavailable: 1 # 동시에 업데이트할 수 있는 노드 수
- RollingUpdate: 순차적으로 업데이트
- OnDelete: 수동으로 Pod 삭제 시에만 업데이트
3. Job
3.1 Job이란?
원문 (kubernetes.io - Jobs): A Job creates one or more Pods and will continue to retry execution of the Pods until a specified number of them successfully terminate. As pods successfully complete, the Job tracks the successful completions. When a specified number of successful completions is reached, the task (ie, Job) is complete. Deleting a Job will clean up the Pods it created.
번역: Job은 하나 이상의 Pod를 생성하고 지정된 수의 Pod가 성공적으로 종료될 때까지 Pod 실행을 계속 재시도한다. Pod가 성공적으로 완료되면 Job은 성공적인 완료를 추적한다. 지정된 수의 성공적인 완료에 도달하면 작업(즉, Job)이 완료된다. Job을 삭제하면 생성된 Pod가 정리된다.
역할/설명:
Job은 일회성 작업을 완료될 때까지 실행하는 컨트롤러다.
1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: batch/v1
kind: Job
metadata:
name: backup-job
spec:
template:
spec:
containers:
- name: backup
image: backup-tool:latest
command: ["/bin/sh", "-c", "backup.sh"]
restartPolicy: OnFailure # Never 또는 OnFailure (Always 불가)
backoffLimit: 4 # 재시도 횟수
3.2 Job 완료 조건
1
2
3
spec:
completions: 5 # 성공적으로 완료해야 하는 Pod 수
parallelism: 2 # 동시에 실행할 Pod 수
| 설정 | 동작 |
|---|---|
completions=1, parallelism=1 | 단일 Pod 실행 (기본) |
completions=5, parallelism=1 | 순차적으로 5개 완료 |
completions=5, parallelism=2 | 2개씩 병렬로 총 5개 완료 |
3.3 Job 정리
1
2
spec:
ttlSecondsAfterFinished: 3600 # 완료 후 1시간 뒤 자동 삭제
4. CronJob
4.1 CronJob이란?
원문 (kubernetes.io - CronJob): A CronJob creates Jobs on a repeating schedule. CronJob is meant for performing regular scheduled actions such as backups, report generation, and so on. One CronJob object is like one line of a crontab (cron table) file on a Unix system. It runs a Job periodically on a given schedule, written in Cron format.
번역: CronJob은 반복 일정에 따라 Job을 생성한다. CronJob은 백업, 보고서 생성 등과 같은 정기적으로 예약된 작업을 수행하기 위한 것이다. 하나의 CronJob 객체는 Unix 시스템의 crontab(cron table) 파일의 한 줄과 같다. Cron 형식으로 작성된 주어진 일정에 따라 주기적으로 Job을 실행한다.
역할/설명:
CronJob은 스케줄에 따라 Job을 생성하는 컨트롤러다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-backup
spec:
schedule: "0 2 * * *" # 매일 오전 2시
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: backup-tool:latest
command: ["/bin/sh", "-c", "backup.sh"]
restartPolicy: OnFailure
4.2 Cron 스케줄 형식
1
2
3
4
5
6
7
┌───────────── 분 (0-59)
│ ┌───────────── 시 (0-23)
│ │ ┌───────────── 일 (1-31)
│ │ │ ┌───────────── 월 (1-12)
│ │ │ │ ┌───────────── 요일 (0-6, 0=일요일)
│ │ │ │ │
* * * * *
참고: Cron 스케줄 형식은 텍스트 기반의 간결한 표현이므로 ASCII 다이어그램으로 유지.
| 예시 | 설명 |
|---|---|
*/5 * * * * | 5분마다 |
0 * * * * | 매시 정각 |
0 2 * * * | 매일 오전 2시 |
0 0 1 * * | 매월 1일 자정 |
0 0 * * 0 | 매주 일요일 자정 |
4.3 동시성 정책
1
2
3
4
5
spec:
concurrencyPolicy: Allow # 기본값
# Allow: 동시 실행 허용
# Forbid: 이전 Job 실행 중이면 스킵
# Replace: 이전 Job 취소하고 새 Job 실행
4.4 시작 기한
1
2
spec:
startingDeadlineSeconds: 200 # 스케줄 시간 후 이 시간 내에 시작 못하면 실패 처리
5. 면접 빈출 질문
Q1. StatefulSet을 사용해야 하는 경우는?
다음 중 하나라도 해당되면 StatefulSet 사용:
- 고유한 네트워크 ID 필요: 각 Pod에 안정적인 DNS 이름
- 안정적인 스토리지 필요: Pod 재시작 후에도 같은 볼륨
- 순서 보장 필요: 순차적 생성/삭제/업데이트
- 클러스터 멤버십 필요: 분산 시스템에서 노드 식별
예: MySQL, PostgreSQL, Kafka, Zookeeper, Elasticsearch
Q2. StatefulSet에서 Headless Service가 필요한 이유는?
Headless Service는 개별 Pod에 대한 DNS 레코드를 생성한다.
일반 Service는 로드 밸런싱을 위해 단일 ClusterIP를 제공하지만, Headless Service는:
- 각 Pod에 고유한 DNS 이름 제공
- 클라이언트가 특정 Pod에 직접 연결 가능
- 분산 시스템에서 노드 간 통신에 필수
Q3. DaemonSet Pod이 특정 노드에서 실행되지 않는 이유는?
- Taint가 있는 노드: Toleration이 없으면 스케줄되지 않음
- nodeSelector/nodeAffinity: 조건에 맞지 않는 노드 제외
- Unschedulable 노드:
kubectl cordon으로 스케줄 비활성화된 노드 - 리소스 부족: Pod의 requests를 만족할 수 없는 노드
Q4. Job의 restartPolicy가 Always일 수 없는 이유는?
Job은 완료를 목적으로 하는데, Always는 컨테이너가 종료될 때마다 무한히 재시작한다. 이는 “완료”라는 개념과 충돌한다.
Job에서 허용되는 정책:
OnFailure: 실패 시에만 재시작 (같은 Pod에서)Never: 재시작 안함 (새 Pod으로 재시도)
6. CKA 실습
6.1 StatefulSet 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# Headless Service 먼저 생성
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: nginx-headless
spec:
clusterIP: None
selector:
app: nginx
ports:
- port: 80
EOF
# StatefulSet 생성
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx
spec:
serviceName: nginx-headless
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
EOF
# Pod 이름 확인 (nginx-0, nginx-1, nginx-2)
kubectl get pods
# DNS 확인
kubectl run -it --rm debug --image=busybox --restart=Never -- nslookup nginx-headless
6.2 DaemonSet 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
spec:
selector:
matchLabels:
name: fluentd
template:
metadata:
labels:
name: fluentd
spec:
containers:
- name: fluentd
image: fluentd
EOF
# 각 노드에 Pod 확인
kubectl get pods -o wide
6.3 Job 생성
1
2
3
4
5
6
7
8
kubectl create job backup --image=busybox -- /bin/sh -c "echo backup done"
# Job 상태 확인
kubectl get jobs
kubectl get pods
# 로그 확인
kubectl logs job/backup
6.4 CronJob 생성
1
2
3
4
5
6
7
kubectl create cronjob daily-backup --image=busybox --schedule="*/1 * * * *" -- /bin/sh -c "date; echo backup"
# CronJob 확인
kubectl get cronjobs
# 생성된 Job 확인
kubectl get jobs
정리
주요 개념 체크리스트
- StatefulSet의 핵심 특징 (순서, 네트워크 ID, 스토리지)
- Headless Service의 역할
- DaemonSet의 용도와 동작
- Job과 CronJob의 차이
- 각 워크로드의 적절한 사용 사례
다음 포스트
Part 10: Job과 CronJob - 배치 작업 처리에서는 배치 작업의 세부 설정을 다룬다.