MySQL Operator for Kubernetes은 innoDB 클러스터를 관리한다. DB서버를 관리하는 담당자가 해야할 일들을 자동으로 처리 해준다. 이 기능들을 세 가지 정도로 구분할 수 있다.
1. 장애상황 대응
2. 스케일링
3. 백업
이 글에서는 장애상황 대응 테스트를 해보려고 한다. innoDB cluster, MySQL Operator 두 가지를 설치한 상태해서 테스트 해볼 예정이다. 설치는 “[DOIK] MySQL Operator for k8s 이해하고 설치하기” 포스팅에서 확인 하길 바란다.
[1] 환경구성
장애상황은 Pod레벨, Node레벨 두가지 상황있을 수 있다. 쉽게 말하면 Pod 다운되는 상황과 Node가 다운되어 그 위의 Pod들을 모두 사용할 수 없게 되는 상황이다. 우선 테스트 모니터링을 위해 구성을 살펴보자.
- MySQL Operator for Kubernets (그림 우측) : 1개 이상의 MySQL InnoDB Cluster 관리
- MySQL InnoDB Cluster (그림 좌측) : 쿠버네티스 API 서버를 통해서 관련 리소스 배포
- MySQL Server instances - MySQL 서버
- MySQL Routers - Proxy 역할로 애플리케이션의 쿼리를 서버에 전달
- MySQL Shell : MySQL Router 와 MySQL Server 에 툴 포함, MySQL 클라이언트 보다 좀 더 확장 기능 제공, 클러스터 관리 가능(AdminAPI)
우선 MySQL InnoDB cluster 부터 보자.
- kubectl get all -n mysql-cluster 명령어로 보면, 가장 아래에 보이는 statefulset.apps/mycluster statefulset에 의해 “pod/mycluster-0~2” 3개의 DB Pod가 배포되었다. mycluster-0가 프라이머리, mycluster-1, 2가 세컨더리이다.
- pod/mycluster-router-5f9758dc8f-xkrzx 라는 router를 통해서 쿼리를 받아 분산해준다.
MySQL Operator for Kubernets는 클러스터를 모니터링 하고 관리해준다. 다른 namespace에 배포되었다.
Every 2.0s: kubectl get all -n mysql-cluster k8s-m: Sun Jun 19 14:29:26 2022
NAME READY STATUS RESTARTS AGE
pod/mycluster-0 2/2 Running 0 26m
pod/mycluster-1 2/2 Running 0 26m
pod/mycluster-2 2/2 Running 0 26m
pod/mycluster-router-5f9758dc8f-xkrzx 1/1 Running 0 24m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/mycluster ClusterIP 10.200.1.7 <none> 3306/TCP,33060/TCP,6446/TCP,6448/TCP,6447/TCP,6449/TCP 26m
service/mycluster-instances ClusterIP None <none> 3306/TCP,33060/TCP,33061/TCP 26m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/mycluster-router 1/1 1 1 26m
NAME DESIRED CURRENT READY AGE
replicaset.apps/mycluster-router-5f9758dc8f 1 1 1 26m
NAME READY AGE
statefulset.apps/mycluster 3/3 26m
[2] Mysql InnoDB Cluster 내부 데이터 복제
Mysql InnoDB Cluster 내부에서 데이터가 복제된다. Write 허용되어 있는 Primery mycluster-0에 data를 넣으면, Secondery 인 mycluster-1, 2에 data가 복제된다. mycluster-0 ~ 2 내부 데이터가 들어가는 것을 볼 수 있도록 하고, Primery mycluster-0에 데이터를 넣어보자.
myclient1 이름의 파드 1대 배포를 한다.
PODNAME=myclient1 envsubst < ~/DOIK/2/myclient.yaml | kubectl apply -f -
파드 내용은 다음과 같아서 ${PODNAME}가 치환되어 배포가 된다.
cat ~/DOIK/2/myclient.yaml
───────┬────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ File: /root/DOIK/2/myclient.yaml
───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ apiVersion: v1
2 │ kind: Pod
3 │ metadata:
4 │ name: ${PODNAME}
5 │ labels:
6 │ app: myclient
7 │ spec:
8 │ nodeName: k8s-m
9 │ containers:
10 │ - name: ${PODNAME}
11 │ image: mysql:8.0.29
12 │ command: ["tail"]
13 │ args: ["-f", "/dev/null"]
14 │ terminationGracePeriodSeconds: 0
───────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
myclient 파드 추가로 2대 배포 한다.
for ((i=2; i<=3; i++)); do PODNAME=myclient$i envsubst < ~/DOIK/2/myclient.yaml | kubectl apply -f - ; done
접근을 위한 접속 주소 변수 설정을 해준다.
MIC=mycluster.mysql-cluster.svc.cluster.local
MDB1=mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local
MDB2=mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local
MDB3=mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local
MYSQLIP=$(kubectl get svc -n mysql-cluster mycluster -o jsonpath={.spec.clusterIP})
테스트를 위한 test 데이터베이스, 테이블을 만들고 insert해서 데이터 하나를 넣었다.
mysql -h $MYSQLIP -uroot -psakila <<EOF
CREATE DATABASE test;
CREATE TABLE test.t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL);
INSERT INTO test.t1 VALUES (1, 'Luis');
EOF
위에서 만든 데이터가 잘 넣어졌다.
mysql -h $MYSQLIP -uroot -psakila -e "SELECT * FROM test.t1;"
+----+------+
| c1 | c2 |
+----+------+
| 1 | Luis |
+----+------+
모니터링을 위해 새로운 쉘 3개를 열고 변수지정을 해준다.
# 모니터링
MIC=mycluster.mysql-cluster.svc.cluster.local
MDB1=mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local
MDB2=mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local
MDB3=mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local
MYSQLIP=$(kubectl get svc -n mysql-cluster mycluster -o jsonpath={.spec.clusterIP})**
모니터링 준비가 끝났으면, 아래 명령어를 보내서 마스터노드에서 DB에 97개의 데이터를 INSERT해보자.
for ((i=3; i<=100; i++)); do mysql -h $MYSQLIP -uroot -psakila -e "SELECT @@HOSTNAME;INSERT INTO test.t1 VALUES ($i, 'Luis$i');";echo; done
mysql -h $MYSQLIP -uroot -psakila -e "SELECT * FROM test.t1;"
프라이머리에 data가 들어가고, 세컨더리에 복제가 잘되는 걸 볼 수 있다.
[3] 장애상황 테스트
3-1. Pod
쿼리 중에 Pod를 삭제해보거나 Node를 drain하여 내부의 Pod들이 다른 Node로 쫒겨나게 해본다. 그 Pod들은 다른 노드들에서 재 생성한다.
새로운 쉘을 열었다면 잊지 않고 mysql router의 주소를 변수 설정 해준다.
MYSQLIP=$(kubectl get svc -n mysql-cluster mycluster -o jsonpath={.spec.clusterIP})
DB 모니터링 설정 한다. 아래 출력을 보면 복제 토폴로지의 VIEW_ID는 리비전 숫자라고 보면 된다. 구성의 변경이 몇번 있었는지 알수 있다. PRIMARY는 mycluster-1, SECONDARY는 mycluster-1, 2인 것을 확인 할 수 있다.
watch -d 'kubectl get pod -o wide -n mysql-cluster;echo;kubectl get pod -o wide'
while true; do mysql -h $MYSQLIP -uroot -psakila -e 'SELECT VIEW_ID FROM performance_schema.replication_group_member_stats LIMIT 1;SELECT MEMBER_HOST, MEMBER_ROLE FROM performance_schema.replication_group_members;'; date;sleep 1; done
# 출력
+---------------------+
| VIEW_ID |
+---------------------+
| 16556249665488370:3 |
+---------------------+
+-----------------------------------------------------------------+-------------+
| MEMBER_HOST
| MEMBER_ROLE |
+-----------------------------------------------------------------+-------------+
| mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local | PRIMARY |
| mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY |
| mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY |
+-----------------------------------------------------------------+-------------+
그럼 쿼리를 해본다.
for ((i=1001; i<=5000; i++)); do mysql -h $MYSQLIP -uroot -psakila -e "SELECT NOW();INSERT INTO test.t1 VALUES ($i, 'Luis$i');";echo; done
쿼리 중 프라이머리 mycluster-0을 죽이면 프라이머리가 바뀌고 잠시 insert 오류가 있다가 다시 정상 inster된다.
kubectl delete pod -n mysql-cluster mycluster-0 && kubectl get pod -n mysql-cluster -w
모니터링을 보면 PRIMARY가 변경된 것을 볼 수 있다. SECONDARY중의 하나인 mycluster-0가 승격되어 PRIMARY역할을 이어받아 수행한다.
+---------------------+
| VIEW_ID |
+---------------------+
| 16556249665488370:5 |
+---------------------+
+-----------------------------------------------------------------+-------------+
| MEMBER_HOST | MEMBER_ROLE |
+-----------------------------------------------------------------+-------------+
| mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY |
| mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local | PRIMARY |
| mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY |
+-----------------------------------------------------------------+-------------+
3-2. Node
A. 첫번째 노드 drain
우선 도메인 질의가 문제 없도록 이중화를 위해 미리 늘려놓는다.
kubectl scale deployment -n kube-system coredns --replicas=3
쿼리를 추가로 진행 한다.
for ((i=5001; i<=10000; i++)); do mysql -h $MYSQLIP -uroot -psakila -e "SELECT NOW();USE test;INSERT INTO t1 VALUES ($i, 'Luis$i');";echo; done
노드 2번을 drain 한다.
kubectl drain k8s-w2 --ignore-daemonsets --delete-emptydir-data
EC2 상태 확인을 해보면 drain 노드가 SchedulingDisabled 상태이다.
kubectl get node
NAME STATUS ROLES AGE VERSION
k8s-m Ready control-plane,master 3h57m v1.23.6
k8s-w1 Ready <none> 3h57m v1.23.6
k8s-w2 Ready,SchedulingDisabled <none> 3h56m v1.23.6
k8s-w3 Ready <none> 3h57m v1.23.6
2번에 있었던 Pod 상태를 확인 해보면 Pending 상태가 되어 있다가 다른 node에서 뜨게 된다.
kubectl get pod -n mysql-cluster -l app.kubernetes.io/component=database -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mycluster-0 0/2 Pending 0 3m44s <none> <none> <none> 0/2
mycluster-1 2/2 Running 0 14m 172.16.2.6 k8s-w3 <none> 2/2
mycluster-2 2/2 Running 0 3h48m 172.16.1.4 k8s-w1 <none> 2/2
A. 두번째 노드 drain
PDB 정책은 최소 정상 노드 개수 상태를 나타낸다. 이미 하나의 노드가 drain되었기 때문에 PDB 정책에 의해 두번째 노드를 drain하려고 해도 되지 않는다. 이 설정이 삭제 되야 노드를 하나 삭제할 수 있다.
정책을 확인해 보면 PDB UNAVAILABLE, 비정상 상태로 될 수 있는 노드는 1개이다.
kubectl get pdb -n mysql-cluster
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
mycluster-pdb N/A 1 0 49m
이 정책을 삭제하고 다른 노드도 drain 해본다.
kubectl delete pdb -n mysql-cluster mycluster-pdb
kubectl drain k8s-w3 --ignore-daemonsets --delete-emptydir-data
정상복귀를 하려면 kubectl uncordon <노드> 명령어를 사용하면 된다.
kubectl uncordon k8s-w2
kubectl uncordon k8s-w3
node/k8s-w2 uncordoned
node/k8s-w3 uncordoned
다시 Pod들을 확인해보면 모두 정상상태가 되었다.
'스터디' 카테고리의 다른 글
[DOIK] Cloud Native PostgreSQL Operator (0) | 2022.06.26 |
---|---|
[DOIK] MySQL Operator for k8s 이해하고 설치하기 (0) | 2022.06.12 |
[DOIK] MySQL innoDB 클러스터 (0) | 2022.06.12 |