MySQL Operator for Kubernetes #2

kimchigood·2022년 6월 11일
0

DOIK Study

목록 보기
3/5

이전 포스팅에서 Kubernetes 위에 MySQL Operator를 설치하고, WordPress와 연동하는 실습까지 진행해보았다.

이번에는 장애상황 발생 시 MySQL Operator의 작동방식과 Scaling, Backup에 대해 실습을 해보겠다.

1. 장애발생

운영중인 Kubernetes에서 MySQL 파드 한 개가 갑자기 evict 되거나, 돌아가셨을 경우를 예로 들어 테스트해보겠다.

# MySQL IP 변수지정
$ MYSQLIP=$(kubectl get svc -n mysql-cluster mycluster -o jsonpath={.spec.clusterIP})

# watch를 통해 MySQL 클러스터 반복조회
$ while true; do mysql -h $MYSQLIP -uroot -psakila -e "SELECT MEMBER_HOST, MEMBER_ROLE FROM performance_schema.replication_group_members;"; date; sleep 1; done

# 파드 강제삭제
$ kubectl delete pod -n mysql-cluster mycluster-0

# 파드 상태조회
$ kubectl get pod -n mysql-cluster -l app.kubernetes.io/component=database --watch

아래 녹화영상을 통해 장애상황과 MySQL Operator의 대처 능력을 확인할 수 있다.

최초 mycluster-0 파드가 PRIMARY 이고, mycluster-1, mycluster-2는 SECONDARY였는데,

강제로 mycluster-0 파드를 죽이니, mycluster-1 파드가 PRIMARY로 승격되고, 다시 부활한 mycluster-0 파드는 SECONDARY로 역할이 바뀌었다.

영상에는 빠졌지만 장애상황 발생 시 SQL 조회를 계속 돌려도 에러가 나지 않는다.

신기하다.

2. Scaling Test

MySQL을 운영 중에 필요에 따라 파드를 Scale OUT/IN을 해야 할 상황이 생길 수 있다.
그냥 쿠버네티스에서 Deployment 스케일링 하는 것과 유사하다.

#현재 상태 확인
$ kubectl get innodbclusters -n mysql-cluster
NAME        STATUS   ONLINE   INSTANCES   ROUTERS   AGE
mycluster   ONLINE   3        3           1         22h

$ while true; do mysql -h $MYSQLIP -uroot -psakila -e "SELECT MEMBER_HOST, MEMBER_ROLE FROM performance_schema.replication_group_members;"; date; sleep 1; done
mysql: [Warning] Using a password on the command line interface can be insecure.
+-----------------------------------------------------------------+-------------+
| MEMBER_HOST                                                     | MEMBER_ROLE |
+-----------------------------------------------------------------+-------------+
| mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local | PRIMARY     |
| mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY   |
| mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY   |
+-----------------------------------------------------------------+-------------+

# MySQL Pod Scale Out(3-->4)
$ kubectl get innodbclusters -n mysql-cluster mycluster -o yaml | sed -e 's|instances: 3|instances: 4|g' | kubectl apply -f -

# 결과확인
mysql: [Warning] Using a password on the command line interface can be insecure.
+-----------------------------------------------------------------+-------------+
| MEMBER_HOST                                                     | MEMBER_ROLE |
+-----------------------------------------------------------------+-------------+
| mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local | PRIMARY     |
| mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY   |
| mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY   |
| mycluster-3.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY   |
+-----------------------------------------------------------------+-------------+

$ kubectl get innodbclusters -n mysql-cluster
NAME        STATUS   ONLINE   INSTANCES   ROUTERS   AGE
mycluster   ONLINE   4        4           1         22h

innodbclusters의 인스턴스가 4개가 된 것을 확인할 수 있다. while문응 걸어놓은 곳에서는 바로 인스턴스가 늘어난게 확인이 되는데, innodbclusters 리소스는 ONLINE이 되기 까지 조금 시간이 걸렸다.

3. Backup

MySQL Operator는 PVC와 OciObjectStorage를 지원한다. (https://dev.mysql.com/doc/mysql-operator/en/mysql-operator-backups.html)

실습은 PVC로 진행해보자. helm 설치시 backup옵션을 주지 않았기 때문에 패치를 해서 세팅해주자.

# PVC 생성
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: backup-pvc
  namespace: mysql-cluster
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
EOF

# 스케줄 백업 설정
cat <<EOT> backup.yaml
backupProfiles:
- name: dump-instance-profile-pvc
  dumpInstance:
    storage:
      persistentVolumeClaim:
        claimName: backup-pvc
backupSchedules:
- name: schedule-inline
  schedule: "*/5 * * * *"
  deleteBackupData: false
  enabled: true
  backupProfileName: dump-instance-profile-pvc
EOT

#helm upgrade 
$ helm upgrade mycluster mysql-operator/mysql-innodbcluster --reuse-values --namespace mysql-cluster -f backup.yaml

#helm value 값 확인
$ helm get values mycluster
USER-SUPPLIED VALUES:
backupProfiles:
- dumpInstance:
    storage:
      persistentVolumeClaim:
        claimName: backup-pvc
  name: dump-instance-profile-pvc
backupSchedules:
- backupProfileName: dump-instance-profile-pvc
  deleteBackupData: false
  enabled: true
  name: schedule-inline
  schedule: '*/5 * * * *'
credentials:
  root:
    password: sakila
tls:
  useSelfSigned: true

helm을 쓸 때, 이력관리를 위헤 helm pull을 받아서 직접 파일을 수정해서 update를 하고 있는데, 이번 스터디에서 저렇게 일부분만 패치를 하는 방법도 있는 걸 알게되었다.

업데이트 된 내용을 보면 cronjob을 통해서 백업을 해주는 것을 알 수 있다.
자, 그럼 실제 백업이 잘되고 있는 지 확인해보자.

#mysqlbackup 리소스 확인
$ kubectl get mysqlbackup -n mysql-cluster
NAME                                    CLUSTER     STATUS      OUTPUT                                  AGE
mycluster-schedule-inline220611080002   mycluster   Completed   mycluster-schedule-inline220611080002   2m34s

#PVC 확인 (백업수행 시 BOUND 상태가 된다)
$ kubectl get pvc -n mysql-cluster backup-pvc
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
backup-pvc   Bound    pvc-36d79076-004b-49db-a064-e3d8a38463ba   10Gi       RWO            local-path     4m24s

#백업된 PVC의 노드위치 확인
$ kubectl describe pvc -n mysql-cluster backup-pvc | grep selected-node
               volume.kubernetes.io/selected-node: k8s-w2
               
#백업 실제경로 확인
$ kubectl describe pv pvc-36d79076-004b-49db-a064-e3d8a38463ba | grep Path:
    Path:          /opt/local-path-provisioner/pvc-36d79076-004b-49db-a064-e3d8a38463ba_mysql-cluster_backup-pvc
    
    
#해당 노드 접근 후 백업경로 확인
/opt/local-path-provisioner/pvc-36d79076-004b-49db-a064-e3d8a38463ba_mysql-cluster_backup-pvc
└── mycluster-schedule-inline220611080002
    ├── @.done.json
    ├── @.json
    ├── @.post.sql
    ├── @.sql
    ├── @.users.sql
    ├── mysql_innodb_cluster_metadata.json
    ├── mysql_innodb_cluster_metadata.sql
    ├── mysql_innodb_cluster_metadata@async_cluster_members.json
    ├── mysql_innodb_cluster_metadata@async_cluster_members.sql
    ├── mysql_innodb_cluster_metadata@async_cluster_members@@0.tsv.zst
    ├── mysql_innodb_cluster_metadata@async_cluster_members@@0.tsv.zst.idx
    ├── mysql_innodb_cluster_metadata@async_cluster_views.json
    ├── mysql_innodb_cluster_metadata@async_cluster_views.sql
    ├── mysql_innodb_cluster_metadata@async_cluster_views@@0.tsv.zst
    ├── mysql_innodb_cluster_metadata@async_cluster_views@@0.tsv.zst.idx
    ├── mysql_innodb_cluster_metadata@clusters.json
    ├── mysql_innodb_cluster_metadata@clusters.sql
    ├── mysql_innodb_cluster_metadata@clusters@@0.tsv.zst
    ├── mysql_innodb_cluster_metadata@clusters@@0.tsv.zst.idx
    ├── mysql_innodb_cluster_metadata@clusterset_members.json
    ├── mysql_innodb_cluster_metadata@clusterset_members.sql
    ├── mysql_innodb_cluster_metadata@clusterset_members@@0.tsv.zst
    ├── mysql_innodb_cluster_metadata@clusterset_members@@0.tsv.zst.idx
    ├── mysql_innodb_cluster_metadata@clusterset_views.json
    ├── mysql_innodb_cluster_metadata@clusterset_views.sql
    ├── mysql_innodb_cluster_metadata@clusterset_views@@0.tsv.zst
    ├── mysql_innodb_cluster_metadata@clusterset_views@@0.tsv.zst.idx
    ├── mysql_innodb_cluster_metadata@clustersets.json
    ├── mysql_innodb_cluster_metadata@clustersets.sql
    ├── mysql_innodb_cluster_metadata@clustersets@@0.tsv.zst
    ├── mysql_innodb_cluster_metadata@clustersets@@0.tsv.zst.idx
    ├── mysql_innodb_cluster_metadata@instances.json
    ├── mysql_innodb_cluster_metadata@instances.sql
    ├── mysql_innodb_cluster_metadata@instances@@0.tsv.zst
    ├── mysql_innodb_cluster_metadata@instances@@0.tsv.zst.idx
    ├── mysql_innodb_cluster_metadata@router_rest_accounts.json
    ├── mysql_innodb_cluster_metadata@router_rest_accounts.sql
    ├── mysql_innodb_cluster_metadata@router_rest_accounts@@0.tsv.zst
    ├── mysql_innodb_cluster_metadata@router_rest_accounts@@0.tsv.zst.idx
    ├── mysql_innodb_cluster_metadata@routers.json
    ├── mysql_innodb_cluster_metadata@routers.sql
    ├── mysql_innodb_cluster_metadata@routers@@0.tsv.zst
    ├── mysql_innodb_cluster_metadata@routers@@0.tsv.zst.idx
    ├── mysql_innodb_cluster_metadata@schema_version.pre.sql
    ├── mysql_innodb_cluster_metadata@schema_version.sql
    ├── mysql_innodb_cluster_metadata@v2_ar_clusters.pre.sql
    ├── mysql_innodb_cluster_metadata@v2_ar_clusters.sql
    ├── mysql_innodb_cluster_metadata@v2_ar_members.pre.sql
    ├── mysql_innodb_cluster_metadata@v2_ar_members.sql
    ├── mysql_innodb_cluster_metadata@v2_clusters.pre.sql
    ├── mysql_innodb_cluster_metadata@v2_clusters.sql
    ├── mysql_innodb_cluster_metadata@v2_cs_clustersets.pre.sql
    ├── mysql_innodb_cluster_metadata@v2_cs_clustersets.sql
    ├── mysql_innodb_cluster_metadata@v2_cs_members.pre.sql
    ├── mysql_innodb_cluster_metadata@v2_cs_members.sql
    ├── mysql_innodb_cluster_metadata@v2_cs_router_options.pre.sql
    ├── mysql_innodb_cluster_metadata@v2_cs_router_options.sql
    ├── mysql_innodb_cluster_metadata@v2_gr_clusters.pre.sql
    ├── mysql_innodb_cluster_metadata@v2_gr_clusters.sql
    ├── mysql_innodb_cluster_metadata@v2_instances.pre.sql
    ├── mysql_innodb_cluster_metadata@v2_instances.sql
    ├── mysql_innodb_cluster_metadata@v2_router_rest_accounts.pre.sql
    ├── mysql_innodb_cluster_metadata@v2_router_rest_accounts.sql
    ├── mysql_innodb_cluster_metadata@v2_routers.pre.sql
    ├── mysql_innodb_cluster_metadata@v2_routers.sql
    ├── mysql_innodb_cluster_metadata@v2_this_instance.pre.sql
    ├── mysql_innodb_cluster_metadata@v2_this_instance.sql
    ├── wordpress.json
    ├── wordpress.sql
    ├── wordpress@wp_commentmeta.json
    ├── wordpress@wp_commentmeta.sql
    ├── wordpress@wp_commentmeta@@0.tsv.zst
    ├── wordpress@wp_commentmeta@@0.tsv.zst.idx
    ├── wordpress@wp_comments.json
    ├── wordpress@wp_comments.sql
    ├── wordpress@wp_comments@@0.tsv.zst
    ├── wordpress@wp_comments@@0.tsv.zst.idx
    ├── wordpress@wp_links.json
    ├── wordpress@wp_links.sql
    ├── wordpress@wp_links@@0.tsv.zst
    ├── wordpress@wp_links@@0.tsv.zst.idx
    ├── wordpress@wp_options.json
    ├── wordpress@wp_options.sql
    ├── wordpress@wp_options@@0.tsv.zst
    ├── wordpress@wp_options@@0.tsv.zst.idx
    ├── wordpress@wp_postmeta.json
    ├── wordpress@wp_postmeta.sql
    ├── wordpress@wp_postmeta@@0.tsv.zst
    ├── wordpress@wp_postmeta@@0.tsv.zst.idx
    ├── wordpress@wp_posts.json
    ├── wordpress@wp_posts.sql
    ├── wordpress@wp_posts@@0.tsv.zst
    ├── wordpress@wp_posts@@0.tsv.zst.idx
    ├── wordpress@wp_term_relationships.json
    ├── wordpress@wp_term_relationships.sql
    ├── wordpress@wp_term_relationships@@0.tsv.zst
    ├── wordpress@wp_term_relationships@@0.tsv.zst.idx
    ├── wordpress@wp_term_taxonomy.json
    ├── wordpress@wp_term_taxonomy.sql
    ├── wordpress@wp_term_taxonomy@@0.tsv.zst
    ├── wordpress@wp_term_taxonomy@@0.tsv.zst.idx
    ├── wordpress@wp_termmeta.json
    ├── wordpress@wp_termmeta.sql
    ├── wordpress@wp_termmeta@@0.tsv.zst
    ├── wordpress@wp_termmeta@@0.tsv.zst.idx
    ├── wordpress@wp_terms.json
    ├── wordpress@wp_terms.sql
    ├── wordpress@wp_terms@@0.tsv.zst
    ├── wordpress@wp_terms@@0.tsv.zst.idx
    ├── wordpress@wp_usermeta.json
    ├── wordpress@wp_usermeta.sql
    ├── wordpress@wp_usermeta@@0.tsv.zst
    ├── wordpress@wp_usermeta@@0.tsv.zst.idx
    ├── wordpress@wp_users.json
    ├── wordpress@wp_users.sql
    ├── wordpress@wp_users@@0.tsv.zst
    └── wordpress@wp_users@@0.tsv.zst.idx

1 directory, 117 files

엄청나다. MySQL의 아키텍처나 백업 설정을 따로 해주지 않고, 오직 helm에서 value값만 바꿔주었는데, 백업이 되고 있다.

퍼블릭 클라우드를 사용하는 것 만큼 편하지 않은가? 정말 대단한 Kubernetes이다. 오늘도 감탄을 금치 못하는 바이다.

profile
Shout out to Kubernetes⎈

3개의 댓글

comment-user-thumbnail
2022년 6월 11일

동작 확인을 위해서 Youtube 영상제작까지 하셨네요. 내용이 쑥쑥 읽히게 잘 정리해주셨네요.

감탄을 금치 못하시다니, 저 역시 마찬가지입니다! 쿠버네티스는 정말 리눅스 만틈의 표준 OS로 대세가 되어 가고 있네요.

남은 스터디 2주도 잘 부탁드립니다!

1개의 답글
comment-user-thumbnail
2024년 2월 13일

안녕하세요 글 잘읽었습니다. 혹시 백업된 파일로 복구하는 과정도 알고계실까요?

답글 달기