쿠버네티스 데이터베이스 오퍼레이터 스터티 2주차 내용
쿠버네티스 오퍼레이터
쿠버네티스 추상화를 통해 관리 대상 소프트웨어의 전체 라이프사이클을 자동화, 애플리케이션을 패키징-배포-관리하는 방법론이고,
오퍼레이터라고하면 오퍼레이터 패턴이라는 개념도 많이 등장하는데
오퍼레이터패턴이란 커스텀 컨트롤러가 사용자가 생성한 Custom Resource를 watch 하고, Custom Resource에 정의된 desired state와 현재 상태를 일치시키기 위한 Custom Resource에 특화된 동작을 하는 것
오퍼레이터 사용방식
오퍼레이터는 https://operatorhub.io/?category=Database 에서 찾아볼 수 있다.
아래의 그림에서 총 5단계가 있으며, 3단계 이상 사용하는 것이 좋다.

선언적 생성방식
아래에는 생성시 사용되는 개념들이다.
- CRD Custom Resource Definition : 오퍼레이터로 사용할 상태 관리용 객체들의 Spec 을 정의
- CR Custom Resource : CRD의 Spec 를 지키는 객체들의 실제 상태 데이터 조합
- CC Custom Controller : CR의 상태를 기준으로 현재의 상태를 규정한 상태로 처리하기 위한 컨트롤 루프
- 생성 yaml
apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition # 사용자 정의 리소스(CRD) 생성 metadata: # name must match the spec fields below, and be in the form: <plural>.< group> name: crontabs.stable.example.com # <NAMES>.<GROUP> 으로 정의 spec: # group name to use for REST API: /apis/<group>/<version> group: stable.example.com # apiVersion 그룹 이름(<GROUP>) 을 지정 # list of versions supported by this CustomResourceDefinition versions: # CRD 버전 정의 - name: v1 # Each version can be enabled/disabled by Served flag. served: true # One and only one version must be marked as the storage version. storage: true schema: openAPIV3Schema: type: object properties: # status에 해당 spec: type: object properties: cronSpec: type: string image: type: string replicas: type: integer # either Namespaced or Cluster scope: Namespaced # Cluster 레벨 리소스인지 vs 네임스페이스 레벨 리소스인지 지정 names: # plural name to be used in the URL: /apis/<group>/<version>/<plural> plural: crontabs # 복수 이름 # singular name to be used as an alias on the CLI and for display singular: crontab # 단수 이름 # kind is normally the CamelCased singular type. Your resource manifests use this. kind: CronTab # Kind 이름 # shortNames allow shorter string to match your resource on the CLI shortNames: # 축약 이름 - ct
데이터베이스 k8s로 운영하기위해서는 쿠버네티스 오퍼레이터가 필요하다. 그 이유는 데이터베이스는 클러스터 상에서 노드간 동기화가 주기적으로 이루어져야하고 노드가 죽을 시 다른 노드가 master를 유지하여 서비스 상 장애가 발생하지 않도록 상태를 유지하는 것이 중요하기 때문
MySQL Operator for k8s
쿠버네티스 클러스터에서 MySQL 데이터베이스 인스턴스나 클러스터를 쉽게 배포하고 관리할 수 있게 해주는 오퍼레이터이며, 쿠버네티스의 커스텀 리소스(Custom Resources)와 컨트롤러 (Controllers)를 사용하여 MySQL의 운영 작업을 자동화하고, 데이터베이스 관리의 복잡성을 줄여 줌. MySQL 8.0.29 버전과 함께 릴리즈 됨

구축
MySQL Operator Install with Helm
설치시 버전은 https://github.com/mysql/mysql-operator/tags 에서 참조
10/28일 기준으로 container-registry.oracle.com/mysql/community-server:8.2.0 image를 pull 할 수 없고 8.1.0 image만 pull 할수 있으나 operator 2.1.0 이 현재 삭제되어 helm을 받을 수가 없어 kubectl로 배포
# helm chart download
wget https://github.com/mysql/mysql-operator/archive/refs/tags/8.1.0-2.1.0.tar.gz
tar -zxvf 8.1.0-2.1.0.tar.gz
cd mysql-operator-8.1.0-2.1.0/helm
# 설치
helm install mysql-operator ./mysql-operator --namespace mysql-operator --create-namespace
helm get manifest mysql-operator -n mysql-operator
# 설치 확
kubectl get deploy,pod -n mysql-operator
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/mysql-operator 1/1 1 1 25s
NAME READY STATUS RESTARTS AGE
pod/mysql-operator-d6ff8f8f6-86fgv 1/1 Running 0 25s
# CRD 확인
kubectl get crd | egrep 'mysql|zalando'
clusterkopfpeerings.zalando.org 2023-10-28T11:13:14Z
innodbclusters.mysql.oracle.com 2023-10-28T11:13:14Z
kopfpeerings.zalando.org 2023-10-28T11:13:14Z
mysqlbackups.mysql.oracle.com 2023-10-28T11:13:14Z
## CRD 상세 정보 확인
kubectl describe crd innodbclusters.mysql.oracle.com
...생략
## 삭제
helm uninstall mysql-operator -n mysql-operator && kubectl delete ns mysql-operator
MySQL InnoDB Cluster Install with Helm
설치
# 파라미터 파일 생성
cat <<EOT> mycnf-values.yaml
credentials:
root:
password: sakilaserverConfig:
mycnf: |
[mysqld]
max_connections=300
default_authentication_plugin=mysql_native_passwordtls:
useSelfSigned: true
EOT
# 차트 설치(기본값) : root 사용자(root), 호스트(%), 서버인스턴스(파드 3개), 라우터인스턴스(파드 1개), serverVersion(8.0.35)
# root 사용자 암호(), tls.useSelfSigned(사용), 네임스페이스 생성 및 적용(mysql-cluster)
helm install mycluster ./mysql-innodbcluster --namespace mysql-cluster -f mycnf-values.yaml --create-namespacehelm get values mycluster -n mysql-clusterhelm get manifest mycluster -n mysql-cluster
watch kubectl get innodbcluster,sts,pod,pvc,svc -n mysql-cluster
NAME STATUS ONLINE INSTANCES ROUTERS AGE
innodbcluster.mysql.oracle.com/mycluster ONLINE 1 3 1 73s
NAME READY AGE
statefulset.apps/mycluster 1/3 73s
NAME READY STATUS RESTARTS AGE
pod/mycluster-0 2/2 Running 0 73s
pod/mycluster-1 2/2 Running 0 73s
pod/mycluster-2 2/2 Running 0 73s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/datadir-mycluster-0 Bound pvc-83a3e2b9-9792-4cd7-b825-8ed6771df817 2Gi RWO gp3 73s
persistentvolumeclaim/datadir-mycluster-1 Bound pvc-e98a7172-3d00-47ad-a7ef-a6e0778b4c6b 2Gi RWO gp3 73s
persistentvolumeclaim/datadir-mycluster-2 Bound pvc-7d81080a-d600-40a5-8b47-9ecba0f8b3a4 2Gi RWO gp3 73s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/mycluster ClusterIP 10.100.241.39 <none> 3306/TCP,33060/TCP,6446/TCP,6448/TCP,6447/TCP,6449/TCP,8443/TCP 73s
service/mycluster-instances ClusterIP None <none> 3306/TCP,33060/TCP,33061/TCP 73s -> headless 서비스, 개벌파드로 직접 접근가능
# 삭제
helm uninstall mycluster -n mysql-cluster && kubectl delete ns 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 2m25s
## 이벤트 확인
kubectl describe innodbcluster -n mysql-cluster | grep Events: -A30
Normal Logging 2m47s kopf Handler 'on_innodbcluster_field_router_instances/spec.router.instances' succeeded.
Normal Logging 2m47s kopf Handler 'on_innodbcluster_field_image_pull_policy/spec.imagePullPolicy' succeeded.
Normal Logging 2m47s kopf Handler 'on_innodbcluster_field_version/spec.version' succeeded.
Normal Logging 2m47s kopf Handler 'on_innodbcluster_field_instances/spec.instances' succeeded.
Normal Logging 2m47s kopf Handler 'on_innodbcluster_create' succeeded.
Normal Logging 2m46s kopf on_innodbcluster_field_tls_use_self_signed
Normal Logging 2m46s kopf Handler 'on_innodbcluster_field_tls_use_self_signed/spec.tlsUseSelfSigned' succeeded.
Normal Logging 2m46s kopf Creation is processed: 6 succeeded; 0 failed.
## 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
## 서버인스턴스 확인(스테이트풀셋) : 3개의 노드에 각각 파드 생성 확인, 사이드카 컨테이너 배포
kubectl get sts -n mysql-cluster; echo; kubectl get pod -n mysql-cluster -l app.kubernetes.io/component=database -owide
NAME READY AGE
mycluster 3/3 3m12s
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mycluster-0 2/2 Running 0 3m13s 192.168.2.141 ip-192-168-2-161.ap-northeast-2.compute.internal <none> 2/2
mycluster-1 2/2 Running 0 3m13s 192.168.3.115 ip-192-168-3-96.ap-northeast-2.compute.internal <none> 2/2
mycluster-2 2/2 Running 0 3m13s 192.168.1.25 ip-192-168-1-32.ap-northeast-2.compute.internal <none> 2/2
## 프로브 확인(Readiness, Liveness, Startup)
describe pod -n mysql-cluster mycluster-0 | egrep 'Liveness|Readiness:|Startup'
Liveness: exec [/livenessprobe.sh] delay=15s timeout=1s period=15s #success=1 #failure=10
Readiness: exec [/readinessprobe.sh] delay=10s timeout=1s period=5s #success=1 #failure=10000
Startup: exec [/livenessprobe.sh 8] delay=5s timeout=1s period=3s #success=1 #failure=10000
## 서버인스턴스가 사용하는 PV(PVC) 확인 : AWS EBS 볼륨 확인해보기
kubectl get sc
kubectl df-pv
pvc,pv -n mysql-cluster
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/datadir-mycluster-0 Bound pvc-83a3e2b9-9792-4cd7-b825-8ed6771df817 2Gi RWO gp3 3m54s
persistentvolumeclaim/datadir-mycluster-1 Bound pvc-e98a7172-3d00-47ad-a7ef-a6e0778b4c6b 2Gi RWO gp3 3m54s
persistentvolumeclaim/datadir-mycluster-2 Bound pvc-7d81080a-d600-40a5-8b47-9ecba0f8b3a4 2Gi RWO gp3 3m54s
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-7d81080a-d600-40a5-8b47-9ecba0f8b3a4 2Gi RWO Delete Bound mysql-cluster/datadir-mycluster-2 gp3 3m50s
persistentvolume/pvc-83a3e2b9-9792-4cd7-b825-8ed6771df817 2Gi RWO Delete Bound mysql-cluster/datadir-mycluster-0 gp3 3m50s
persistentvolume/pvc-e98a7172-3d00-47ad-a7ef-a6e0778b4c6b 2Gi RWO Delete Bound mysql-cluster/datadir-mycluster-1 gp3 3m50s
## 서버인스턴스 각각 접속을 위한 헤드리스 Headless 서비스 확인
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
service/mycluster-instances ClusterIP None <none> 3306/TCP,33060/TCP,33061/TCP 4m37s
NAME ENDPOINTS AGE
endpoints/mycluster-instances 192.168.1.25:33060,192.168.2.141:33060,192.168.3.115:33060 + 6 more... 4m37s
## 라우터인스턴스(디플로이먼트) 확인 : 1대의 파드 생성 확인
deploy -n mysql-cluster; -n mysql-cluster -l app.kubernetes.io/component=router
NAME READY UP-TO-DATE AVAILABLE AGE
mycluster-router 1/1 1 1 5m6s
NAME READY STATUS RESTARTS AGE
mycluster-router-65469cb756-9nbf2 1/1 Running 0 3m38s
## 라우터인스턴스 접속을 위한 서비스(ClusterIP) 확인
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@$ --password=sakila --sqlx --execute="SHOW VARIABLES LIKE 'max_connections';"
WARNING: Using a password on the command line interface can be insecure.
Variable_name Value
max_connections 300
정보확인 및 설정
MySQL 접속
Headless 서비스 주소로 개별 MySQL 서버(파드)로 직접 접속 → 각각의 db 서버로 직접접근
headless 이기에 mycluster-0, mycluster-1, mycluster-2 식으로 pod name이 번호를 붙여가며 순차적으로 네이밍 결정되어 고정된 주소로 접근 가능
# MySQL 라우터 접속을 위한 서비스 정보 확인 : 실습 환경은 Cluster-IP Type
kubectl get svc -n mysql-cluster mycluster
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mycluster ClusterIP 10.100.241.39 <none> 3306/TCP,33060/TCP,6446/TCP,6448/TCP,6447/TCP,6449/TCP,8443/TCP 6m7s
# MySQL 서버(파드) 접속을 위한 서비스 정보 확인 : Headless 서비스
kubectl get svc -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 6m18s
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 6m29s 192.168.2.141 ip-192-168-2-161.ap-northeast-2.compute.internal <none> 2/2
mycluster-1 2/2 Running 0 6m29s 192.168.3.115 ip-192-168-3-96.ap-northeast-2.compute.internal <none> 2/2
mycluster-2 2/2 Running 0 6m29s 192.168.1.25 ip-192-168-1-32.ap-northeast-2.compute.internal <none> 2/2
# netshoot 파드에 zsh 접속해서 DNS 쿼리 수행
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
10.100.241.39 -> cluster ip 10.100.241.39 와 일치
dig mycluster.mysql-cluster.svc.cluster.local
# Headless 서비스 주소로 개별 MySQL 서버(파드)로 직접 접속을 위한 DNS 쿼리
dig mycluster-instances.mysql-cluster.svc +search
;; ANSWER SECTION:
mycluster-instances.mysql-cluster.svc.cluster.local. 5 IN A 192.168.1.25
mycluster-instances.mysql-cluster.svc.cluster.local. 5 IN A 192.168.3.115
mycluster-instances.mysql-cluster.svc.cluster.local. 5 IN A 192.168.2.141
;; Query time: 0 msec
;; SERVER: 10.100.0.10#53(10.100.0.10) (UDP)
;; WHEN: Sat Oct 28 11:23:34 UTC 2023
;; MSG SIZE rcvd: 293
dig mycluster-instances.mysql-cluster.svc.cluster.local +short
192.168.3.115
192.168.1.25
192.168.2.141
# MySQL 서버(파드)마다 고유한 SRV 레코드가 있고, 해당 도메인 주소로 접속 시 MySQL 라우터를 경유하지 않고 지정된 MySQL 서버(파드)로 접속됨
dig mycluster-instances.mysql-cluster.svc.cluster.local SRV
..(생략)...
;; ADDITIONAL SECTION:
mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local. 5 IN A 192.168.3.115
mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local. 5 IN A 192.168.2.141
mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local. 5 IN A 192.168.1.25
# mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local 로 접근 시 192.168.3.115 로 맵핑된다.
# zsh 빠져나오기
exit
-------
# 접속 주소 변수 지정
MIC=mycluster.mysql-cluster.svc.cluster.local
MDB1=mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local
MDB2=mycluster-1MDB3=mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local
# MySQL 라우터를 통한 MySQL 파드 접속
# kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$ --password=sakila
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$ --password=sakila --sqlx --execute='show databases;'
Database
information_schema
mysql
mysql_innodb_cluster_metadata
performance_schema
sys
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$ --password=sakila --sqlx --execute="SHOW VARIABLES LIKE 'max_connections';"
Variable_name Value
max_connections 300
# 개별 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-1mysqlsh mysqlx://root@$MDB3 --password=sakila --sqlx --execute='SELECT @@hostname;'mycluster-2
MySQL Shell 8.x 를 통한 연결 → shell로 통합접근, 다양한 언어(sql, python, javascript) client 환경을 사용할 수 있음
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh
MySQL JS >
MySQL JS > \connect root@mycluster.mysql-cluster.svc
Fetching schema names for auto-completion... Press ^C to stop.
Your MySQL connection id is 1968 (X protocol)
Server version: 8.1.0 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.
MySQL mycluster.mysql-cluster.svc:33060+ ssl JS >
## MySQL InnoDB Cluster 상태 확인 : JavaScript 모드
\status
MySQL Shell version 8.1.0
Connection Id: 1968
Default schema:
Current schema:
Current user: root@ip-192-168-2-131.ap-northeast-2.compute.internal
SSL: Cipher in use: TLS_AES_256_GCM_SHA384 TLSv1.3
Using delimiter: ;
Server version: 8.1.0 MySQL Community Server - GPL
Protocol version: X protocol
Client library: 8.1.0
Connection: mycluster.mysql-cluster.svc via TCP/IP
TCP port: 33060
Server characterset: utf8mb4
Schema characterset: utf8mb4
Client characterset: utf8mb4
Conn. characterset: utf8mb4
Result characterset: utf8mb4
Compression: Disabled
Uptime: 14 min 8.0000 sec
## Python 모드로 전환
MySQL mycluster.mysql-cluster.svc:33060+ ssl JS > \py
Switching to Python mode...
MySQL mycluster.mysql-cluster.svc:33060+ ssl Py >
# 종료
\exit
MySQL 라우터 확인 & 프라이머리 변경
# MySQL 라우터 bash 셸 접속
kubectl exec -it -n mysql-cluster deploy/mycluster-router -- bash
--------------------
# help
mysqlrouter --help
mysqlrouter --version
# 관련 파일 확인
ls -al /tmp/mysqlrouter/
total 16
drwx--S--- 5 mysqlrouter mysqlrouter 118 Oct 28 11:16 .
drwxrwsrwx 3 root mysqlrouter 69 Oct 28 11:16 ..
drwx--S--- 2 mysqlrouter mysqlrouter 116 Oct 28 11:16 data
drwx--S--- 2 mysqlrouter mysqlrouter 29 Oct 28 11:16 log
-rw------- 1 mysqlrouter mysqlrouter 1870 Oct 28 11:16 mysqlrouter.conf
-rw------- 1 mysqlrouter mysqlrouter 87 Oct 28 11:16 mysqlrouter.key
drwx--S--- 2 mysqlrouter mysqlrouter 6 Oct 28 11:16 run
-rwx------ 1 mysqlrouter mysqlrouter 135 Oct 28 11:16 start.sh
-rwx------ 1 mysqlrouter mysqlrouter 158 Oct 28 11:16 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-robinfirst-available,round-robin-with-fallback)
# role 이 PRIMART_AND_SECONDARY 시 : 기본 round-robin 동작, 2가지 중 선택(round-robinfirst-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 서버로 연결할지를 설정, 여기서는 세컨터리 타입 서버로 연결(읽기전용)
round-robin-with-fallback
# 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
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 --
...(생략)...
# 메타데이터 캐시 정보 확인
kubectl exec -it -n mysql-cluster deploy/mycluster-router -- cat /tmp/mysqlrouter/data/state.json | jq
{
"metadata-cache": {
"group-replication-id": "65d7ad70-7583-11ee-9cca-5afbb6fcc8c7",
"cluster-metadata-servers": [
"mysql://mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local:3306",
"mysql://mycluster-2.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
WbiA2-8=sZ9-Jt~qn-GrSzE-ol=x3
# (옵션) 모니터링
watch -d "kubectl exec -it -n mysql-cluster deploy/mycluster-router -- cat /tmp/mysqlrouter/data/state.json"
샘플 대용량 데이터베이스 주입
30만명 직원의 400만 개의 레코드로 구성, 6개의 테이블, 160메가 링크 Github
터미널은 미리 2개를 띄워놓고 시작
각각의 db에 headless로 접근하여 데이터가 동일하다면 복제가 정상적으로 수행됨을 확인 할 수 있음
# [터미널1] 포트 포워딩
kubectl -n mysql-cluster port-forward service/mycluster mysql
Forwarding from 127.0.0.1:3306 -> 6446
Forwarding from [::1]:3306 -> 6446
-> local에서 3306 으로 접근 시 해당 서비스 포트포워딩
# [터미널2] 아래부터는 터미널2에서 입력
------------------------------
# 포트 포워드 확인
ss -tnlp | grep kubectl
LISTEN 0 128 127.0.0.1:3306 0.0.0.0:* users:(("kubectl",pid=13357,fd=8))
LISTEN 0 128 [::1]:3306 [::]:* users:(("kubectl",pid=13357,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;"
+-------------+
| @@hostname |
+-------------+
| mycluster-0 |
+-------------+
# 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;"
+-------------------------------+
| Database |
+-------------------------------+
| employees |
| information_schema |
| mysql |
| mysql_innodb_cluster_metadata |
| performance_schema |
| sys |
+-------------------------------+
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;"
+--------+------------+------------+-----------+--------+------------+
| 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 |
| 10006 | 1953-04-20 | Anneke | Preusig | F | 1989-06-02 |
| 10007 | 1957-05-23 | Tzvetan | Zielinski | F | 1989-02-10 |
| 10008 | 1958-02-19 | Saniya | Kalloufi | M | 1994-09-15 |
| 10009 | 1952-04-19 | Sumant | Peac | F | 1985-02-18 |
| 10010 | 1963-06-01 | Duangkaew | Piveteau | F | 1989-08-24 |
+--------+------------+------------+-----------+--------+------------+
# 각각 헤드리스 서비스 주소로 각각의 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;"
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
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MDB2 --password=sakila --sqlx --execute="USE employees;SELECT * FROM employees LIMIT 5;"
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
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MDB3 --password=sakila --sqlx --execute="USE employees;SELECT * FROM employees LIMIT 5;"
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
# 정상적으로 데이터가 복제됨
테스트
복제테스트
# 접속
MIC=mycluster.mysql-cluster.svc.cluster.local
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MIC --password=sakila --sqlx
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 |
+-------------------------------------------+
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MDB1 --password=sakila --sqlx --execute='USE test; SELECT * FROM t1;'
c1 c2
1 Luis
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MDB2 --password=sakila --sqlx --execute='USE test; SELECT * FROM t1;'
c1 c2
1 Luis
kubectl exec -it -n mysql-operator deploy/mysql-operator -- mysqlsh mysqlx://root@$MDB3 --password=sakila --sqlx --execute='USE test; SELECT * FROM t1;'
c1 c2
1 Luis
SQL > SELECT group_replication_get_communication_protocol();
+------------------------------------------------+
| group_replication_get_communication_protocol() |
+------------------------------------------------+
| 8.0.27 |
+------------------------------------------------+
성능테스트
다수의 MySQL 클라이언트 파드를 통해 MySQL 라우터 시 부하분산 확인
시나리오
- 3개의 client 가 mysql router를 통해 접근 시 라우터 정책이 first-available 일때와 round-robin-with-fallback 일 경우 접근하는 db 가 어떤것인지를 확인하고 부하분사 여부를 확인한다.
- 접속 port 6446 는 first-available 이고, 6447 round-robin-with-fallback 이다.
- 라운드 정책설정은 MySQL 라우터 확인 & 프라이머리 변경 참조
# 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
NAME READY STATUS RESTARTS AGE
myclient1 1/1 Running 0 82s
myclient2 1/1 Running 0 74s
myclient3 1/1 Running 0 72s
# 파드1에서 mysql 라우터 서비스로 접속 확인 : TCP 3306
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "SHOW DATABASES;"
+-------------------------------+
| Database |
+-------------------------------+
| employees |
| information_schema |
| mysql |
| mysql_innodb_cluster_metadata |
| performance_schema |
| sys |
| test |
+-------------------------------+
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;"
+-------------+-------------+
| @@HOSTNAME | @@SERVER_ID |
+-------------+-------------+
| mycluster-0 | 1000 |
+-------------+-------------+
# 접속하는 host가 0으로 계속 접근(0번 db가 계속 살아 있기때문)
# 파드1에서 mysql 라우터 서비스로 접속 확인 : TCP 6447 >> 3초 간격으로 확인!
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila --port=6447 -e "SELECT @@HOSTNAME,@@SERVER_ID;"
+-------------+-------------+
| @@HOSTNAME | @@SERVER_ID |
+-------------+-------------+
| mycluster-2 | 1002 |
+-------------+-------------+
3초 간격
+-------------+-------------+
| @@HOSTNAME | @@SERVER_ID |
+-------------+-------------+
| mycluster-1 | 1001 |
+-------------+-------------+
# 접속하는 host가 변경됨
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: [Warning] Using a password on the command line interface can be insecure.
+-------------+-------------+-------------------+
| @@hostname | @@read_only | @@super_read_only |
+-------------+-------------+-------------------+
| mycluster-0 | 0 | 0 |
+-------------+-------------+-------------------+
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------+-------------+-------------------+
| @@hostname | @@read_only | @@super_read_only |
+-------------+-------------+-------------------+
| mycluster-0 | 0 | 0 |
+-------------+-------------+-------------------+
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------+-------------+-------------------+
| @@hostname | @@read_only | @@super_read_only |
+-------------+-------------+-------------------+
| mycluster-0 | 0 | 0 |
+-------------+-------------+-------------------+
# 파드들에서 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
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------+--------------------------------------------------------+
| @@HOSTNAME | host |
+-------------+--------------------------------------------------------+
| mycluster-2 | ip-192-168-2-131.ap-northeast-2.compute.internal:54362 |
+-------------+--------------------------------------------------------+
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------+---------------------------------------------------------------+
| @@HOSTNAME | host |
+-------------+---------------------------------------------------------------+
| mycluster-1 | 192-168-2-131.mycluster.mysql-cluster.svc.cluster.local:45424 |
+-------------+---------------------------------------------------------------+
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------+--------------------------------------------------------+
| @@HOSTNAME | host |
+-------------+--------------------------------------------------------+
| mycluster-2 | ip-192-168-2-131.ap-northeast-2.compute.internal:54366 |
+-------------+--------------------------------------------------------+
반복적으로 데이터 INSERT 및 MySQL 서버에 복제 확인 : 세컨더리파드에 INSERT 시도
한쪽에서는 insert를 하고 한쪽에서는 조회를 하기 위해 터미너를 두개 생성해서 실습한다.
# 파드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;"
+----+------+
| c1 | c2 |
+----+------+
| 1 | Luis |
+----+------+
# 추가 후 조회
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;"
+----+-------+
| c1 | c2 |
+----+-------+
| 1 | Luis |
| 2 | Luis2 |
+----+-------+
# 반복 추가 및 조회
# [터미널1]
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
# [터미널2]
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "USE test;SELECT * FROM t1;"
+----+--------+
| c1 | c2 |
+----+--------+
| 1 | Luis |
| 2 | Luis2 |
| 3 | Luis3 |
| 4 | Luis4 |
| 5 | Luis5 |
| 6 | Luis6 |
| 7 | Luis7 |
| 8 | Luis8 |
| 9 | Luis9 |
| 10 | Luis10 |
+----+--------+
# 모니터링 : 신규 터미널 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;'"
+----+--------+
| c1 | c2 |
+----+--------+
| 49 | Luis49 |
| 48 | Luis48 |
| 47 | Luis47 |
| 46 | Luis46 |
| 45 | Luis45 |
+----+--------+
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;'"
+----+--------+
| c1 | c2 |
+----+--------+
| 49 | Luis49 |
| 48 | Luis48 |
| 47 | Luis47 |
| 46 | Luis46 |
| 45 | Luis45 |
+----+--------+
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;'"
+----+--------+]
| c1 | c2 |
+----+--------+
| 49 | Luis49 |
| 48 | Luis48 |
| 47 | Luis47 |
| 46 | Luis46 |
| 45 | Luis45 |
+----+--------+
# (참고) 세컨더리 MySQL 서버 파드에 INSERT 가 되지 않는다 : --super-read-only option
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');"
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 1062 (23000) at line 1: Duplicate entry '1089' for key 't1.PRIMARY'
command terminated with exit code 1
사전 준비: 워드프레스 설치
# NFS 마운트 확인
ssh ec2-user@$N1 sudo df -hT --type nfs4
df: no file systems processed
ssh ec2-user@$N2 sudo df -hT --type nfs4
df: no file systems processed
ssh ec2-user@$N3 sudo df -hT --type nfs4
df: no file systems processed
# 위와 같이 마운트 안되어있는것이 정상 워드프레스 설치 후 생성됨
# 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
NAME READY STATUS RESTARTS AGE
my-wordpress-5c65fbdfb6-bp75s 0/1 Running 0 43s
my-wordpress-5c65fbdfb6-v77f2 0/1 Running 1 (17s ago) 43s
my-wordpress-5c65fbdfb6-vg8g5 0/1 Running 0 43s
kubectl get sc,pv
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
storageclass.storage.k8s.io/efs-sc efs.csi.aws.com Delete Immediate false 3h26m
storageclass.storage.k8s.io/gp2 kubernetes.io/aws-ebs Delete WaitForFirstConsumer false 3h45m
storageclass.storage.k8s.io/gp3 (default) ebs.csi.aws.com Delete WaitForFirstConsumer true 3h26m
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-54146fe0-658b-4ba0-8225-ff5a5b0915eb 10Gi RWX Delete Bound default/my-wordpress efs-sc 58s
persistentvolume/pvc-7d81080a-d600-40a5-8b47-9ecba0f8b3a4 2Gi RWO Delete Bound mysql-cluster/datadir-mycluster-2 gp3 75m
persistentvolume/pvc-83a3e2b9-9792-4cd7-b825-8ed6771df817 2Gi RWO Delete Bound mysql-cluster/datadir-mycluster-0 gp3 75m
persistentvolume/pvc-e98a7172-3d00-47ad-a7ef-a6e0778b4c6b 2Gi RWO Delete Bound mysql-cluster/datadir-mycluster-1 gp3 75m
# NFS 마운트 확인
ssh ec2-user@$N1 sudo df -hT --type nfs4
Filesystem Type Size Used Avail Use% Mounted on
127.0.0.1:/ nfs4 8.0E 0 8.0E 0% /var/lib/kubelet/pods/c41481ea-8599-4aa8-ac12-de9b61882956/volumes/kubernetes.io~csi/pvc-54146fe0-658b-4ba0-8225-ff5a5b0915eb/mount
127.0.0.1:/wordpress nfs4 8.0E 0 8.0E 0% /var/lib/kubelet/pods/c41481ea-8599-4aa8-ac12-de9b61882956/volume-subpaths/pvc-54146fe0-658b-4ba0-8225-ff5a5b0915eb/wordpress/0
ssh ec2-user@$N2 sudo df -hT --type nfs4
ssh ec2-user@$N3 sudo df -hT --type nfs4
Filesystem Type Size Used Avail Use% Mounted on
127.0.0.1:/ nfs4 8.0E 0 8.0E 0% /var/lib/kubelet/pods/0991bc9d-4e06-49a3-aa56-1f102ca04220/volumes/kubernetes.io~csi/pvc-54146fe0-658b-4ba0-8225-ff5a5b0915eb/mount
127.0.0.1:/wordpress nfs4 8.0E 0 8.0E 0% /var/lib/kubelet/pods/0991bc9d-4e06-49a3-aa56-1f102ca04220/volume-subpaths/pvc-54146fe0-658b-4ba0-8225-ff5a5b0915eb/wordpress/0
# 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
+----------------+
| post_title |
+----------------+
| Hello world! |
| Sample Page |
| Privacy Policy |
+----------------+
# (참고) 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;"
+------------------+---------+----------+-------------+--------+-------+
| term_taxonomy_id | term_id | taxonomy | description | parent | count |
+------------------+---------+----------+-------------+--------+-------+
| 1 | 1 | category | | 0 | 2 |
| 2 | 2 | wp_theme | | 0 | 1 |
+------------------+---------+----------+-------------+--------+-------+
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila -e "SELECT post_content FROM wordpress.wp_posts;"
장애 테스트
[장애1] MySQL 서버 파드(인스턴스) 1대 강제 삭제 및 동작 확인 : 워드프레스 정상 접속 및 포스팅 작성 가능, 데이터베이스에 반복해서 INSERT
사전 준비
mycluster-1 이 PRIMARY 인 상황
# 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;'
+---------------------+
| VIEW_ID |
+---------------------+
| 16984917709340217:5 |
+---------------------+
+-----------------------------------------------------------------+-------------+
| MEMBER_HOST | MEMBER_ROLE |
+-----------------------------------------------------------------+-------------+
| mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local | PRIMARY |
| mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY |
| mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY |
+-----------------------------------------------------------------+-------------+
kubectl get pod -n mysql-cluster -owide
mycluster-0 2/2 Running 0 127m 192.168.2.141 ip-192-168-2-161.ap-northeast-2.compute.internal <none> 2/2
mycluster-1 2/2 Running 0 127m 192.168.3.115 ip-192-168-3-96.ap-northeast-2.compute.internal <none> 2/2
mycluster-2 2/2 Running 0 127m 192.168.1.25 ip-192-168-1-32.ap-northeast-2.compute.internal <none> 2/2
mycluster-router-65469cb756-9nbf2 1/1 Running 0 126m 192.168.2.131 ip-192-168-2-161.ap-northeast-2.compute.internal <none> <none>
# 파드들에서 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;"
+-------------+-------------+
| @@HOSTNAME | @@SERVER_ID |
+-------------+-------------+
| mycluster-2 | 1002 |
+-------------+-------------+
3초 간격
kubectl exec -it myclient1 -- mysql -h mycluster.mysql-cluster -uroot -psakila --port=6447 -e "SELECT @@HOSTNAME,@@SERVER_ID;"
+-------------+-------------+
| @@HOSTNAME | @@SERVER_ID |
+-------------+-------------+
| mycluster-1 | 1001 |
+-------------+-------------+
장애 동작 확인
mycluster-1 이 PRIMARY 이므로 mycluster-1 을 삭제 후 wordpress에서 게시글 작성 가능확인
# 모니터링 : 터미널 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-1 && kubectl get pod -n mysql-cluster -w
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mycluster-0 2/2 Running 0 2m8s 192.168.2.141 ip-192-168-2-161.ap-northeast-2.compute.internal <none> 2/2
mycluster-1 2/2 Terminating 0 134m 192.168.3.115 ip-192-168-3-96.ap-northeast-2.compute.internal <none> 2/2
mycluster-2 2/2 Running 0 134m 192.168.1.25 ip-192-168-1-32.ap-northeast-2.compute.internal <none> 2/2
mycluster-router-65469cb756-9nbf2 1/1 Running 0 133m 192.168.2.131 ip-192-168-2-161.ap-northeast-2.compute.internal <none> <none>
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-wordpress-5c65fbdfb6-4b8z7 1/1 Running 1 (9m11s ago) 12m 192.168.3.61 ip-192-168-3-96.ap-northeast-2.compute.internal <none> <none>
my-wordpress-5c65fbdfb6-mxbx7 1/1 Running 1 (11m ago) 12m 192.168.2.179 ip-192-168-2-161.ap-northeast-2.compute.internal <none> <none>
my-wordpress-5c65fbdfb6-nfk99 1/1 Running 1 (9m58s ago) 12m 192.168.1.218 ip-192-168-1-32.ap-northeast-2.compute.internal <none> <none>
myclient1 1/1 Running 0 88m 192.168.1.203 ip-192-168-1-32.ap-northeast-2.compute.internal <none> <none>
myclient2 1/1 Running 0 88m 192.168.3.185 ip-192-168-3-96.ap-northeast-2.compute.internal <none> <none>
myclient3 1/1 Running 0 88m 192.168.2.96 ip-192-168-2-161.ap-northeast-2.compute.internal <none> <none>
+---------------------+
| VIEW_ID |
+---------------------+
| 16984917709340217:6 |
+---------------------+
+-----------------------------------------------------------------+-------------+
| MEMBER_HOST | MEMBER_ROLE |
+-----------------------------------------------------------------+-------------+
| mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local | PRIMARY |
| mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY |
+-----------------------------------------------------------------+-------------+
# 워드프레스에 글 작성 및 접속 확인 : 1초 미만으로 자동 절체! >> 원상복구 FailBack 확인(파드 재생성 후 그룹 멤버 Join 확인)
# 만약 <세컨더리 MySQL 서버파드> 를 삭제했을 경우에는 자동 Join 되지 않음 >> 아래 수동 Join 실행하자

mycluster-2가 PRIMARY로 바로 변경되었고 wordpress에서도 정상적으로 posting 가능하며 조회에도 이상없음을 확인함으로 써primary db가 죽어도 바로 복구되며 정상적인 서비스 운용이 가능함을 확인 할 수 있음
[장애2] MySQL 서버 파드(인스턴스) 가 배포된 노드 1대 drain 설정 및 동작 확인 : 워드프레스 정상 접속 및 포스팅 작성 가능, 데이터베이스에 반복해서 INSERT 시도
mycluster-2 가 PRIMARY 인 상황
# 모니터링 : 터미널 3개 >> 장애1 모니터링과 상동
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
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mycluster-0 2/2 Running 0 4m57s 192.168.2.141 ip-192-168-2-161.ap-northeast-2.compute.internal <none> 2/2
mycluster-1 2/2 Running 0 107s 192.168.3.80 ip-192-168-3-96.ap-northeast-2.compute.internal <none> 2/2
mycluster-2 2/2 Running 0 137m 192.168.1.25 ip-192-168-1-32.ap-northeast-2.compute.internal <none> 2/2
mycluster-router-65469cb756-9nbf2 1/1 Running 0 136m 192.168.2.131 ip-192-168-2-161.ap-northeast-2.compute.internal <none> <none>
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-wordpress-5c65fbdfb6-4b8z7 1/1 Running 1 (12m ago) 15m 192.168.3.61 ip-192-168-3-96.ap-northeast-2.compute.internal <none> <none>
my-wordpress-5c65fbdfb6-mxbx7 1/1 Running 1 (14m ago) 15m 192.168.2.179 ip-192-168-2-161.ap-northeast-2.compute.internal <none> <none>
my-wordpress-5c65fbdfb6-nfk99 1/1 Running 1 (12m ago) 15m 192.168.1.218 ip-192-168-1-32.ap-northeast-2.compute.internal <none> <none>
myclient1 1/1 Running 0 91m 192.168.1.203 ip-192-168-1-32.ap-northeast-2.compute.internal <none> <none>
myclient2 1/1 Running 0 91m 192.168.3.185 ip-192-168-3-96.ap-northeast-2.compute.internal <none> <none>
myclient3 1/1 Running 0 91m 192.168.2.96 ip-192-168-2-161.ap-northeast-2.compute.internal <none> <none>
+---------------------+
| VIEW_ID |
+---------------------+
| 16984917709340217:7 |
+---------------------+
+-----------------------------------------------------------------+-------------+
| MEMBER_HOST | MEMBER_ROLE |
+-----------------------------------------------------------------+-------------+
| mycluster-1.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY |
| mycluster-2.mycluster-instances.mysql-cluster.svc.cluster.local | PRIMARY |
| mycluster-0.mycluster-instances.mysql-cluster.svc.cluster.local | SECONDARY |
+-----------------------------------------------------------------+-------------+
+-------------+
| @@HOSTNAME |
+-------------+
| mycluster-0 |
+-------------+
Sat Oct 28 22:32:52 KST 2023
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------+
| @@HOSTNAME |
+-------------+
| mycluster-1 |
+-------------+
# 신규터미널4 : test 데이터베이스에 원하는 갯수 만큼 데이터 INSERT, CTRL+C 로 취소
for ((i=5001; i<=10000; 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

node가 drain 되었을 때 wordpress 접속 시 접속이 바로 안됬지만 금방 다른 노드에 떠있는 파드로 연결되어 정상적으로 접속되었고 글도 바로 작성가능하며 db도 primary가 바로 변경되었음.
scale 테스트
MySQL 서버 파드(인스턴스) / 라우터 파드 증가 및 감소해보기
# 현재 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'스터디 > [gasida] 쿠버네티스 데이터베이스 오퍼레이터' 카테고리의 다른 글
| 쿠버네티스 데이터베이스 오퍼레이터 6주차 (0) | 2025.12.05 |
|---|---|
| 쿠버네티스 데이터베이스 오퍼레이터 5주차 (0) | 2025.12.05 |
| 쿠버네티스 데이터베이스 오퍼레이터 - 4주차 (0) | 2025.12.05 |
| 쿠버네티스 데이터베이스 오퍼레이터 - 3주차 (0) | 2025.12.05 |
| 쿠버네티스 데이터베이스 오퍼레이터 - 1주차 (0) | 2025.12.05 |