1. 개요
부끄럽지만, innoDB 클러스터라는 이름을 처음 들었을 때, 되게 낯설었고, 다른 DBMS 종류의 클러스터인 줄 알았는데, MySQL에서 고가용성을 위해 만든 서비스라고 한다. 왠지 반가웠다.
MySQL에서 기본적으로 사용하는 innoDB 스토리지 엔진을 기반으로 동작한다고 해서 innoDB Cluster라는 이름이 붙여졌다고 한다.
사내의 개발환경에서는 데이터베이스를 컨테이너로 사용하고 있지만, 운영환경만큼은 아니었다.
개발 환경내에서 데이터베이스를 컨테이너로 사용해 보면서, 가용성 측면이나 자동화 등 좋은 부분들이 정말 많았지만 가장 중요한 데이터 안정성 등을 보장하기가 어려웠고 불안했다.
하지만 이번 innoDB Cluster를 공부해보면서 어쩌면 DB를 컨테이너로 도입에 하는데 한 걸음 나아갈 수 있을 것 같다.
2. InnoDB 클러스터란?
mysql 5.7.17 버전부터 Failover, 이중화 구조, 수동 설정 등, 안정성에 대한 문제들을 편리하게 해결할 수 있도록 빌트인 고가용성 솔루션 innoDB 클러스터를 도입하였다. 다양한 써드파티 제품들이 많지만 innoDB Cluster는 mysql에서 직접 개발하였기 때문에 더욱 믿고 쓸 만하다.
2.1 InnoDB 클러스터
mysql innodb cluster
- MySQL 서버들은 그룹 복제 형태로 복제가 구성된다.
- 각 서버들은 Primary(읽기,쓰기-rw), Secondary(읽기 - r)로 구성이 되고, 구성하는 방식에 따라 나눠진다.
- 복제 그룹이 정상적으로 동작하려면 최소 3대 이상의 구성이 필요하다
그룹 복제
- 기존 MySQL에서의 기본적인 데이터 복제 뿐만이 아니라 멤버십 관리( 멤버 추가 및 제거) 역할을 담당한다.
MySQL 라우터
- 애플리케이션 서버와 MySQL 서버 사이에서 MySQL의 프록시 역할을 한다.
- 클라이언트는 각 서버로 접근하는 것이 아닌 MySQL 라우터에 연결해서 쿼리를 실행한다.
- 클라이언트는 각 서버의 정보를 알 필요가 없고, MySQL 라우터의 정보만 알면 됨
MySQL 쉘
- 기존의 mysql client과 달리 SQL 이외에도 자바스크립트, 파이썬 기반의 스크립트 작성 등 확장된 기능을 제공한다
2.2 MySQL Operator for Kubernetes
- MySQL 8.0.29 버전과 같이 GA가 되었고, 1개 이상의 MySQL InnoDB Cluster를 관리해 준다.
3. InnoDB 클러스터 실습
innoDB 클러스터는 mysql-operator와 함께 설치되어야 한다.
3.1 Mysql operator 설치
# 레포 추가
helm repo add mysql-operator https://mysql.github.io/mysql-operator/
helm repo update
# 설치
helm install mysql-operator mysql-operator/mysql-operator --namespace mysql-operator --create-namespace --version 2.0.12
# 설치 확인
kubectl get deployment,pod,service -n mysql-operator
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/mysql-operator 1/1 1 1
NAME READY STATUS RESTARTS AGE
pod/mysql-operator-8fdcd47d9-w7v4p 1/1 Running 0
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/mysql-operator ClusterIP 10.233.248.223 <none> 9443/TCP
# CRD 확인
kubectl get crd | egrep 'mysql|zalando'
clusterkopfpeerings.zalando.org 2023-10-23T13:12:03Z
innodbclusters.mysql.oracle.com 2023-10-23T13:12:03Z
kopfpeerings.zalando.org 2023-10-23T13:12:03Z
mysqlbackups.mysql.oracle.com 2023-10-23T13:12:03Z
3.2 MySQL InnoDB Cluster 설치
※ default 스토리지 클래스가 있으므로 PVC 설정은 생략한다.
# my.cnf 파라미터 파일 생성
cat <<EOT> mycnf-values.yaml
credentials:
root:
password: sakila
serverConfig:
mycnf: |
[mysqld]
max_connections=300
default_authentication_plugin=mysql_native_password
tls:
useSelfSigned: true
EOT
# 설치
helm install mycluster mysql-operator/mysql-innodbcluster --namespace mysql-cluster --version 2.0.12 -f mycnf-values.yaml --create-namespace
# 설치 확인
ubectl get innodbcluster,sts,pod,pvc,svc,pdb,all -n mysql-cluster
NAME STATgUS ONLINE INSTANCES ROUTERS AGE
innodbcluster.mysql.oracle.com/mycluster ONLINE 3 3 1 4d21h
NAME READY AGE
statefulset.apps/mycluster 3/3 4d21h
NAME READY STATUS RESTARTS AGE
pod/mycluster-0 2/2 Running 0 4d21h
pod/mycluster-1 2/2 Running 0 4d21h
pod/mycluster-2 2/2 Running 0 4d21h
pod/mycluster-router-5756bbd88c-4znh2 1/1 Running 0 4d21h
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/data-my-wordpress-mariadb-0 Bound pvc-6dbca6ee-71d8-4dfd-a0de-70bb18706cff 8Gi RWO cmp 3d22h
persistentvolumeclaim/datadir-mycluster-0 Bound pvc-a1a3ccc5-a89e-49b1-9063-0d33cc257e4a 2Gi RWO cmp 4d21h
persistentvolumeclaim/datadir-mycluster-1 Bound pvc-092c0c0b-e8f4-4330-8445-77ceeffd13ec 2Gi RWO cmp 4d21h
persistentvolumeclaim/datadir-mycluster-2 Bound pvc-bba23644-fcb6-48bc-bec6-bd44096c98d0 2Gi RWO cmp 4d21h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/mycluster ClusterIP 10.233.199.159 <none> 3306/TCP,33060/TCP,6446/TCP,6448/TCP,6447/TCP,6449/TCP,8443/TCP 4d21h
service/mycluster-instances ClusterIP None <none> 3306/TCP,33060/TCP,33061/TCP 4d21h
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
poddisruptionbudget.policy/mycluster-pdb N/A 1 1 4d21h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/mycluster-router 1/1 1 1 4d21h
NAME DESIRED CURRENT READY AGE
replicaset.apps/mycluster-router-5756bbd88c 1 1 1 4d21h
3.3 MySQL InnoDB Cluster 접속
MySQL 라우터를 통해 MySQL 파드로 접속해 보자
SQL 이외에도 자바스크립트, 파이썬 기반의 스크립트 작성 등 확장된 기능을 제공한다
# MySQL 라우터를 통한 MySQL 파드 접속(기본 패스워드는 sakilad)
# kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$innoDB 클러스터 헤드리스 서비스 --password=sakila
# mysql
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@mycluster.mysql-cluster.svc.cluster.local --password=sakila --sqlx
# js
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@mycluster.mysql-cluster.svc.cluster.local --password=sakila --js'
# python
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@mycluster.mysql-cluster.svc.cluster.local --password=sakila --python;'
# 빠져나기기
\exit
4. 복제 테스트
샘플 대용량 데이터베이스를 주입해 보고, 각 데이터베이스 서버 파드가 잘 복제가 되는지 확인해 보자
# 샘플 데이터 베이스 git clone
git clone https://github.com/datacharmer/test_db && cd test_db/
# mysql-client 설치
apt-get install -y mysql-client
# mysql client가 붙기 위해 포트포워딩
kubectl -n mysql-cluster port-forward service/mycluster mysql
# 샘플 데이터 덤프
mysql -h127.0.0.1 -P3306 -uroot -psakila -t < employees.sql
# 접속 주소 변수로 지정
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
# 개별 MySQL 파드 접속 : 헤드리스 서비스
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MDB1 --password=sakila --sqlx --execute='SELECT @@hostname;'
mycluster-0
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MDB2 --password=sakila --sqlx --execute='SELECT @@hostname;'
mycluster-1
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MDB3 --password=sakila --sqlx --execute='SELECT @@hostname;'
mycluster-2
각 개별 파드에 접속해서 데이터를 확인해 보면 정상적으로 잘 복제가 되는 것을 볼 수 있다.
5. 장애 테스트
primary 파드를 강제로 삭제해보고 mysql의 멤버와 역할을 확인해보고, 간단하게 조회 쿼리가 동작하는지도 확인해보자
# mysql 서버 파드 확인
$ k get pod
NAME READY STATUS RESTARTS AGE
mycluster-0 2/2 Running 0 86m
mycluster-1 2/2 Running 0 5m26s
mycluster-2 2/2 Running 0 7m43s
mycluster-router-7b87c7657f-s7jtx 1/1 Running 0 82m
# 역할 확인
$ mysql -h 127.0.0.1 -uroot -psakila -e "SELECT MEMBER_HOST, MEMBER_ROLE FROM
performance_schema.replication_group_members;"; date; sleep 1;
mysql: [Warning] Using a password on the command line interface can be insecure.
+-----------------------------------------------------------------+-------------+
| 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 |
+-----------------------------------------------------------------+-------------+
# primary 파드 삭제
kubectl delete pod -n mysql-cluster mycluster-0
# 역할 확인
# 0번 서버가 멤버에서 없어지고, 1번이 primary로 승격되는 것을 확인
$ mysql -h 127.0.0.1 -uroot -psakila -e "SELECT MEMBER_HOST, MEMBER_ROLE FROM performance_schema.replication_group_members;"; date; sleep 1;
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-2.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY |
+-----------------------------------------------------------------+-------------+
# 조회 쿼리 확인
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@mycluster.mysql-cluster.svc.cluster.local --password=sakila --sqlx --execute "USE employees;SELECT * FROM employees LIMIT 5;"
WARNING: Using a password on the command line interface can be insecure.
emp_no birth_date first_name last_name gender hire_date
10001 1953-09-02 Georgi Facello M 1986-06-26
10002 1964-06-02 Bezalel Simmel F 1985-11-21
10003 1959-12-03 Parto Bamford M 1986-08-28
10004 1954-05-01 Chirstian Koblick M 1986-12-01
10005 1955-01-21 Kyoichi Maliniak M 1989-09-12
# 0번 서버가 살아나면 다시 멤버에 조인이 된다.
mysql -h 127.0.0.1 -uroot -psakila -e "SELECT MEMBER_HOST, MEMBER_ROLE FROM
performance_schema.replication_group_members;"; date; sleep 1;
mysql: [Warning] Using a password on the command line interface can be insecure.
+-----------------------------------------------------------------+-------------+
| 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 |
+-----------------------------------------------------------------+-------------+
6. 스케일(Scale) 테스트
MySQL 서버(인스턴스)와 라우터를 쉽게 스케일링하고 innodbclusters라는 CR을 통해 상태까지 쉽게 확인할 수 있다.
# MySQL InnoDB Cluster 정보확인 - 서버파드(인스턴스)는 3대, 라우터파드(인스턴스)는 1대
$ kubectl get innodbclusters -n mysql-cluster
NAME STATUS ONLINE INSTANCES ROUTERS AGE
mycluster ONLINE 3 3 1 101m
# 파드 확인
$ kubectl get pod -n mysql-cluster
NAME READY STATUS RESTARTS AGE
mycluster-0 2/2 Running 9 (4m53s ago) 104m
mycluster-1 2/2 Running 0 23m
mycluster-2 2/2 Running 0 26m
mycluster-router-7b87c7657f-s7jtx 1/1 Running 0 100m
# mysql 라우터 파드 1대 추가
helm upgrade mycluster mysql-operator/mysql-innodbcluster --reuse-values --set serverInstances=2 --namespace mysql-cluster
$ kubectl get innodbclusters -n mysql-cluster
NAME STATUS ONLINE INSTANCES ROUTERS AGE
mycluster ONLINE 3 3 2 102m
# 파드 확인
$ kubectl get pod -n mysql-cluster
NAME READY STATUS RESTARTS AGE
mycluster-0 2/2 Running 9 (4m53s ago) 104m
mycluster-1 2/2 Running 0 23m
mycluster-2 2/2 Running 0 26m
mycluster-router-7b87c7657f-s7jtx 1/1 Running 0 100m
mycluster-router-86d876fc24-cdxlc 1/1 Running 0 1m
# mysql 서버 파드 1대 삭제
helm upgrade mycluster mysql-operator/mysql-innodbcluster --reuse-values --set serverInstances=2 --namespace mysql-cluster
$ kubectl get innodbclusters -n mysql-cluster
NAME STATUS ONLINE INSTANCES ROUTERS AGE
mycluster ONLINE 3 2 2 103m
# 파드 확인
$ kubectl get pod -n mysql-cluster
NAME READY STATUS RESTARTS AGE
mycluster-0 2/2 Running 9 (4m53s ago) 104m
mycluster-1 2/2 Running 0 23m
mycluster-router-7b87c7657f-s7jtx 1/1 Running 0 100m
mycluster-router-86d876fc24-cdxlc 1/1 Running 0 1m
7. 느낀 점
postion, binarylog, GTID 등 사실 낯익은 용어들이었다.
과거 프로젝트에서 데이터베이스를 구축하고 Source-Replica 구조로 이중화해 본 구축 경험이 있었다. 데이터베이스는 너무 중요한 요소이기에 무서웠고, 복잡하다고 느껴졌다.
그저 실수만 하지 말자는 생각에 사수의 스크립트를 보고 생각 없이 무작정 복사 붙여넣기만 했었다. 하지만 이번에 innoDB에 대해 포스팅하면서 트랜잭션 및 복제 등 데이터베이스를 공부해 보고 조금이나마 개념을 알고 나니 과거의 작업들이 이제야 어느 정도 이해가 되는 것 같았다. ( 역시 무엇이든 겁먹지 말고 일단 해보는 게 중요한 것 같다.)
또한 데이터베이스를 이중화하는 게 쉽지 않았는데 proxysql, haproxy, maxscale, galera 등 다양한 서비스를 사용해 보면서
깊게는 아니지만 HA를 구성하는 게 엄청나게 고려할게 많고 어렵게 느껴져 손놓고만 있었는데, innodb를 사용한다면 데이터베이스 엔지니어뿐만 아니라, 개발자도 충분히 데이터베이스를 운영할 수 있을 것 같다.
innodb Cluster를 실습해 보면서 Operator 등의 강력함과 많은 발전을 느꼈고 , 더욱더 데이터베이스와, 오퍼레이터에 관심 가져 볼만하다는 생각이 들었다.
'Kubernetes > Database Operator' 카테고리의 다른 글
Kafka Operator(Strimzi) - DOIK2_5주차 (0) | 2023.11.19 |
---|---|
MongoDB Operator - Percona Operator for MongoDB(PSMDB) - DOIK 4주차 (3) | 2023.11.12 |
PostgreSQL Operator - CloudNative PostgreSQL - DOIK2_3주차 (0) | 2023.11.05 |
쿠버네티스 오퍼레이터란? (Kubernetes Operator) - DOIK_2주차 1 (2) | 2023.10.28 |
쿠버네티스 스테이트풀셋이란? (Kubernetes Statefulset) - DOIK2_1주차 (3) | 2023.10.21 |