Kubernetes

Kubernetes NFS StorageClass with MySQL

김 정출 2024. 7. 10. 12:35

Kubernetes Persistent Volume (PV)NFS Stoarge를 활용해보겠습니다.

Container 의 생명 주기에 따라 내부 데이터는 사라지게 됩니다. 내부에 저장된 데이터를 유지하려면 Volume mount를 통해 데이터를 저장하게 됩니다.

Kubernetes cluster에 생성된 Pod들의 데이터를 유지하려면 실행된 Node의 host path에 저장하거나, NFS Server 또는 분산 Storage인 Ceph 를 활용해 저장할 수 있습니다.

아래는 hostPath를 활용해 실행되는 Node에 저장하는 방법입니다. 단 nginx-app Pod가 동일한 노드에 스케줄링 되지 않는다면 같은 데이터를 바라볼 수 없게 됩니다.

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-app
  template:
    metadata:
      labels:
        app: nginx-app
    spec:
      containers:
      - name: web-server
        image: nginx:latest
        volumeMounts:
        - name: web-content
          mountPath: /usr/share/nginx/html
        ports:
        - containerPort: 80
        volumes:
        - name: web-content
          hostPath:  <----
            type: Directory
            path: /data

이러한 문제를 해결하기 위해 NFS Storage Class를 생성해보겠습니다.

Master와 각 Worker node에 NFS 서버 패키지인 nfs-common 을 설치합니다.

apt install -y nfs-common

Kubernetes Cluster의 master node 에 helm 을 설치하여 NFS를 배포해보겠습니다.

sudo snap install helm --classic

Data volume을 설정해야 합니다. 저는 4TB의 HDD가 마운트된 경로로 지정하겠습니다.

  • data volume path: /data

nfs-server-provisioner를 설치할 Namespace는 다음과 같습니다.

  • namespace: kubernetes-nfs

Namespace 생성을 먼저 진행합니다.

kubectl create ns kubernetes-nfs

nfs-server-provisioner helm 배포를 진행하겠습니다.

helm repo add stable https://charts.helm.sh/stable --force-update
helm install kubernetes-nfs stable/nfs-server-provisioner --namespace kubernetes-nfs

배포된 helm을 확인해봅니다.

helm list -n kubernetes-nfs

배포된 Statefulset, Pod, Service를 확인해봅니다.

Statefulset을 수정합니다.

  • 특정 Node에 배포하려면 nodeSelector를 활용해 지정할 수 있습니다.
kubectl edit statefulset -nkubernetes-nfs kubernetes-nfs-nfs-server-provisioner
----
volumes:
  - hostPath:
      path: /data
      type: ""
    name: data

배포된 Pod를 다시 재확인해보고 정상 기동되면 /data 볼륨을 확인해봅니다.

kubectl get pods -nkubernetes-nfs
ls /data

다음은 StorageClass를 확인해보겠습니다.

kubectl get storageclass -nkubernetes-nfs

StorageClass의 mount options 수정해보겠습니다.

PV가 lock이 걸리는 문제를 해결하기 위해 다음을 수정합니다.

  • nolock
  • rw
kubectl edit storageclass -nkubernetes-nfs nfs
---
mountOptions:
- nolock
- rw
...

NFS StorageClass 테스트를 MySQL DB에 사용될 PVC를 생성해보겠습니다.

vi ~/k8s-yaml/mysql/pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: db
  namespace: mysql
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 50Gi
  storageClassName: nfs
  volumeMode: Filesystem
status:
  accessModes:
  - ReadWriteMany
  capacity:
    storage: 50Gi
  phase: Bound
kubectl apply -f ~/k8s-yaml/mysql/pvc.yaml
kubectl get pv -nmysql
kubectl get pvc -nmysql

MySQL을 배포해보겠습니다.

ConfigMap 을 먼저 배포해봅니다.

kubectl create configmap configmap-mysql -nmysql --from-literal MYSQL_USER=jckim --from-literal MYSQL_ROOT_HOST=%



다음은 DB 패스워드를 위한 Secret을 배포하겠습니다.

kubectl create secret generic secret-mysql -nmysql --from-literal MYSQL_PASSWORD=test2024! --from-literal MYSQL_ROOT_PASSWORD=test2024!

다음은 Deployment를 생성해보겠습니다.

  • PVC는 위에서 생성한 db 로 진행하겠습니다.
vi ~/k8s-yaml/mysql/deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
  namespace: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql/mysql-server:latest
        name: mysql
        env:
        - name: MYSQL_USER
          valueFrom:
            configMapKeyRef:
              name: configmap-mysql
              key: MYSQL_USER
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: secret-mysql
              key: MYSQL_PASSWORD
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: secret-mysql
              key: MYSQL_ROOT_PASSWORD
        - name: MYSQL_ROOT_HOST
          valueFrom:
            configMapKeyRef:
              name: configmap-mysql
              key: MYSQL_ROOT_HOST
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: db
kubectl apply -f ~/k8s-yaml/mysql/deployment.yaml
kubectl get pod -nmysql

kubectl logs -f -nmysql [POD_NAME]

해당 데이터 볼륨을 확인해보겠습니다.

data 내에 생성된 pvc로 생성된 디렉터리 내부에 MySQL Server에서 생성된 파일들을 확인해봅니다.

MySQL 에 접근하여 데이터 베이스를 생성해보겠습니다.

kubectl get pod -nmysql
kubectl exec -it -nmysql mysql-5c5d695d65-77dxx -- /bin/bash
mysql -u root -p
password : test2024!
CREATE DATABASE test_jckim;

Pod가 강제 종료되어도 해당 data 들을 mount되어 MySQL DB가 실행되어 DB Schema, Table, Data들이 재복구됩니다.

kubectl get pod -nmysql
kubectl delete pod -nmysql mysql-5c5d695d65-77dxx
kubectl logs -f -nmysql mysql-5c5d695d65-jjvd8

다시 MySQL DB에 접속해보겠습니다.

kubectl exec -it -nmysql mysql-5c5d695d65-jjvd8 -- /bin/bash
mysql -u root -p
> show databases;
> CREATE DATABASE test;

이를 통해 NFS Storage class를 활용해 Pod가 재기동되어도 Data가 복구되었음을 확인할 수 있습니다.

감사합니다.