一、前言

文章Kubernetes Mysql Operator方案对比》详细分析了Presslabs的MySQL Operator方案的架构、功能特点和Presslabs在他们产品中的实践效果,本文根据Presslabs官网的描述《Getting started with MySQL operator》来部署Operator和MySQL集群,并分析一些运维操作。

  Operator架构图

二、部署Presslabs MySQL Operator

2.1 通过helm部署charts

通过查看chart文件,可以看到当前operator的版本是0.3.8;并且在values.yaml中,进行了如下设置:

  replicas: 3
  ...
  installCRDs: true
  ...
  persistence:
    enabled: true
    If defined, storageClassName: <storageClass>
    If set to "-", storageClassName: "", which disables dynamic provisioning
    If undefined (the default) or set to null, no storageClassName spec is
      set, choosing the default provisioner.  (gp2 on AWS, standard on
      GKE, AWS & OpenStack)
   
    storageClass: "ceph-rbd"
    accessMode: "ReadWriteOnce"
    size: 1Gi

下面一些orchestrator的配置没有做改动,比如:

    Automated recovery (this is opt-in, so we need to set these)
    Prevent recovery flip-flop, by disabling auto-recovery for 5 minutes per
    cluster
    RecoveryPeriodBlockSeconds: 300
    Do not ignore any host for auto-recovery
    RecoveryIgnoreHostnameFilters: []
    Recover both, masters and intermediate masters
    RecoverMasterClusterFilters: ['.*']
    RecoverIntermediateMasterClusterFilters: ['.*']
    reset slave all and set read_only=0 on promoted master
    ApplyMySQLPromotionAfterMasterFailover: false
    MasterFailoverDetachReplicaMasterHost: true
    https://github.com/github/orchestrator/blob/master/docs/configuration-recovery.md#promotion-actions
    Safety! do not disable unless you know what you are doing
    FailMasterPromotionIfSQLThreadNotUpToDate: true
    DetachLostReplicasAfterMasterFailover: true

    orchestrator hooks called in the following order
    for more information about template: https://github.com/github/orchestrator/blob/master/go/logic/topology_recovery.go#L256
    ProcessesShellCommand: "sh"

通过helm部署的资源如下:

-rwxr-xr-x 1 root root 5672 1月  22 21:56 statefulset.yaml
-rwxr-xr-x 1 root root  327 1月  22 21:56 serviceaccount.yaml
-rwxr-xr-x 1 root root  536 1月  22 21:56 role.yaml
-rwxr-xr-x 1 root root  680 1月  22 21:56 rolebinding.yaml
-rwxr-xr-x 1 root root 1353 1月  22 21:56 podsecuritypolicy.yaml
-rwxr-xr-x 1 root root  735 1月  22 21:56 pdb.yaml
-rwxr-xr-x 1 root root 1541 1月  22 21:56 orc-service.yaml
-rwxr-xr-x 1 root root  655 1月  22 21:56 orc-secret.yaml
-rwxr-xr-x 1 root root  887 1月  22 21:56 orc-ingress.yaml
-rwxr-xr-x 1 root root 1407 1月  22 21:56 orc-config.yaml
-rwxr-xr-x 1 root root 2443 1月  22 21:56 _helpers.tpl
-rwxr-xr-x 1 root root 1660 1月  22 21:56 crds.yaml
-rwxr-xr-x 1 root root 1407 1月  22 21:56 clusterrole.yaml
-rwxr-xr-x 1 root root  618 1月  22 21:56 clusterrolebinding.yaml

另外还通过ingress暴露一个域名来访问orchestractor的UI:

piVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
  name: presslab-mysql-operator-ingress
  namespace: operator
spec:
  rules:
  - host: mysql-operator.lab.local
    http:
      paths:
      - backend:
          serviceName: presslabs-mysql-operator
          servicePort: 80
        path: /

通过下面的命令进行部署:

helm install presslabs-mysql-operator presslabs/mysql-operator -f values.yaml --namespace operator

这里要说明的是operator不支持动态扩容,否则新生成的POD里面的key会和原集群的key match不上:

2020-04-22 06:23:53 ERROR x509: certificate is valid for MySQL_Server_5.7.26-29_Auto_Generated_Server_Certificate, not my-cluster-1-mysql-0.mysql.operator-cluster

我们这里部署的operator集群有三个节点:

 

operator部署之后还会注册如下2个CRD:

kubectl get crd | grep presslabs
mysqlbackups.mysql.presslabs.org                  2020-03-27T05:40:31Z
mysqlclusters.mysql.presslabs.org                 2020-03-27T05:40:37Z

并创建如下的服务,其中由于orchestrator是通过raft协议进行集群管理的,所以POD的10008端口会打开:

[root@k8s-master-01 mysql-operator-presslabs]# kubectl get svc -n operator

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

presslabs-mysql-operator ClusterIP 10.96.251.251 80/TCP 30m

presslabs-mysql-operator-0-svc ClusterIP 10.96.205.25 80/TCP,10008/TCP 30m

presslabs-mysql-operator-1-svc ClusterIP 10.96.30.76 80/TCP,10008/TCP 30m

presslabs-mysql-operator-2-svc ClusterIP 10.96.208.222 80/TCP,10008/TCP 30m

里面的operator容器和orchestrator容器都会有选主的过程,Operator POD在起来之后,其中给一个会成为leader(这个例子中presslabs-mysql-operator-2是leader):

2020/04/22 08:02:19 [DEBUG] raft: Votes needed: 2

2020/04/22 08:02:19 [DEBUG] raft: Vote granted from 10.96.208.222:10008. Tally: 1

2020/04/22 08:02:19 [DEBUG] raft: Vote granted from 10.96.30.76:10008. Tally: 2

2020/04/22 08:02:19 [INFO] raft: Election won. Tally: 2

2020/04/22 08:02:19 [INFO] raft: Node at 10.96.208.222:10008 [Leader] entering Leader state

2020/04/22 08:02:19 [INFO] raft: pipelining replication to peer 10.96.30.76:10008

后续部署cluster的处理会由presslabs-mysql-operator-2来进行:

[root@k8s-master-01 ~]# kubectl logs -f presslabs-mysql-operator-2 -n operator -c operator
{"level":"info","ts":1587542531.5914958,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"mysqlbackup-controller","source":"kind source: /, Kind="}
{"level":"info","ts":1587542531.5917566,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"mysqlbackup-controller","source":"kind source: /, Kind="}
{"level":"info","ts":1587542531.5919662,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"mysqlbackupcron-controller","source":"kind source: /, Kind="}
{"level":"info","ts":1587542531.592171,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"controller.mysqlcluster","source":"kind source: /, Kind="}
{"level":"info","ts":1587542531.5922117,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"controller.mysqlcluster","source":"kind source: /, Kind="}
{"level":"info","ts":1587542531.592351,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"controller.mysqlcluster","source":"kind source: /, Kind="}
{"level":"info","ts":1587542531.5924706,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"controller.mysqlcluster","source":"kind source: /, Kind="}
{"level":"info","ts":1587542531.592589,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"controller.mysqlcluster","source":"kind source: /, Kind="}
{"level":"info","ts":1587542531.5927098,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"controller.mysqlcluster","source":"kind source: /, Kind="}
{"level":"info","ts":1587542531.6496875,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"controller.mysqlNode","source":"kind source: /, Kind="}
{"level":"info","ts":1587542531.649941,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"controller.orchestrator","source":"kind source: /, Kind="}
{"level":"info","ts":1587542531.64998,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"controller.orchestrator","source":"channel source: 0xc0006a4870"}
I0422 08:02:11.650174 1 leaderelection.go:205] attempting to acquire leader lease operator/mysql-operator-leader-election...
{"level":"info","ts":1587544037.4970384,"logger":"controller.mysqlcluster","msg":"syncing cluster","cluster":"operator-cluster/my-cluster-1"}
{"level":"info","ts":1587544037.497133,"logger":"upgrades.cluster","msg":"annotation not set on cluster"}
{"level":"info","ts":1587544037.5643048,"logger":"controller.mysqlcluster","msg":"cluster status","status":{}}
{"level":"info","ts":1587544037.6648607,"logger":"controller.mysqlcluster","msg":"syncing cluster","cluster":"operator-cluster/my-cluster-1"}
{"level":"info","ts":1587544037.6691175,"logger":"controller.mysqlcluster","msg":"cluster status","status":{}}
{"level":"info","ts":1587544038.4630077,"logger":"controller.orchestrator","msg":"reconciling cluster","cluster":"operator-cluster/my-cluster-1"}
{"level":"info","ts":1587544038.4685085,"logger":"controller.mysqlcluster","msg":"syncing cluster","cluster":"operator-cluster/my-cluster-1"}
{"level":"info","ts":1587544038.4726372,"logger":"controller.mysqlcluster","msg":"cluster status","status":{}}

在operator部署完毕之后,可以通过ingress定义的域名来访问orchestrator的Web UI:

 

三、部署Presslabs MySQL Cluster

可以通过如下方式部署一个5节点的percona mysql集群:

apiVersion: mysql.presslabs.org/v1alpha1
kind: MysqlCluster
metadata:
  name: my-cluster-1
spec:
  replicas: 5
  secretName: my-secret
  volumeSpec:
    persistentVolumeClaim:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: ceph-rbd
      resources:
        requests:
          storage: 1Gi

此时operator会监控到mysqlclusters这个CRD的创建请求,进行下述集群的部署:

[root@k8s-master-01 examples]# kubectl get pod -o wide -n operator-cluster 
NAME                   READY     STATUS    RESTARTS   AGE       IP               NODE          NOMINATED NODE
my-cluster-1-mysql-0   4/4       Running   0          29m       10.244.127.222   k8s-node-04   <none>
my-cluster-1-mysql-1   4/4       Running   0          13m       10.244.127.234   k8s-node-04   <none>
my-cluster-1-mysql-2   4/4       Running   0          26m       10.244.154.208   k8s-node-01   <none>
my-cluster-1-mysql-3   4/4       Running   0          25m       10.244.89.180    k8s-node-03   <none>
my-cluster-1-mysql-4   4/4       Running   0          24m       10.244.45.38     k8s-node-02   <none>

并且会部署两个kubernetes服务:

my-cluster-1-mysql ClusterIP 10.96.216.54 3306/TCP 25m

my-cluster-1-mysql-master ClusterIP 10.96.225.228 3306/TCP 25m

在节点部署之后,会借助orchestractor进行leader选举,其中my-cluster-1-mysql-master对于当前选举出来的的master节点,可以进行读写操作:

kubectl describe svc my-cluster-1-mysql-master -n operator-cluster

Name: my-cluster-1-mysql-master

......

Selector: app.kubernetes.io/managed-by=mysql.presslabs.org,app.kubernetes.io/name=mysql,mysql.presslabs.org/cluster=my-cluster-1,role=master

Type: ClusterIP

IP: 10.96.225.228

Port: mysql 3306/TCP

TargetPort: 3306/TCP

Endpoints: 10.244.127.222:3306

Session Affinity: None

Events:

而my-cluster-1-mysql后面是所有的mysql节点,可以用于读操作:

kubectl describe svc my-cluster-1-mysql -n operator-cluster 
Name:              my-cluster-1-mysql
Namespace:         operator-cluster
Labels:            app.kubernetes.io/component=database
                   app.kubernetes.io/instance=my-cluster-1
                   app.kubernetes.io/managed-by=mysql.presslabs.org
                   app.kubernetes.io/name=mysql
                   app.kubernetes.io/version=5.7.26
                   mysql.presslabs.org/cluster=my-cluster-1
                   mysql.presslabs.org/service-type=ready-nodes
Annotations:       <none>
Selector:          app.kubernetes.io/managed-by=mysql.presslabs.org,app.kubernetes.io/name=mysql,healthy=yes,mysql.presslabs.org/cluster=my-cluster-1
Type:              ClusterIP
IP:                10.96.216.54
Port:              mysql  3306/TCP
TargetPort:        3306/TCP
Endpoints:         10.244.127.234:3306,10.244.154.208:3306,10.244.44.254:3306 + 2 more...
Session Affinity:  None
Events:            <none>

这两个服务的管理通过operator更新mysql节点的tag来进行。

四、运维Presslabs MySQL Cluster

4.1 operator/orchestrator HA

如果operator/orchestrator发生故障,如果又恰好是leader,则会重新进行leader选举,比如我将现在的leader presslabs-mysql-operator-2 POD重启,则presslabs-mysql-operator-0成为了新的leader:

2020/04/22 09:10:56 [DEBUG] raft-net: 10.96.205.25:10008 accepted connection from: 172.2.2.11:40546

2020/04/22 09:10:56 [INFO] raft: Duplicate RequestVote for same term: 81

2020/04/22 09:10:57 [WARN] raft: Election timeout reached, restarting election

2020/04/22 09:10:57 [INFO] raft: Node at 10.96.205.25:10008 [Candidate] entering Candidate state

2020/04/22 09:10:57 [DEBUG] raft: Votes needed: 2

2020/04/22 09:10:57 [DEBUG] raft: Vote granted from 10.96.205.25:10008. Tally: 1

2020/04/22 09:10:57 [DEBUG] raft: Vote granted from 10.96.30.76:10008. Tally: 2

2020/04/22 09:10:57 [INFO] raft: Election won. Tally: 2

2020/04/22 09:10:57 [INFO] raft: Node at 10.96.205.25:10008 [Leader] entering Leader state

2020/04/22 09:10:57 [INFO] raft: pipelining replication to peer 10.96.30.76:10008

2020/04/22 09:10:57 [DEBUG] raft: Node 10.96.205.25:10008 updated peer set (2): [10.96.205.25:10008 10.96.208.222:10008 10.96.30.76:10008]

整个过程mysql业务集群没有任何感知和影响。

4.2 mysql master HA

如果mysql集群的master被重启,则也会进行leader选举,新的leader会被打上master的tag,则my-cluster-1-mysql-master的后端端点也会变化:

[root@k8s-master-01 examples]# kubectl describe svc my-cluster-1-mysql-master -n operator-cluster

......

Selector: app.kubernetes.io/managed-by=mysql.presslabs.org,app.kubernetes.io/name=mysql,mysql.presslabs.org/cluster=my-cluster-1,role=master

Type: ClusterIP

IP: 10.96.225.228

Port: mysql 3306/TCP

TargetPort: 3306/TCP

Endpoints: 10.244.127.234:3306

Session Affinity: None

Events:

4.3 orchestrator UI操作

1) 改变mysql replica关系,通过拖拽就能完成:

 

 

 2) 改变master

 

 

3) 操作mysql节点

 

4) 建立co-master