Certified Kubernetes Application Developer (CKAD) #6 Workloads 2: DaemonSet, StatefulSet
#5 Workloads 1: Deployment, ReplicaSet, rolling update와 rollback에서는 Deployment로 stateless 한 Pod 묶음을 굴리고 롤링 업데이트와 롤백을 다뤘습니다. 그러나 쿠버네티스의 워크로드 컨트롤러가 Deployment 하나만 있는 것은 아닙니다. 모든 노드에 똑같이 Pod를 한 개씩 깔아야 하는 작업, 그리고 Pod마다 고유한 정체성과 전용 스토리지가 필요한 작업은 Deployment로 풀 수 없습니다.
이번 글에서는 그 두 가지를 책임지는 컨트롤러인 DaemonSet과 StatefulSet을 실기 관점에서 정리하겠습니다. CKAD는 빈 터미널에 직접 만들어 내는 시험이므로, 두 리소스의 개념을 짚은 뒤 곧바로 YAML과 kubectl로 손에 익혀 보겠습니다.
Deployment로는 풀 수 없는 두 가지 #
Deployment는 상호 교체가 가능한 stateless Pod들의 집합을 가정합니다. replicas를 3으로 두면 어느 노드에 몇 번째 Pod가 뜨는지는 스케줄러가 알아서 정하고, 어느 Pod가 죽어도 동일한 새 Pod로 대체되면 그만입니다. 이름도 web-7d8f...처럼 무작위 해시가 붙습니다.
그러나 실무에는 이 가정이 맞지 않는 작업이 있습니다.
- 모든 노드에 정확히 하나씩 돌아야 하는 Pod. 로그 수집기나 노드 모니터링 에이전트는 노드 수와 Pod 수가 같아야 합니다. 이때는 DaemonSet을 씁니다.
- 각 Pod가 고유한 이름과 전용 디스크를 유지해야 하는 Pod. 데이터베이스 클러스터의 각 노드는 자기만의 데이터를 갖고, 재시작해도 같은 정체성으로 돌아와야 합니다. 이때는 StatefulSet을 씁니다.
이 두 컨트롤러가 이번 글의 주제입니다.
DaemonSet: 모든 노드에 Pod 하나씩 #
DaemonSet은 클러스터의 모든(또는 일부) 노드에 Pod를 정확히 한 개씩 배치하는 컨트롤러입니다. 노드가 새로 추가되면 그 노드에도 자동으로 Pod가 뜨고, 노드가 빠지면 해당 Pod도 함께 사라집니다. replicas라는 개념이 없습니다. 노드 수가 곧 Pod 수입니다.
어디에 쓰나 #
DaemonSet은 노드 단위로 동작해야 하는 시스템 컴포넌트에 주로 쓰입니다.
- 로그 수집기. Fluentd, Fluent Bit처럼 각 노드의 로그를 긁어 중앙으로 보내는 에이전트
- 노드 모니터링. node-exporter처럼 노드의 CPU,메모리,디스크 지표를 수집하는 에이전트
- CNI와 스토리지 플러그인. 각 노드에 네트워크나 스토리지 기능을 설치하는 컴포넌트
nodeSelector와 tolerations로 일부 노드만 한정 #
기본적으로 DaemonSet은 control plane의 taint가 걸린 노드를 제외한 모든 워커 노드에 Pod를 띄웁니다. 특정 노드에만 띄우고 싶다면 nodeSelector로 라벨을 지정합니다.
spec:
template:
spec:
nodeSelector:
disktype: ssd반대로 control plane 노드처럼 taint가 걸린 노드에도 Pod를 띄워야 한다면 tolerations로 해당 taint를 허용합니다. 모니터링 에이전트는 control plane 노드도 봐야 하므로 이 설정을 자주 씁니다.
spec:
template:
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule업데이트 전략 #
DaemonSet의 updateStrategy는 두 가지입니다.
- RollingUpdate(기본값).
maxUnavailable로 한 번에 교체할 Pod 수를 제한하며 순차 교체합니다. - OnDelete. 자동으로 교체하지 않고, 사용자가 기존 Pod를 직접 삭제할 때만 새 버전으로 뜹니다.
DaemonSet YAML 예제 #
DaemonSet에는 kubectl create generator가 없습니다. 시험에서는 Deployment 뼈대를 generator로 뽑은 뒤 kind를 DaemonSet으로 바꾸고 replicas와 strategy 줄을 지우는 방식이 빠릅니다.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-agent
namespace: logging
spec:
selector:
matchLabels:
app: log-agent
template:
metadata:
labels:
app: log-agent
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
containers:
- name: fluent-bit
image: fluent/fluent-bit:2.2
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi생성과 확인은 다음과 같습니다.
k apply -f ds.yaml
k get daemonset -n logging
k get pods -n logging -o wide # 노드마다 한 개씩 떠 있는지 확인DESIRED와 CURRENT 값이 노드 수와 일치하면 정상입니다.
StatefulSet: 정체성과 스토리지를 가진 Pod #
StatefulSet은 각 Pod에 안정적이고 고유한 정체성을 부여하는 컨트롤러입니다. Deployment가 무작위 해시 이름을 붙이는 것과 달리, StatefulSet은 Pod에 web-0, web-1, web-2처럼 순서가 있는 고정 이름을 줍니다.
StatefulSet이 보장하는 세 가지 #
- 안정적 네트워크 ID. 각 Pod는
web-0같은 고정 이름과,web-0.web.default.svc.cluster.local형태의 고정 DNS 이름을 갖습니다. Pod가 재시작되어도 이름과 DNS는 그대로 유지됩니다. - 순서 보장 생성과 삭제. Pod는
web-0→web-1→web-2순서로 차례대로 생성되고, 삭제와 스케일 다운은 역순으로 진행됩니다. 앞 Pod가 Running이 되어야 다음 Pod가 뜹니다. - 안정적 스토리지. 각 Pod는
volumeClaimTemplates로 만들어진 자기 전용 PVC를 갖습니다. Pod가 다시 스케줄되어도 같은 PVC에 다시 연결되어 데이터가 보존됩니다.
headless Service가 필요한 이유 #
StatefulSet은 headless Service를 함께 정의해야 합니다. headless Service는 clusterIP: None으로 지정한 Service로, 클러스터 IP를 할당하지 않고 각 Pod의 개별 DNS 레코드를 만들어 줍니다. 이 Service의 이름을 StatefulSet의 serviceName에 적으면 web-0.web, web-1.web처럼 Pod 하나하나에 직접 접근할 수 있습니다. 데이터베이스 클러스터에서 특정 노드(예: 프라이머리)에 직접 접속해야 할 때 이 고정 주소가 필수입니다.
volumeClaimTemplates로 Pod 별 PVC #
volumeClaimTemplates는 StatefulSet이 Pod마다 PVC를 자동으로 찍어내는 틀입니다. replicas가 3이면 data-web-0, data-web-1, data-web-2라는 PVC가 각각 생성됩니다. 주의할 점은 StatefulSet을 삭제해도 이 PVC는 자동으로 지워지지 않는다는 것입니다. 데이터 보존이 목적이므로 의도된 동작이며, 정리하려면 PVC를 직접 삭제해야 합니다.
언제 쓰나 #
StatefulSet은 각 인스턴스가 고유한 정체성과 데이터를 갖는 워크로드에 씁니다.
- 데이터베이스. PostgreSQL, MySQL의 복제 구성처럼 노드마다 데이터가 다른 경우
- 분산 시스템. Kafka, ZooKeeper, Elasticsearch처럼 멤버 간 역할과 순서가 중요한 클러스터
StatefulSet YAML 예제 (headless Service + volumeClaimTemplates) #
StatefulSet도 generator가 없으므로 직접 작성합니다. headless Service와 StatefulSet을 한 파일에 함께 두면 관리가 편합니다.
apiVersion: v1
kind: Service
metadata:
name: web # serviceName과 일치해야 함
namespace: default
spec:
clusterIP: None # headless: 클러스터 IP 미할당
selector:
app: web
ports:
- port: 80
name: http
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
namespace: default
spec:
serviceName: web # 위 headless Service 이름
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
name: http
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi생성 후 Pod 이름과 PVC가 순서대로 만들어졌는지 확인합니다.
k apply -f sts.yaml
k get statefulset web
k get pods -l app=web # web-0, web-1, web-2 순서로 생성
k get pvc # data-web-0, data-web-1, data-web-2스케일 조정은 Deployment와 같은 명령으로 하지만, Pod는 역순으로 정리됩니다.
k scale statefulset web --replicas=5 # web-3, web-4 추가
k scale statefulset web --replicas=2 # web-4, web-3, web-2 역순 삭제Deployment vs StatefulSet vs DaemonSet 비교 #
세 컨트롤러의 차이를 한 표로 정리하면 다음과 같습니다.
| 항목 | Deployment | StatefulSet | DaemonSet |
|---|---|---|---|
| Pod 정체성 | 무작위 해시 이름 | 순서 있는 고정 이름(web-0,1,2) | 노드별 한 개 |
| Pod 수 | replicas로 지정 | replicas로 지정 | 노드 수와 동일 |
| 생성,삭제 순서 | 순서 보장 없음 | 순서 보장(생성 정순, 삭제 역순) | 노드 추가,삭제에 연동 |
| 전용 스토리지 | 없음(공유 또는 무상태) | Pod 별 PVC(volumeClaimTemplates) | 보통 노드 hostPath |
| headless Service | 불필요 | 필수(serviceName) | 불필요 |
| 대표 용도 | stateless 웹,API | DB,분산 시스템 | 로그,모니터링,CNI |
핵심 판단 기준은 간단합니다. 상호 교체 가능하면 Deployment, 고유 정체성과 전용 데이터가 필요하면 StatefulSet, 노드마다 하나씩 깔아야 하면 DaemonSet 입니다.
시험 포인트 #
CKAD에서 이 두 컨트롤러가 나올 때 자주 걸리는 부분을 정리하겠습니다.
- generator가 없다. DaemonSet과 StatefulSet은
kubectl create로 뼈대를 못 만듭니다. Deployment 뼈대를k create deploy ... $do > x.yaml로 뽑은 뒤kind를 바꾸고 불필요한 필드를 손보는 방식이 가장 빠릅니다. - DaemonSet으로 바꿀 때 지울 것. Deployment 뼈대에서
replicas와strategy,status줄을 삭제해야 유효한 DaemonSet이 됩니다. - StatefulSet의 serviceName 누락.
serviceName은 필수 필드이며, 가리키는 headless Service(clusterIP: None)가 실제로 존재해야 Pod의 DNS가 동작합니다. 둘 중 하나라도 빠지면 감점입니다. - volumeClaimTemplates와 volumeMounts의 name 일치.
volumeClaimTemplates의metadata.name과 컨테이너volumeMounts의name이 같아야 PVC가 마운트됩니다. - PVC는 남는다. StatefulSet을 지워도 PVC는 자동 삭제되지 않습니다. 문제에서 정리까지 요구하면 PVC를 별도로 삭제합니다.
- DaemonSet의 Pod 수 확인. 정답 검증은
k get pods -o wide로 노드마다 한 개씩 떠 있는지를 봅니다. control plane 노드에도 떠야 하면 tolerations를 빠뜨리지 않습니다.
정리 #
이번 글에서 잡은 것은 다음과 같습니다.
- DaemonSet은 모든(또는 일부) 노드에 Pod를 하나씩 배치하는 컨트롤러. 로그 수집기,노드 모니터링,CNI에 쓰며,
nodeSelector와tolerations로 대상 노드를 한정 - StatefulSet은 안정적 네트워크 ID, 순서 보장 생성,삭제, 전용 스토리지를 제공. DB,분산 시스템에 쓰며 headless Service와
volumeClaimTemplates가 핵심 - headless Service는
clusterIP: None으로 각 Pod의 개별 DNS를 만들어web-0.web같은 고정 주소를 제공 - 세 컨트롤러 선택 기준. 상호 교체면 Deployment, 고유 정체성,데이터면 StatefulSet, 노드별 한 개면 DaemonSet
- 시험 주의. generator 없음, serviceName 필수, volume name 일치, PVC 잔존
워크로드 컨트롤러의 큰 그림이 더 필요하다면 Kubernetes 중급 트랙에서 같은 리소스를 운영 관점으로 더 깊게 다룹니다.
다음: Workloads 3 #
DaemonSet과 StatefulSet까지 익히며 상시 실행되는 워크로드는 정리했습니다. 이제 남은 것은 한 번 또는 주기적으로 실행되고 끝나는 작업입니다.
#7 Workloads 3: Job, CronJob (백오프, 동시성)에서는 배치 작업을 담당하는 Job과 CronJob을 다루겠습니다. completions와 parallelism으로 병렬 실행을 제어하는 법, backoffLimit와 activeDeadlineSeconds로 실패와 타임아웃을 다루는 법, CronJob의 concurrencyPolicy와 스케줄 표기까지 YAML로 만들어 보며 정리하겠습니다.