deploy,replica의 경우 pvc공유 같은 곳에 저장되는거임
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: myapp
spec:
serviceName: myapp-headless # Headless Service 연동
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: your-image
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
storageClassName: standard
StatefulSet은 일반적인 Deployment와는 다르게, 파드마다 고유한 네트워크 ID와 볼륨을 유지하는 데 중점을 둡니다.
다음은 StatefulSet이 제공하는 주요 기능들입니다.
name-ordinal 형식으로 생성됩니다.myapp-0, myapp-1, myapp-2myapp-0.myapp-headless.default.svc.cluster.localvolumeClaimTemplates 필드를 통해myapp-0 → data-myapp-0 PVCmyapp-1 → data-myapp-1 PVCmyapp-0이 Ready 되어야 myapp-1이 생성됨clusterIP: None 으로 설정된 Headless Service와 함께 사용하면myapp-0.myapp-headless.default.svc이 특성들 덕분에 StatefulSet은 각 인스턴스가 자기 데이터를 유지해야 하는
쿠버네티스에서 파드를 여러 개 띄우는 대표적인 방식은 Deployment와 StatefulSet입니다.
이 둘은 파드를 관리하는 방식, 스토리지 사용 여부, 네트워크 접근 방식 등에서 큰 차이를 보입니다.
| 항목 | Deployment (무상태) | StatefulSet (상태 저장) |
|---|---|---|
| 파드 이름 | 무작위 이름 (예: nginx-7d5f7c5d9b-abcde) | 고정 이름 (예: nginx-0, nginx-1) |
| 생성/삭제 순서 | 무순서 (동시 처리 가능) | 순차적으로 생성/삭제 (OrderedReady) |
| 네트워크 접근 방식 | ClusterIP + 라운드로빈 로드밸런싱 | Headless Service + 고정 DNS (pod-0.svc) |
| 스토리지 | 공유 PVC 사용하거나 볼륨 없음 | 파드마다 고유 PVC (volumeClaimTemplates) |
| 파드 교체 시 상태 유지 여부 | 유지 안 됨 (새 파드 = 새 볼륨 또는 무볼륨) | 유지됨 (이전 PVC 그대로 재사용) |
| 주요 사용 사례 | 웹 서버, API 서버, 워커 등 무상태 서비스 | DB, Kafka, Redis 등 상태 저장 서비스 |
쿠버네티스에서 파드는 기본적으로 휘발성(ephemeral) 입니다.
즉, 파드가 삭제되거나 재시작되면 로컬 디스크에 있던 모든 데이터가 사라집니다.
하지만 상태 저장 서비스(데이터베이스, 업로드 파일 저장 등)는 다음과 같은 이유로
지속적인 저장소(Persistent Storage) 가 필요합니다:
PV (PersistentVolume)
PVC (PersistentVolumeClaim)
# PVC 예시
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
storageClassName: standard
StatefulSet에서는 volumeClaimTemplates를 통해
파드마다 자동으로 고유 PVC를 생성하여 바인딩합니다.
파드가 사라져도 데이터를 유지하고 싶다면 → PVC + PV 필수
StatefulSet은 PVC를 파드 수만큼 자동으로 생성해 관리합니다.
쿠버네티스에서 PVC(PersistentVolumeClaim)를 만들 때,
어떻게 PV(PersistentVolume)를 연결하느냐에 따라 프로비저닝 방식은 두 가지로 나뉩니다.
StorageClass에 따라 Kubernetes가 PV를 자동으로 생성하고 연결합니다.apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dynamic-pvc
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
storageClassName: standard
volumeName 을 명시해 특정 PV를 직접 바인딩합니다.apiVersion: v1
kind: PersistentVolume
metadata:
name: static-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /mnt/data/static
persistentVolumeReclaimPolicy: Retain
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: static-pvc
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
volumeName: static-pv
위 PVC는 volumeName: static-pv 를 통해
사전에 수동으로 만들어둔 PersistentVolume 리소스에 직접 연결됩니다.
storageClassName 을 사용하지 않으며,accessModesresources.requests.storage (크기)volumeMode (Block/File)PVC가 바인딩되었는지 확인하려면 다음 명령어를 실행합니다:
kubectl get pvc static-pvc
동적 프로비저닝은 PVC만 만들면 Kubernetes가 자동으로 PV를 생성해 주는 방식입니다.
실무에서 가장 일반적이고, 대부분의 StatefulSet, Deployment, 웹서비스 등에서 사용됩니다.
정적 프로비저닝은 운영자가 직접 PV를 만들어 놓고,
PVC에서 해당 PV를 volumeName으로 명시해 연결하는 방식입니다.
일반적인 상황에서는 불편하지만, 특정 디스크나 특수한 환경에서는 필요할 수 있습니다.
둘의 핵심 차이는 다음과 같습니다:
| 항목 | 동적 프로비저닝 | 정적 프로비저닝 |
|---|---|---|
| PV 생성 시점 | PVC 생성 시 자동 생성 | 운영자가 미리 수동 생성 |
| PVC 작성 방식 | storageClassName 지정만 필요 | volumeName 으로 특정 PV 직접 지정 |
| 자동화 수준 | 높음 (간편함) | 낮음 (수동 매핑 필요) |
| 세부 설정 제어 수준 | 낮음 (StorageClass에 위임됨) | 높음 (PV 정의에서 직접 제어 가능) |
| 일반적인 사용 예시 | 대부분의 워크로드, StatefulSet 포함 | 테스트 환경, 로컬 디스크, 수동 디스크 지정 시 등 |
StatefulSet을 사용할 때 파드 간에 데이터를 공유하거나,
파드 재시작 후에도 데이터를 유지하려면 영속적이고 공유 가능한 파일 시스템이 필요합니다.
CephFS는 ReadWriteMany(RWX)를 지원하는 대표적인 분산 파일 시스템이며,
쿠버네티스에서는 Ceph CSI 드라이버를 통해 StorageClass 로 연동할 수 있습니다.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: cephfs
provisioner: rook-ceph.cephfs.csi.ceph.com
parameters:
clusterID: rook-ceph
fsName: myfs
pool: myfs-data0
csi.storage.k8s.io/provisioner-secret-name: rook-csi-cephfs-provisioner
csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph
csi.storage.k8s.io/node-stage-secret-name: rook-csi-cephfs-node
csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: true
이 StorageClass는 CephFS 클러스터를 기반으로 PVC 요청이 들어올 때마다
RWX(ReadWriteMany)를 지원하는 공유 볼륨을 자동으로 프로비저닝합니다.
provisioner: CephFS CSI 드라이버를 사용하여 실제 볼륨을 생성합니다.parameters: Ceph 클러스터 ID, 풀 이름, 인증 시크릿 등을 설정합니다.volumeBindingMode: Immediate: PVC가 생성되자마자 볼륨이 바인딩됩니다.allowVolumeExpansion: true: 나중에 PVC 용량을 확장할 수 있습니다.이 StorageClass는 StatefulSet의 volumeClaimTemplates 내부에서
storageClassName: cephfs 로 연결해 사용할 수 있습니다.
apiVersion: v1
kind: Service
metadata:
name: myapp-headless
spec:
clusterIP: None
selector:
app: myapp
ports:
- port: 80
이 Headless Service는 clusterIP: None 으로 설정되어 있어
파드마다 고유한 DNS 이름이 생성됩니다.
예를 들어, StatefulSet 파드가 myapp이라는 이름으로 구성되었다면:
myapp-0.myapp-headless.default.svc.cluster.localmyapp-1.myapp-headless.default.svc.cluster.local이런 식으로 각 파드별 DNS가 자동 생성되어,
다른 파드나 외부에서 직접 해당 파드로 통신할 수 있는 주소를 제공합니다.
이 구조는 다음과 같은 상황에서 유용합니다:
Headless Service는 StatefulSet의 특성과 함께 사용할 때
고정된 네트워크 식별자를 제공하는 핵심 역할을 합니다.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: myapp
spec:
serviceName: myapp-headless
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: uploads
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: uploads
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 5Gi
storageClassName: cephfs
이 StatefulSet 구성은 다음과 같은 목적을 가지고 있습니다:
replicas: 2 → myapp-0, myapp-1 파드가 생성됩니다.serviceName: myapp-headless → Headless Service를 통해volumeClaimTemplates → StatefulSet이 자동으로 PVC를 생성합니다.uploads-myapp-0, uploads-myapp-1storageClassName: cephfs → CephFS StorageClass를 통해 RWX 지원 볼륨을 사용합니다.mountPath: /usr/share/nginx/html → 정적 콘텐츠를 공유 디렉터리에 저장합니다.이 구성을 통해 파드가 재시작되어도 데이터는 유지되며,
여러 파드가 동일한 콘텐츠를 공유할 수 있습니다.
apiVersion: vitess.io/v1
kind: VitessCluster
metadata:
name: reservation-db
spec:
keyspaces:
- name: reservation
partitions: 3
tabletPools:
readWrite:
replicas: 1
storage:
pvc:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
storageClassName: ceph-rbd
이 예시는 Vitess를 사용해 MySQL을 샤딩하고,
각 샤드를 StatefulSet 구조로 파드별 고정 볼륨(PVC) 에 연결하는 구조입니다.
partitions: 3
→ reservation keyspace를 3개의 샤드로 분할합니다.
tabletPools.readWrite.replicas: 1
→ 각 샤드마다 1개의 writable MySQL 인스턴스를 띄웁니다.
storage.pvc
→ 파드별로 PVC를 생성하며, ceph-rbd StorageClass를 사용해
Ceph RBD 볼륨을 자동 프로비저닝합니다.
각 샤드(vttablet Pod)는 자기만의 데이터를 저장해야 하므로,
파드 이름(Pod ordinal)과 1:1 매핑되는 고정된 PVC가 필요합니다.
예:
vttablet-0 → 샤드 0 → pvc-vttablet-0 vttablet-1 → 샤드 1 → pvc-vttablet-1 vttablet-2 → 샤드 2 → pvc-vttablet-2이처럼 StatefulSet은 샤드 분할과 고정 볼륨 제공이라는 두 가지 요구사항을 동시에 만족시켜 줍니다.
StatefulSet은 상태 저장 워크로드에 최적화된 쿠버네티스 리소스로,
파드별로 고유한 이름, DNS, 볼륨(PVC)을 자동으로 관리해 줍니다.
하지만, 이를 잘못 구성하면 오히려 Deployment보다 못한 결과를 초래할 수 있습니다.
emptyDir 사용)apiVersion: apps/v1
kind: StatefulSet
metadata:
name: bad-statefulset
spec:
serviceName: bad-headless
replicas: 2
selector:
matchLabels:
app: bad
template:
metadata:
labels:
app: bad
spec:
containers:
- name: web
image: nginx
volumeMounts:
- name: tmp
mountPath: /usr/share/nginx/html
volumes:
- name: tmp
emptyDir: {}