zoukankan      html  css  js  c++  java
  • 在 k8s 上部署 mysql operator

    原文地址: http://maoqide.live/post/cloud/deploy-mysql-on-kubernetes/

    本文通过 mysql-operator 在kubernetes集群部署高可用的mysql statefulset。

    环境准备

    本文使用的开源 operator 项目 mysql-operator 配死只支持 mysql 8.0.11 以上的版本,改了下代码,支持 5.7.0 以上版本,项目地址,本文部署的是 mysql-5.7.26,使用的 dockerhub 上的镜像 mysql/mysql-server:5.7.26。

    代码编译

    git clone 下载该项目,进入到代码目录,执行sh hack/build.sh,编译代码得到二进制文件 mysql-agent 和 mysql-operator,将二进制文件放入 bin/linux_amd64,执行docker build -f docker/mysql-agent/Dockerfile -t $IMAGE_NAME_AGENT .docker build -f docker/mysql-operator/Dockerfile -t $IMAGE_NAME_OPERATOR .构建镜像,mysql-operator 生成的镜像为 operator 的镜像,mysql-agent 生成的是镜像,在创建mysql服务时,作为sidecar和mysql-server容器起在同一个pod中。

    部署 operator

    先根据 文档 部署 mysql-operator 的 Deployment,文档中是使用 helm 安装,不希望安装 helm 和 tiller 的话,可以只安装一个 helm 客户端,进入到代码目录,再执行helm template --name mysql-operator mysql-operator生成部署所需要的yaml文件,然后直接执行kubectl apply -f mysql-operator.yaml创建 operator。这个yaml创建了operator所需的CRD类型,operator 的 Deployment 和 operator 所需的 RBAC 权限等。

    # change directory into mysql-operator
    cd mysql-operator
    # generate mysql-operator.yaml
    helm template --name mysql-operator mysql-operator > mysql-operator.yaml
    # deploy on kubernetes
    kubectl apply -f mysql-operator.yaml
    # deployed.
    [root@localhost]$ kubectl get deploy -n mysql-operator
    NAME             READY   UP-TO-DATE   AVAILABLE   AGE
    mysql-operator   1/1     1            1           2d5h
    

    创建 mysql 集群

    本文创建的集群为3节点的 mysql,一个节点为 master,二个为 slave,master节点可读写,slave节点为只读,使用 kubernetes Local PV 作持久化存储。
    首先,为每个节点创建一个PV,Local PV 需要定义nodeAffinity,约束创建的节点。

    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: mypv0
    spec:
      capacity:
        storage: 1Gi
      volumeMode: Filesystem
      accessModes:
      - ReadWriteOnce
      persistentVolumeReclaimPolicy: Delete
      storageClassName: mysql-storage
      local:
        path: /data/mysql-data
      nodeAffinity:
        required:
          nodeSelectorTerms:
          - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
              - 192.168.0.1
    
    ---
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: mypv1
    spec:
      capacity:
        storage: 1Gi
      volumeMode: Filesystem
      accessModes:
      - ReadWriteOnce
      persistentVolumeReclaimPolicy: Delete
      storageClassName: mysql-storage
      local:
        path: /data/mysql-data
      nodeAffinity:
        required:
          nodeSelectorTerms:
          - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
              - 192.168.0.2
    
    ---
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: mypv2
    spec:
      capacity:
        storage: 1Gi
      volumeMode: Filesystem
      accessModes:
      - ReadWriteOnce
      persistentVolumeReclaimPolicy: Delete
      storageClassName: mysql-storage
      local:
        path: /data/mysql-data
      nodeAffinity:
        required:
          nodeSelectorTerms:
          - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
              - 192.168.0.3
    
    # create pv
    kubectl create -f pv.yaml
    # get presistence volume
    [root@localhost]$ kubectl get pv
    mypv-0               1Gi        RWO            Delete           Available                                                                     mysql-storage               4s
    mypv-1               1Gi        RWO            Delete           Available                                                                     mysql-storage               4s
    mypv-2               1Gi        RWO            Delete           Available                                                                     mysql-storage               4s
    

    接着,需要在创建 mysql 的 namespace 下,为要创建的 mysql 创建对应的 RBAC 权限。

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: mysql-agent
      namespace: mysql2
    ---
    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1beta1
    metadata:
      name: mysql-agent
      namespace: mysql2
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: mysql-agent
    subjects:
    - kind: ServiceAccount
      name: mysql-agent
      namespace: mysql2
    

    如果需要自定义 mysql 的密码,需要为其创建一个 secret,密码需要使用base64加密。linux 下执行 echo -n 'password' | base64 为密码加密。

    apiVersion: v1
    data:
      password: cm9vdA==
    kind: Secret
    metadata:
      labels:
        v1alpha1.mysql.oracle.com/cluster: mysql
      name: mysql-pv-root-password
      namespace: mysql2
    
    kubectl apply -f rbac.yaml
    kubectl apply -f secret.yaml
    

    在创建 operator 的时候,已经创建了如下的crd类型,部署mysql集群所需创建的就是 mysqlclusters 类型的资源。

    [root@localhost]$ kubectl get crd  | grep mysql
    mysqlbackups.mysql.oracle.com                2019-05-14T02:51:11Z
    mysqlbackupschedules.mysql.oracle.com        2019-05-14T02:51:11Z
    mysqlclusters.mysql.oracle.com               2019-05-14T02:51:11Z
    mysqlrestores.mysql.oracle.com               2019-05-14T02:51:11Z
    

    接下来开始创建 operator 自定义资源类型(CRD)的实例 mysqlclusters。

    apiVersion: mysql.oracle.com/v1alpha1
    kind: Cluster
    metadata:
      name: mysql
      namespace: mysql2
    spec:  
      # 和mysql-server镜像版本的tag一直
      version: 5.7.26
      repository: 20.26.28.56/dcos/mysql-server
      # 节点数量
      members: 3
      # 指定 mysql 密码,和之前创建的secret名称一致
      rootPasswordSecret:
        name: mysql-pv-root-password
      resources:
        agent:
          limits:
            cpu: 500m
            memory: 200Mi
          requests:
            cpu: 300m
            memory: 100Mi
        server:
          limits:
            cpu: 1000m
            memory: 1000Mi
          requests:
            cpu: 500m
            memory: 500Mi
      volumeClaimTemplate:
        metadata:
          name: mysql-pv
        spec:
          accessModes: [ "ReadWriteOnce" ]
          storageClassName: "mysql-storage"
          resources:
            requests:
              storage: 1Gi
    
    kubectl apply -f mysql.yaml
    

    执行后,会看到 kubernetes 在该 namespace 下开始拉起 mysql 的 statefulset,并会创建一个 headless service。

    [root@localhost]$ kubectl get all -n mysql2
    NAME                                                      READY   STATUS    RESTARTS   AGE
    pod/mysql-0                                               2/2     Running   0          8h
    pod/mysql-1                                               2/2     Running   0          8h
    pod/mysql-2                                               2/2     Running   0          8h
    
    
    NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
    service/mysql     ClusterIP   None            <none>        3306/TCP   21h
    
    
    NAME                     READY   AGE
    statefulset.apps/mysql   1/1     21h
    

    此时执行hack/cluster-status.sh脚本,会得到如下集群信息:

    {
        "clusterName": "Cluster", 
        "defaultReplicaSet": {
            "name": "default", 
            "primary": "mysql-0.mysql:3306", 
            "ssl": "DISABLED", 
            "status": "OK_NO_TOLERANCE", 
            "statusText": "Cluster is NOT tolerant to any failures. 2 members are not active", 
            "topology": {
                "mysql-0.mysql:3306": {
                    "address": "mysql-0.mysql:3306", 
                    "mode": "R/W", 
                    "readReplicas": {}, 
                    "role": "HA", 
                    "status": "ONLINE"
                }, 
                "mysql-1.mysql:3306": {
                    "address": "mysql-1.mysql:3306", 
                    "mode": "n/a", 
                    "readReplicas": {}, 
                    "role": "HA", 
                    "status": "ONLINE"
                }, 
                "mysql-2.mysql:3306": {
                    "address": "mysql-2.mysql:3306", 
                    "mode": "n/a", 
                    "readReplicas": {}, 
                    "role": "HA", 
                    "status": "ONLINE"
                }
            }, 
            "topologyMode": "Single-Primary"
        }, 
        "groupInformationSourceMember": "mysql-0.mysql:3306"
    }
    

    通过DNS地址 mysql-0.mysql.mysql2.svc.cluster.local:3306 可以连接到数据库进行读写操作。此时一个多节点的mysql集群已经部署完成,但是,集群外部的服务还无法访问数据库。

    通过 haproxy-ingress 允许外部访问

    首先,headless service只能通过集群内 DNS 访问服务,要外部访问,还需要另外创建一个 Service。为了让外部可以访问到 mysql-0 的服务,我们为 mysql-0 创建一个ClusterIP 类型的服务。

    kind: Service
    apiVersion: v1
    metadata:
      name: mysql-0
      namespace: mysql2
    spec:
      selector:
        # 通过 selector 将 pod 约束到 mysql-0
       statefulset.kubernetes.io/pod-name: mysql-0
      ports:
        - protocol: TCP
          port: 3306
          targetPort: 3306
    

    接着,需要创建一个ingress-controller,本文选用的是 haproxy-ingress
    由于 mysql 服务通过 TCP 协议通信,kubernetes ingress 默认只支持 http 和 https,haproxy-ingress 提供了通过 configmap 的方法,配置 TCP 服务的端口,需要先创建一个 configmap,configmap的data中,key为HAProxy监听的端口,value 为需要转发的 service 的服务和端口。

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: mysql-tcp
      namespace: mysql2
    data:
      "3306": "mysql2/mysql-0:3306"
    
    kubectl apply -f mysql-0.yaml
    kubectl apply -f tcp-svc.yaml
    

    接下来创建 ingress-controller,

    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
      labels:
        run: haproxy-ingress
      name: haproxy-ingress-192.168.0.1-30080
      namespace: mysql2
    spec:
      replicas: 1
      selector:
        matchLabels:
          run: haproxy-ingress
      strategy:
        type: RollingUpdate
      template:
        metadata:
          labels:
            run: haproxy-ingress
        spec:
          tolerations:
          - key: app 
            operator: Equal 
            value: haproxy
            effect: NoSchedule
          serviceAccount: ingress-controller      
          nodeSelector:
            kubernetes.io/hostname: 192.168.0.1
          containers:
          - args:
            - --tcp-services-configmap=$(POD_NAMESPACE)/mysql-tcp
            - --default-backend-service=$(POD_NAMESPACE)/mysql
            - --default-ssl-certificate=$(POD_NAMESPACE)/tls-secret
            - --ingress-class=ha-mysql
            env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.namespace
            image: jcmoraisjr/haproxy-ingress
            name: haproxy-ingress
            ports:
              # 和 configmap 中定义的端口对应
            - containerPort: 3306
              hostPort: 3306
              name: http
              protocol: TCP
            - containerPort: 443
              name: https
              protocol: TCP
            - containerPort: 1936
              hostPort: 30081
              name: stat
              protocol: TCP
    
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: ingress-controller
    
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: ingress-controller
    rules:
      - apiGroups:
          - ""
        resources:
          - configmaps
          - pods
          - secrets
          - namespaces
        verbs:
          - get
      - apiGroups:
          - ""
        resources:
          - configmaps
        verbs:
          - get
          - update
      - apiGroups:
          - ""
        resources:
          - configmaps
        verbs:
          - create
      - apiGroups:
          - ""
        resources:
          - endpoints
        verbs:
          - get
          - create
          - update
    
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: ingress-controller
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: ingress-controller
    subjects:
      - kind: ServiceAccount
        name: ingress-controller
      - apiGroup: rbac.authorization.k8s.io
        kind: User
        name: ingress-controller
    
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: ingress-controller
    rules:
      - apiGroups:
          - ""
        resources:
          - configmaps
          - endpoints
          - nodes
          - pods
          - secrets
        verbs:
          - list
          - watch
      - apiGroups:
          - ""
        resources:
          - nodes
        verbs:
          - get
      - apiGroups:
          - ""
        resources:
          - services
        verbs:
          - get
          - list
          - watch
      - apiGroups:
          - "extensions"
        resources:
          - ingresses
        verbs:
          - get
          - list
          - watch
      - apiGroups:
          - ""
        resources:
          - events
        verbs:
          - create
          - patch
      - apiGroups:
          - "extensions"
        resources:
          - ingresses/status
        verbs:
          - update
    
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: ingress-controller
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: ingress-controller
    subjects:
      - kind: ServiceAccount
        name: ingress-controller
        namespace: mysql2
      - apiGroup: rbac.authorization.k8s.io
        kind: User
        name: ingress-controller
    
    kubectl apply -f ingress-controller.yaml
    kubectl apply -f ingress-rbac.yaml -n mysql2
    

    最后创建 ingress 规则:

    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      annotations:
        ingress.kubernetes.io/ssl-redirect: "false"
        kubernetes.io/ingress.class: ha-mysql
      name: ha-mysql
    spec:
      rules:
      - http:
          paths:
          - backend:
              serviceName: mysql-0
              servicePort: 3306
            path: /
    

    此时可以通过 haproxy 的 IP + 映射端口访问到 mysql 集群。

    附件

    以下是上面用到的 yaml 文件:

  • 相关阅读:
    集合
    字典
    元组
    列表
    数字类型和字符串类型
    Python 数据类型
    jq的一点点
    常用到jq的ajax
    上传
    下载
  • 原文地址:https://www.cnblogs.com/maoqide/p/11258515.html
Copyright © 2011-2022 走看看