[Daily morning study] Kubernetes StatefulSet vs Deployment ๋น„๊ต

#daily morning study

Image


Deployment vs StatefulSet โ€” ์–ธ์ œ ๋ฌด์—‡์„ ์จ์•ผ ํ•˜๋‚˜

Kubernetes์—์„œ ํŒŒ๋“œ(Pod)๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ์‹คํ–‰ํ•  ๋•Œ ๋ณดํ†ต Deployment๋ฅผ ์“ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ๋ฉ”์‹œ์ง€ ํ์ฒ˜๋Ÿผ ์ƒํƒœ(state)๋ฅผ ๊ฐ€์ง€๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์˜ฌ๋ฆฌ๋ ค ํ•˜๋ฉด Deployment๋งŒ์œผ๋กœ๋Š” ๋ถ€์กฑํ•˜๋‹ค๋Š” ๊ฑธ ๊ธˆ๋ฐฉ ์•Œ๊ฒŒ ๋œ๋‹ค. ์ด ์ฐจ์ด๋ฅผ ์ •๋ฆฌํ•ด ๋‘๋ฉด ์„ค๊ณ„ํ•  ๋•Œ ํ—ท๊ฐˆ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.


Deployment

Deployment๋Š” ๋ฌด์ƒํƒœ(stateless) ์›Œํฌ๋กœ๋“œ๋ฅผ ์œ„ํ•ด ์„ค๊ณ„๋๋‹ค. ํ•ต์‹ฌ ํŠน์„ฑ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ํŒŒ๋“œ ์ด๋ฆ„์ด {deployment-name}-{replicaset-hash}-{random-suffix} ํ˜•ํƒœ๋กœ ๋ฌด์ž‘์œ„ ์ƒ์„ฑ๋จ
  • ํŒŒ๋“œ๋ผ๋ฆฌ ๋™๋“ฑ(interchangeable)ํ•˜๋‹ค โ€” ์–ด๋А ํŒŒ๋“œ๊ฐ€ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•ด๋„ ์ƒ๊ด€์—†์Œ
  • ํŒŒ๋“œ๊ฐ€ ์ฃฝ์œผ๋ฉด ์ƒˆ๋กœ์šด ์ด๋ฆ„์œผ๋กœ ์žฌ์ƒ์„ฑ๋จ
  • ๋ณผ๋ฅจ์„ ๋ถ™์—ฌ๋„ ํŒŒ๋“œ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ๊ฐ™์€ ๋ณผ๋ฅจ์„ ๋ณด์žฅํ•˜์ง€ ์•Š์Œ
  • ๋กค๋ง ์—…๋ฐ์ดํŠธ ์‹œ ์ˆœ์„œ ๋ณด์žฅ ์—†์ด ๋ณ‘๋ ฌ๋กœ ๊ต์ฒด ๊ฐ€๋Šฅ
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: app
          image: my-app:v2

์›น ์„œ๋ฒ„, API ์„œ๋ฒ„์ฒ˜๋Ÿผ ์š”์ฒญ๋งˆ๋‹ค ๋…๋ฆฝ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์„œ๋น„์Šค์— ์ ํ•ฉํ•˜๋‹ค.


StatefulSet

StatefulSet์€ ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๋Š” ์›Œํฌ๋กœ๋“œ๋ฅผ ์œ„ํ•ด ๋งŒ๋“ค์–ด์กŒ๋‹ค. Deployment์™€ ๊ฒฐ์ •์ ์œผ๋กœ ๋‹ค๋ฅธ ์ ๋“ค์ด ์žˆ๋‹ค.

์•ˆ์ •์ ์ธ ๋„คํŠธ์›Œํฌ ID

ํŒŒ๋“œ ์ด๋ฆ„์ด {statefulset-name}-0, {statefulset-name}-1, {statefulset-name}-2 ์ฒ˜๋Ÿผ ์ˆœ์„œ๊ฐ€ ์žˆ๋Š” ๊ณ ์ • ์ด๋ฆ„์œผ๋กœ ์ƒ์„ฑ๋œ๋‹ค. ํŒŒ๋“œ๊ฐ€ ์žฌ์‹œ์ž‘๋˜์–ด๋„ ์ด๋ฆ„์ด ์œ ์ง€๋œ๋‹ค.

mysql-0   โ† ํ•ญ์ƒ ์ด ์ด๋ฆ„
mysql-1
mysql-2

Headless Service์™€ DNS

StatefulSet์€ ๋ณดํ†ต Headless Service(.spec.clusterIP: None)์™€ ํ•จ๊ป˜ ์“ด๋‹ค. ๊ฐ ํŒŒ๋“œ์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ DNS ์ฃผ์†Œ๊ฐ€ ์ž๋™์œผ๋กœ ๋ถ€์—ฌ๋œ๋‹ค.

{pod-name}.{service-name}.{namespace}.svc.cluster.local

# ์˜ˆ์‹œ
mysql-0.mysql.default.svc.cluster.local
mysql-1.mysql.default.svc.cluster.local

ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํŠน์ • ํŒŒ๋“œ๋ฅผ ์ง์ ‘ ์ง€์ •ํ•ด์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค. Primary DB์—๋งŒ ์“ฐ๊ธฐ๋ฅผ ๋ณด๋‚ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์ด๊ฒŒ ํ•„์ˆ˜๋‹ค.

์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋œ ๋ฐฐํฌ ๋ฐ ์‚ญ์ œ

  • ์ƒ์„ฑ: 0 โ†’ 1 โ†’ 2 ์ˆœ์„œ๋กœ ํ•˜๋‚˜์”ฉ ์˜ฌ๋ผ์˜ด (์ด์ „ ํŒŒ๋“œ๊ฐ€ Running์ด ๋ผ์•ผ ๋‹ค์Œ ๊ฒƒ ์ƒ์„ฑ)
  • ์‚ญ์ œ: 2 โ†’ 1 โ†’ 0 ์ˆœ์„œ๋กœ ์—ญ์ˆœ ์‚ญ์ œ
  • ์—…๋ฐ์ดํŠธ: ๊ธฐ๋ณธ์ ์œผ๋กœ ์—ญ์ˆœ(๊ฐ€์žฅ ๋†’์€ ๋ฒˆํ˜ธ๋ถ€ํ„ฐ)์œผ๋กœ ๋กค๋ง ์—…๋ฐ์ดํŠธ

์ด ์ˆœ์„œ ๋ณด์žฅ ๋•๋ถ„์— ํด๋Ÿฌ์Šคํ„ฐ ๋ฉค๋ฒ„์‹ญ ๊ด€๋ฆฌ๊ฐ€ ์•ˆ์ „ํ•ด์ง„๋‹ค.

์•ˆ์ •์ ์ธ ์Šคํ† ๋ฆฌ์ง€ (PVC ๋ณด์žฅ)

StatefulSet์€ volumeClaimTemplates์„ ํ†ตํ•ด ํŒŒ๋“œ๋งˆ๋‹ค ๊ณ ์œ ํ•œ PVC(PersistentVolumeClaim)๋ฅผ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด ์ค€๋‹ค.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql:8.0
          volumeMounts:
            - name: data
              mountPath: /var/lib/mysql
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 10Gi

์œ„ ์„ค์ •์ด๋ฉด data-mysql-0, data-mysql-1, data-mysql-2 PVC๊ฐ€ ์ž๋™ ์ƒ์„ฑ๋œ๋‹ค. ํŒŒ๋“œ๊ฐ€ ์žฌ์Šค์ผ€์ค„๋˜์–ด๋„ ๊ฐ™์€ PVC์— ๋‹ค์‹œ ๋ถ™๋Š”๋‹ค. Deployment์—์„œ๋Š” ์ด๊ฒŒ ์•ˆ ๋œ๋‹ค.


๋‘ ๊ฐ€์ง€๋ฅผ ํ•œ๋ˆˆ์— ๋น„๊ต

ํ•ญ๋ชฉDeploymentStatefulSet
ํŒŒ๋“œ ์ด๋ฆ„๋žœ๋ค suffix์ˆœ์„œ ์žˆ๋Š” ๊ณ ์ • ์ด๋ฆ„ (-0, -1)
๋„คํŠธ์›Œํฌ IDํŒŒ๋“œ ์žฌ์‹œ์ž‘ ์‹œ ๋ณ€๊ฒฝ์žฌ์‹œ์ž‘ํ•ด๋„ ๋™์ผ
์Šคํ† ๋ฆฌ์ง€ํŒŒ๋“œ๋งˆ๋‹ค ๋ณด์žฅ ์—†์ŒํŒŒ๋“œ๋งˆ๋‹ค ์ „์šฉ PVC
๋ฐฐํฌ/์‚ญ์ œ ์ˆœ์„œ์ˆœ์„œ ์—†์ด ๋ณ‘๋ ฌ ๊ฐ€๋Šฅ์ˆœ์„œ ๋ณด์žฅ
์ฃผ์š” ์‚ฌ์šฉ์ฒ˜์›น์„œ๋ฒ„, API์„œ๋ฒ„DB, ๋ฉ”์‹œ์ง€ ํ, ZooKeeper

์‹ค์ œ๋กœ ์–ด๋–ค ์ƒํ™ฉ์—์„œ ์“ฐ๋‚˜

StatefulSet์„ ์จ์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ

  • MySQL, PostgreSQL Replication (Primary/Replica ๊ตฌ๋ถ„์ด ํ•„์š”)
  • Kafka, RabbitMQ (๋ธŒ๋กœ์ปค ID๊ฐ€ ๊ณ ์ •์ด์–ด์•ผ ํด๋Ÿฌ์Šคํ„ฐ๊ฐ€ ์œ ์ง€๋จ)
  • Elasticsearch, Cassandra (ํด๋Ÿฌ์Šคํ„ฐ ๋…ธ๋“œ ๊ฐ„ ํ†ต์‹  ์‹œ ์•ˆ์ •์ ์ธ ์ฃผ์†Œ ํ•„์š”)
  • ZooKeeper (์ฟผ๋Ÿผ ๊ตฌ์„ฑ์„ ์œ„ํ•ด ๊ฐ ๋…ธ๋“œ ์‹๋ณ„ ํ•„์ˆ˜)

Deployment๋กœ ์ถฉ๋ถ„ํ•œ ๊ฒฝ์šฐ

  • Nginx, Apache ๋“ฑ ์›น ์„œ๋ฒ„
  • ์™ธ๋ถ€ DB๋ฅผ ์“ฐ๋Š” REST API ์„œ๋ฒ„
  • ์Šคํ…Œ์ดํŠธ๋ฆฌ์Šค ์›Œ์ปค ํ”„๋กœ์„ธ์Šค

StatefulSet์˜ ์ฃผ์˜์ 

PVC๋Š” ์ž๋™์œผ๋กœ ์‚ญ์ œ๋˜์ง€ ์•Š๋Š”๋‹ค. StatefulSet์„ kubectl delete ํ•ด๋„ PVC๋Š” ๋‚จ๋Š”๋‹ค. ๋ฐ์ดํ„ฐ ๋ณดํ˜ธ๋ฅผ ์œ„ํ•œ ์˜๋„์ ์ธ ์„ค๊ณ„์ธ๋ฐ, ์ง์ ‘ PVC๋ฅผ ์‚ญ์ œํ•ด์•ผ ์™„์ „ํžˆ ์ •๋ฆฌ๋œ๋‹ค.

# StatefulSet ์‚ญ์ œ ํ›„์—๋„ PVC๊ฐ€ ๋‚จ์•„ ์žˆ์Œ
kubectl get pvc
# data-mysql-0   Bound   ...
# data-mysql-1   Bound   ...
# data-mysql-2   Bound   ...

# ์ง์ ‘ ์‚ญ์ œํ•ด์•ผ ํ•จ
kubectl delete pvc data-mysql-0 data-mysql-1 data-mysql-2

์Šค์ผ€์ผ ๋‹ค์šด ์‹œ ๋ฐ์ดํ„ฐ ์†์‹ค ์œ„ํ—˜

replicas๋ฅผ 3 โ†’ 1๋กœ ์ค„์ด๋ฉด mysql-1, mysql-2 ํŒŒ๋“œ๊ฐ€ ์‚ญ์ œ๋˜์ง€๋งŒ PVC๋Š” ๋‚จ๋Š”๋‹ค. ๋‹ค์‹œ ์Šค์ผ€์ผ ์—…ํ•˜๋ฉด ๊ฐ™์€ PVC์— ๋‹ค์‹œ ์—ฐ๊ฒฐ๋œ๋‹ค. ํ•˜์ง€๋งŒ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ์—์„œ ํด๋Ÿฌ์Šคํ„ฐ ๋ฉค๋ฒ„ ์ œ๊ฑฐ ์ฒ˜๋ฆฌ๋ฅผ ๋จผ์ € ํ•ด์ค˜์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์œผ๋‹ˆ ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค.


์ •๋ฆฌ

Deployment์™€ StatefulSet์˜ ๋ณธ์งˆ์ ์ธ ์ฐจ์ด๋Š” ํŒŒ๋“œ์˜ ์ •์ฒด์„ฑ(identity)์„ ๋ณด์žฅํ•˜๋А๋ƒ ์•„๋‹ˆ๋ƒ๋‹ค.

  • ์–ด๋–ค ํŒŒ๋“œ๊ฐ€ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•ด๋„ ์ƒ๊ด€์—†๋‹ค โ†’ Deployment
  • ๊ฐ ํŒŒ๋“œ๊ฐ€ ๊ณ ์œ ํ•œ ์—ญํ• ์„ ๋งก๊ณ , ์žฌ์‹œ์ž‘ํ•ด๋„ ๊ฐ™์€ ์ด๋ฆ„ยท๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•œ๋‹ค โ†’ StatefulSet

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์„ Kubernetes ์œ„์— ์˜ฌ๋ฆด ๋•Œ StatefulSet์„ ์ดํ•ดํ•˜์ง€ ์•Š์œผ๋ฉด ํด๋Ÿฌ์Šคํ„ฐ๊ฐ€ ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š” ์ƒํ™ฉ์„ ๋งŒ๋‚˜๊ฒŒ ๋œ๋‹ค. ๊ฐœ๋…์„ ์žก์•„๋‘๋ฉด Helm Chart๋ฅผ ์ฝ์„ ๋•Œ๋„ ์„ค๊ณ„ ์˜๋„๊ฐ€ ๋ฐ”๋กœ ๋ณด์ธ๋‹ค.