본문 바로가기!

K8S Operator

[K8S Operator] Operator& MySQL Operator

728x90
반응형

1. K8S Controller란?

  • 쿠버네티스에서 클러스터의 상태를 관찰 한 다음, 필요한 경우에 생성 또는 변경을 요청하는 '컨트롤 루프'
  • 컨트롤 루프란?
    • Control plane은 종료되지 않는 루프(무한루프)를 실행하면서 상태를 제어
    • 컨트롤 루프는 크게 3가지 동작 (모니터링, 상태 차이 발견, 액션)
    • 위 3가지 동작을 통해 현재 상태를 정의, 의도한 상태와 같아지도록 동작하는 역할을 한다.
  • Controller의 종류에는 쿠버네티스에서 자체적으로 제공되는 컨트롤러와 기능 개발을 통한 커스텀 컨트롤러 2가지 방식이 있다.
  • K8S에서 기본적으로 제공되는 컨트롤러는 Control plane에서 동작한다.
  • 다음과 같이 자체적으로 제공되는 컨트롤러와 오퍼레이터에 의한 컨트롤러에 의한 컨트롤러의 아키텍쳐를 볼 수 있다. 참조 Docs

자체적으로 제공되는 일반적인 컨트롤러

 

오퍼레이터에 의한 컨트롤러

  • 위 그림과 같이 자체적으로 제공되는 컨트롤러는 Control plane에서 실행되지만 오퍼레이터에 의한 컨트롤러는 Worker node에서 실행된다.
  • control plane의 controller manager는 모든 컨트롤러를 관리할 수 있으므로, Custom contoller(오퍼레이터 컨트롤러)또한 controller manager의 컨트롤 루프에 추가된다.
  • 즉, Control plane의 controller manager가 실행되면 컨트롤 루프가 실행되고 여기서는 control plane에 있는 표준 컨트롤러와 worker node에 있는 Custom contoller(오퍼레이터 컨트롤러)를 모두 실행시킨다.

 

 

2. Operator란?

  • 오퍼레이터 패턴에 사용되는 쿠버네티스 익스텐션
  • 오퍼레이터는 쿠버네티스 원칙, 특히 컨트롤 루프를 따른다.
  • 오퍼레이터 패턴은 K8S 자체가 제공하는 기능이 아닌 운영을 하면서 사용자가 작업을 자동화하기 위한 방법이다.
  • 즉, 오퍼레이터(extension)은 쿠버네티스 동작을 이용하여 애플리케이션 운영을 자동화하는 것이다.
  • ex) 프로메테우스 오퍼레이터
    • 프로메테우스는 기본적으로 설정(rule)을 configmap에 설정하고 Pod를 재시작하며 사용한다.
    • 위와 같이 사용하게 되면 운영환경에서 운영이 번거롭기 때문에 오퍼레이터를 사용하게 되면 설정(rule)을 쿠버네티스 CRD로 정의하고 오퍼레이터가 설정 적용을 자동화하여 관리할 수 있다.

참고 유튜브) https://www.youtube.com/watch?v=sL2dVvDq32E

 

 

3. K8S CRD와 Operator의 관계

  • K9S CRD(CustomResourceDefinitions)를 이용하여 애플리케이션의 의도한 상태를 정의하며 Operator Custom Controller가 돌면서 현재 상태가 정의한 의도한 상태와 일치하는지 확인하며 일치하도록 동작한다.

 

4. Mysql Operator 실습

4-1) MySQL Operator for K8S 

Chart

Docs

# helm repo 추가 및 업데이트
$ helm repo add mysql-operator https://mysql.github.io/mysql-operator/
$ helm repo update

# elm을 통해 패키지를 배포
$ helm install mysql-operator mysql-operator/mysql-operator --namespace mysql-operator --create-namespace --version 2.1.1

# maifest 출력
$ 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           50

NAME                                 READY   STATUS    RESTARTS   AGE
pod/mysql-operator-d6ff8f8f6-as4fa   1/1     Running   0          100s

# CRD 확인
$ kubectl get crd | egrep 'mysql|zalando'
clusterkopfpeerings.zalando.org              2023-10-28T19:25:20Z
innodbclusters.mysql.oracle.com              2023-10-28T19:25:20Z
kopfpeerings.zalando.org                     2023-10-28T19:25:20Z
mysqlbackups.mysql.oracle.com                2023-10-28T19:25:20Z

 

4-2) InnoDB 클러스터(Mysql DB) 배포

  • MySQL 서버 자체적으로 페일오버 Failover 를 처리하는 기능을 제공하지 않으므로 사용자는 장애가 발생했을 때 레플리카 서버가 새로운 소스 서버가 될 수 있도록 일련의 작업들을 수행해야 한다.
  • 즉 레플리카 서버에 설정된 읽기 모드를 해제해야 하며, 스플릿 브레인 Split-Brain 현상을 방지하기 위해 장애가 발생한 소스 서버에서 데이터 변경을 실행하지 못하도록 해야 한다.
  • 그리고 애플리케이션 서버는 새로운 소스 서버를 바라보도록 커넥션 설정을 변경해야 한다.
  • 이러한 작업들은 모두 수동으로 처리할 수 밖에 없으며 완료되기까지 적지 않은 시간이 소요된다.
  • MySQL 5.7.17 버전에서 빌트인 형태의 HA 솔루션InnnDB 클러스터가 도입되면서 사용자는 좀 더 편리하게 고가용성을 실현할 수 있게 됐다.

Docs

Chart

  • Helm chart(mysql-innodbcluster) 배포
# (참고) Helm Chart Default Values 확인
$ helm show values mysql-operator/mysql-innodbcluster

# 파라미터 파일 생성
$ cat <<EOT> mycnf-values.yaml
credentials:
  root:
    password: leejunho
serverConfig:
  mycnf: |
    [mysqld]
     max_connections=300
     default_authentication_plugin=mysql_native_password
tls:
  useSelfSigned: true
EOT

# 차트 설치(기본값) : root 사용자(root), 호스트(%), 서버인스턴스(파드 3개), 라우터인스턴스(파드 1개), serverVersion(8.0.35)
# root 사용자 암호(leejunho), tls.useSelfSigned(사용), 네임스페이스 생성 및 적용(mysql-cluster)
$ helm install mycluster mysql-operator/mysql-innodbcluster --namespace mysql-cluster --version 2.1.1 -f mycnf-values.yaml --create-namespace

$ helm get values mycluster -n mysql-cluster

$ helm get manifest mycluster -n mysql-cluster

 

  • helm chart 로 배포된 리소스 확인
$ watch kubectl get innodbcluster,sts,pod,pvc,svc -n mysql-cluster
NAME                                       STATUS   ONLINE   INSTANCES   ROUTERS   AGE
innodbcluster.mysql.oracle.com/mycluster   ONLINE   3        3           1         1m55s

NAME                         READY   AGE
statefulset.apps/mycluster   3/3     1m55s

NAME                                    READY   STATUS    RESTARTS   AGE
pod/mycluster-0                         2/2     Running   0          1m55s
pod/mycluster-1                         2/2     Running   0          1m55s
pod/mycluster-2                         2/2     Running   0          1m55s
pod/mycluster-router-65469cb756-2xcm8   1/1     Running   0          65s

NAME                                        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/datadir-mycluster-0   Bound    pvc-b3242d6f-kuh0-832d-129f-23cc034f09c3   2Gi        RWO            gp2            1m55s
persistentvolumeclaim/datadir-mycluster-1   Bound    pvc-20dd0e4b-9cb0-4c9c-b102-a7fae3715687   2Gi        RWO            gp2            1m55s
persistentvolumeclaim/datadir-mycluster-2   Bound    pvc-734a0a91-0a27-82c2-befa-b74eb4b97c19   2Gi        RWO            gp2            1m55s

NAME                          TYPE        CLUSTER-IP	  EXTERNAL-IP   PORT(S)                                                           AGE
service/mycluster             ClusterIP   10.100.64.124   <none>        3306/TCP,33060/TCP,6446/TCP,6448/TCP,6447/TCP,6449/TCP,8443/TCP   1m55s
service/mycluster-instances   ClusterIP   None            <none>        3306/TCP,33060/TCP,33061/TCP                                      1m55s

 

  • PV 디스크 통계 확인
$ kubectl df-pv
PV NAME                                   PVC NAME             NAMESPACE      NODE NAME                                         POD NAME     VOLUME MOUNT NAME  SIZE    USED   AVAILABLE  %USED  IUSED  IFREE   %IUSED
pvc-b3242d6f-kuh0-832d-129f-23cc034f09c3  datadir-mycluster-0  mysql-cluster  ip-192-168-3-11.ap-northeast-2.compute.internal   mycluster-0  datadir            1945Mi  198Mi  1731Mi     10.19  219    130853  0.17
pvc-20dd0e4b-9cb0-4c9c-b102-a7fae3715687  datadir-mycluster-1  mysql-cluster  ip-192-168-2-33.ap-northeast-2.compute.internal   mycluster-1  datadir            1945Mi  198Mi  1731Mi     10.19  219    130853  0.17
pvc-734a0a91-0a27-82c2-befa-b74eb4b97c19  datadir-mycluster-2  mysql-cluster  ip-192-168-1-214.ap-northeast-2.compute.internal  mycluster-2  datadir            1945Mi  198Mi  1731Mi     10.19  219    130853  0.17

 

  • 프로브, Service, deploy, endpoint 확인
## 프로브 확인(Readiness, Liveness, Startup)
$ kubectl describe pod -n mysql-cluster mycluster-0 | egrep 'Liveness|Readiness:|Startup'

## 서버인스턴스 각각 접속을 위한 헤드리스 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
mycluster-instances   ClusterIP   None         <none>        3306/TCP,33060/TCP,33061/TCP   19m

## 라우터인스턴스(디플로이먼트) 확인  : 1대의 파드 생성 확인
$ kubectl get deploy -n mysql-cluster;kubectl get pod -n mysql-cluster -l app.kubernetes.io/component=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';"

 

4-3) MySQL 서버 접속 방법

  • Headless 서비스를 통해 개별 MySQL 서버(파드)로 직접 접속이 가능하다.
# MySQL 라우터 접속을 위한 서비스 정보 확인 : 실습 환경은 Cluster-IP Type
$ kubectl get svc -n mysql-cluster mycluster

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

# 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
$ dig mycluster.mysql-cluster.svc.cluster.local +search +short

# Headless 서비스 주소로 개별 MySQL 서버(파드)로 직접 접속을 위한 DNS 쿼리
$ dig mycluster-instances.mysql-cluster.svc +search
$ dig mycluster-instances.mysql-cluster.svc.cluster.local +short

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

# 접속 주소 변수 지정
$ 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

 

728x90
반응형