이번 포스팅에서는 Statefulset 형태로 구현이 가능한 대표적인 클러스터인 Galera Cluster를 다루어 볼 것이다. 그 중에서도 Galera Cluster의 각 노드(Pod)의 상태를 etcd 데이터 스토어에 기록하고 사용할 수 있도록 etcd cluster를 동시에 활용하는 방식을 적용해 보려 한다. 주요 참조 기술 및 블로그는 글 말미의 Reference 를 참조하기 바란다.  앞서의 포스팅들과는 다르게 여기서는 Kubernetes 의 현 시점의 최신 버전인 1.11.3 을 활용하였다.



Mariadb Galera Cluster 구현을 위한 준비와 개념도


[Prerequisites]


  • Running k8s cluster with persistent storage(glusterfs or nfs storageclass, etc.)
  • Tested kubernetes version: 1.9.x, 1.11.x


Kubernetes 상에서 동작하는 Galera Cluster Application의 구조는 다음의 그림에 나타나 있다


 


  • galera-cluster-etcd-k8s-mod 프로젝트 clone
먼저 프로젝트의 manifest 를 clone 또는 다운로드해 둔다. 아래 github 프로젝트를 브라우저로 열고 내용을 확인하면서 개별 작업해도 무방
 

# git clone https://github.com/DragOnMe/galera-cluster-etcd-k8s-mod.git

# cd galera-cluster-etcd-k8s-mod.git


  • Container image re-building

docker.io/severalnines/mariadb 이미지도 역시 훌륭하게 동작하지만, 한글 데이터, 소문자 테이블명 등의 처리가 가능하도록 하기 위해서,  해당 이미지의 빌드 소스 위치인 github.com/severalnines/galera-docker-mariadb 레포지터리의 my.cnf 설정 파일을 변경하여 새로운 이미지를 빌드 & 푸쉬해서 사용하도록 한다.


여기서는 drlee001 대신에 github.com 사이트에 본인 각자의 account 를 발급 닫아 사용하면 된다


# cd galera-docker-mariadb

# docker build -t  drlee001/mariadb:10.1-modcnf .

# docker login -u drlee001

# docker push  drlee001/mariadb:10.1-modcnf



ETCD와 Galera Cluster 구축


  • etcd cluster 생성 및 점검

Galera Cluster 의 각 Mariadb node 상태 정보가 etcd store에 실시간 저장하고 관리되므로 가장 먼저 etcd 저장소를 구축해야 한다. 해당 아키텍처는 위 그림에 표현되어 있으니 참고하자.


실제로는 모든 galera 노드들이 자신의 상태 값을 주기적으로 etcd에 저장하며, 이 값들은 일정 시간이 지나면(TTL: 10초) 자동으로 expire 되는 식으로 동작한다.


# cd ..

# kubectl config set-context $(kubectl config current-context) --namespace=ns-galera-etcd

Context "kubernetes-admin@kubernetes" modified.


# kubectl create -f 00-etcd-cluster.yaml 

namespace/ns-galera-etcd created

service/etcd-client created

pod/etcd0 created

service/etcd0 created

pod/etcd1 created

service/etcd1 created

pod/etcd2 created

service/etcd2 created


# kubectl get pod,svc

NAME        READY     STATUS    RESTARTS   AGE

pod/etcd0   1/1       Running   0          4m

pod/etcd1   1/1       Running   0          4m

pod/etcd2   1/1       Running   0          4m


NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE

service/etcd-client   ClusterIP   10.134.158.165   <none>        2379/TCP            4m

service/etcd0         ClusterIP   10.142.79.41     <none>        2379/TCP,2380/TCP   4m

service/etcd1         ClusterIP   10.136.241.253   <none>        2379/TCP,2380/TCP   4m

service/etcd2         ClusterIP   10.128.106.181   <none>        2379/TCP,2380/TCP   4m


# curl -L http://10.134.158.165:2379/version

{"etcdserver":"3.3.8","etcdcluster":"3.3.0"}


# kubectl exec -it etcd0 -- etcdctl cluster-health

member ade526d28b1f92f7 is healthy: got healthy result from http://etcd1:2379

member cf1d15c5d194b5c9 is healthy: got healthy result from http://etcd0:2379

member d282ac2ce600c1ce is healthy: got healthy result from http://etcd2:2379

cluster is healthy


# kubectl exec -it etcd0 -- etcdctl --version

etcdctl version: 3.3.8

API version: 2

* ETCD의 API 서버로 쿼리를 보내는 방법은 위에 나온 것 처럼 etcdclient 서비스로 curl 명령을 통해서 API 호툴을 하거나 또는 각 etcd pod 중 하나로 직접 접속해서 etcdctl 명령을 보내는 2가지 방법이 있다 



  • Galera Cluster 의 구축 및 etcd 데이터 확인

# kubectl create -f 01-galera-mariadb-ss.yaml 

service/galera-hs created

statefulset.apps/galera-ss created


# kubectl get pods -w

NAME          READY     STATUS    RESTARTS   AGE

etcd0         1/1       Running   0          20m

etcd1         1/1       Running   0          20m

etcd2         1/1       Running   0          20m

galera-ss-0   0/1       Running   0          38s

galera-ss-0   1/1       Running   0         2m

galera-ss-1   0/1       Pending   0         0s

galera-ss-1   0/1       Pending   0         0s

galera-ss-1   0/1       Pending   0         8s

galera-ss-1   0/1       ContainerCreating   0         8s

galera-ss-1   0/1       Running   0         11s

galera-ss-0   0/1       Running   0         3m

galera-ss-0   1/1       Running   0         3m

galera-ss-1   1/1       Running   0         2m

galera-ss-2   0/1       Pending   0         0s

galera-ss-2   0/1       Pending   0         0s

galera-ss-2   0/1       Pending   0         19s

galera-ss-2   0/1       ContainerCreating   0         19s

galera-ss-2   0/1       Running   0         24s

galera-ss-0   0/1       Running   0         6m

galera-ss-0   1/1       Running   0         6m

galera-ss-2   0/1       Running   1         2m

galera-ss-2   1/1       Running   1         4m


# kubectl get pod,svc

NAME              READY     STATUS    RESTARTS   AGE

pod/etcd0         1/1       Running   0          32m

pod/etcd1         1/1       Running   0          32m

pod/etcd2         1/1       Running   0          32m

pod/galera-ss-0   1/1       Running   0          12m

pod/galera-ss-1   1/1       Running   0          10m

pod/galera-ss-2   1/1       Running   1          8m


NAME                                                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE

service/etcd-client                                   ClusterIP   10.134.158.165   <none>        2379/TCP            32m

service/etcd0                                         ClusterIP   10.142.79.41     <none>        2379/TCP,2380/TCP   32m

service/etcd1                                         ClusterIP   10.136.241.253   <none>        2379/TCP,2380/TCP   32m

service/etcd2                                         ClusterIP   10.128.106.181   <none>        2379/TCP,2380/TCP   32m

service/galera-hs                                     ClusterIP   None             <none>        3306/TCP            12m

service/glusterfs-dynamic-mysql-datadir-galera-ss-0   ClusterIP   10.139.193.16    <none>        1/TCP               12m

service/glusterfs-dynamic-mysql-datadir-galera-ss-1   ClusterIP   10.136.64.128    <none>        1/TCP               10m

service/glusterfs-dynamic-mysql-datadir-galera-ss-2   ClusterIP   10.138.84.181    <none>        1/TCP               7m


# curl -L http://10.134.158.165:2379/v2/keys

{"action":"get","node":{"dir":true,"nodes":[{"key":"/galera","dir":true,"modifiedIndex":8,"createdIndex":8}]}}


# curl -L http://10.134.158.165:2379/v2/keys/galera/mariadb_galera_ss

{"action":"get","node":{"key":"/galera/mariadb_galera_ss","dir":true,"nodes":[{"key":"/galera/mariadb_galera_ss/10.40.0.3","dir":true,"modifiedIndex":8,"createdIndex":8},{"key":"/galera/mariadb_galera_ss/10.44.0.5","dir":true,"modifiedIndex":49,"createdIndex":49},{"key":"/galera/mariadb_galera_ss/10.32.0.6","dir":true,"modifiedIndex":129,"createdIndex":129}],"modifiedIndex":8,"createdIndex":8}}


# kubectl exec -it etcd0 -- etcdctl ls /galera/mariadb_galera_ss

/galera/mariadb_galera_ss/10.40.0.3

/galera/mariadb_galera_ss/10.44.0.5

/galera/mariadb_galera_ss/10.32.0.6

* 각 데이터 노드(pod)는 0번 부터 하나씩 순차적으로 만들어지며, 클러스터 내의 각 노드들은 자동으로 discover 된다



Galera Cluster의 데이터 동기화 점검


Galera Cluster 각 노드의 주요 상태를 조회해 보면 다음과 같다

 

# ./88-galera-test.sh 

+---------------------------+----------------+

| VARIABLE_NAME             | VARIABLE_VALUE |

+---------------------------+----------------+

| WSREP_CLUSTER_SIZE        | 3              |

| WSREP_CLUSTER_STATUS      | Primary        |

| WSREP_CONNECTED           | ON             |

| WSREP_LOCAL_INDEX         | 1              |

| WSREP_LOCAL_STATE_COMMENT | Synced         |

| WSREP_READY               | ON             |

+---------------------------+----------------+

+---------------------------+----------------+

| VARIABLE_NAME             | VARIABLE_VALUE |

+---------------------------+----------------+

| WSREP_CLUSTER_SIZE        | 3              |

| WSREP_CLUSTER_STATUS      | Primary        |

| WSREP_CONNECTED           | ON             |

| WSREP_LOCAL_INDEX         | 2              |

| WSREP_LOCAL_STATE_COMMENT | Synced         |

| WSREP_READY               | ON             |

+---------------------------+----------------+

+---------------------------+----------------+

| VARIABLE_NAME             | VARIABLE_VALUE |

+---------------------------+----------------+

| WSREP_CLUSTER_SIZE        | 3              |

| WSREP_CLUSTER_STATUS      | Primary        |

| WSREP_CONNECTED           | ON             |

| WSREP_LOCAL_INDEX         | 0              |

| WSREP_LOCAL_STATE_COMMENT | Synced         |

| WSREP_READY               | ON             |

+---------------------------+----------------+



데이터 동기화를 위해 각 데이터베이스 노드에서 데이터를 Insert, Select 및 삭제가 정상적으로 동작하고 동기화 되는지 점검한다


# ./89-galera-sync-test.sh 

1. Create database & table(node 0):

2. Insert 2 records into table(node 0):

+-------------------+----------------+

| VARIABLE_NAME     | VARIABLE_VALUE |

+-------------------+----------------+

| WSREP_LOCAL_INDEX | 1              |

+-------------------+----------------+

+------+-----------------+

| id   | name            |

+------+-----------------+

| aaa  | xxx             |

| id   | this is my name |

+------+-----------------+

3. Show data(node 1):

+-------------------+----------------+

| VARIABLE_NAME     | VARIABLE_VALUE |

+-------------------+----------------+

| WSREP_LOCAL_INDEX | 2              |

+-------------------+----------------+

+------+-----------------+

| id   | name            |

+------+-----------------+

| aaa  | xxx             |

| id   | this is my name |

+------+-----------------+

4. Show data(node 2):

+-------------------+----------------+

| VARIABLE_NAME     | VARIABLE_VALUE |

+-------------------+----------------+

| WSREP_LOCAL_INDEX | 0              |

+-------------------+----------------+

+------+-----------------+

| id   | name            |

+------+-----------------+

| aaa  | xxx             |

| id   | this is my name |

+------+-----------------+

5. Delete data(node 2):

6. Show data(node 0):

+-------------------+----------------+

| VARIABLE_NAME     | VARIABLE_VALUE |

+-------------------+----------------+

| WSREP_LOCAL_INDEX | 1              |

+-------------------+----------------+

+------+-----------------+

| id   | name            |

+------+-----------------+

| id   | this is my name |

+------+-----------------+

7. Show data(node 1):

+-------------------+----------------+

| VARIABLE_NAME     | VARIABLE_VALUE |

+-------------------+----------------+

| WSREP_LOCAL_INDEX | 2              |

+-------------------+----------------+

+------+-----------------+

| id   | name            |

+------+-----------------+

| id   | this is my name |

+------+-----------------+

8. Delete database & table(node 1):

+--------------------+

| Database           |

+--------------------+

| information_schema |

| mydatabase         |

| mysql              |

| performance_schema |

+--------------------+



Galera Cluster 의 리셋, 삭제


Galera Cluster에서 사용하는 etcd 데이터 영역을 Reset 하려면, 먼저 Galera Cluster 를 먼저 삭제한 후,


# curl http://10.134.158.165:2379:2379/v2/keys/galera/mariadb_galera_ss?recursive=true -XDELETE 


또는


# kubectl exec -it etcd0 -- etcdctl rm /galera/mariadb_galera_ss --recursive


을 수행한다


만약 전체 영역을 모두 삭제하고 싶다면 아래 script 를 수행하면 된다.


# ./99-teardown.sh 

service "galera-hs" deleted

statefulset.apps "galera-ss" deleted

persistentvolumeclaim "mysql-datadir-galera-ss-0" deleted

persistentvolumeclaim "mysql-datadir-galera-ss-1" deleted

persistentvolumeclaim "mysql-datadir-galera-ss-2" deleted

namespace "ns-galera-etcd" deleted

service "etcd-client" deleted

pod "etcd0" deleted

service "etcd0" deleted

pod "etcd1" deleted

service "etcd1" deleted

pod "etcd2" deleted

service "etcd2" deleted

No resources found.

Id:6d779dbd1e08b7ff7348360190091632    Cluster:60147830250310129f7b11084613a204    Name:heketidbstorage



# kubectl config set-context $(kubectl config current-context) --namespace=default

Context "kubernetes-admin@kubernetes" modified.



Reference


  • https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/
  • https://severalnines.com/blog/mysql-docker-deploy-homogeneous-galera-cluster-etcd
  • https://github.com/severalnines/galera-docker-mariadb



- Barracuda -



블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들