์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ Kubernetes StatefulSet์œผ๋กœ DB ํด๋Ÿฌ์Šคํ„ฐ ๊ด€๋ฆฌํ•˜๊ธฐ

๐Ÿš€ Kubernetes StatefulSet์œผ๋กœ DB ํด๋Ÿฌ์Šคํ„ฐ ๊ด€๋ฆฌํ•˜๊ธฐ

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์šด์˜ํ•˜๋Š” ์‹ค์ „ ๊ฐ€์ด๋“œ

๐Ÿ‘‹ ์•ˆ๋…•! ์˜ค๋Š˜์€ Kubernetes์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ํ•ต์‹ฌ ๋„๊ตฌ์ธ StatefulSet์— ๋Œ€ํ•ด ๊นŠ์ด ์žˆ๊ฒŒ ์•Œ์•„๋ณผ ๊ฑฐ์•ผ. ํด๋ผ์šฐ๋“œ ๋„ค์ดํ‹ฐ๋ธŒ ์‹œ๋Œ€์— DB๋ฅผ ์ปจํ…Œ์ด๋„ˆ๋กœ ์šด์˜ํ•˜๋Š” ๊ฑด ๋” ์ด์ƒ ์„ ํƒ์ด ์•„๋‹Œ ํ•„์ˆ˜๊ฐ€ ๋˜์–ด๊ฐ€๊ณ  ์žˆ์–ด. ํ•˜์ง€๋งŒ ์ผ๋ฐ˜์ ์ธ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๋‹ฌ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•˜๊ณ , ์ˆœ์„œ๊ฐ€ ์ค‘์š”ํ•˜๋ฉฐ, ๊ฐ ์ธ์Šคํ„ด์Šค๊ฐ€ ๊ณ ์œ ํ•œ ์ •์ฒด์„ฑ์„ ๊ฐ€์ ธ์•ผ ํ•˜์ง€. ๋ฐ”๋กœ ์ด๋Ÿฐ ์š”๊ตฌ์‚ฌํ•ญ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํƒ„์ƒํ•œ ๊ฒŒ StatefulSet์ด์•ผ! ๐Ÿ’ก

Kubernetes StatefulSet Architecture StatefulSet Controller Pod-0 Master DB Pod-1 Replica DB Pod-2 Replica DB PVC-0 Persistent Storage PVC-1 Persistent Storage PVC-2 Persistent Storage Key Features โœ“ Stable Network Identity โœ“ Ordered Deployment โœ“ Ordered Scaling โœ“ Persistent Storage โœ“ Stable DNS Names โœ“ Graceful Termination Replication Flow

๐ŸŽฏ StatefulSet์ด ๋ญ๊ธธ๋ž˜?

๋จผ์ € ๊ธฐ๋ณธ๋ถ€ํ„ฐ ์งš๊ณ  ๋„˜์–ด๊ฐ€์ž. Kubernetes์—๋Š” ์—ฌ๋Ÿฌ ์›Œํฌ๋กœ๋“œ ๋ฆฌ์†Œ์Šค๊ฐ€ ์žˆ์–ด. Deployment, ReplicaSet, DaemonSet ๋“ฑ๋“ฑ... ๊ทธ์ค‘์—์„œ๋„ StatefulSet์€ ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•ด ์„ค๊ณ„๋œ ํŠน๋ณ„ํ•œ ์ปจํŠธ๋กค๋Ÿฌ์•ผ. ๐ŸŽช

๐Ÿ” Deployment vs StatefulSet

์ผ๋ฐ˜์ ์ธ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ƒ๊ฐํ•ด๋ด. ํ”„๋ก ํŠธ์—”๋“œ ์„œ๋ฒ„ 3๋Œ€๊ฐ€ ์žˆ๋‹ค๋ฉด, ์ด ์„œ๋ฒ„๋“ค์€ ์„œ๋กœ ๊ตฌ๋ณ„ํ•  ํ•„์š”๊ฐ€ ์—†์–ด. ์–ด๋–ค ์„œ๋ฒ„๋กœ ์š”์ฒญ์ด ๊ฐ€๋“  ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ๋‚ด๋†“์œผ๋‹ˆ๊นŒ. ์ด๋Ÿฐ ๊ฒฝ์šฐ์—” Deployment๊ฐ€ ์™„๋ฒฝํ•ด.


ํ•˜์ง€๋งŒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ๋‹ฌ๋ผ! MySQL ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ์˜ˆ๋กœ ๋“ค๋ฉด:
โ€ข Master DB๋Š” ์“ฐ๊ธฐ ์ž‘์—…์„ ๋‹ด๋‹น
โ€ข Replica DB๋“ค์€ ์ฝ๊ธฐ ์ž‘์—…์„ ๋‹ด๋‹น
โ€ข ๊ฐ DB ์ธ์Šคํ„ด์Šค๋Š” ๊ณ ์œ ํ•œ ๋ฐ์ดํ„ฐ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์•ผ ํ•จ
โ€ข ๋ณต์ œ ์ˆœ์„œ๊ฐ€ ์ค‘์š”ํ•จ (Master โ†’ Replica1 โ†’ Replica2)


์ด๋Ÿฐ ์š”๊ตฌ์‚ฌํ•ญ์„ ์ถฉ์กฑ์‹œํ‚ค๋ ค๋ฉด StatefulSet์ด ํ•„์š”ํ•ด! โœจ

๐Ÿ’ก ํ•ต์‹ฌ ํฌ์ธํŠธ
StatefulSet์€ ๊ฐ Pod์—๊ฒŒ ์•ˆ์ •์ ์ด๊ณ  ๊ณ ์œ ํ•œ ๋„คํŠธ์›Œํฌ ์‹๋ณ„์ž์™€ ์˜๊ตฌ ์Šคํ† ๋ฆฌ์ง€๋ฅผ ์ œ๊ณตํ•ด. ์˜ˆ๋ฅผ ๋“ค์–ด mysql-0, mysql-1, mysql-2 ๊ฐ™์€ ์‹์œผ๋กœ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋˜๊ณ , ์žฌ์‹œ์ž‘ํ•ด๋„ ๊ฐ™์€ ์ด๋ฆ„๊ณผ ์Šคํ† ๋ฆฌ์ง€๋ฅผ ์œ ์ง€ํ•˜์ง€.

๐Ÿ—๏ธ StatefulSet์˜ ํ•ต์‹ฌ ํŠน์ง•๋“ค

StatefulSet์ด ์ œ๊ณตํ•˜๋Š” ํŠน๋ณ„ํ•œ ๊ธฐ๋Šฅ๋“ค์„ ํ•˜๋‚˜์”ฉ ์‚ดํŽด๋ณด์ž. ์ด ํŠน์ง•๋“ค์ด ์™œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํด๋Ÿฌ์Šคํ„ฐ ๊ด€๋ฆฌ์— ํ•„์ˆ˜์ ์ธ์ง€ ์ดํ•ดํ•˜๋ฉด, ์‹ค์ „์—์„œ ํ›จ์”ฌ ํšจ๊ณผ์ ์œผ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด. ๐Ÿš€

1๏ธโƒฃ ์•ˆ์ •์ ์ธ ๋„คํŠธ์›Œํฌ ์‹๋ณ„์ž (Stable Network Identity)

StatefulSet์œผ๋กœ ์ƒ์„ฑ๋œ ๊ฐ Pod๋Š” {statefulset-name}-{ordinal} ํ˜•์‹์˜ ์ด๋ฆ„์„ ๊ฐ€์ ธ. ์˜ˆ๋ฅผ ๋“ค์–ด:


mysql-0
mysql-1
mysql-2

์ด ์ด๋ฆ„๋“ค์€ Pod๊ฐ€ ์žฌ์‹œ์ž‘๋˜๊ฑฐ๋‚˜ ์žฌ์Šค์ผ€์ค„๋ง๋˜์–ด๋„ ์ ˆ๋Œ€ ๋ฐ”๋€Œ์ง€ ์•Š์•„. ๊ทธ๋ฆฌ๊ณ  ๊ฐ Pod๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ DNS ์ด๋ฆ„์„ ์ž๋™์œผ๋กœ ์–ป๊ฒŒ ๋ผ:


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

์˜ˆ์‹œ: mysql-0.mysql-service.default.svc.cluster.local


์ด๊ฒŒ ์™œ ์ค‘์š”ํ•˜๋ƒ๊ณ ? ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ณต์ œ ์„ค์ •์„ ์ƒ๊ฐํ•ด๋ด. Master DB์˜ ์ฃผ์†Œ๋ฅผ Replica์— ์„ค์ •ํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์ด ์ฃผ์†Œ๊ฐ€ ๊ณ„์† ๋ฐ”๋€Œ๋ฉด ๋ณต์ œ๊ฐ€ ๊นจ์ง€์ž–์•„! StatefulSet์€ ์ด๋Ÿฐ ๋ฌธ์ œ๋ฅผ ์›์ฒœ์ ์œผ๋กœ ํ•ด๊ฒฐํ•ด์ค˜. ๐ŸŽฏ

2๏ธโƒฃ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋˜๋Š” ๋ฐฐํฌ์™€ ์Šค์ผ€์ผ๋ง

StatefulSet์€ Pod๋ฅผ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•  ๋•Œ ์ˆœ์„œ๋ฅผ ์—„๊ฒฉํ•˜๊ฒŒ ์ง€์ผœ. ์ด๊ฒŒ DB ํด๋Ÿฌ์Šคํ„ฐ์—์„œ ์–ผ๋งˆ๋‚˜ ์ค‘์š”ํ•œ์ง€ ์•Œ์•„๋ณด์ž:

๐Ÿ“Š ๋ฐฐํฌ ์ˆœ์„œ (Scale Up)
โ€ข mysql-0์ด ์™„์ „ํžˆ Ready ์ƒํƒœ๊ฐ€ ๋˜๋ฉด
โ€ข mysql-1์„ ์ƒ์„ฑํ•˜๊ณ , Ready๊ฐ€ ๋˜๋ฉด
โ€ข mysql-2๋ฅผ ์ƒ์„ฑ

์ด๋ ‡๊ฒŒ ํ•˜๋‚˜์”ฉ ์ˆœ์ฐจ์ ์œผ๋กœ ์ƒ์„ฑ๋ผ. Master DB๊ฐ€ ๋จผ์ € ์ค€๋น„๋˜๊ณ , ๊ทธ ๋‹ค์Œ์— Replica๋“ค์ด ์ˆœ์„œ๋Œ€๋กœ Master์— ์—ฐ๊ฒฐ๋˜๋Š” ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์ง€!
โš ๏ธ ์ถ•์†Œ ์ˆœ์„œ (Scale Down)
๋ฐ˜๋Œ€๋กœ ์Šค์ผ€์ผ์„ ์ค„์ผ ๋•Œ๋Š” ์—ญ์ˆœ์œผ๋กœ ์ง„ํ–‰๋ผ:
โ€ข mysql-2๋ฅผ ๋จผ์ € ์‚ญ์ œํ•˜๊ณ 
โ€ข mysql-1์„ ์‚ญ์ œํ•˜๊ณ 
โ€ข ๋งˆ์ง€๋ง‰์œผ๋กœ mysql-0์„ ์‚ญ์ œ

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด Master DB๋ฅผ ๋งˆ์ง€๋ง‰๊นŒ์ง€ ๋ณดํ˜ธํ•  ์ˆ˜ ์žˆ์–ด. ๋ฐ์ดํ„ฐ ์†์‹ค ์œ„ํ—˜์„ ์ตœ์†Œํ™”ํ•˜๋Š” ๊ฑฐ์ง€!

3๏ธโƒฃ ์˜๊ตฌ ๋ณผ๋ฅจ ํด๋ ˆ์ž„ (Persistent Volume Claims)

์ด๊ฒŒ ์ง„์งœ ํ•ต์‹ฌ์ด์•ผ! StatefulSet์€ ๊ฐ Pod๋งˆ๋‹ค ๋…๋ฆฝ์ ์ธ PVC(Persistent Volume Claim)๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ด์ค˜. ๐Ÿ’พ

volumeClaimTemplates๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด:


volumeClaimTemplates:
- metadata:
    name: data
  spec:
    accessModes: [ "ReadWriteOnce" ]
    resources:
      requests:
        storage: 100Gi

์ด๋ ‡๊ฒŒ ์„ค์ •ํ•˜๋ฉด ๊ฐ Pod๋งˆ๋‹ค:


โ€ข mysql-0 โ†’ data-mysql-0 (100GB)
โ€ข mysql-1 โ†’ data-mysql-1 (100GB)
โ€ข mysql-2 โ†’ data-mysql-2 (100GB)


์ด๋Ÿฐ ์‹์œผ๋กœ ๋…๋ฆฝ์ ์ธ ์Šคํ† ๋ฆฌ์ง€๊ฐ€ ํ• ๋‹น๋ผ. ๊ทธ๋ฆฌ๊ณ  Pod๊ฐ€ ์‚ญ์ œ๋˜์–ด๋„ PVC๋Š” ๋‚จ์•„์žˆ์–ด์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณด์กด๋˜์ง€! ๐Ÿ›ก๏ธ

๐Ÿ’ก ์‹ค์ „ ํŒ
PVC๋Š” Pod๊ฐ€ ์‚ญ์ œ๋˜์–ด๋„ ์ž๋™์œผ๋กœ ์‚ญ์ œ๋˜์ง€ ์•Š์•„. ์ด๊ฑด ๋ฐ์ดํ„ฐ ๋ณดํ˜ธ๋ฅผ ์œ„ํ•œ ์˜๋„์ ์ธ ์„ค๊ณ„์•ผ. ์Šคํ† ๋ฆฌ์ง€๋ฅผ ์ •๋ฆฌํ•˜๋ ค๋ฉด ์ˆ˜๋™์œผ๋กœ PVC๋ฅผ ์‚ญ์ œํ•ด์•ผ ํ•ด. ์ด ์  ๊ผญ ๊ธฐ์–ตํ•ด๋‘ฌ!

4๏ธโƒฃ ์—…๋ฐ์ดํŠธ ์ „๋žต (Update Strategy)

StatefulSet์€ ๋‘ ๊ฐ€์ง€ ์—…๋ฐ์ดํŠธ ์ „๋žต์„ ์ œ๊ณตํ•ด:

์ „๋žต ์„ค๋ช… ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค
RollingUpdate ์—ญ์ˆœ์œผ๋กœ ํ•˜๋‚˜์”ฉ ์—…๋ฐ์ดํŠธ (๊ธฐ๋ณธ๊ฐ’) ์ผ๋ฐ˜์ ์ธ ์—…๋ฐ์ดํŠธ, ์ž๋™ํ™”๋œ ๋ฐฐํฌ
OnDelete ์ˆ˜๋™์œผ๋กœ Pod๋ฅผ ์‚ญ์ œํ•ด์•ผ ์—…๋ฐ์ดํŠธ ์‹ ์ค‘ํ•œ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•œ ํ”„๋กœ๋•์…˜ DB

RollingUpdate๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด mysql-2๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ mysql-0๊นŒ์ง€ ์ˆœ์ฐจ์ ์œผ๋กœ ์—…๋ฐ์ดํŠธ๋ผ. ๊ฐ Pod๊ฐ€ Ready ์ƒํƒœ๊ฐ€ ๋˜๋ฉด ๋‹ค์Œ Pod๋กœ ๋„˜์–ด๊ฐ€๋Š” ๋ฐฉ์‹์ด์•ผ. ๐Ÿ”„

๐Ÿ› ๏ธ ์‹ค์ „! MySQL ํด๋Ÿฌ์Šคํ„ฐ ๊ตฌ์ถ•ํ•˜๊ธฐ

์ด์ œ ์ด๋ก ์€ ์ถฉ๋ถ„ํžˆ ๋ฐฐ์› ์œผ๋‹ˆ, ์‹ค์ œ๋กœ MySQL ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ StatefulSet์œผ๋กœ ๊ตฌ์ถ•ํ•ด๋ณด์ž! ๋‹จ๊ณ„๋ณ„๋กœ ์ฐจ๊ทผ์ฐจ๊ทผ ๋”ฐ๋ผ์˜ค๋ฉด ๋ผ. ๐Ÿ‘จโ€๐Ÿ’ป

๐Ÿ“‹ Step 1: ConfigMap ์ค€๋น„

๋จผ์ € MySQL ์„ค์ • ํŒŒ์ผ์„ ConfigMap์œผ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•ด. Master์™€ Replica์˜ ์„ค์ •์ด ๋‹ฌ๋ผ์•ผ ํ•˜๋‹ˆ๊นŒ ๋‘ ๊ฐœ๋ฅผ ์ค€๋น„ํ•˜์ž:

apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-config
  namespace: database
data:
  master.cnf: |
    [mysqld]
    log-bin=mysql-bin
    server-id=1
    binlog-format=ROW
    
  slave.cnf: |
    [mysqld]
    server-id=2
    relay-log=relay-bin
    read-only=1

์—ฌ๊ธฐ์„œ log-bin์€ ๋ฐ”์ด๋„ˆ๋ฆฌ ๋กœ๊ทธ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋Š” ์„ค์ •์ด๊ณ , server-id๋Š” ๊ฐ MySQL ์ธ์Šคํ„ด์Šค๋ฅผ ๊ตฌ๋ณ„ํ•˜๋Š” ๊ณ ์œ  ID์•ผ. Master๋Š” ์“ฐ๊ธฐ๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ณ , Slave๋Š” read-only=1๋กœ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ์„ค์ •ํ–ˆ์–ด. โš™๏ธ

๐Ÿ” Step 2: Secret ์ƒ์„ฑ

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์ ˆ๋Œ€ ํ‰๋ฌธ์œผ๋กœ ์ €์žฅํ•˜๋ฉด ์•ˆ ๋ผ! Secret์„ ์‚ฌ์šฉํ•˜์ž:

apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
  namespace: database
type: Opaque
data:
  root-password: bXlzcWxyb290cGFzc3dvcmQ=  # base64 encoded
  replication-password: cmVwbGljYXRpb25wYXNz  # base64 encoded

๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ base64๋กœ ์ธ์ฝ”๋”ฉํ•˜๋ ค๋ฉด ํ„ฐ๋ฏธ๋„์—์„œ:

echo -n "mysqlrootpassword" | base64

์ด๋ ‡๊ฒŒ ์‹คํ–‰ํ•˜๋ฉด ๋ผ. ๐Ÿ”’

๐ŸŒ Step 3: Headless Service ์ƒ์„ฑ

StatefulSet์€ Headless Service์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผ ํ•ด. ์ด๊ฒŒ ๊ฐ Pod์— ์•ˆ์ •์ ์ธ DNS ์ด๋ฆ„์„ ์ œ๊ณตํ•˜๊ฑฐ๋“ :

apiVersion: v1
kind: Service
metadata:
  name: mysql-headless
  namespace: database
spec:
  clusterIP: None  # Headless Service์˜ ํ•ต์‹ฌ!
  selector:
    app: mysql
  ports:
  - name: mysql
    port: 3306
    targetPort: 3306

clusterIP: None์ด ํ•ต์‹ฌ์ด์•ผ. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด Service๊ฐ€ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ์„ ํ•˜์ง€ ์•Š๊ณ , ๊ฐ Pod์˜ IP๋ฅผ ์ง์ ‘ DNS๋กœ ๋ฐ˜ํ™˜ํ•ด์ค˜. ๊ทธ๋ž˜์„œ mysql-0.mysql-headless.database.svc.cluster.local ๊ฐ™์€ ์ฃผ์†Œ๋กœ ํŠน์ • Pod์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์ง€! ๐ŸŽฏ

๐Ÿ’ก Headless Service๊ฐ€ ๋ญ์•ผ?
์ผ๋ฐ˜ Service๋Š” ์—ฌ๋Ÿฌ Pod๋ฅผ ๋ฌถ์–ด์„œ ํ•˜๋‚˜์˜ ๊ฐ€์ƒ IP(ClusterIP)๋ฅผ ์ œ๊ณตํ•ด. ํ•˜์ง€๋งŒ Headless Service๋Š” ClusterIP๋ฅผ None์œผ๋กœ ์„ค์ •ํ•ด์„œ, DNS ์ฟผ๋ฆฌ ์‹œ ๊ฐ Pod์˜ ์‹ค์ œ IP ๋ชฉ๋ก์„ ๋ฐ˜ํ™˜ํ•ด. ์ด๊ฒŒ StatefulSet๊ณผ ์™„๋ฒฝํ•˜๊ฒŒ ์–ด์šธ๋ฆฌ๋Š” ์ด์œ ์ง€!

๐Ÿš€ Step 4: StatefulSet ์ •์˜

๋“œ๋””์–ด ๋ฉ”์ธ ์ด๋ฒคํŠธ! StatefulSet์„ ์ •์˜ํ•ด๋ณด์ž. ์ด๊ฒŒ ์ข€ ๊ธธ๊ธด ํ•œ๋ฐ, ํ•˜๋‚˜์”ฉ ๋œฏ์–ด๋ณด๋ฉด ์–ด๋ ต์ง€ ์•Š์•„:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
  namespace: database
spec:
  serviceName: mysql-headless
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      initContainers:
      - name: init-mysql
        image: mysql:8.0
        command:
        - bash
        - "-c"
        - |
          set -ex
          # Pod ์ˆœ์„œ ๋ฒˆํ˜ธ ์ถ”์ถœ
          [[ $(hostname) =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          
          # Master(0๋ฒˆ)์™€ Slave ๊ตฌ๋ถ„
          if [[ $ordinal -eq 0 ]]; then
            cp /mnt/config-map/master.cnf /mnt/conf.d/
          else
            cp /mnt/config-map/slave.cnf /mnt/conf.d/
            # Slave์˜ server-id๋Š” ์ˆœ์„œ๋ฒˆํ˜ธ + 100
            sed -i "s/server-id=2/server-id=$((100 + $ordinal))/" /mnt/conf.d/slave.cnf
          fi
        volumeMounts:
        - name: conf
          mountPath: /mnt/conf.d
        - name: config-map
          mountPath: /mnt/config-map
          
      - name: clone-mysql
        image: gcr.io/google-samples/xtrabackup:1.0
        command:
        - bash
        - "-c"
        - |
          set -ex
          [[ $(hostname) =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          
          # Master๋Š” ๋ณต์ œํ•  ํ•„์š” ์—†์Œ
          [[ $ordinal -eq 0 ]] && exit 0
          
          # ์ด์ „ Pod์—์„œ ๋ฐ์ดํ„ฐ ๋ณต์ œ
          ncat --recv-only mysql-$(($ordinal-1)).mysql-headless 3307 | xbstream -x -C /var/lib/mysql
          xtrabackup --prepare --target-dir=/var/lib/mysql
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
          
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: root-password
        ports:
        - name: mysql
          containerPort: 3306
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 500m
            memory: 1Gi
          limits:
            cpu: 1000m
            memory: 2Gi
        livenessProbe:
          exec:
            command: ["mysqladmin", "ping"]
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
        readinessProbe:
          exec:
            command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
          initialDelaySeconds: 5
          periodSeconds: 2
          timeoutSeconds: 1
          
      - name: xtrabackup
        image: gcr.io/google-samples/xtrabackup:1.0
        ports:
        - name: xtrabackup
          containerPort: 3307
        command:
        - bash
        - "-c"
        - |
          set -ex
          cd /var/lib/mysql
          
          # ๋ณต์ œ ์š”์ฒญ ๋Œ€๊ธฐ
          if [[ -f xtrabackup_slave_info ]]; then
            mv xtrabackup_slave_info change_master_to.sql.in
            rm -f xtrabackup_binlog_info
          elif [[ -f xtrabackup_binlog_info ]]; then
            [[ $(cat xtrabackup_binlog_info) =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
            echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
                  MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in
          fi
          
          # ๋ณต์ œ ๋ฐ์ดํ„ฐ ์ „์†ก
          exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \
            "xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root"
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
          
      volumes:
      - name: conf
        emptyDir: {}
      - name: config-map
        configMap:
          name: mysql-config
          
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: standard
      resources:
        requests:
          storage: 100Gi

์™€, ์ฝ”๋“œ๊ฐ€ ์—„์ฒญ ๊ธธ์ง€? ํ•˜๋‚˜์”ฉ ๋œฏ์–ด๋ณด์ž! ๐Ÿ”ฌ

๐Ÿ“Œ ํ•ต์‹ฌ ๊ตฌ์„ฑ ์š”์†Œ ์„ค๋ช…

1. serviceName
serviceName: mysql-headless๋กœ Headless Service์™€ ์—ฐ๊ฒฐํ•ด. ์ด๊ฒŒ ๊ฐ Pod์— ์•ˆ์ •์ ์ธ DNS ์ด๋ฆ„์„ ์ œ๊ณตํ•˜๋Š” ๊ฑฐ์•ผ.

2. initContainers
๋ฉ”์ธ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์‹œ์ž‘๋˜๊ธฐ ์ „์— ์‹คํ–‰๋˜๋Š” ์ดˆ๊ธฐํ™” ์ปจํ…Œ์ด๋„ˆ๋“ค์ด์•ผ:
โ€ข init-mysql: Pod ์ˆœ์„œ์— ๋”ฐ๋ผ Master/Slave ์„ค์ • ํŒŒ์ผ์„ ๋ณต์‚ฌ
โ€ข clone-mysql: ์ด์ „ Pod์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณต์ œ (Slave๋งŒ ํ•ด๋‹น)

3. containers
โ€ข mysql: ์‹ค์ œ MySQL ์„œ๋ฒ„
โ€ข xtrabackup: ๋ฐ์ดํ„ฐ ๋ณต์ œ๋ฅผ ์œ„ํ•œ ์‚ฌ์ด๋“œ์นด ์ปจํ…Œ์ด๋„ˆ

4. volumeClaimTemplates
๊ฐ Pod๋งˆ๋‹ค 100GB ์Šคํ† ๋ฆฌ์ง€๋ฅผ ์ž๋™์œผ๋กœ ํ”„๋กœ๋น„์ €๋‹ํ•ด์ค˜!

๐ŸŽฌ Step 5: ๋ฐฐํฌ ๋ฐ ํ™•์ธ

์ด์ œ ์‹ค์ œ๋กœ ๋ฐฐํฌํ•ด๋ณด์ž:

# ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์ƒ์„ฑ
kubectl create namespace database

# ConfigMap, Secret ์ ์šฉ
kubectl apply -f mysql-configmap.yaml
kubectl apply -f mysql-secret.yaml

# Service ์ƒ์„ฑ
kubectl apply -f mysql-service.yaml

# StatefulSet ๋ฐฐํฌ
kubectl apply -f mysql-statefulset.yaml

# ๋ฐฐํฌ ์ƒํƒœ ํ™•์ธ
kubectl get statefulset -n database
kubectl get pods -n database -w

Pod๊ฐ€ ์ˆœ์ฐจ์ ์œผ๋กœ ์ƒ์„ฑ๋˜๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ:
mysql-0 โ†’ Running โ†’ Ready
mysql-1 โ†’ Running โ†’ Ready
mysql-2 โ†’ Running โ†’ Ready


๊ฐ ๋‹จ๊ณ„๊ฐ€ ์™„๋ฃŒ๋˜์–ด์•ผ ๋‹ค์Œ Pod๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ๊ฑฐ ๋ณด์ด์ง€? ์ด๊ฒŒ ๋ฐ”๋กœ StatefulSet์˜ ์ˆœ์ฐจ ๋ฐฐํฌ์•ผ! ๐ŸŽฏ

๐Ÿ’ก ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ํŒ
Pod๊ฐ€ Pending ์ƒํƒœ์— ๋จธ๋ฌผ๋Ÿฌ ์žˆ๋‹ค๋ฉด:
kubectl describe pod mysql-0 -n database
์ด ๋ช…๋ น์œผ๋กœ ์ด๋ฒคํŠธ๋ฅผ ํ™•์ธํ•ด๋ด. ๋Œ€๋ถ€๋ถ„ PVC ๋ฐ”์ธ๋”ฉ ๋ฌธ์ œ๊ฑฐ๋‚˜ ๋ฆฌ์†Œ์Šค ๋ถ€์กฑ ๋ฌธ์ œ์•ผ.

๐Ÿ”ง ๋ณต์ œ ์„ค์ • ๋ฐ ๊ฒ€์ฆ

StatefulSet์œผ๋กœ Pod๋Š” ๋งŒ๋“ค์—ˆ์ง€๋งŒ, MySQL ๋ณต์ œ๋Š” ์•„์ง ์„ค์ •ํ•˜์ง€ ์•Š์•˜์–ด. ์ด์ œ Master-Slave ๋ณต์ œ๋ฅผ ๊ตฌ์„ฑํ•ด๋ณด์ž! ๐Ÿ”—

๐Ÿ‘‘ Master ์„ค์ •

๋จผ์ € Master(mysql-0)์— ๋ณต์ œ์šฉ ์‚ฌ์šฉ์ž๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•ด:

# Master Pod์— ์ ‘์†
kubectl exec -it mysql-0 -n database -- mysql -uroot -p

# ๋ณต์ œ ์‚ฌ์šฉ์ž ์ƒ์„ฑ
CREATE USER 'replicator'@'%' IDENTIFIED BY 'replication_password';
GRANT REPLICATION SLAVE ON *.* TO 'replicator'@'%';
FLUSH PRIVILEGES;

# Master ์ƒํƒœ ํ™•์ธ
SHOW MASTER STATUS;

์ถœ๋ ฅ ๊ฒฐ๊ณผ์—์„œ File๊ณผ Position ๊ฐ’์„ ๊ธฐ์–ตํ•ด๋‘ฌ. ์ด๊ฒŒ ๋ณต์ œ ์‹œ์ž‘์ ์ด์•ผ! ๐Ÿ“

๐Ÿ‘ฅ Slave ์„ค์ •

์ด์ œ ๊ฐ Slave(mysql-1, mysql-2)๋ฅผ Master์— ์—ฐ๊ฒฐํ•˜์ž:

# Slave Pod์— ์ ‘์† (mysql-1)
kubectl exec -it mysql-1 -n database -- mysql -uroot -p

# Master ์—ฐ๊ฒฐ ์„ค์ •
CHANGE MASTER TO
  MASTER_HOST='mysql-0.mysql-headless.database.svc.cluster.local',
  MASTER_USER='replicator',
  MASTER_PASSWORD='replication_password',
  MASTER_LOG_FILE='mysql-bin.000001',  # SHOW MASTER STATUS์—์„œ ํ™•์ธํ•œ ๊ฐ’
  MASTER_LOG_POS=154;                   # SHOW MASTER STATUS์—์„œ ํ™•์ธํ•œ ๊ฐ’

# ๋ณต์ œ ์‹œ์ž‘
START SLAVE;

# ๋ณต์ œ ์ƒํƒœ ํ™•์ธ
SHOW SLAVE STATUS\G

Slave_IO_Running: Yes์™€ Slave_SQL_Running: Yes๊ฐ€ ๋ณด์ด๋ฉด ์„ฑ๊ณต์ด์•ผ! โœ…


mysql-2๋„ ๋˜‘๊ฐ™์ด ์„ค์ •ํ•ด์ฃผ๋ฉด ๋ผ. ์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ๊ฑด Master ํ˜ธ์ŠคํŠธ๋ฅผ mysql-0.mysql-headless.database.svc.cluster.local๋กœ ์ง€์ •ํ–ˆ๋‹ค๋Š” ๊ฑฐ์•ผ. StatefulSet ๋•๋ถ„์— ์ด DNS ์ด๋ฆ„์ด ์ ˆ๋Œ€ ๋ฐ”๋€Œ์ง€ ์•Š๊ฑฐ๋“ ! ๐ŸŽฏ

๐Ÿงช ๋ณต์ œ ํ…Œ์ŠคํŠธ

์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ•ด๋ณด์ž:

# Master์—์„œ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
kubectl exec -it mysql-0 -n database -- mysql -uroot -p

CREATE DATABASE testdb;
USE testdb;
CREATE TABLE users (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(100),
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO users (name) VALUES ('Alice'), ('Bob'), ('Charlie');

# Slave์—์„œ ํ™•์ธ (mysql-1)
kubectl exec -it mysql-1 -n database -- mysql -uroot -p

USE testdb;
SELECT * FROM users;

Master์—์„œ ์ž…๋ ฅํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ Slave์—๋„ ๋˜‘๊ฐ™์ด ๋ณด์ด๋ฉด ๋ณต์ œ๊ฐ€ ์ •์ƒ ์ž‘๋™ํ•˜๋Š” ๊ฑฐ์•ผ! ๐ŸŽ‰

๐ŸŽŠ ์ถ•ํ•˜ํ•ด!
์ด์ œ ์™„์ „ํžˆ ์ž‘๋™ํ•˜๋Š” MySQL ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ Kubernetes์—์„œ ์šด์˜ํ•˜๊ณ  ์žˆ์–ด. Master 1๋Œ€์™€ Replica 2๋Œ€๋กœ ๊ตฌ์„ฑ๋œ ๊ณ ๊ฐ€์šฉ์„ฑ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œ์Šคํ…œ์ด์ง€. ์ฝ๊ธฐ ๋ถ€ํ•˜๋ฅผ Replica๋“ค์— ๋ถ„์‚ฐ์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ณ , Master์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋ฉด Replica ์ค‘ ํ•˜๋‚˜๋ฅผ ์Šน๊ฒฉ์‹œํ‚ฌ ์ˆ˜๋„ ์žˆ์–ด!

๐Ÿ“Š ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ๊ด€๋ฆฌ

ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ๋งŒ๋“ค์—ˆ์œผ๋ฉด ์ด์ œ ์ œ๋Œ€๋กœ ๊ด€๋ฆฌํ•ด์•ผ์ง€! ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ์ผ์ƒ์ ์ธ ๊ด€๋ฆฌ ์ž‘์—…๋“ค์„ ์•Œ์•„๋ณด์ž. ๐Ÿ‘€

๐Ÿ“ˆ ์ƒํƒœ ๋ชจ๋‹ˆํ„ฐ๋ง

StatefulSet์˜ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๋Š” ์—ฌ๋Ÿฌ ๋ฐฉ๋ฒ•๋“ค:

# StatefulSet ์ „์ฒด ์ƒํƒœ
kubectl get statefulset mysql -n database

# ์ถœ๋ ฅ ์˜ˆ์‹œ:
# NAME    READY   AGE
# mysql   3/3     2h

# ๊ฐ Pod ์ƒ์„ธ ์ •๋ณด
kubectl get pods -n database -l app=mysql -o wide

# ํŠน์ • Pod์˜ ๋กœ๊ทธ ํ™•์ธ
kubectl logs mysql-0 -n database -c mysql --tail=100

# ๋ณต์ œ ์ง€์—ฐ ํ™•์ธ (Slave์—์„œ)
kubectl exec mysql-1 -n database -- mysql -uroot -p -e "SHOW SLAVE STATUS\G" | grep Seconds_Behind_Master

Seconds_Behind_Master๊ฐ€ 0์ด๋ฉด ๋ณต์ œ๊ฐ€ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ž˜ ๋˜๊ณ  ์žˆ๋Š” ๊ฑฐ์•ผ. ์ˆซ์ž๊ฐ€ ํฌ๋ฉด ๋ณต์ œ ์ง€์—ฐ์ด ๋ฐœ์ƒํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๋œป์ด์ง€. โฑ๏ธ

๐Ÿ” Prometheus์™€ Grafana ์—ฐ๋™

ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” ์ œ๋Œ€๋กœ ๋œ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ์ด ํ•„์ˆ˜์•ผ. MySQL Exporter๋ฅผ ์‚ฌ์ด๋“œ์นด๋กœ ์ถ”๊ฐ€ํ•ด๋ณด์ž:

StatefulSet์˜ containers ์„น์…˜์— ์ถ”๊ฐ€:

- name: mysql-exporter
  image: prom/mysqld-exporter:latest
  env:
  - name: DATA_SOURCE_NAME
    value: "root:$(MYSQL_ROOT_PASSWORD)@(localhost:3306)/"
  ports:
  - name: metrics
    containerPort: 9104
  resources:
    requests:
      cpu: 100m
      memory: 128Mi
    limits:
      cpu: 200m
      memory: 256Mi

๊ทธ๋ฆฌ๊ณ  Service์— metrics ํฌํŠธ ์ถ”๊ฐ€:

- name: metrics
  port: 9104
  targetPort: 9104

์ด์ œ Prometheus๊ฐ€ ๊ฐ MySQL ์ธ์Šคํ„ด์Šค์˜ ๋ฉ”ํŠธ๋ฆญ์„ ์ˆ˜์ง‘ํ•  ์ˆ˜ ์žˆ์–ด. Grafana ๋Œ€์‹œ๋ณด๋“œ๋กœ ์‹œ๊ฐํ™”ํ•˜๋ฉด ์™„๋ฒฝํ•˜์ง€! ๐Ÿ“Š

๐Ÿ’ก ๋ชจ๋‹ˆํ„ฐ๋ง ํ•ต์‹ฌ ์ง€ํ‘œ
โ€ข Connections: ํ˜„์žฌ ์—ฐ๊ฒฐ ์ˆ˜์™€ ์ตœ๋Œ€ ์—ฐ๊ฒฐ ์ˆ˜
โ€ข Queries per second: ์ดˆ๋‹น ์ฟผ๋ฆฌ ์ˆ˜
โ€ข Replication lag: ๋ณต์ œ ์ง€์—ฐ ์‹œ๊ฐ„
โ€ข Buffer pool usage: InnoDB ๋ฒ„ํผ ํ’€ ์‚ฌ์šฉ๋ฅ 
โ€ข Slow queries: ๋А๋ฆฐ ์ฟผ๋ฆฌ ๊ฐœ์ˆ˜

์ด ์ง€ํ‘œ๋“ค์„ ์ฃผ์‹œํ•˜๋ฉด ๋ฌธ์ œ๋ฅผ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์–ด!

๐Ÿ’พ ๋ฐฑ์—… ์ „๋žต

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ฐฑ์—…์€ ์ƒ๋ช…์ค„์ด์•ผ. CronJob์„ ์‚ฌ์šฉํ•ด์„œ ์ž๋™ ๋ฐฑ์—…์„ ์„ค์ •ํ•ด๋ณด์ž:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: mysql-backup
  namespace: database
spec:
  schedule: "0 2 * * *"  # ๋งค์ผ ์ƒˆ๋ฒฝ 2์‹œ
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: mysql:8.0
            command:
            - /bin/bash
            - -c
            - |
              BACKUP_DATE=$(date +%Y%m%d_%H%M%S)
              mysqldump -h mysql-0.mysql-headless \
                -uroot -p${MYSQL_ROOT_PASSWORD} \
                --all-databases \
                --single-transaction \
                --quick \
                --lock-tables=false \
                > /backup/mysql_backup_${BACKUP_DATE}.sql
              
              # S3๋‚˜ ๋‹ค๋ฅธ ์Šคํ† ๋ฆฌ์ง€๋กœ ์—…๋กœ๋“œ
              aws s3 cp /backup/mysql_backup_${BACKUP_DATE}.sql \
                s3://my-backup-bucket/mysql/
              
              # 30์ผ ์ด์ƒ ๋œ ๋ฐฑ์—… ์‚ญ์ œ
              find /backup -name "mysql_backup_*.sql" -mtime +30 -delete
            env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: root-password
            - name: AWS_ACCESS_KEY_ID
              valueFrom:
                secretKeyRef:
                  name: aws-credentials
                  key: access-key
            - name: AWS_SECRET_ACCESS_KEY
              valueFrom:
                secretKeyRef:
                  name: aws-credentials
                  key: secret-key
            volumeMounts:
            - name: backup
              mountPath: /backup
          restartPolicy: OnFailure
          volumes:
          - name: backup
            persistentVolumeClaim:
              claimName: mysql-backup-pvc

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋งค์ผ ์ž๋™์œผ๋กœ ๋ฐฑ์—…์ด ์ƒ์„ฑ๋˜๊ณ  S3์— ์ €์žฅ๋ผ. 30์ผ ์ด์ƒ ๋œ ๋ฐฑ์—…์€ ์ž๋™์œผ๋กœ ์‚ญ์ œ๋˜๋‹ˆ๊นŒ ์Šคํ† ๋ฆฌ์ง€ ๊ด€๋ฆฌ๋„ ํŽธํ•ด! ๐Ÿ—„๏ธ

โšก ์Šค์ผ€์ผ๋ง

ํŠธ๋ž˜ํ”ฝ์ด ์ฆ๊ฐ€ํ•ด์„œ Replica๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค๋ฉด? ๊ฐ„๋‹จํ•ด:

# Replica ๊ฐœ์ˆ˜ ๋Š˜๋ฆฌ๊ธฐ (3 โ†’ 5)
kubectl scale statefulset mysql --replicas=5 -n database

# ์ƒํƒœ ํ™•์ธ
kubectl get pods -n database -l app=mysql -w

mysql-3๊ณผ mysql-4๊ฐ€ ์ˆœ์ฐจ์ ์œผ๋กœ ์ƒ์„ฑ๋  ๊ฑฐ์•ผ. ๊ทธ๋ฆฌ๊ณ  ์ž๋™์œผ๋กœ mysql-2์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณต์ œํ•ด์„œ ํด๋Ÿฌ์Šคํ„ฐ์— ํ•ฉ๋ฅ˜ํ•˜์ง€!


๋ฐ˜๋Œ€๋กœ ์ค„์ผ ๋•Œ๋Š”:

kubectl scale statefulset mysql --replicas=3 -n database

mysql-4๋ถ€ํ„ฐ ์—ญ์ˆœ์œผ๋กœ ์‚ญ์ œ๋ผ. Master(mysql-0)๋Š” ํ•ญ์ƒ ๋งˆ์ง€๋ง‰๊นŒ์ง€ ๋ณดํ˜ธ๋˜๋‹ˆ๊นŒ ์•ˆ์‹ฌํ•ด๋„ ๋ผ! ๐Ÿ›ก๏ธ

โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ
์Šค์ผ€์ผ ๋‹ค์šดํ•  ๋•Œ PVC๋Š” ์ž๋™์œผ๋กœ ์‚ญ์ œ๋˜์ง€ ์•Š์•„. ๋ฐ์ดํ„ฐ๋ฅผ ์™„์ „ํžˆ ์‚ญ์ œํ•˜๋ ค๋ฉด:
kubectl delete pvc data-mysql-4 -n database
์ด๋ ‡๊ฒŒ ์ˆ˜๋™์œผ๋กœ ์‚ญ์ œํ•ด์•ผ ํ•ด. ์‹ค์ˆ˜๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ ๋ฆฌ๋Š” ๊ฑธ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์•ˆ์ „์žฅ์น˜์•ผ!

๐Ÿšจ ์žฅ์•  ๋Œ€์‘๊ณผ ๋ณต๊ตฌ

์™„๋ฒฝํ•œ ์‹œ์Šคํ…œ์€ ์—†์–ด. ์žฅ์• ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์–ด๋–ป๊ฒŒ ๋Œ€์‘ํ•ด์•ผ ํ•˜๋Š”์ง€ ์•Œ์•„๋ณด์ž. ๐Ÿ”ฅ

๐Ÿ’ฅ Pod ์žฅ์•  ์‹œ๋‚˜๋ฆฌ์˜ค

Replica Pod(mysql-1) ํ•˜๋‚˜๊ฐ€ ์ฃฝ์—ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž:

์ž๋™ ๋ณต๊ตฌ ํ”„๋กœ์„ธ์Šค:


1. Kubernetes๊ฐ€ Pod ์žฅ์• ๋ฅผ ๊ฐ์ง€ (livenessProbe ์‹คํŒจ)
2. ๊ฐ™์€ ์ด๋ฆ„(mysql-1)์œผ๋กœ ์ƒˆ Pod ์ƒ์„ฑ
3. ๊ฐ™์€ PVC(data-mysql-1)๋ฅผ ๋งˆ์šดํŠธ
4. ๋ฐ์ดํ„ฐ๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€๋˜๋ฏ€๋กœ ๋ณต์ œ ์žฌ๊ฐœ
5. readinessProbe ํ†ต๊ณผ ํ›„ ์„œ๋น„์Šค ์žฌ๊ฐœ


์ด ๋ชจ๋“  ๊ณผ์ •์ด ์ž๋™์œผ๋กœ ์ง„ํ–‰๋ผ! StatefulSet์˜ ์ž๊ฐ€ ์น˜์œ  ๋Šฅ๋ ฅ์ด์ง€. ๐Ÿ”„

๐Ÿ‘‘ Master ์žฅ์•  ๋Œ€์‘

Master(mysql-0)์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋ฉด ์ข€ ๋” ์‹ ์ค‘ํ•˜๊ฒŒ ๋Œ€์‘ํ•ด์•ผ ํ•ด:

์ˆ˜๋™ ํŽ˜์ผ์˜ค๋ฒ„ ์ ˆ์ฐจ:


# 1. ๊ฐ€์žฅ ์ตœ์‹  Replica ์„ ํƒ (๋ณต์ œ ์ง€์—ฐ์ด ๊ฐ€์žฅ ์ ์€)
kubectl exec mysql-1 -n database -- mysql -uroot -p -e "SHOW SLAVE STATUS\G" | grep Seconds_Behind_Master

# 2. ์„ ํƒํ•œ Replica๋ฅผ Master๋กœ ์Šน๊ฒฉ
kubectl exec mysql-1 -n database -- mysql -uroot -p

STOP SLAVE;
RESET SLAVE ALL;
SET GLOBAL read_only = OFF;

# 3. ๋‹ค๋ฅธ Replica๋“ค์„ ์ƒˆ Master๋กœ ์žฌ์„ค์ •
kubectl exec mysql-2 -n database -- mysql -uroot -p

STOP SLAVE;
CHANGE MASTER TO
  MASTER_HOST='mysql-1.mysql-headless.database.svc.cluster.local',
  MASTER_USER='replicator',
  MASTER_PASSWORD='replication_password',
  MASTER_AUTO_POSITION=1;
START SLAVE;

# 4. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์“ฐ๊ธฐ ์—”๋“œํฌ์ธํŠธ๋ฅผ mysql-1๋กœ ๋ณ€๊ฒฝ

์ด ๊ณผ์ •์„ ์ž๋™ํ™”ํ•˜๋ ค๋ฉด Orchestrator๋‚˜ ProxySQL ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด. ๐Ÿค–

๐Ÿ’ก ์ž๋™ ํŽ˜์ผ์˜ค๋ฒ„ ๋„๊ตฌ
ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” ์ˆ˜๋™ ํŽ˜์ผ์˜ค๋ฒ„๊ฐ€ ๋„ˆ๋ฌด ๋А๋ ค. ๋‹ค์Œ ๋„๊ตฌ๋“ค์„ ๊ณ ๋ คํ•ด๋ด:
โ€ข Orchestrator: MySQL ํ† ํด๋กœ์ง€ ๊ด€๋ฆฌ ๋ฐ ์ž๋™ ํŽ˜์ผ์˜ค๋ฒ„
โ€ข ProxySQL: ์ฟผ๋ฆฌ ๋ผ์šฐํŒ… ๋ฐ ์ž๋™ ์žฅ์•  ๊ฐ์ง€
โ€ข Vitess: ๋Œ€๊ทœ๋ชจ MySQL ํด๋Ÿฌ์Šคํ„ฐ ๊ด€๋ฆฌ (YouTube๊ฐ€ ์‚ฌ์šฉ)

์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ DB ๊ด€๋ฆฌ ์ „๋ฌธ๊ฐ€๋ฅผ ์ฐพ์•„ ์ปจ์„คํŒ…๋ฐ›๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ!

๐Ÿ”ง ๋ฐ์ดํ„ฐ ๋ณต๊ตฌ

์ตœ์•…์˜ ์‹œ๋‚˜๋ฆฌ์˜ค: ๋ฐ์ดํ„ฐ๊ฐ€ ์†์ƒ๋˜์—ˆ๊ฑฐ๋‚˜ ์‹ค์ˆ˜๋กœ ์‚ญ์ œํ–ˆ์„ ๋•Œ:

๋ฐฑ์—…์—์„œ ๋ณต๊ตฌํ•˜๊ธฐ:

# 1. ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š” Pod ์ค‘์ง€
kubectl scale statefulset mysql --replicas=0 -n database

# 2. ๋ฐฑ์—… ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ
aws s3 cp s3://my-backup-bucket/mysql/mysql_backup_20240115_020000.sql /tmp/

# 3. ์ƒˆ๋กœ์šด Pod๋กœ ๋ณต๊ตฌ
kubectl scale statefulset mysql --replicas=1 -n database
kubectl wait --for=condition=ready pod/mysql-0 -n database

# 4. ๋ฐฑ์—… ๋ณต์›
kubectl cp /tmp/mysql_backup_20240115_020000.sql mysql-0:/tmp/ -n database
kubectl exec mysql-0 -n database -- mysql -uroot -p < /tmp/mysql_backup_20240115_020000.sql

# 5. ๋‚˜๋จธ์ง€ Replica ์žฌ์‹œ์ž‘
kubectl scale statefulset mysql --replicas=3 -n database

Point-in-Time Recovery(PITR)๊ฐ€ ํ•„์š”ํ•˜๋ฉด ๋ฐ”์ด๋„ˆ๋ฆฌ ๋กœ๊ทธ๋ฅผ ํ•จ๊ป˜ ๋ฐฑ์—…ํ•ด์•ผ ํ•ด. โฐ

๐Ÿ” ๋ณต์ œ ๋ฌธ์ œ ํ•ด๊ฒฐ

๋ณต์ œ๊ฐ€ ๋ฉˆ์ท„์„ ๋•Œ ํ”ํ•œ ์›์ธ๋“ค๊ณผ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

๋ฌธ์ œ ์›์ธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
Slave_IO_Running: No Master ์—ฐ๊ฒฐ ์‹คํŒจ ๋„คํŠธ์›Œํฌ, ์ธ์ฆ ์ •๋ณด ํ™•์ธ
Slave_SQL_Running: No SQL ์‹คํ–‰ ์˜ค๋ฅ˜ ์—๋Ÿฌ ๋กœ๊ทธ ํ™•์ธ ํ›„ SKIP ๋˜๋Š” ์ˆ˜๋™ ์ˆ˜์ •
Seconds_Behind_Master ์ฆ๊ฐ€ ๋ณต์ œ ์ง€์—ฐ Slave ๋ฆฌ์†Œ์Šค ์ฆ์„ค, ์ฟผ๋ฆฌ ์ตœ์ ํ™”
Duplicate key error ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜ pt-table-checksum์œผ๋กœ ๊ฒ€์ฆ ํ›„ ๋™๊ธฐํ™”

๋ณต์ œ ์žฌ์‹œ์ž‘ ๋ฐฉ๋ฒ•:

# Slave์—์„œ
STOP SLAVE;

# ์—๋Ÿฌ ํ™•์ธ
SHOW SLAVE STATUS\G

# ํŠน์ • ์—๋Ÿฌ ๊ฑด๋„ˆ๋›ฐ๊ธฐ (์ฃผ์˜ํ•ด์„œ ์‚ฌ์šฉ!)
SET GLOBAL sql_slave_skip_counter = 1;

# ๋ณต์ œ ์žฌ์‹œ์ž‘
START SLAVE;

# ์ƒํƒœ ์žฌํ™•์ธ
SHOW SLAVE STATUS\G

๐ŸŽฏ ์„ฑ๋Šฅ ์ตœ์ ํ™”

ํด๋Ÿฌ์Šคํ„ฐ๊ฐ€ ์ž˜ ๋Œ์•„๊ฐ€๊ณ  ์žˆ๋‹ค๋ฉด, ์ด์ œ ์„ฑ๋Šฅ์„ ๋Œ์–ด์˜ฌ๋ฆด ์ฐจ๋ก€์•ผ! ๐Ÿš€

โš™๏ธ MySQL ์„ค์ • ํŠœ๋‹

ConfigMap์—์„œ MySQL ์„ค์ •์„ ์ตœ์ ํ™”ํ•ด๋ณด์ž:

apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-config-optimized
  namespace: database
data:
  master.cnf: |
    [mysqld]
    # ๊ธฐ๋ณธ ์„ค์ •
    log-bin=mysql-bin
    server-id=1
    binlog-format=ROW
    
    # ์„ฑ๋Šฅ ํŠœ๋‹
    innodb_buffer_pool_size=4G          # ์ „์ฒด ๋ฉ”๋ชจ๋ฆฌ์˜ 70-80%
    innodb_log_file_size=512M
    innodb_flush_log_at_trx_commit=2    # ์„ฑ๋Šฅ vs ์•ˆ์ •์„ฑ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„
    innodb_flush_method=O_DIRECT
    
    # ์—ฐ๊ฒฐ ์„ค์ •
    max_connections=500
    max_connect_errors=100000
    
    # ์ฟผ๋ฆฌ ์บ์‹œ (MySQL 8.0์—์„œ๋Š” ์ œ๊ฑฐ๋จ)
    # query_cache_type=1
    # query_cache_size=256M
    
    # ์Šค๋ ˆ๋“œ ํ’€
    thread_cache_size=100
    
    # ํ…Œ์ด๋ธ” ์บ์‹œ
    table_open_cache=4000
    table_definition_cache=2000
    
    # ์ž„์‹œ ํ…Œ์ด๋ธ”
    tmp_table_size=256M
    max_heap_table_size=256M
    
    # ๋ฐ”์ด๋„ˆ๋ฆฌ ๋กœ๊ทธ
    expire_logs_days=7
    max_binlog_size=100M
    binlog_cache_size=1M
    
    # ๋ณต์ œ ์ตœ์ ํ™”
    slave_parallel_workers=4
    slave_parallel_type=LOGICAL_CLOCK

์ด ์„ค์ •๋“ค์€ 8GB ๋ฉ”๋ชจ๋ฆฌ ๊ธฐ์ค€์ด์•ผ. ์‹ค์ œ ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ์กฐ์ •ํ•ด์•ผ ํ•ด! ๐ŸŽ›๏ธ

โš ๏ธ ํŠœ๋‹ ์ฃผ์˜์‚ฌํ•ญ
innodb_buffer_pool_size๋Š” ๊ฐ€์žฅ ์ค‘์š”ํ•œ ์„ค์ •์ด์•ผ. ๋„ˆ๋ฌด ํฌ๊ฒŒ ์„ค์ •ํ•˜๋ฉด OOM(Out of Memory) ํ‚ฌ๋Ÿฌ๊ฐ€ Pod๋ฅผ ์ฃฝ์ผ ์ˆ˜ ์žˆ์–ด. Kubernetes์˜ memory limits๋ณด๋‹ค ์ž‘๊ฒŒ ์„ค์ •ํ•ด์•ผ ํ•ด!

๊ถŒ์žฅ: memory limit์˜ 60-70% ์ •๋„๋กœ ์„ค์ •

๐Ÿ“Š ๋ฆฌ์†Œ์Šค ํ• ๋‹น ์ตœ์ ํ™”

Pod์˜ ๋ฆฌ์†Œ์Šค ์š”์ฒญ๊ณผ ์ œํ•œ์„ ์ ์ ˆํžˆ ์„ค์ •ํ•˜๋Š” ๊ฒŒ ์ค‘์š”ํ•ด:

resources:
  requests:
    cpu: 2000m      # 2 CPU cores
    memory: 4Gi     # 4GB RAM
  limits:
    cpu: 4000m      # ์ตœ๋Œ€ 4 CPU cores
    memory: 8Gi     # ์ตœ๋Œ€ 8GB RAM

requests vs limits:
โ€ข requests: Pod๊ฐ€ ๋ณด์žฅ๋ฐ›๋Š” ์ตœ์†Œ ๋ฆฌ์†Œ์Šค. ์Šค์ผ€์ค„๋ง ์‹œ ์ด ๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ ๋…ธ๋“œ๋ฅผ ์„ ํƒํ•ด.
โ€ข limits: Pod๊ฐ€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ตœ๋Œ€ ๋ฆฌ์†Œ์Šค. ์ด๋ฅผ ์ดˆ๊ณผํ•˜๋ฉด throttling(CPU) ๋˜๋Š” OOM kill(๋ฉ”๋ชจ๋ฆฌ)์ด ๋ฐœ์ƒํ•ด.


ํ”„๋กœ๋•์…˜์—์„œ๋Š” requests์™€ limits๋ฅผ ๊ฐ™๊ฒŒ ์„ค์ •ํ•˜๋Š” ๊ฒŒ ์ข‹์•„. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด Guaranteed QoS ํด๋ž˜์Šค๋ฅผ ๋ฐ›์•„์„œ ๊ฐ€์žฅ ์•ˆ์ •์ ์œผ๋กœ ์šด์˜ํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ ! ๐ŸŽฏ

๐Ÿ’พ ์Šคํ† ๋ฆฌ์ง€ ์„ฑ๋Šฅ ํ–ฅ์ƒ

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ฑ๋Šฅ์€ ์Šคํ† ๋ฆฌ์ง€ ์„ฑ๋Šฅ์— ํฌ๊ฒŒ ์ขŒ์šฐ๋ผ:

StorageClass ์„ ํƒ:

volumeClaimTemplates:
- metadata:
    name: data
  spec:
    accessModes: ["ReadWriteOnce"]
    storageClassName: fast-ssd  # SSD ๊ธฐ๋ฐ˜ ์Šคํ† ๋ฆฌ์ง€ ์‚ฌ์šฉ
    resources:
      requests:
        storage: 100Gi

ํด๋ผ์šฐ๋“œ ํ™˜๊ฒฝ๋ณ„ ๊ถŒ์žฅ ์Šคํ† ๋ฆฌ์ง€:


โ€ข AWS: gp3 (๋ฒ”์šฉ SSD) ๋˜๋Š” io2 (ํ”„๋กœ๋น„์ €๋‹๋œ IOPS)
โ€ข GCP: pd-ssd (SSD ์˜๊ตฌ ๋””์Šคํฌ)
โ€ข Azure: Premium SSD ๋˜๋Š” Ultra Disk


HDD๋Š” ์ ˆ๋Œ€ ์‚ฌ์šฉํ•˜์ง€ ๋งˆ! ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—๋Š” IOPS๊ฐ€ ์ƒ๋ช…์ด์•ผ. โšก

๐Ÿ”€ ์ฝ๊ธฐ/์“ฐ๊ธฐ ๋ถ„๋ฆฌ

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ์—์„œ ์ฝ๊ธฐ์™€ ์“ฐ๊ธฐ๋ฅผ ๋ถ„๋ฆฌํ•˜๋ฉด ์„ฑ๋Šฅ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋ผ:

Service ๋ถ„๋ฆฌ:

# ์“ฐ๊ธฐ์šฉ Service (Master๋งŒ)
apiVersion: v1
kind: Service
metadata:
  name: mysql-write
  namespace: database
spec:
  selector:
    app: mysql
    statefulset.kubernetes.io/pod-name: mysql-0
  ports:
  - port: 3306
    targetPort: 3306

---
# ์ฝ๊ธฐ์šฉ Service (๋ชจ๋“  Replica)
apiVersion: v1
kind: Service
metadata:
  name: mysql-read
  namespace: database
spec:
  selector:
    app: mysql
  ports:
  - port: 3306
    targetPort: 3306

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ:

// ์“ฐ๊ธฐ ์ž‘์—…
writeDB = mysql.connect("mysql-write.database.svc.cluster.local")
writeDB.execute("INSERT INTO users ...")

// ์ฝ๊ธฐ ์ž‘์—…
readDB = mysql.connect("mysql-read.database.svc.cluster.local")
readDB.query("SELECT * FROM users ...")

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ฝ๊ธฐ ๋ถ€ํ•˜๊ฐ€ ๋ชจ๋“  Replica์— ๋ถ„์‚ฐ๋ผ์„œ Master์˜ ๋ถ€๋‹ด์ด ์ค„์–ด๋“ค์–ด! ๐Ÿ“ˆ

๐Ÿ’ก ProxySQL ํ™œ์šฉ
์ฝ๊ธฐ/์“ฐ๊ธฐ ๋ถ„๋ฆฌ๋ฅผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ์—์„œ ํ•˜๊ธฐ ์‹ซ๋‹ค๋ฉด ProxySQL์„ ์ค‘๊ฐ„์— ๋‘๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์–ด. ProxySQL์ด ์ž๋™์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๋ถ„์„ํ•ด์„œ ์ ์ ˆํ•œ ์„œ๋ฒ„๋กœ ๋ผ์šฐํŒ…ํ•ด์ค˜. ์ฝ”๋“œ ๋ณ€๊ฒฝ ์—†์ด ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ์–ป์„ ์ˆ˜ ์žˆ์ง€!

๐Ÿ” ๋ณด์•ˆ ๊ฐ•ํ™”

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ๊ฐ€์žฅ ์ค‘์š”ํ•œ ์ž์‚ฐ์ด์•ผ. ๋ณด์•ˆ์„ ์ฒ ์ €ํžˆ ํ•ด์•ผ ํ•ด! ๐Ÿ›ก๏ธ

๐Ÿ”’ ๋„คํŠธ์›Œํฌ ์ •์ฑ…

NetworkPolicy๋ฅผ ์‚ฌ์šฉํ•ด์„œ DB ์ ‘๊ทผ์„ ์ œํ•œํ•˜์ž:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: mysql-network-policy
  namespace: database
spec:
  podSelector:
    matchLabels:
      app: mysql
  policyTypes:
  - Ingress
  - Egress
  ingress:
  # ๊ฐ™์€ ๋„ค์ž„์ŠคํŽ˜์ด์Šค์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๋งŒ ํ—ˆ์šฉ
  - from:
    - namespaceSelector:
        matchLabels:
          name: application
    ports:
    - protocol: TCP
      port: 3306
  # ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ ํ—ˆ์šฉ
  - from:
    - namespaceSelector:
        matchLabels:
          name: monitoring
    ports:
    - protocol: TCP
      port: 9104
  egress:
  # DNS ์กฐํšŒ ํ—ˆ์šฉ
  - to:
    - namespaceSelector:
        matchLabels:
          name: kube-system
    ports:
    - protocol: UDP
      port: 53
  # ๊ฐ™์€ ๋„ค์ž„์ŠคํŽ˜์ด์Šค ๋‚ด๋ถ€ ํ†ต์‹  (๋ณต์ œ)
  - to:
    - podSelector:
        matchLabels:
          app: mysql
    ports:
    - protocol: TCP
      port: 3306
    - protocol: TCP
      port: 3307

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ—ˆ๊ฐ€๋œ ๋„ค์ž„์ŠคํŽ˜์ด์Šค์—์„œ๋งŒ DB์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด. ์™ธ๋ถ€์—์„œ ์ง์ ‘ ์ ‘๊ทผ์€ ์™„์ „ํžˆ ์ฐจ๋‹จ๋˜์ง€! ๐Ÿšซ

๐Ÿ”‘ ์•”ํ˜ธํ™”

๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”๋Š” ํ•„์ˆ˜์•ผ:

1. ์ „์†ก ์ค‘ ์•”ํ˜ธํ™” (TLS/SSL):

[mysqld]
require_secure_transport=ON
ssl-ca=/etc/mysql/certs/ca.pem
ssl-cert=/etc/mysql/certs/server-cert.pem
ssl-key=/etc/mysql/certs/server-key.pem

2. ์ €์žฅ ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”:

[mysqld]
early-plugin-load=keyring_file.so
keyring_file_data=/var/lib/mysql-keyring/keyring

# ํ…Œ์ด๋ธ” ์ƒ์„ฑ ์‹œ
CREATE TABLE sensitive_data (
  id INT PRIMARY KEY,
  data VARCHAR(255)
) ENCRYPTION='Y';

3. ๋ฐฑ์—… ์•”ํ˜ธํ™”:

# mysqldump ์‹œ ์•”ํ˜ธํ™”
mysqldump --all-databases | \
  openssl enc -aes-256-cbc -salt -out backup.sql.enc

# ๋ณต์› ์‹œ
openssl enc -d -aes-256-cbc -in backup.sql.enc | \
  mysql -uroot -p

๐Ÿ‘ค ์ ‘๊ทผ ์ œ์–ด

์ตœ์†Œ ๊ถŒํ•œ ์›์น™์„ ์ ์šฉํ•˜์ž:

# ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์šฉ ์‚ฌ์šฉ์ž (์ฝ๊ธฐ/์“ฐ๊ธฐ)
CREATE USER 'app_user'@'%' IDENTIFIED BY 'strong_password';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'app_user'@'%';

# ์ฝ๊ธฐ ์ „์šฉ ์‚ฌ์šฉ์ž (๋ถ„์„, ๋ฆฌํฌํŒ…)
CREATE USER 'readonly_user'@'%' IDENTIFIED BY 'another_password';
GRANT SELECT ON myapp.* TO 'readonly_user'@'%';

# ๋ฐฑ์—…์šฉ ์‚ฌ์šฉ์ž
CREATE USER 'backup_user'@'localhost' IDENTIFIED BY 'backup_password';
GRANT SELECT, LOCK TABLES, SHOW VIEW, RELOAD, REPLICATION CLIENT ON *.* TO 'backup_user'@'localhost';

# ๋ชจ๋‹ˆํ„ฐ๋ง์šฉ ์‚ฌ์šฉ์ž
CREATE USER 'exporter'@'localhost' IDENTIFIED BY 'exporter_password';
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'localhost';

FLUSH PRIVILEGES;

์ ˆ๋Œ€๋กœ root ๊ณ„์ •์„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์‚ฌ์šฉํ•˜์ง€ ๋งˆ! โŒ

โš ๏ธ ๋ณด์•ˆ ์ฒดํฌ๋ฆฌ์ŠคํŠธ
โœ… Secret์œผ๋กœ ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ด€๋ฆฌ
โœ… NetworkPolicy๋กœ ์ ‘๊ทผ ์ œํ•œ
โœ… TLS/SSL ํ™œ์„ฑํ™”
โœ… ์ตœ์†Œ ๊ถŒํ•œ ์›์น™ ์ ์šฉ
โœ… ์ •๊ธฐ์ ์ธ ๋ณด์•ˆ ํŒจ์น˜
โœ… ๊ฐ์‚ฌ ๋กœ๊ทธ ํ™œ์„ฑํ™”
โœ… ๋ฐฑ์—… ์•”ํ˜ธํ™”
โœ… Pod Security Policy ์ ์šฉ

ํ•˜๋‚˜๋ผ๋„ ๋น ๋œจ๋ฆฌ๋ฉด ๋ณด์•ˆ ๊ตฌ๋ฉ์ด ์ƒ๊ธธ ์ˆ˜ ์žˆ์–ด!

๐ŸŒŸ ๊ณ ๊ธ‰ ์ฃผ์ œ๋“ค

๊ธฐ๋ณธ์„ ๋„˜์–ด์„œ ๋” ๊นŠ์ด ๋“ค์–ด๊ฐ€๋ณด์ž! ๐Ÿš€

๐ŸŽญ Multi-Master ๊ตฌ์„ฑ

Group Replication์„ ์‚ฌ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ Master๋ฅผ ์šด์˜ํ•  ์ˆ˜ ์žˆ์–ด:

[mysqld]
# Group Replication ์„ค์ •
plugin-load-add=group_replication.so
group_replication_group_name="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
group_replication_start_on_boot=off
group_replication_local_address="mysql-0.mysql-headless:33061"
group_replication_group_seeds="mysql-0.mysql-headless:33061,mysql-1.mysql-headless:33061,mysql-2.mysql-headless:33061"
group_replication_bootstrap_group=off
group_replication_single_primary_mode=OFF  # Multi-Primary ๋ชจ๋“œ
group_replication_enforce_update_everywhere_checks=ON

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ชจ๋“  ๋…ธ๋“œ๊ฐ€ ์“ฐ๊ธฐ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด. ํ•˜์ง€๋งŒ ์ถฉ๋Œ ํ•ด๊ฒฐ์ด ๋ณต์žกํ•ด์ง€๋‹ˆ๊นŒ ์‹ ์ค‘ํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ด! โš–๏ธ

๐ŸŒ ์ง€์—ญ ๊ฐ„ ๋ณต์ œ

์—ฌ๋Ÿฌ Kubernetes ํด๋Ÿฌ์Šคํ„ฐ์— ๊ฑธ์ณ ๋ณต์ œ๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜๋„ ์žˆ์–ด:

์‹œ๋‚˜๋ฆฌ์˜ค: ์„œ์šธ ํด๋Ÿฌ์Šคํ„ฐ(Primary)์™€ ๋„์ฟ„ ํด๋Ÿฌ์Šคํ„ฐ(DR)


# ์„œ์šธ ํด๋Ÿฌ์Šคํ„ฐ์˜ Master๋ฅผ ์™ธ๋ถ€๋กœ ๋…ธ์ถœ
apiVersion: v1
kind: Service
metadata:
  name: mysql-external
  namespace: database
spec:
  type: LoadBalancer
  selector:
    app: mysql
    statefulset.kubernetes.io/pod-name: mysql-0
  ports:
  - port: 3306
    targetPort: 3306

# ๋„์ฟ„ ํด๋Ÿฌ์Šคํ„ฐ์—์„œ ์„œ์šธ Master๋กœ ๋ณต์ œ ์„ค์ •
CHANGE MASTER TO
  MASTER_HOST='seoul-mysql-lb.example.com',
  MASTER_USER='replicator',
  MASTER_PASSWORD='password',
  MASTER_AUTO_POSITION=1;
START SLAVE;

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์žฌํ•ด ๋ณต๊ตฌ(DR) ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์–ด. ์„œ์šธ ๋ฐ์ดํ„ฐ์„ผํ„ฐ๊ฐ€ ํ†ต์งธ๋กœ ๋‚ ์•„๊ฐ€๋„ ๋„์ฟ„์—์„œ ์„œ๋น„์Šค๋ฅผ ๊ณ„์†ํ•  ์ˆ˜ ์žˆ์ง€! ๐ŸŒ

๐Ÿค– Operator ํŒจํ„ด

StatefulSet์„ ์ง์ ‘ ๊ด€๋ฆฌํ•˜๋Š” ๋Œ€์‹  Operator๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ›จ์”ฌ ํŽธํ•ด:

์ธ๊ธฐ ์žˆ๋Š” MySQL Operator๋“ค:


โ€ข Oracle MySQL Operator: ๊ณต์‹ Operator, InnoDB Cluster ์ง€์›
โ€ข Percona XtraDB Cluster Operator: Percona์˜ MySQL ํด๋Ÿฌ์Šคํ„ฐ
โ€ข Vitess Operator: ๋Œ€๊ทœ๋ชจ ํ™˜๊ฒฝ์— ์ตœ์ ํ™”


Operator๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด:

apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: mycluster
spec:
  secretName: mysql-secret
  instances: 3
  router:
    instances: 2
  version: "8.0.32"
  storage:
    size: 100Gi
    storageClassName: fast-ssd

์ด๋ ‡๊ฒŒ ๊ฐ„๋‹จํ•œ YAML๋กœ ์ „์ฒด ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด. ์ž๋™ ํŽ˜์ผ์˜ค๋ฒ„, ๋ฐฑ์—…, ์—…๊ทธ๋ ˆ์ด๋“œ๊นŒ์ง€ ๋ชจ๋‘ ์ž๋™ํ™”๋˜์ง€! โœจ

๐ŸŽ“ ํ•™์Šต ํŒ
Operator ํŒจํ„ด์€ Kubernetes์˜ ๊ณ ๊ธ‰ ์ฃผ์ œ์•ผ. ์žฌ๋Šฅ๋„ท์—์„œ Kubernetes ์ „๋ฌธ๊ฐ€๋ฅผ ์ฐพ์•„ 1:1 ๋ฉ˜ํ† ๋ง์„ ๋ฐ›์œผ๋ฉด ํ›จ์”ฌ ๋น ๋ฅด๊ฒŒ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์–ด. ์‹ค๋ฌด ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ์ „๋ฌธ๊ฐ€์˜ ๋…ธํ•˜์šฐ๋ฅผ ์ง์ ‘ ์ „์ˆ˜๋ฐ›๋Š” ๊ฑด ์ฑ…์œผ๋กœ๋Š” ๋ฐฐ์šธ ์ˆ˜ ์—†๋Š” ๊ฐ€์น˜๊ฐ€ ์žˆ๊ฑฐ๋“ !

๐Ÿ“Š ์ƒค๋”ฉ (Sharding)

๋ฐ์ดํ„ฐ๊ฐ€ ์ •๋ง ๋งŽ์•„์ง€๋ฉด ์ƒค๋”ฉ์„ ๊ณ ๋ คํ•ด์•ผ ํ•ด:

์ˆ˜ํ‰ ์ƒค๋”ฉ ์ „๋žต:


์‚ฌ์šฉ์ž ID ๊ธฐ๋ฐ˜์œผ๋กœ 3๊ฐœ ์ƒค๋“œ๋กœ ๋ถ„ํ• :


# Shard 0: user_id % 3 == 0
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql-shard-0
spec:
  replicas: 3
  ...

# Shard 1: user_id % 3 == 1
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql-shard-1
spec:
  replicas: 3
  ...

# Shard 2: user_id % 3 == 2
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql-shard-2
spec:
  replicas: 3
  ...

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ƒค๋“œ ์„ ํƒ:

def get_shard(user_id):
    shard_num = user_id % 3
    return f"mysql-shard-{shard_num}.database.svc.cluster.local"

# ์‚ฌ์šฉ
db = connect(get_shard(user_id))
db.query("SELECT * FROM users WHERE id = ?", user_id)

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฅผ ์—ฌ๋Ÿฌ ํด๋Ÿฌ์Šคํ„ฐ์— ๋ถ„์‚ฐ์‹œ์ผœ์„œ ๋ฌดํ•œํžˆ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์–ด! โ™พ๏ธ

โš ๏ธ ์ƒค๋”ฉ์˜ ๋ณต์žก์„ฑ
์ƒค๋”ฉ์€ ๊ฐ•๋ ฅํ•˜์ง€๋งŒ ๋ณต์žกํ•ด:
โ€ข ์กฐ์ธ ์ฟผ๋ฆฌ๊ฐ€ ์–ด๋ ค์›Œ์ง
โ€ข ํŠธ๋žœ์žญ์…˜์ด ๋ณต์žกํ•ด์ง
โ€ข ๋ฆฌ์ƒค๋”ฉ์ด ๋งค์šฐ ์–ด๋ ค์›€
โ€ข ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ์ง์ด ๋ณต์žกํ•ด์ง

์ •๋ง ํ•„์š”ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ๋ฉด ํ”ผํ•˜๋Š” ๊ฒŒ ์ข‹์•„. ๋จผ์ € ์ฝ๊ธฐ Replica ์ถ”๊ฐ€, ์บ์‹ฑ, ์ฟผ๋ฆฌ ์ตœ์ ํ™” ๋“ฑ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ๋ชจ๋‘ ์‹œ๋„ํ•ด๋ด!

๐Ÿ’ผ ์‹ค์ „ ์‚ฌ๋ก€ ์—ฐ๊ตฌ

์ด๋ก ๋งŒ์œผ๋กœ๋Š” ๋ถ€์กฑํ•ด. ์‹ค์ œ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉ๋˜๋Š”์ง€ ์‚ดํŽด๋ณด์ž! ๐Ÿข

๐Ÿ›’ ์‚ฌ๋ก€ 1: E-commerce ํ”Œ๋žซํผ

์š”๊ตฌ์‚ฌํ•ญ:

โ€ข ์ฃผ๋ฌธ ๋ฐ์ดํ„ฐ (๋†’์€ ์“ฐ๊ธฐ ๋ถ€ํ•˜)
โ€ข ์ƒํ’ˆ ์นดํƒˆ๋กœ๊ทธ (๋†’์€ ์ฝ๊ธฐ ๋ถ€ํ•˜)
โ€ข 99.9% ๊ฐ€์šฉ์„ฑ
โ€ข ํ”ผํฌ ์‹œ๊ฐ„ ๋Œ€์‘


์•„ํ‚คํ…์ฒ˜:

# ์ฃผ๋ฌธ DB (์“ฐ๊ธฐ ์ง‘์ค‘)
- Master: 1๋Œ€ (๊ณ ์„ฑ๋Šฅ ์ธ์Šคํ„ด์Šค)
- Replica: 2๋Œ€ (์ฝ๊ธฐ ๋ถ€ํ•˜ ๋ถ„์‚ฐ)
- ๋ฆฌ์†Œ์Šค: 8 CPU, 16GB RAM
- ์Šคํ† ๋ฆฌ์ง€: 500GB SSD (io2, 10000 IOPS)

# ์ƒํ’ˆ DB (์ฝ๊ธฐ ์ง‘์ค‘)
- Master: 1๋Œ€
- Replica: 5๋Œ€ (์ฝ๊ธฐ ๋ถ€ํ•˜ ๋ถ„์‚ฐ)
- ๋ฆฌ์†Œ์Šค: 4 CPU, 8GB RAM
- ์Šคํ† ๋ฆฌ์ง€: 200GB SSD (gp3)

# ์บ์‹ฑ ๋ ˆ์ด์–ด
- Redis Cluster (์ƒํ’ˆ ์ •๋ณด ์บ์‹ฑ)
- ์บ์‹œ ํžˆํŠธ์œจ: 95%

๊ฒฐ๊ณผ:
โ€ข ํ‰๊ท  ์‘๋‹ต ์‹œ๊ฐ„: 50ms โ†’ 15ms
โ€ข ํ”ผํฌ ์‹œ๊ฐ„ ์ฒ˜๋ฆฌ๋Ÿ‰: 10,000 TPS
โ€ข ์—ฐ๊ฐ„ ๋‹ค์šดํƒ€์ž„: 8์‹œ๊ฐ„ โ†’ 30๋ถ„
โ€ข ์ธํ”„๋ผ ๋น„์šฉ: 30% ์ ˆ๊ฐ (์˜คํ† ์Šค์ผ€์ผ๋ง)

๐Ÿ“ฑ ์‚ฌ๋ก€ 2: ์†Œ์…œ ๋ฏธ๋””์–ด ์•ฑ

์š”๊ตฌ์‚ฌํ•ญ:

โ€ข ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ (10์–ต+ ๋ ˆ์ฝ”๋“œ)
โ€ข ํƒ€์ž„๋ผ์ธ ํ”ผ๋“œ (์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ)
โ€ข ๊ธ€๋กœ๋ฒŒ ์„œ๋น„์Šค (์ง€์—ญ๋ณ„ ๋ ˆ์ดํ„ด์‹œ ์ตœ์†Œํ™”)
โ€ข ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ


์•„ํ‚คํ…์ฒ˜:

# ์ง€์—ญ๋ณ„ ํด๋Ÿฌ์Šคํ„ฐ
- ์„œ์šธ: 1 Master + 3 Replicas
- ๋„์ฟ„: 1 Master + 3 Replicas
- ์‹ฑ๊ฐ€ํฌ๋ฅด: 1 Master + 3 Replicas

# ์ƒค๋”ฉ ์ „๋žต
- ์‚ฌ์šฉ์ž ID ๊ธฐ๋ฐ˜ 16๊ฐœ ์ƒค๋“œ
- ๊ฐ ์ƒค๋“œ: 1 Master + 2 Replicas

# ๋ฐ์ดํ„ฐ ๋ถ„๋ฆฌ
- ํ•ซ ๋ฐ์ดํ„ฐ: MySQL (์ตœ๊ทผ 30์ผ)
- ์ฝœ๋“œ ๋ฐ์ดํ„ฐ: S3 + Athena (30์ผ ์ด์ƒ)

# ์บ์‹ฑ
- Redis: ์‚ฌ์šฉ์ž ์„ธ์…˜, ํ”„๋กœํ•„
- Memcached: ํƒ€์ž„๋ผ์ธ ํ”ผ๋“œ

๊ฒฐ๊ณผ:
โ€ข ๊ธ€๋กœ๋ฒŒ ํ‰๊ท  ๋ ˆ์ดํ„ด์‹œ: 100ms
โ€ข ์ผ์ผ ํ™œ์„ฑ ์‚ฌ์šฉ์ž: 5์ฒœ๋งŒ๋ช…
โ€ข ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋…ธ๋“œ: 192๊ฐœ
โ€ข ์ด ๋ฐ์ดํ„ฐ: 500TB

๐Ÿฆ ์‚ฌ๋ก€ 3: ๊ธˆ์œต ์„œ๋น„์Šค

์š”๊ตฌ์‚ฌํ•ญ:

โ€ข ํŠธ๋žœ์žญ์…˜ ๋ฌด๊ฒฐ์„ฑ (ACID ๋ณด์žฅ)
โ€ข ๊ฐ์‚ฌ ๋กœ๊ทธ (๋ชจ๋“  ๋ณ€๊ฒฝ ์ถ”์ )
โ€ข ๊ทœ์ œ ์ค€์ˆ˜ (๋ฐ์ดํ„ฐ ๋ณด์กด 7๋…„)
โ€ข ์ œ๋กœ ๋ฐ์ดํ„ฐ ์†์‹ค


์•„ํ‚คํ…์ฒ˜:

# ๊ณ ๊ฐ€์šฉ์„ฑ ์„ค์ •
- Primary: 1๋Œ€ (๋™๊ธฐ ๋ณต์ œ)
- Standby: 1๋Œ€ (๋™๊ธฐ ๋ณต์ œ, ์ž๋™ ํŽ˜์ผ์˜ค๋ฒ„)
- Replica: 2๋Œ€ (๋น„๋™๊ธฐ ๋ณต์ œ, ์ฝ๊ธฐ ์ „์šฉ)

# ๋ฐฑ์—… ์ „๋žต
- ์ „์ฒด ๋ฐฑ์—…: ๋งค์ผ ์ƒˆ๋ฒฝ 2์‹œ
- ์ฆ๋ถ„ ๋ฐฑ์—…: ๋งค์‹œ๊ฐ„
- ๋ฐ”์ด๋„ˆ๋ฆฌ ๋กœ๊ทธ: ์‹ค์‹œ๊ฐ„ ์•„์นด์ด๋น™
- ๋ณด๊ด€ ๊ธฐ๊ฐ„: 7๋…„ (S3 Glacier)

# ๋ณด์•ˆ
- TLS 1.3 ๊ฐ•์ œ
- ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™” (AES-256)
- ์ ‘๊ทผ ์ œ์–ด: RBAC + MFA
- ๊ฐ์‚ฌ ๋กœ๊ทธ: ๋ชจ๋“  ์ฟผ๋ฆฌ ๊ธฐ๋ก

# ์žฌํ•ด ๋ณต๊ตฌ
- RTO: 5๋ถ„
- RPO: 0 (๋™๊ธฐ ๋ณต์ œ)
- DR ์‚ฌ์ดํŠธ: ๋‹ค๋ฅธ ๋ฆฌ์ „

๊ฒฐ๊ณผ:
โ€ข ์—ฐ๊ฐ„ ๊ฐ€์šฉ์„ฑ: 99.99%
โ€ข ๋ฐ์ดํ„ฐ ์†์‹ค: 0๊ฑด
โ€ข ํ‰๊ท  ํŽ˜์ผ์˜ค๋ฒ„ ์‹œ๊ฐ„: 30์ดˆ
โ€ข ๊ทœ์ œ ๊ฐ์‚ฌ: ํ†ต๊ณผ

๐Ÿ’ก ๋ฐฐ์šด ๊ตํ›ˆ
1. ์š”๊ตฌ์‚ฌํ•ญ์— ๋งž๋Š” ์„ค๊ณ„: ๋ชจ๋“  ์ƒํ™ฉ์— ๋งž๋Š” ์™„๋ฒฝํ•œ ์•„ํ‚คํ…์ฒ˜๋Š” ์—†์–ด. ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ์„ ์ •ํ™•ํžˆ ํŒŒ์•…ํ•˜๊ณ  ๊ทธ์— ๋งž๊ฒŒ ์„ค๊ณ„ํ•ด์•ผ ํ•ด.

2. ๋‹จ๊ณ„์  ํ™•์žฅ: ์ฒ˜์Œ๋ถ€ํ„ฐ ๋ณต์žกํ•˜๊ฒŒ ๋งŒ๋“ค์ง€ ๋งˆ. ๊ฐ„๋‹จํ•˜๊ฒŒ ์‹œ์ž‘ํ•ด์„œ ํ•„์š”์— ๋”ฐ๋ผ ํ™•์žฅํ•ด.

3. ๋ชจ๋‹ˆํ„ฐ๋ง์ด ํ•ต์‹ฌ: ๋ฌธ์ œ๋ฅผ ๋นจ๋ฆฌ ๋ฐœ๊ฒฌํ•˜๊ณ  ๋Œ€์‘ํ•˜๋ ค๋ฉด ์ฒ ์ €ํ•œ ๋ชจ๋‹ˆํ„ฐ๋ง์ด ํ•„์ˆ˜์•ผ.

4. ์ž๋™ํ™”: ์ˆ˜๋™ ์ž‘์—…์€ ์‹ค์ˆ˜๋ฅผ ๋ถ€๋ฅด๊ณ  ์‹œ๊ฐ„์„ ๋‚ญ๋น„ํ•ด. ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ๊ฑธ ์ž๋™ํ™”ํ•ด.

5. ํ…Œ์ŠคํŠธ, ํ…Œ์ŠคํŠธ, ํ…Œ์ŠคํŠธ: ํ”„๋กœ๋•์…˜์—์„œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๊ธฐ ์ „์— ์ฒ ์ €ํžˆ ํ…Œ์ŠคํŠธํ•ด. ํŠนํžˆ ์žฅ์•  ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๋ฐ˜๋“œ์‹œ ํ…Œ์ŠคํŠธํ•ด๋ด์•ผ ํ•ด!

๐ŸŽ“ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์™€, ์ •๋ง ๊ธด ์—ฌ์ •์ด์—ˆ์ง€? Kubernetes StatefulSet์„ ํ™œ์šฉํ•œ DB ํด๋Ÿฌ์Šคํ„ฐ ๊ด€๋ฆฌ์— ๋Œ€ํ•ด ๋งŽ์€ ๊ฑธ ๋ฐฐ์› ์–ด! ๐ŸŽ‰

๐Ÿ“ ํ•ต์‹ฌ ์š”์•ฝ


1. StatefulSet์˜ ํŠน๋ณ„ํ•จ
โ€ข ์•ˆ์ •์ ์ธ ๋„คํŠธ์›Œํฌ ์‹๋ณ„์ž
โ€ข ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋˜๋Š” ๋ฐฐํฌ/์Šค์ผ€์ผ๋ง
โ€ข ์˜๊ตฌ ๋ณผ๋ฅจ ์ž๋™ ๊ด€๋ฆฌ
โ€ข ์ƒํƒœ ์œ ์ง€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ตœ์ ํ™”


2. ์‹ค์ „ ๊ตฌ์ถ• ๋‹จ๊ณ„
โ€ข ConfigMap์œผ๋กœ ์„ค์ • ๊ด€๋ฆฌ
โ€ข Secret์œผ๋กœ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณดํ˜ธ
โ€ข Headless Service๋กœ DNS ์ œ๊ณต
โ€ข volumeClaimTemplates๋กœ ์Šคํ† ๋ฆฌ์ง€ ์ž๋™ ํ”„๋กœ๋น„์ €๋‹


3. ์šด์˜ ๊ด€๋ฆฌ
โ€ข ๋ชจ๋‹ˆํ„ฐ๋ง (Prometheus + Grafana)
โ€ข ์ž๋™ ๋ฐฑ์—… (CronJob)
โ€ข ์žฅ์•  ๋Œ€์‘ (์ž๋™ ๋ณต๊ตฌ + ์ˆ˜๋™ ํŽ˜์ผ์˜ค๋ฒ„)
โ€ข ์„ฑ๋Šฅ ์ตœ์ ํ™” (ํŠœ๋‹ + ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ)


4. ๋ณด์•ˆ
โ€ข NetworkPolicy๋กœ ์ ‘๊ทผ ์ œํ•œ
โ€ข TLS/SSL ์•”ํ˜ธํ™”
โ€ข ์ตœ์†Œ ๊ถŒํ•œ ์›์น™
โ€ข ์ •๊ธฐ์ ์ธ ๋ณด์•ˆ ํŒจ์น˜


5. ๊ณ ๊ธ‰ ์ฃผ์ œ
โ€ข Multi-Master ๊ตฌ์„ฑ
โ€ข ์ง€์—ญ ๊ฐ„ ๋ณต์ œ
โ€ข Operator ํŒจํ„ด
โ€ข ์ƒค๋”ฉ

๐Ÿš€ ๋‹ค์Œ ๋‹จ๊ณ„


์ด์ œ ์ด๋ก ์€ ์ถฉ๋ถ„ํžˆ ๋ฐฐ์› ์–ด. ๋‹ค์Œ์€ ์‹ค์Šต์ด์•ผ!


์ถ”์ฒœ ํ•™์Šต ๊ฒฝ๋กœ:
1. ๋กœ์ปฌ Minikube์—์„œ ๊ฐ„๋‹จํ•œ MySQL StatefulSet ๊ตฌ์ถ•
2. ๋ณต์ œ ์„ค์ • ๋ฐ ํ…Œ์ŠคํŠธ
3. ์žฅ์•  ์‹œ๋‚˜๋ฆฌ์˜ค ํ…Œ์ŠคํŠธ (Pod ์‚ญ์ œ, ๋„คํŠธ์›Œํฌ ๋‹จ์ ˆ ๋“ฑ)
4. ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ ๊ตฌ์ถ•
5. ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ ๋ฐฐํฌ (์Šคํ…Œ์ด์ง• ๋จผ์ €!)


๋ง‰ํžˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด Kubernetes ์ปค๋ฎค๋‹ˆํ‹ฐ๋‚˜ ์žฌ๋Šฅ๋„ท์—์„œ ์ „๋ฌธ๊ฐ€์˜ ๋„์›€์„ ๋ฐ›๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ. ํ˜ผ์ž ๊ณ ๋ฏผํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๊ฒฝํ—˜์ž์˜ ์กฐ์–ธ ํ•œ๋งˆ๋””๊ฐ€ ํ›จ์”ฌ ๋น ๋ฅด๊ฒŒ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์ฃผ๊ฑฐ๋“ ! ๐Ÿค

๐Ÿ“š ์ถ”๊ฐ€ ํ•™์Šต ์ž๋ฃŒ


๊ณต์‹ ๋ฌธ์„œ:
โ€ข Kubernetes StatefulSet ๊ณต์‹ ๋ฌธ์„œ
โ€ข MySQL 8.0 Reference Manual
โ€ข Percona XtraBackup ๋ฌธ์„œ


๋„๊ตฌ:
โ€ข kubectl: Kubernetes CLI
โ€ข k9s: ํ„ฐ๋ฏธ๋„ ๊ธฐ๋ฐ˜ Kubernetes UI
โ€ข Lens: Kubernetes IDE
โ€ข pt-toolkit: Percona Toolkit (DB ๊ด€๋ฆฌ ๋„๊ตฌ)


์ปค๋ฎค๋‹ˆํ‹ฐ:
โ€ข Kubernetes Slack
โ€ข MySQL Community
โ€ข Stack Overflow
โ€ข ์žฌ๋Šฅ๋„ท ์ง€์‹์ธ์˜ ์ˆฒ (์—ฌ๊ธฐ!)

Thank You! Happy Kubernetes Journey Start Learn Master

๐ŸŽฏ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํด๋Ÿฌ์Šคํ„ฐ ๊ด€๋ฆฌ๋Š” ์–ด๋ ต์ง€๋งŒ, ์ œ๋Œ€๋กœ ๋ฐฐ์šฐ๋ฉด ์—„์ฒญ๋‚œ ๊ฐ€์น˜๋ฅผ ์ œ๊ณตํ•ด.
StatefulSet์€ ๊ทธ ์—ฌ์ •์˜ ์™„๋ฒฝํ•œ ๋™๋ฐ˜์ž์•ผ.

๐Ÿ’ช ํฌ๊ธฐํ•˜์ง€ ๋ง๊ณ  ๊ณ„์† ๋„์ „ํ•ด๋ด. ์‹คํŒจ๋Š” ๋ฐฐ์›€์˜ ๊ธฐํšŒ๋‹ˆ๊นŒ!

๐ŸŒŸ ํ–‰์šด์„ ๋นŒ์–ด! ๋ฉ‹์ง„ ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด๋ด!

๐ŸŽ“ ๋” ๋ฐฐ์šฐ๊ณ  ์‹ถ๋‹ค๋ฉด?

์žฌ๋Šฅ๋„ท์—์„œ Kubernetes, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ํด๋ผ์šฐ๋“œ ์ธํ”„๋ผ ์ „๋ฌธ๊ฐ€๋“ค์„ ๋งŒ๋‚˜๋ณด์„ธ์š”!
์‹ค๋ฌด ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ๋ฉ˜ํ† ๋“ค์ด ๋‹น์‹ ์˜ ์„ฑ์žฅ์„ ๋„์™€๋“œ๋ฆฝ๋‹ˆ๋‹ค.

www.jaenung.net - ์žฌ๋Šฅ์„ ๋‚˜๋ˆ„๊ณ , ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ๊ณณ

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.