Ver3.0 ๐ Kubernetes StatefulSet์ผ๋ก DB ํด๋ฌ์คํฐ ๊ด๋ฆฌํ๊ธฐ

๐ Kubernetes StatefulSet์ผ๋ก DB ํด๋ฌ์คํฐ ๊ด๋ฆฌํ๊ธฐ
๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฟ ๋ฒ๋คํฐ์ค์์ ์์ ํ๊ฒ ์ด์ํ๋ ์ค์ ๊ฐ์ด๋
๐ ์๋ ! ์ค๋์ Kubernetes์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํด๋ฌ์คํฐ๋ฅผ ๊ด๋ฆฌํ๋ ํต์ฌ ๋๊ตฌ์ธ StatefulSet์ ๋ํด ๊น์ด ์๊ฒ ์์๋ณผ ๊ฑฐ์ผ. ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ์๋์ DB๋ฅผ ์ปจํ ์ด๋๋ก ์ด์ํ๋ ๊ฑด ๋ ์ด์ ์ ํ์ด ์๋ ํ์๊ฐ ๋์ด๊ฐ๊ณ ์์ด. ํ์ง๋ง ์ผ๋ฐ์ ์ธ ์น ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ๋ฌ๋ฆฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ์ํ๋ฅผ ์ ์งํด์ผ ํ๊ณ , ์์๊ฐ ์ค์ํ๋ฉฐ, ๊ฐ ์ธ์คํด์ค๊ฐ ๊ณ ์ ํ ์ ์ฒด์ฑ์ ๊ฐ์ ธ์ผ ํ์ง. ๋ฐ๋ก ์ด๋ฐ ์๊ตฌ์ฌํญ์ ํด๊ฒฐํ๊ธฐ ์ํด ํ์ํ ๊ฒ StatefulSet์ด์ผ! ๐ก
๐ฏ 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 ํด๋ฌ์คํฐ์์ ์ผ๋ง๋ ์ค์ํ์ง ์์๋ณด์:
โข mysql-0์ด ์์ ํ Ready ์ํ๊ฐ ๋๋ฉด
โข mysql-1์ ์์ฑํ๊ณ , Ready๊ฐ ๋๋ฉด
โข mysql-2๋ฅผ ์์ฑ
์ด๋ ๊ฒ ํ๋์ฉ ์์ฐจ์ ์ผ๋ก ์์ฑ๋ผ. Master DB๊ฐ ๋จผ์ ์ค๋น๋๊ณ , ๊ทธ ๋ค์์ Replica๋ค์ด ์์๋๋ก Master์ ์ฐ๊ฒฐ๋๋ ๊ตฌ์กฐ๋ฅผ ๋ง๋ค ์ ์์ง!
๋ฐ๋๋ก ์ค์ผ์ผ์ ์ค์ผ ๋๋ ์ญ์์ผ๋ก ์งํ๋ผ:
โข 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์ ์ง์ ์ ๊ทผํ ์ ์์ง! ๐ฏ
์ผ๋ฐ 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์ด ์๋์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ๋ถ์ํด์ ์ ์ ํ ์๋ฒ๋ก ๋ผ์ฐํ ํด์ค. ์ฝ๋ ๋ณ๊ฒฝ ์์ด ์ฑ๋ฅ ํฅ์์ ์ป์ ์ ์์ง!
๐ ๋ณด์ ๊ฐํ
๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ๊ฐ์ฅ ์ค์ํ ์์ฐ์ด์ผ. ๋ณด์์ ์ฒ ์ ํ ํด์ผ ํด! ๐ก๏ธ
๐ ๋คํธ์ํฌ ์ ์ฑ
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
โข ์ฌ๋ฅ๋ท ์ง์์ธ์ ์ฒ (์ฌ๊ธฐ!)
๐ฏ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํด๋ฌ์คํฐ ๊ด๋ฆฌ๋ ์ด๋ ต์ง๋ง, ์ ๋๋ก ๋ฐฐ์ฐ๋ฉด ์์ฒญ๋ ๊ฐ์น๋ฅผ ์ ๊ณตํด.
StatefulSet์ ๊ทธ ์ฌ์ ์ ์๋ฒฝํ ๋๋ฐ์์ผ.
๐ช ํฌ๊ธฐํ์ง ๋ง๊ณ ๊ณ์ ๋์ ํด๋ด. ์คํจ๋ ๋ฐฐ์์ ๊ธฐํ๋๊น!
๐ ํ์ด์ ๋น์ด! ๋ฉ์ง ํด๋ฌ์คํฐ๋ฅผ ๋ง๋ค์ด๋ด!
๐ ๋ ๋ฐฐ์ฐ๊ณ ์ถ๋ค๋ฉด?
์ฌ๋ฅ๋ท์์ Kubernetes, ๋ฐ์ดํฐ๋ฒ ์ด์ค, ํด๋ผ์ฐ๋ ์ธํ๋ผ ์ ๋ฌธ๊ฐ๋ค์ ๋ง๋๋ณด์ธ์!
์ค๋ฌด ๊ฒฝํ์ด ํ๋ถํ ๋ฉํ ๋ค์ด ๋น์ ์ ์ฑ์ฅ์ ๋์๋๋ฆฝ๋๋ค.
www.jaenung.net - ์ฌ๋ฅ์ ๋๋๊ณ , ํจ๊ป ์ฑ์ฅํ๋ ๊ณณ
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.