카테고리 없음

오퍼레이터 & MySQL 오퍼레이터

참도다리 2023. 10. 29. 03:21
-목차-
1. Kubernetes Operator 소개
2. MySQL 8.0
3. InnoDB Cluster
4. MySQL Operator for Kubernetes
5. MySQL Operator for Kubernetes 설치
6. 워드프레스
7. 장애테스트
8. 스케일 테스트

 

1. Kubernetes Operator

쿠버네티스 오퍼레이터란?

오퍼레이터의 동작원리를 이해하기 위해 기존 쿠버네티스의 동작 흐름과 오퍼레이터의 동작흐름을 비교해보자

 

                                       <쿠버네티스 동작 흐름>                                                         <오퍼레이터 동작 흐름>

&amp;lt;쿠버네티스 동작 흐름&amp;gt;

쿠버네티스의 동작 흐름을 보면 yaml파일을 통해 배포할 리소스를 선언시 중간에 위치한 컨트롤러가 선언한 대로 배포되어있는지에 대해 루프를 돌며 끊임없이 모니터링하고 관리하는데, 이러한 동작 흐름을 오퍼레이터에서 도입한다.

기존 쿠버네티스에서 정의되어있는 오브젝트들인 Pod,Deploymnet 등 외에 별도로 커스텀 리소스를 직접 선언하는 것이 가능한데,

예를 들어 mysql master 서버를 커스텀 리소스로 선언해놓고 컨트롤러가 해당 리소스를 모니터링하고 관리하도록 할 수 있다.

이를 오퍼레이터를 개발한다 라고 표현한다.

하지만 오퍼레이터를 개발하는 것은 쉽지 않기 때문에 이미 개발되어 있는 오퍼레이터들이 있다.

OperatorHub에서 제공하는 데이터베이스 카테고리에서 여러가지 오퍼레이터 확인해보고 활용해보자.

주의해야 할 사항은 Capability Levels(성숙도) 확인이 필요하다는 점이다.

레벨은 1부터 5까지 있으며, 숫자가 높을수록 쓸만한 오퍼레이터라고 보면 된다.

 

2. MySQL 8.0 - Real MySQL 8.0

MySQL 8.0.23버전부터 적용되는 사항으로 복제 관련 용어가 변경된다.

Master -> Source(Primary)

Slave -> Replica(Secondary)

이에 따라 해당 용어가 포함되던 명령어들도 수정되었으니 참고하자.

 

 

3. InnoDB Cluster

InnoDB Cluster의 등장배경

작년에 카카오 데이터센터에 장애가 발생하여 복구기간 5일이 소요되었다.

복구과정이 더뎠던기 때문에 이후에는 해결책으로 Master-Slave구조에서 Cluster 구조로 변경하게 된다.

예를 들어 세개의 데이터 센터 중 두개의 데이터 센터가 문제가 생겼을 경우까지 처리하기 위해서는 결국 Cluster 기반의 아키텍처가 필요하다. Primary가 문제가 되더라도, 빠르게 다음 Primary가 올라와야하기 때문.

이미지 출처 : 카카오의 다짐 - 우리가 부족했던 이유(https://www.kakaocorp.com/page/detail/9902)

MySQL 서버 자체적으로는 페일오버 Failover 를 처리하는 기능을 제공하지 않는다.

따라서 사용자는 장애가 발생했을 때 레플리카 서버가 새로운 소스 서버가 될 수 있도록 아래와 같은 작업들을 직접 수행해야한다.

 

1) Replica Server에 설정된 Read 모드를 해제해야 한다.
2 Split-Brain 현상을 방지하기 위해 장애가 발생한 Source Server에서 데이터 변경을 실행하지 못하도록 해야 한다.

3)Application 서버에서는 새로운 Source Server를 바라보도록 커넥션 설정을 변경해야 한다.

 

이러한 작업들은 모두 수동으로 처리할 수 밖에 없으며 완료되기까지 적지 않은 시간이 소요된다.

MySQL 5.7.17 버전에서 빌트인 형태의 HA 솔루션인 InnnDB 클러스터가 도입되면서 사용자는 좀 더 편리하게 고가용성을 실현할 수 있게 됐다.

 

Group Replication

기존에는 디비가 Master-Slave의 동기식으로 복제가 되었는데, 그룹형태로 복제할 수 있는 형태로 동작하는것이다.

Source Server의 데이터를 Replica Server 로 동기화하는 기본적인 복제 역할뿐만 아니라 복제에 참여하는 MySQL 서버들에 대한 멤버쉽 관리(그룹에 새로운 멤버의 추가 및 제거 등)을 자동화하여 관리한다.

 

MySQL Router(이중화 가능)

애플리케이션 서버와 MySQL 서버 사이에서 동작하는 미들웨어 프로그램으로 애플리케이션이 실행한 쿼리(Read/Write)를 적절한 MySQL Server (Pod)로 전달하는 프락시 Proxy 역할을 한다.

 

MySQL 셸

기존 MySQL 클라이언트보다 좀 더 확장된 기능을 가진 새로운 클라이언트 프로그램이며 기본적인 SQL문 실행뿐만 아니라 자바스크립트 및 파이썬 기반의 스크립트 작성 기능과 MySQL 서버에 대한 클러스터 구성 등의 어드민 작업을 할 수 있게 하는 API AdminAPI 를 제공한다.

 

MySQL Operator for Kubernetes

MySQL 8.0 버전과 함께 쿠버네티스 환경에서 오퍼레이터로 동작할 수 있도록  GA되었음.

MySQL 오퍼레이터가 하나 떠있어야 하며(Operator 통해서 CRD 선언시 파드배포됨)

StatefulSet으로 관리된다.

 

MySQL Operator 설치하기 - with Helm

# Repo 추가
helm repo add mysql-operator https://mysql.github.io/mysql-operator/
helm repo update

# 실험 버전 설치 : 차트 버전(--version 2.1.0),  애플리케이션 버전(8.1.0-2.1.4) >> 해당 버전은 '23.10.28 현재 삭제됨 Innovation Release 라서 역시 실습으로도 쓰지 말것
helm install mysql-operator mysql-operator/mysql-operator --namespace mysql-operator --create-namespace --version 2.0.12
helm get manifest mysql-operator -n mysql-operator

# 설치 확인
kubectl get deploy,pod -n mysql-operator

# CRD 확인 - 오퍼레이터에 의해서 CRD가 4개 생성된점  확인
kubectl get crd | egrep 'mysql|zalando'

## CRD 상세 정보 확인
kubectl describe crd innodbclusters.mysql.oracle.com

InnoDB Cluster 설치하기 - with Helm

# 파라미터 파일 생성 
# 파라미터 수정 - MySQL에 접근하는 루트계정에 패스워드, ServerConfig(접속인증관련)
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

# 차트 설치(기본값) : root 사용자(root), 호스트(%), 서버인스턴스(MYSQL서버. 파드 3개), 라우터인스턴스(MYSQL Router. 파드 1개), serverVersion(8.0.35)
# root 사용자 암호(sakila), tls.useSelfSigned(사용), 네임스페이스 생성 및 적용(mysql-cluster)

helm install mycluster mysql-operator/mysql-innodbcluster --namespace mysql-cluster --version 2.0.12 -f mycnf-values.yaml --create-namespace
helm get values mycluster -n mysql-cluster

# 스토리지클래스 gp3(default)여부 확인
k get sc

# 디스크에서 사용중인 용량확인 
k df-pv

helm get manifest mycluster -n mysql-cluster

# (옵션) 모니터링 << pv 는 왜 mysql-cluster 네임스페이스내에서 안 보일지? 
watch kubectl get innodbcluster,sts,pod,pvc,svc -n mysql-cluster

# 설치 확인
kubectl get innodbcluster,sts,pod,pvc,svc,pdb,all -n mysql-cluster
kubectl df-pv
kubectl resource-capacity

## MySQL InnoDB Cluster 구성요소 확인
kubectl get InnoDBCluster -n mysql-cluster
NAME        STATUS   ONLINE   INSTANCES   ROUTERS   AGE
mycluster   ONLINE   3        3           1         6m20s

## 이벤트 확인
kubectl describe innodbcluster -n mysql-cluster | grep Events: -A30

## MySQL InnoDB Cluster 초기 설정 확인
kubectl get configmap -n mysql-cluster mycluster-initconf -o json | jq -r '.data["my.cnf.in"]'
kubectl get configmap -n mysql-cluster mycluster-initconf -o yaml | yh
kubectl describe configmap -n mysql-cluster mycluster-initconf

# 그룹 리플리케이션의 세팅내용 확인 
01-group_replication.cnf:
----
# GR and replication related options
# Do not edit.
[mysqld]
log_bin=mycluster
enforce_gtid_consistency=ON
gtid_mode=ON                    # 그룹 복제 모드 사용을 위해서 GTID 활성화
relay_log_info_repository=TABLE # 복제 메타데이터는 데이터 일관성을 위해 릴레이로그를 파일이 아닌 테이블에 저장
skip_slave_start=1


# 파라미터 튜닝 적용된점 확인
99-extra.cnf:
----
# Additional user configurations taken from spec.mycnf in InnoDBCluster.
# Do not edit directly.
[mysqld]
 max_connections=300            # max_connections default 기본값은 151
 default_authentication_plugin=mysql_native_password


## 서버인스턴스 확인(스테이트풀셋) : 3개의 노드에 각각 파드 생성 확인, 사이드카 컨테이너 배포
# 배포된 mysql 서버 확인
kubectl get sts -n mysql-cluster; echo; kubectl get pod -n mysql-cluster -l app.kubernetes.io/component=database -owide

## 프로브 확인(Readiness, Liveness, Startup)
# 3가지 설정 확인가능
kubectl describe pod -n mysql-cluster mycluster-0 | egrep 'Liveness|Readiness:|Startup'

## 서버인스턴스가 사용하는 PV(PVC) 확인 : AWS EBS 볼륨 확인해보기
kubectl get sc
kubectl df-pv
kubectl get pvc,pv -n mysql-cluster

## 서버인스턴스 각각 접속을 위한 헤드리스 Headless 서비스 확인
# mysql pod 중  특정 파드에 접속해야 할 경우 지정하기 위한것이 헤드리스 서비스이다.
# 헤드리스 서비스의 선언은 클러스터 선언시 클러스터IP를 선언하지 않으면 된다.
kubectl describe svc -n mysql-cluster mycluster-instances

kubectl get svc,ep -n mysql-cluster mycluster-instances
NAME                  TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                        AGE
mycluster-instances   ClusterIP   None         <none>        3306/TCP,33060/TCP,33061/TCP   19m

## 라우터인스턴스(디플로이먼트) 확인  : 1대의 파드 생성 확인
# 단일 접속지점을 만들기 위해서 Router가 필요한데, ReadOnly인 세컨더리 Mysql서버가 10대일 수 있으니 앞단의 라우터가 동적으로 멤버관리해야한다. 
# 확인하기 - Router가 하나 배포되어있어야 하며, 얘는 ClusterIP로 접근가능하다.
kubectl get deploy -n mysql-cluster;kubectl get pod -n mysql-cluster -l app.kubernetes.io/component=router

# Router에 접근해보기
## 라우터인스턴스 접속을 위한 서비스(ClusterIP) 확인
kubectl get svc,ep -n mysql-cluster mycluster

# max_connections 설정 값 확인 : MySQL 라우터를 통한 MySQL 파드 접속 >> Helm 차트 설치 시 파라미터러 기본값(151 -> 300)을 변경함
MIC=mycluster.mysql-cluster.svc.cluster.local
echo "export MIC=mycluster.mysql-cluster.svc.cluster.local" >> /etc/profile
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MIC --password=sakila --sqlx --execute="SHOW VARIABLES LIKE 'max_connections';"

MYSQL Server 접속하기 (Headless 서비스 주소로 내가 원하는 개별 MySQL 서버(파드)로 직접 접속하기)

# MySQL 라우터 접속을 위한 서비스 접속정보 확인하기
# 실습 환경은 Cluster-IP Type
kubectl get svc -n mysql-cluster mycluster

# MySQL 서버(파드) 접속을 위한 서비스 정보 확인 : Headless 서비스
# Cluster IP가 없을것
kubectl get svc -n mysql-cluster mycluster-instances
kubectl get pod -n mysql-cluster -l app.kubernetes.io/component=database -owide

# netshoot 파드에 zsh 접속해서 DNS 쿼리 수행
# 도메인접속할건데 도메인 주소가 어떤 주소인지 정보 확인하기.netwhoot이라는 pod 이미지로 임시로 수행하고 삭제
kubectl run -it --rm netdebug --image=nicolaka/netshoot* --restart=Never -- zsh
-------
# dig 툴로 도메인 질의 : <서비스명>.<**네임스페이스**>.svc 혹은 <서비스명>.<네임스페이스>.svc.cluster.local
# 아래 도메인 주소로 접근 시 MySQL 라우터를 통해서 MySQL 서버(파드)로 접속됨
dig mycluster.mysql-cluster.svc +search +short
dig mycluster.mysql-cluster.svc.cluster.local  +search +short

# Headless 서비스 주소로 개별 MySQL 서버(파드)로 직접 접속을 위한 DNS 쿼리
dig mycluster-instances.mysql-cluster.svc +search +short
# 세개의 ip 나올텐데, mysql pod의 ip임.
dig mycluster-instances.mysql-cluster.svc.cluster.local +short

# MySQL 서버(파드)마다 고유한 SRV 레코드가 있고, 해당 도메인 주소로 접속 시 MySQL 라우터를 경유하지 않고 지정된 MySQL 서버(파드)로 접속됨
dig mycluster-instances.mysql-cluster.svc.cluster.local SRV

# 여기서 조회되는 도메인 주소로 접근하면 해당 파드에 접속할 수 있다.
;; ADDITIONAL SECTION: (예시)
mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local. 30 IN A 172.16.1.11
mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local. 30 IN A 172.16.3.14
mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local. 30 IN A 172.16.2.12

# zsh 빠져나오기
exit
-------

# 접속 주소 변수 지정해서 접근해보기
# 접속주서 변수 지정
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 라우터를 통한 MySQL 파드 접속
#kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MIC --password=sakila
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MIC --password=sakila --sqlx --execute='show databases;'

#커넥션정보확인
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MIC --password=sakila --sqlx --execute="SHOW VARIABLES LIKE 'max_connections';"
*Variable_name	Value
max_connections	151*

# 개별 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*

 

MYSQL Shell을 통한 연결 확인

- 기본적으로 자바스크립트 모드이며 변경가능

# MySQL Shell 은 mysql-operator 파드나 서버인스턴스(사이드카 컨테이너)에 포함되어 있어서 바로 사용 가능
# (참고) kubectl run --rm -it myshell --image=mysql/mysql-operator -- mysqlsh
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh

## MySQL InnoDB Cluster 접속  - 라우터인스턴스의 도메인주소로 접속
# 항상 보안이 중요하므로 DB에도 접근시에도 SSL기반 암호화를 한다.
MySQL  JS > \connect root@mycluster.mysql-cluster.svc
Creating a session to 'root@mycluster.mysql-cluster.svc'
Please provide the password for 'root@mycluster.mysql-cluster.svc': ******  # 암호입력 sakila

## MySQL InnoDB Cluster 상태 확인 : JavaScript 모드
MySQL  mycluster.mysql-cluster.svc:33060+ ssl  JS > \status
MySQL Shell version 8.1.0

## SQL 모드로 전환
MySQL  mycluster.mysql-cluster.svc:33060+ ssl  JS > \sql
Switching to SQL mode... Commands end with ;

## Python 모드로 전환
MySQL  mycluster.mysql-cluster.svc:33060+ ssl  SQL > \py
Switching to Python mode...

## MySQL Shell 종료
MySQL  mycluster.mysql-cluster.svc:33060+ ssl  Py > \exit
Bye!

 

 

샘플 대용량 데이터베이스 주입(30만명 직원의 400만 개의 레코드로 구성, 6개의 테이블, 160메가)

# [터미널1] 포트 포워딩
kubectl -n mysql-cluster port-forward service/mycluster mysql
Forwarding from 127.0.0.1:3306 -> 6446
Forwarding from [::1]:3306 -> 6446

# [터미널2] 아래부터는 터미널2에서 입력
------------------------------
# 포트 포워드 확인
ss -tnlp | grep kubectl
LISTEN 0      128        127.0.0.1:3306       0.0.0.0:*    users:(("kubectl",pid=16778,fd=8))   
LISTEN 0      128            [::1]:3306          [::]:*    users:(("kubectl",pid=16778,fd=9))

# 샘플 데이터베이스 git clone
git clone https://github.com/datacharmer/test_db && cd test_db/

# 마스터 노드에 mariadb-client 툴 설치
yum install mariadb -y
mysql -h127.0.0.1 -P3306 -uroot -psakila -e "SELECT @@hostname;"

# To import the data into your MySQL instance, load the data through the mysql command-line tool: 1분 10초 정도 소요
mysql -h127.0.0.1 -P3306 -uroot -psakila -t < employees.sql

# 확인
mysql -h127.0.0.1 -P3306 -uroot -psakila -e "SHOW DATABASES;"
mysql -h127.0.0.1 -P3306 -uroot -psakila -e "USE employees;SELECT * FROM employees;"
mysql -h127.0.0.1 -P3306 -uroot -psakila -e "USE employees;SELECT * FROM employees LIMIT 10;"

# 각각 헤드리스 서비스 주소로 각각의 mysql 파드로 접속하여 데이터 조회 확인 : 대용량 데이터 복제가 잘 되었는지 확인해보기!
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MDB1 --password=sakila --sqlx --execute="USE employees;SELECT * FROM employees LIMIT 5;"
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MDB2 --password=sakila --sqlx --execute="USE employees;SELECT * FROM employees LIMIT 5;"
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MDB3 --password=sakila --sqlx --execute="USE employees;SELECT * FROM employees LIMIT 5;"

 

MYSQL InnoDB Cluster의 상세정보  확인, MySQL Shell의 SQL모드를 확인하고 모드 변경하기

# 접속
MIC=mycluster.mysql-cluster.svc.cluster.local
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MIC --password=sakila --sqlx
------------------------

# 데이터베이스 확인
SQL > show databases;

# PERSIST 설정된 시스템 변수 확인
# 디폴트는 싱글프라이머리 모드인데, 멀티프라이머리 모드도 적용 가능하다.
SQL > SELECT * FROM performance_schema.persisted_variables;
+----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| VARIABLE_NAME                                      | VARIABLE_VALUE                                                                                                                            |
+----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| auto_increment_offset                              | 2                                                                                                                                         |
| auto_increment_increment                           | 1                                                                                                                                         |
| super_read_only                                    | ON                                                                                                                                        |
| binlog_transaction_dependency_tracking             | WRITESET                                                                                                                                  |
| group_replication_communication_stack              | MYSQL                                                                                                                                     |
| group_replication_start_on_boot                    | OFF                                                                                                                                       |
| replica_parallel_workers                           | 4                                                                                                                                         |
| slave_parallel_workers                             | 4                                                                                                                                         |
| group_replication_paxos_single_leader              | OFF                                                                                                                                       |
| group_replication_enforce_update_everywhere_checks | OFF                                                                                                                                       |
| group_replication_exit_state_action                | ABORT_SERVER                                                                                                                              |
| group_replication_ssl_mode                         | REQUIRED                                                                                                                                  |
| group_replication_group_name                       | 4411d3c6-7007-11ee-a919-b64e04162b41                                                                                                      |
| group_replication_view_change_uuid                 | 4412077a-7007-11ee-a919-b64e04162b41                                                                                                      |
| group_replication_local_address                    | mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local:3306                                                                      |
| group_replication_single_primary_mode              | ON                                                                                                                                        |
| group_replication_recovery_use_ssl                 | ON                                                                                                                                        |
| group_replication_group_seeds                      | mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local:3306,mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local:3306 |
| group_replication_recovery_ssl_verify_server_cert  | OFF                                                                                                                                       |
+----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+

# 현재 싱글 프라이머리 모드 동작 중이다.
# mycluster-0 파드가 프라이머리(소스)서버이며 나머지 파드는 세컨더리(레플리카)서버이다.
SQL > SELECT MEMBER_HOST, MEMBER_ROLE FROM performance_schema.replication_group_members;
+-----------------------------------------------------------------+-------------+
| MEMBER_HOST                                                     | MEMBER_ROLE |
+-----------------------------------------------------------------+-------------+
| mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY   |
| mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local | PRIMARY     |
| mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY   |
+-----------------------------------------------------------------+-------------+

# 그룹 멤버 상태 확인
SQL > SELECT MEMBER_ID,MEMBER_STATE FROM performance_schema.replication_group_members;
+--------------------------------------+--------------+
| MEMBER_ID                            | MEMBER_STATE |
+--------------------------------------+--------------+
| e87266a2-db74-11ec-bce8-0235afde6de4 | ONLINE       |
| f1af17d8-db74-11ec-bd27-5a10126add15 | ONLINE       |
| f299e9c3-db74-11ec-bca0-729b96e7fbcf | ONLINE       |
+--------------------------------------+--------------+

# 그룹 복제 Group Replication 에 묶인 그룹 멤버 상태 정보 확인
SQL > SELECT * FROM performance_schema.replication_group_members \G
*************************** 1. row ***************************
              CHANNEL_NAME: group_replication_applier
                 MEMBER_ID: 719da3f9-d2a8-11ec-af79-ba7e60d88c8d
               MEMBER_HOST: mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local
               MEMBER_PORT: 3306
              MEMBER_STATE: ONLINE
               MEMBER_ROLE: SECONDARY
            MEMBER_VERSION: 8.1.0
MEMBER_COMMUNICATION_STACK: MySQL
*************************** 2. row ***************************
              CHANNEL_NAME: group_replication_applier
                 MEMBER_ID: 7e48b4c1-d2a8-11ec-b050-de005de3704a
               MEMBER_HOST: mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local
               MEMBER_PORT: 3306
              MEMBER_STATE: ONLINE
               MEMBER_ROLE: PRIMARY
            MEMBER_VERSION: 8.1.0
MEMBER_COMMUNICATION_STACK: MySQL
*************************** 3. row ***************************
              CHANNEL_NAME: group_replication_applier
                 MEMBER_ID: 80df97bc-d2a8-11ec-afa2-12ecfd025671
               MEMBER_HOST: mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local
               MEMBER_PORT: 3306
              MEMBER_STATE: ONLINE
               MEMBER_ROLE: SECONDARY
            MEMBER_VERSION: 8.1.0
MEMBER_COMMUNICATION_STACK: MySQL

# Group Replication 이 관리하는 멤버 목록과 상태 정보(View ID) 확인 : 그룹 멤버 변경 시 변경됨
SQL > SELECT VIEW_ID FROM performance_schema.replication_group_member_stats LIMIT 1;
+---------------------+
| VIEW_ID             |
+---------------------+
| 16524381853527486:3 |
+---------------------+

# 그룹 복제에서 추방 시 다시 재가입 동작 여부 확인 : 0 일 경우 다시 가입 시도하지 않는다, 예를 들어 3이라면 그룹 재가입을 3번 시도한다
SQL > SELECT COUNT(*) FROM performance_schema.events_stages_current WHERE EVENT_NAME LIKE '%auto-rejoin%';
+----------+
| COUNT(*) |
+----------+
|        0 |
+----------+

# 스토리지 엔진 : InnoDB 사용!
SQL > SHOW ENGINES;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine             | Support | Comment                                                        | Transactions | XA   | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| ndbcluster         | NO      | Clustered, fault-tolerant tables                               | NULL         | NULL | NULL       |
| FEDERATED          | NO      | Federated MySQL storage engine                                 | NULL         | NULL | NULL       |
| MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables      | NO           | NO   | NO         |
| InnoDB             | DEFAULT | Supports transactions, row-level locking, and foreign keys     | YES          | YES  | YES        |
| PERFORMANCE_SCHEMA | YES     | Performance Schema                                             | NO           | NO   | NO         |
| MyISAM             | YES     | MyISAM storage engine                                          | NO           | NO   | NO         |
| ndbinfo            | NO      | MySQL Cluster system information storage engine                | NULL         | NULL | NULL       |
| MRG_MYISAM         | YES     | Collection of identical MyISAM tables                          | NO           | NO   | NO         |
| BLACKHOLE          | YES     | /dev/null storage engine (anything you write to it disappears) | NO           | NO   | NO         |
| CSV                | YES     | CSV storage engine                                             | NO           | NO   | NO         |
| ARCHIVE            | YES     | Archive storage engine                                         | NO           | NO   | NO         |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+

 

추가 정보 확인 및 복제 테스트

# 플러그인 확인
SQL > SHOW PLUGINS;
+---------------------------------+----------+--------------------+----------------------+---------+
| Name                            | Status   | Type               | Library              | License |
+---------------------------------+----------+--------------------+----------------------+---------+
| binlog                          | ACTIVE   | STORAGE ENGINE     | NULL                 | GPL     |
...(생략)...
| group_replication               | ACTIVE   | GROUP REPLICATION  | group_replication.so | GPL     |
| clone                           | ACTIVE   | CLONE              | mysql_clone.so       | GPL     |
+---------------------------------+----------+--------------------+----------------------+---------+

# 그룹 멤버 확인
SQL > SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-----------------------------------------------------------------+-------------+--------------+-------------+----------------+----------------------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST                                                     | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | MEMBER_COMMUNICATION_STACK |
+---------------------------+--------------------------------------+-----------------------------------------------------------------+-------------+--------------+-------------+----------------+----------------------------+
| group_replication_applier | 3896825f-7007-11ee-abea-92ad8ae2be7d | mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local |        3306 | ONLINE       | SECONDARY   | 8.1.0          | MySQL                      |
| group_replication_applier | 38b3bcf4-7007-11ee-acb3-b64e04162b41 | mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local |        3306 | ONLINE       | PRIMARY     | 8.1.0          | MySQL                      |
| group_replication_applier | 3a440805-7007-11ee-ad59-e2b30a30be9b | mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local |        3306 | ONLINE       | SECONDARY   | 8.1.0          | MySQL                      |
+---------------------------+--------------------------------------+-----------------------------------------------------------------+-------------+--------------+-------------+----------------+----------------------------+

# 그룹 멤버 포트, 버전 확인
SQL > SELECT MEMBER_HOST,MEMBER_PORT,MEMBER_VERSION FROM performance_schema.replication_group_members;
+-----------------------------------------------------------------+-------------+----------------+
| MEMBER_HOST                                                     | MEMBER_PORT | MEMBER_VERSION |
+-----------------------------------------------------------------+-------------+----------------+
| mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local |        3306 | 8.1.0          |
| mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local |        3306 | 8.1.0          |
| mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local |        3306 | 8.1.0          |
+-----------------------------------------------------------------+-------------+----------------+

# 복제 테스트
CREATE DATABASE test;
USE test;
CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL);
INSERT INTO t1 VALUES (1, 'Luis');
SELECT * FROM t1;
SHOW BINLOG EVENTS;
+------------------+-----+----------------+-----------+-------------+----------------------------------+
| Log_name         | Pos | Event_type     | Server_id | End_log_pos | Info                             |
+------------------+-----+----------------+-----------+-------------+----------------------------------+
| mycluster.000001 |   4 | Format_desc    |      1000 |         126 | Server ver: 8.1.0, Binlog ver: 4 |
| mycluster.000001 | 126 | Previous_gtids |      1000 |         157 |                                  |
| mycluster.000001 | 157 | Stop           |      1000 |         180 |                                  |
+------------------+-----+----------------+-----------+-------------+----------------------------------+

# Using Group Replication Group Write Consensus : Inspecting a Group's Write Concurrency
SQL > SELECT group_replication_get_write_concurrency();
+-------------------------------------------+
| group_replication_get_write_concurrency() |
+-------------------------------------------+
|                                        10 |
+-------------------------------------------+

# A Group's Communication Protocol Version
SQL > SELECT group_replication_get_communication_protocol();
+------------------------------------------------+
| group_replication_get_communication_protocol() |
+------------------------------------------------+
| 8.0.27                                         |
+------------------------------------------------+

# PERSIST 로 설정된 시스템 변수 목록 조회
SQL > SELECT * FROM performance_schema.persisted_variables;
+----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| VARIABLE_NAME                                      | VARIABLE_VALUE                                                                                                                            |
+----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| auto_increment_offset                              | 2                                                                                                                                         |
| auto_increment_increment                           | 1                                                                                                                                         |
| super_read_only                                    | ON                                                                                                                                        |
| binlog_transaction_dependency_tracking             | WRITESET                                                                                                                                  |
| group_replication_communication_stack              | MYSQL                                                                                                                                     |
| group_replication_start_on_boot                    | OFF                                                                                                                                       |
| replica_parallel_workers                           | 4                                                                                                                                         |
| slave_parallel_workers                             | 4                                                                                                                                         |
| group_replication_paxos_single_leader              | OFF                                                                                                                                       |
| group_replication_enforce_update_everywhere_checks | OFF                                                                                                                                       |
| group_replication_exit_state_action                | ABORT_SERVER                                                                                                                              |
| group_replication_ssl_mode                         | REQUIRED                                                                                                                                  |
| group_replication_group_name                       | 4411d3c6-7007-11ee-a919-b64e04162b41                                                                                                      |
| group_replication_view_change_uuid                 | 4412077a-7007-11ee-a919-b64e04162b41                                                                                                      |
| group_replication_local_address                    | mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local:3306                                                                      |
| group_replication_single_primary_mode              | ON                                                                                                                                        |
| group_replication_recovery_use_ssl                 | ON                                                                                                                                        |
| group_replication_group_seeds                      | mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local:3306,mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local:3306 |
| group_replication_recovery_ssl_verify_server_cert  | OFF                                                                                                                                       |
+----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+

라우터 확인 및 프라이머리 변경하기

# MySQL 라우터 bash 셸 접속
kubectl exec -it -n mysql-cluster deploy/mycluster-router -- bash
--------------------
# help
mysqlrouter --help
mysqlrouter --version

# 관련 파일 확인
ls -al /tmp/mysqlrouter/
total 36
drwx------ 5 mysqlrouter mysqlrouter 4096 May 24 15:20 .
drwxrwxrwt 1 root        root        4096 May 24 15:20 ..
drwx------ 2 mysqlrouter mysqlrouter 4096 May 24 15:20 data
drwx------ 2 mysqlrouter mysqlrouter 4096 May 24 15:20 log
-rw------- 1 mysqlrouter mysqlrouter 1870 May 24 15:20 mysqlrouter.conf
-rw------- 1 mysqlrouter mysqlrouter   87 May 24 15:20 mysqlrouter.key
drwx------ 2 mysqlrouter mysqlrouter 4096 May 24 15:20 run
-rwx------ 1 mysqlrouter mysqlrouter  135 May 24 15:20 start.sh
-rwx------ 1 mysqlrouter mysqlrouter  158 May 24 15:20 stop.sh

cat /tmp/mysqlrouter/mysqlrouter.conf
[DEFAULT]
...
connect_timeout=5
read_timeout=30
dynamic_state=/tmp/mysqlrouter/data/state.json
...

[metadata_cache:bootstrap]  # 라우터에 접속할 InnoDB 클러스터의 정보를 구성하고 관리
cluster_type=gr
router_id=1
user=mysqlrouter
metadata_cluster=mycluster
ttl=0.5                     # MySQL 라우터가 내부적으로 캐싱하고 있는 클러스터 메타데이터를 갱신하는 주기, 단위(초)
auth_cache_ttl=-1
auth_cache_refresh_interval=2
use_gr_notifications=0      # 해당 옵션 활성화시, 클러스터의 그룹 복제 변경사항을 MySQL 라우터가 알람을 받을 수 있다, 알람 받으면 클러스터 메타데이터를 갱신한다

# 각각 MySQL 기본 프로토콜로 연결되는 '읽기전용포트', 읽기-쓰기포트'와 X프로토콜로 연결되는 읽기전용포트', 읽기-쓰기포트'로 총 4개의 TCP 포트를 사용
# role 이 PRIMART 시 : 기본 round-robin 동작, MySQL 라우터 부트스트랩 설정 시 first-available 설정이 자동 지정, 2가지 중 선택(round-robin,first-available)
# role 이 SECONDARY 시 : 기본 round-robin 동작, MySQL 라우터 부트스트랩 설정 시 round-robin-with-fallback 설정이 자동 지정, 3가지 중 선택(round-robin,first-available,round-robin-with-fallback)
# role 이 PRIMART_AND_SECONDARY 시 : 기본 round-robin 동작, 2가지 중 선택(round-robin,first-available)
[routing:bootstrap_rw]
bind_address=0.0.0.0
bind_port=6446
destinations=metadata-cache://mycluster/?role=PRIMARY   # 라우팅 전달 대상이 URL 포맷은 동적이 대상임, role 프라이머리 서버로 연결(읽기-쓰기)
routing_strategy=first-available    # 쿼리 요청 전달 전략(4가지): round-robin, round-robin-with-fallback(세컨더리 서버에 RR, 세컨더리 없으면 프라이어머로 RR)        
protocol=classic                    # 쿼리 요청 전달 전략(이어서): first-available(목록 중 사용 가능 첫번째 서버 연결, 연결안되면 그 다음 서버로 시도)
                                    # 쿼리 요청 전달 전략(이어서): next-available(first-available 와 동일하나, 연결 오류 서버는 연결 불가로 표시하고, 연결 대상에서 제외, 단 정적으로 서버 지정시만 가능)
[routing:bootstrap_ro]
bind_address=0.0.0.0
bind_port=6447
destinations=metadata-cache://mycluster/?role=SECONDARY # role 는 어떤 타입의 MySQL 서버로 연결할지를 설정, 여기서는 세컨터리 타입 서버로 연결(읽기전용)
routing_strategy=round-robin-with-fallback
protocol=classic     # 3306 기존 mysql TCP 통신 방법

[routing:bootstrap_x_rw]
bind_address=0.0.0.0
bind_port=6448
destinations=metadata-cache://mycluster/?role=PRIMARY
routing_strategy=first-available
protocol=x

[routing:bootstrap_x_ro]
bind_address=0.0.0.0
bind_port=6449
destinations=metadata-cache://mycluster/?role=SECONDARY
routing_strategy=round-robin-with-fallback
protocol=x

[http_server]
port=8443
ssl=1
ssl_cert=/tmp/mysqlrouter/data/router-cert.pem
ssl_key=/tmp/mysqlrouter/data/router-key.pem
...

exit
--------------------

# mysqlrouter 설정 확인
# 동적으로 연결되어있다. 직접 서버를 추가해줄 필요 없음!
kubectl exec -it -n mysql-cluster deploy/mycluster-router -- mysqlrouter --help
kubectl exec -it -n mysql-cluster deploy/mycluster-router -- cat /tmp/mysqlrouter/mysqlrouter.conf
...(생략)...

# 메타데이터 캐시 정보 확인
kubectl exec -it -n mysql-cluster deploy/mycluster-router -- cat /tmp/mysqlrouter/data/state.json | jq
{
  "metadata-cache": {
    "group-replication-id": "4411d3c6-7007-11ee-a919-b64e04162b41",
    "cluster-metadata-servers": [
      "mysql://mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local:3306",
      "mysql://mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local:3306",
      "mysql://mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local:3306"
    ]
  },
  "version": "1.0.0"
}

# 라우터 계정 정보 확인
kubectl get secret -n mysql-cluster  mycluster-router -o jsonpath={.data.routerUsername} | base64 -d;echo
mysqlrouter

kubectl get secret -n mysql-cluster  mycluster-router -o jsonpath={.data.routerPassword} | base64 -d;echo
LLYjH-2wUqE-0=vmS-V673B-fWuia

# (옵션) 모니터링
watch -d "kubectl exec -it -n mysql-cluster deploy/mycluster-router -- cat /tmp/mysqlrouter/data/state.json"

 

 

SQL 확인하기

#
MIC=mycluster.mysql-cluster.svc.cluster.local
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MIC --password=sakila --sqlx
--------------------
# 유저 정보
SQL > SELECT user,host FROM mysql.user;
+---------------------------+-----------+
| user                      | host      |
+---------------------------+-----------+
| mysql_innodb_cluster_1000 | %         |
| mysql_innodb_cluster_1001 | %         |
| mysql_innodb_cluster_1002 | %         |
| mysqladmin                | %         |
| mysqlbackup               | %         |
| mysqlrouter               | %         |
| root                      | %         |
| localroot                 | localhost |
| mysql.infoschema          | localhost |
| mysql.session             | localhost |
| mysql.sys                 | localhost |
| mysqlhealthchecker        | localhost |
+---------------------------+-----------+#

#
SQL > SHOW GRANTs FOR 'mysqlrouter'@'%';
+---------------------------------------------------------------------------------------------------+
| Grants for mysqlrouter@%                                                                          |
+---------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `mysqlrouter`@`%`                                                           |
| GRANT SELECT, EXECUTE ON `mysql_innodb_cluster_metadata`.* TO `mysqlrouter`@`%`                   |
| GRANT INSERT, UPDATE, DELETE ON `mysql_innodb_cluster_metadata`.`routers` TO `mysqlrouter`@`%`    |
| GRANT INSERT, UPDATE, DELETE ON `mysql_innodb_cluster_metadata`.`v2_routers` TO `mysqlrouter`@`%` |
| GRANT SELECT ON `performance_schema`.`global_variables` TO `mysqlrouter`@`%`                      |
| GRANT SELECT ON `performance_schema`.`replication_group_member_stats` TO `mysqlrouter`@`%`        |
| GRANT SELECT ON `performance_schema`.`replication_group_members` TO `mysqlrouter`@`%`             |
+---------------------------------------------------------------------------------------------------+

 

자바스크립트 모드로 변경하여 여러가지 클러스터 정보 확인하기

# JS 변경
\js

#
JS > var cluster = dba.getCluster()

# InnoDB 클러스터에 등록된 라우터 정보 확인
JS > cluster.listRouters()
{
    "clusterName": "mycluster",
    "routers": {
        "mycluster-router-5f9758dc8f-fx8pv::": {
            "hostname": "mycluster-router-5f9758dc8f-fx8pv",
            "lastCheckIn": "2022-05-24 15:24:57",
            "roPort": "6447",
            "roXPort": "6449",
            "rwPort": "6446",
            "rwXPort": "6448",
            "version": "8.1.0"
        }
    }
}

# 복제 토폴로지 구성 간략 확인
JS > cluster.describe()
{
    "clusterName": "mycluster",
    "defaultReplicaSet": {
        "name": "default",
        "topology": [
            {
                "address": "mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local:3306",
                "label": "mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local:3306",
                "role": "HA"
            },
            {
                "address": "mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local:3306",
                "label": "mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local:3306",
                "role": "HA"
            },
            {
                "address": "mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local:3306",
                "label": "mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local:3306",
                "role": "HA"
            }
        ],
        "topologyMode": "Single-Primary"
    }
}

# 좀 더 상세히 확인
JS > cluster.status({'extended':1})
{
    "clusterName": "mycluster",
    "defaultReplicaSet": {
        "GRProtocolVersion": "8.0.27",
        "communicationStack": "MYSQL",
        "groupName": "01cc0ff4-db75-11ec-80aa-729b96e7fbcf",
        "groupViewChangeUuid": "01cc181a-db75-11ec-80aa-729b96e7fbcf",
        "groupViewId": "16534056132873617:5",
        "name": "default",
        "primary": "mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local:3306",
        "ssl": "REQUIRED",
        "status": "OK",
        "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
        "topology": {
            "mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local:3306": {
                "address": "mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local:3306",
                "applierWorkerThreads": 4,
                "fenceSysVars": [             # 해당 인스턴스에서 활성화돼 있는 차단 시스템 변수 목록
                    "read_only",
                    "super_read_only"
                ],
                "memberId": "f299e9c3-db74-11ec-bca0-729b96e7fbcf",
                "memberRole": "SECONDARY",
                "memberState": "ONLINE",
                "mode": "R/O",
                "readReplicas": {},
                "replicationLag": null,
                "role": "HA",
                "status": "ONLINE",
                "version": "8.0.29"
            },
            "mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local:3306": {
                "address": "mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local:3306",
                "applierWorkerThreads": 4,
                "fenceSysVars": [],
                "memberId": "e87266a2-db74-11ec-bce8-0235afde6de4",
                "memberRole": "PRIMARY",
                "memberState": "ONLINE",
                "mode": "R/W",
                "readReplicas": {},
                "replicationLag": null,
                "role": "HA",
                "status": "ONLINE",
                "version": "8.0.29"
            },
            "mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local:3306": {
                "address": "mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local:3306",
                "applierWorkerThreads": 4,
                "fenceSysVars": [
                    "read_only",
                    "super_read_only"
                ],
                "memberId": "f1af17d8-db74-11ec-bd27-5a10126add15",
                "memberRole": "SECONDARY",
                "memberState": "ONLINE",
                "mode": "R/O",
                "readReplicas": {},
                "replicationLag": null,
                "role": "HA",
                "status": "ONLINE",
                "version": "8.0.29"
            }
        },
        "topologyMode": "Single-Primary"
    },
    "groupInformationSourceMember": "mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local:3306",
    "metadataVersion": "2.1.0"
}


# 아래부터는 Skip
---------------
# 클러스터 전체 정보
JS > var cluster = dba.getCluster()
JS > cluster.options()
...(생략)...

# 클러스터 인스턴스의 그룹 복제 전체 설정 정보
JS > cluster.options({all:true})
...(생략)...

## 프라이머리 서버 변경 : 현재 mycluster-1 이 프라이머리였는데, mycluster-2 로 아래처럼 변경함
JS > cluster.setPrimaryInstance('mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local:3306')
Setting instance 'mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local:3306' as the primary instance of cluster 'mycluster'...

Instance 'mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local:3306' was switched from PRIMARY to SECONDARY.
Instance 'mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local:3306' was switched from SECONDARY to PRIMARY.
Instance 'mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local:3306' remains SECONDARY.

WARNING: The cluster internal session is not the primary member anymore. For cluster management operations please obtain a fresh cluster handle using dba.getCluster().

The instance 'mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local:3306' was successfully elected as primary.

 

MYSQL Router의 부하분산 확인하기

# mysql 클라이언트 파드 YAML 내용 확인
curl -s https://raw.githubusercontent.com/gasida/DOIK/main/2/myclient-new.yaml -o myclient.yaml
cat myclient.yaml | yh

# myclient 파드 1대 배포 : envsubst 활용
PODNAME=myclient1 envsubst < myclient.yaml | kubectl apply -f -

# myclient 파드 추가로 2대 배포
for ((i=2; i<=3; i++)); do PODNAME=myclient$i envsubst < myclient.yaml | kubectl apply -f - ; done

# myclient 파드들 확인
kubectl get pod -l app=myclient

# 파드1에서 mysql 라우터 서비스로 접속 확인 : TCP 3306
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "SHOW DATABASES;"
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "SELECT @@HOSTNAME,@@SERVER_ID;"
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "SELECT @@HOSTNAME,host from information_schema.processlist WHERE ID=connection_id();"

# 파드1에서 mysql 라우터 서비스로 접속 확인 : TCP 6446
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila --port=6446 -e "SELECT @@HOSTNAME,@@SERVER_ID;"

# 파드1에서 mysql 라우터 서비스로 접속 확인 : TCP 6447 >> 3초 간격으로 확인!
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila --port=6447 -e "SELECT @@HOSTNAME,@@SERVER_ID;"
3초 간격
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila --port=6447 -e "SELECT @@HOSTNAME,@@SERVER_ID;"

# 파드들에서 mysql 라우터 서비스로 접속 확인 : MySQL 라우터정책이 first-available 라서 무조건 멤버 (프라이머리) 첫번쨰로 전달, host 에는 라우터의 IP가 찍힌다.
for ((i=1; i<=3; i++)); do kubectl exec -it myclient$i -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "select @@hostname, @@read_only, @@super_read_only";echo; done
for ((i=1; i<=3; i++)); do kubectl exec -it myclient$i -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "SELECT @@HOSTNAME,host from information_schema.processlist WHERE ID=connection_id();";echo; done
for ((i=1; i<=3; i++)); do kubectl exec -it myclient$i -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "SELECT @@HOSTNAME;USE employees;SELECT * FROM employees LIMIT $i;";echo; done

# 파드들에서 mysql 라우터 서비스로 접속 확인 : TCP 6447 접속 시 round-robin-with-fallback 정책에 의해서 2대에 라운드 로빈(부하분산) 접속됨
for ((i=1; i<=3; i++)); do kubectl exec -it myclient$i -- mysql -h mycluster.mysql-cluster -uroot -psakila --port=6447 -e "SELECT @@HOSTNAME,host from information_schema.processlist WHERE ID=connection_id();";echo; done
for ((i=1; i<=3; i++)); do kubectl exec -it myclient$i -- mysql -h mycluster.mysql-cluster -uroot -psakila --port=6447 -e "SELECT @@HOSTNAME;USE employees;SELECT * FROM employees LIMIT $i;";echo; done
for ((i=1; i<=3; i++)); do kubectl exec -it myclient$i -- mysql -h mycluster.mysql-cluster -uroot -psakila --port=6447 -e "select @@hostname, @@read_only, @@super_read_only";echo; done

 

 

세컨더리 파드에 강제로 Write 가능한지 테스트해보기

- 개별파드로 들어가서 확인하면, 실시간으로 프라이머리에 쓰고, 나머지 세컨더리로 복제가 일어나는것 확인가능하다.

- 세컨더리에 들어가서 직접 insert 하더라도 에러 뜨는점 확인가능하다.

-Read만 가능하고 Write가 되지 않는다.

# 파드1에서 mysql 라우터 서비스로 접속 확인
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila
--------------------
# 간단한 데이터베이스 생성
CREATE DATABASE test;
USE test;
CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL);
INSERT INTO t1 VALUES (1, 'Luis');
SELECT * FROM t1;
exit
--------------------

# 조회
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "USE test;SELECT * FROM t1;"

# 추가 후 조회
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "USE test;INSERT INTO t1 VALUES (2, 'Luis2');"
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "USE test;SELECT * FROM t1;"

# 반복 추가 및 조회
for ((i=3; i<=100; i++)); do kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "SELECT @@HOSTNAME;USE test;INSERT INTO t1 VALUES ($i, 'Luis$i');";echo; done
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "USE test;SELECT * FROM t1;"

# 모니터링 : 신규 터미널 3개
watch -d "kubectl exec -it myclient1 -- mysql -h mycluster-0.mycluster-instances.mysql-cluster.svc -uroot -psakila -e 'USE test;SELECT * FROM t1 ORDER BY c1 DESC LIMIT 5;'"
watch -d "kubectl exec -it myclient2 -- mysql -h mycluster-1.mycluster-instances.mysql-cluster.svc -uroot -psakila -e 'USE test;SELECT * FROM t1 ORDER BY c1 DESC LIMIT 5;'"
watch -d "kubectl exec -it myclient3 -- mysql -h mycluster-2.mycluster-instances.mysql-cluster.svc -uroot -psakila -e 'USE test;SELECT * FROM t1 ORDER BY c1 DESC LIMIT 5;'"

# 원하는 갯수 만큼 추가, CTRL+C 로 취소
for ((i=101; i<=1000; i++)); do kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "SELECT @@HOSTNAME;USE test;INSERT INTO t1 VALUES ($i, 'Luis$i');";echo; done

# (참고) 세컨더리 MySQL 서버 파드에 INSERT 가 되지 않는다 : --super-read-only option
kubectl exec -it myclient1 -- mysql -h mycluster-0.mycluster-instances.mysql-cluster.svc -uroot -psakila -e "USE test;INSERT INTO t1 VALUES (1089, 'Luis1089');" 
혹은
kubectl exec -it myclient1 -- mysql -h mycluster-1.mycluster-instances.mysql-cluster.svc -uroot -psakila -e "USE test;INSERT INTO t1 VALUES (1089, 'Luis1089');" 
혹은
kubectl exec -it myclient1 -- mysql -h mycluster-2.mycluster-instances.mysql-cluster.svc -uroot -psakila -e "USE test;INSERT INTO t1 VALUES (1089, 'Luis1089');" 
ERROR 1290 (HY000) at line 1: The MySQL server is running with the --super-read-only option so it cannot execute this statement
command terminated with exit code 1

 

워드프레스 설치하기

- 설치까지 시간이 꽤 오래 걸린다. EFS 구성과 연결에 시간이 소요되기 때문.

- EFS 저장소에 연결되기 때문에 글 작성시 어떠한 워드프레스에 들어가더라도 동일하게 확인 가능하다.

# NFS 마운트 확인
ssh ec2-user@$N1 sudo df -hT --type nfs4
ssh ec2-user@$N2 sudo df -hT --type nfs4
ssh ec2-user@$N3 sudo df -hT --type nfs4

# MySQL 에 wordpress 데이터베이스 생성
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "create database wordpress;"
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "show databases;"

# 파라미터 파일 생성
cat <<EOT > wp-values.yaml
wordpressUsername: admin
wordpressPassword: "password"
wordpressBlogName: "DOIK Study"
replicaCount: 3
service:
  type: NodePort
ingress:
  enabled: true
  ingressClassName: alb
  hostname: wp.$MyDomain
  path: /*
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
    alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
    alb.ingress.kubernetes.io/success-codes: 200-399
    alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
    alb.ingress.kubernetes.io/group.name: study
    alb.ingress.kubernetes.io/ssl-redirect: '443'
persistence:
  enabled: true
  storageClass: "efs-sc"
  accessModes:
    - ReadWriteMany
mariadb:
  enabled: false
externalDatabase:
  host: mycluster.mysql-cluster.svc
  port: 3306
  user: root
  password: sakila
  database: wordpress
EOT

# wordpress 설치 : MySQL 접속 주소(mycluster.mysql-cluster.svc), MySQL 데이터베이스 이름 지정(wordpress) , 장애 테스트를 위해서 3대의 파드 배포
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install my-wordpress bitnami/wordpress --version 18.0.7 -f wp-values.yaml
helm get values my-wordpress

# 설치 확인
watch -d kubectl get pod,svc,pvc
kubectl get deploy,ingress,pvc my-wordpress
kubectl get pod -l app.kubernetes.io/instance=my-wordpress
kubectl get sc,pv
 
# NFS 마운트 확인
ssh ec2-user@$N1 sudo df -hT --type nfs4
ssh ec2-user@$N2 sudo df -hT --type nfs4
ssh ec2-user@$N3 sudo df -hT --type nfs4

# Wordpress 웹 접속 주소 확인 : 블로그, 관리자
echo -e "Wordpress Web   URL = https://wp.$MyDomain"
echo -e "Wordpress Admin URL = https://wp.$MyDomain/admin"   # 관리자 페이지 : admin, password

# 모니터링
while true; do kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "SELECT post_title FROM wordpress.wp_posts;"; date;sleep 1; done

# (참고) EFS 확인
mount -t efs -o tls $EFS_ID:/ /mnt/myefs
df -hT --type nfs4
tree /mnt/myefs/ -L 4

# (참고) 관리자 로그인 후 새 글 작성(이미지 첨부) 후 아래 확인
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "SELECT * FROM wordpress.wp_term_taxonomy;"
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "SELECT post_content FROM wordpress.wp_posts;"

장애 테스트

  - MySQL 서버 파드(인스턴스) 1대 강제 삭제 및 동작 확인하기

  - 워드프레스 정상 접속 및 포스팅 작성 가능, 데이터베이스에 반복해서 INSERT 시도하기

  - Insert 도중 Primary를 강제로 삭제했을 때 어떻게 되는지? -> 세컨더리가 바로 프라이머리로 올라가며 정상적으로 Write된다. 삭제된 pod는 새롭게 올라오고 동기화가 진행된다.

### 사전준비
# PRIMARY 파드 정보 확인
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -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;'
kubectl get pod -n mysql-cluster -owide

# 파드들에서 mysql 라우터 서비스로 접속 확인 : TCP 6447 접속 시 round-robin-with-fallback 정책에 의해서 2대에 라운드 로빈(부하분산) 접속됨 >> 3초 간격으로 확인!
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila --port=6447 -e "SELECT @@HOSTNAME,@@SERVER_ID;"
3초 간격
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila --port=6447 -e "SELECT @@HOSTNAME,@@SERVER_ID;"

### 동작확인
# 모니터링 : 터미널 3개
watch -d 'kubectl get pod -o wide -n mysql-cluster;echo;kubectl get pod -o wide'
while true; do kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -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
while true; do kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila --port=6447 -e 'SELECT @@HOSTNAME;'; date;sleep 2; done

# 신규터미널4 : test 데이터베이스에 원하는 갯수 만큼 데이터 INSERT, CTRL+C 로 취소
for ((i=1001; i<=5000; i++)); do kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "SELECT NOW();INSERT INTO test.t1 VALUES ($i, 'Luis$i');";echo; done

# 신규터미널5 : 프라이머리 파드 삭제 kubectl delete pod -n mysql-cluster <현재 프라이머리 MySQL 서버파드 이름> && kubectl get pod -n mysql-cluster -w
kubectl delete pod -n mysql-cluster mycluster-0 && kubectl get pod -n mysql-cluster -w
혹은
kubectl delete pod -n mysql-cluster mycluster-1 && kubectl get pod -n mysql-cluster -w
혹은
kubectl delete pod -n mysql-cluster mycluster-2 && kubectl get pod -n mysql-cluster -w

# 워드프레스에 글 작성 및 접속 확인 : 1초 미만으로 자동 절체! >> 원상복구 FailBack 확인(파드 재생성 후 그룹 멤버 Join 확인)
# 만약 <세컨더리 MySQL 서버파드> 를 삭제했을 경우에는 자동 Join 되지 않음 >> 아래 수동 Join 실행하자

 

 

 

- MySQL 서버 파드(인스턴스) 가 배포된 노드 1대 drain 설정 및 동작 확인하기

  - 워드프레스 정상 접속 및 포스팅 작성 가능, 데이터베이스에 반복해서 INSERT 시도하기

  - worker node에 문제 발생시키기

  - 시나리오 : 워커노드 세개 중, 한 노드를 보안패치 때문에 재부팅해야하는 상황이며 쫓아내는 drain 진행

  - primary가 바로 변경되며 워드프레스 접속시 즉시는 아니지만 짧은 시간 후 다른 노드의 파드로 연결되어 정상적으로 접속되고 정상적으로 Insert된다.

# 모니터링 : 터미널 3개
watch -d 'kubectl get pod -o wide -n mysql-cluster;echo;kubectl get pod -o wide'
while true; do kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -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
while true; do kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila --port=6447 -e 'SELECT @@HOSTNAME;'; date;sleep 2; done

# 신규터미널4 : test 데이터베이스에 원하는 갯수 만큼 데이터 INSERT, CTRL+C 로 취소
for ((i=1001; i<=5000; i++)); do kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "SELECT NOW();INSERT INTO test.t1 VALUES ($i, 'Luis$i');";echo; done


# 신규터미널5 : EC2 노드 1대 drain(중지) 설정 : 세컨더리 노드 먼저 테스트 =>> 이후 프라이머리 노드 테스트 해보자! 결과 비교!
kubectl get pdb -n mysql-cluster # 왜 오퍼레이터는 PDB 를 자동으로 설정했을까요?
*# kubectl drain <<노드>> --ignore-daemonsets --delete-emptydir-data*
kubectl get node
NODE=*<각자 자신의 EC2 노드 이름 지정>*
NODE=ip-192-168-3-96.ap-northeast-2.compute.internal # 3번째 node을 drain
kubectl drain $NODE --ignore-daemonsets --delete-emptydir-data --force* && kubectl get pod -n mysql-cluster -w

# 워드프레스에 글 작성 및 접속 확인 & INSERT 및 확인

# 노드 상태 확인
kubectl get node
NAME                                               STATUS                     ROLES    AGE     VERSION
ip-192-168-1-32.ap-northeast-2.compute.internal    Ready                      <none>   4h42m   v1.27.5-eks-43840fb
ip-192-168-2-161.ap-northeast-2.compute.internal   Ready                      <none>   4h42m   v1.27.5-eks-43840fb
ip-192-168-3-96.ap-northeast-2.compute.internal    Ready,SchedulingDisabled   <none>   4h42m   v1.27.5-eks-43840fb

# 파드 상태 확인
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   2/2     Running   0          9m4s   192.168.2.141   ip-192-168-2-161.ap-northeast-2.compute.internal   <none>           2/2
mycluster-1   0/2     Pending   0          18s    <none>          <none>                                             <none>           0/2
mycluster-2   2/2     Running   0          141m   192.168.1.25    ip-192-168-1-32.ap-northeast-2.compute.internal    <none>           2/2

# db 확인
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -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             |
+---------------------+
| 16984917709340217:8 |
+---------------------+
+-----------------------------------------------------------------+-------------+
| MEMBER_HOST                                                     | MEMBER_ROLE |
+-----------------------------------------------------------------+-------------+
| mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local | PRIMARY     |
| mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY   |
+-----------------------------------------------------------------+-------------+

**# EC2 노드 1대 uncordon(정상복귀) 설정**
*# kubectl uncordon <<노드>>*
**kubectl uncordon $NODE**

 

 

Scale 테스트

# 현재 MySQL InnoDB Cluster 정보 확인 : 서버파드(인스턴스)는 3대, 라우터파드(인스턴스)는 1대
kubectl get innodbclusters -n mysql-cluster
NAME        STATUS   ONLINE   INSTANCES   ROUTERS   AGE
mycluster   ONLINE   3        3           1         17m

# 모니터링
while true; do kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -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

# MySQL 서버 파드(인스턴스) 2대 추가 : 기본값(serverInstances: 3, routerInstances: 1) >> 복제 그룹 멤버 정상 상태(그후 쿼리 분산)까지 다소 시간이 걸릴 수 있다(데이터 복제 등)
helm upgrade mycluster mysql-operator/mysql-innodbcluster --reuse-values --set serverInstances=5 --namespace mysql-cluster

# MySQL 라우터 파드 3대로 증가
helm upgrade mycluster mysql-operator/mysql-innodbcluster --reuse-values --set routerInstances=3 --namespace mysql-cluster

# 확인
kubectl get innodbclusters -n mysql-cluster
NAME        STATUS   ONLINE   INSTANCES   ROUTERS   AGE
mycluster   ONLINE   3        5           3         145m
kubectl get pod -n mysql-cluster -l app.kubernetes.io/component=database
kubectl get pod -n mysql-cluster -l app.kubernetes.io/component=router

# MySQL 서버 파드(인스턴스) 1대 삭제 : 스테이트풀셋이므로 마지막에 생성된 서버 파드(인스턴스)가 삭제됨 : PV/PVC 는 어떻게 될까요?
helm upgrade mycluster mysql-operator/mysql-innodbcluster --reuse-values --set serverInstances=4 --namespace mysql-cluster

# MySQL 라우터 파드 1대로 축소
helm upgrade mycluster mysql-operator/mysql-innodbcluster --reuse-values --set routerInstances=1 --namespace mysql-cluster

# 확인
kubectl get innodbclusters -n mysql-cluster
NAME        STATUS   ONLINE   INSTANCES   ROUTERS   AGE
mycluster   ONLINE   3        2           1         148m