在 Kubernetes 中基於 StatefulSet 部署 MySQL(下)

大家好,我是老 Z!

上篇文章實現了 MySQL 數據庫在基於 KubeSphere 部署的 K8s 集羣上的安裝部署,部署方式採用了圖形化界面這種形式。本文將會介紹如何使用 GitOps 來部署 MySQL,部署過程涉及的所有 YAML 文件都會使用 Git 進行版本管理,並存放在 Git 倉庫中。因此,本文還會涉及 GitOps 的基礎操作。

原生 K8s 使用 GitOps 部署 MySQL

上篇文章我們完成了通過 KubeSphere 部署單實例 MySQL,那麼原生的 K8s 又該如何操作?GitOps 又是什麼、又該如何實現?

什麼是 GitOps(網文摘抄)

  • GitOps 是一套使用 Git 來管理基礎架構和應用配置的實踐,而 Git 指的是一個開源版控制系統。
  • GitOps 在運行過程中以 Git 爲聲明性基礎架構和應用的單一事實來源。
  • GitOps 使用 Git 拉取請求來自動管理基礎架構的置備和部署。
  • Git 存儲庫包含系統的全部狀態,因此係統狀態的修改痕跡既可查看也可審計。
  • GitOps 經常被用作 K8s 和雲原生應用開發的運維模式,並且可以實現對 K8s 的持續部署。
  • GitOps 是一種持續交付的方式。它的核心思想是將應用系統的聲明性基礎架構和應用程序存放在 Git 版本庫中。

準備資源配置清單-思路梳理

我們知道玩 K8s 的必備技能就是要手寫資源配置清單,一般使用 YAML 格式的文件來創建我們預期的資源配置。

此時我們也要手寫 MySQL 的資源配置清單?我很慌,參數我記不全啊。

NO!NO!NO!投機取巧的時刻到了,前面賣的關子在這揭開了。

前面我們已經通過 KubeSphere 的圖形界面創建了 MySQL 的資源配置,而且 KubeSphere 一個很棒的功能就是可以直接在線編輯資源的 YAML 文件。

我們可以在創建資源的時候,直接編輯 YAML 文件創建資源。也可以通過編輯 YAML 的方式修改已有的資源。

當然啊,你不用圖形界面,直接在 K8s 底層用命令行的方式去獲取 YAML 格式的輸出,再編輯,也是可以的。

梳理一下 MySQL 涉及的資源配置清單包含的資源。

  • StatefulSet(有狀態副本集)
  • Service(服務)
    • 集羣內部(Headless)
    • 集羣外部(自定義服務)
  • ConfigMap
  • Secret

接下來我們就分別獲取這些資源配置清單。

準備資源配置清單

ConfigMap

配置->配置字典,找到 mysql-cnf,點擊右側的三個豎點,點擊編輯 YAML

打開編輯 YAML 頁面,可以直接複製所有內容,也可以點擊右上角的下載圖標,下載文件 (也可以利用上傳圖標上傳文件)。

獲取的現網配置不能完全的拿來就用,需要修改,把系統自動添加的一些元數據信息清理掉。

現網的 mysql-cfm.yaml

kind: ConfigMap
apiVersion: v1
metadata:
  name: mysql-cnf
  namespace: lstack
  annotations:
    kubesphere.io/creator: lstack
data:
  custom.cnf: |-
    [mysqld]
    #performance setttings
    lock_wait_timeout = 3600
    open_files_limit    = 65535
    back_log = 1024
    max_connections = 512
    max_connect_errors = 1000000
    table_open_cache = 1024
    table_definition_cache = 1024
    thread_stack = 512K
    sort_buffer_size = 4M
    join_buffer_size = 4M
    read_buffer_size = 8M
    read_rnd_buffer_size = 4M
    bulk_insert_buffer_size = 64M
    thread_cache_size = 768
    interactive_timeout = 600
    wait_timeout = 600
    tmp_table_size = 32M
    max_heap_table_size = 32M
    

修改後的 mysql-cfm.yaml

kind: ConfigMap
apiVersion: v1
metadata:
  name: mysql-cnf
  namespace: lstack
data:
  custom.cnf: |-
    [mysqld]
    #performance setttings
    lock_wait_timeout = 3600
    open_files_limit    = 65535
    back_log = 1024
    max_connections = 512
    max_connect_errors = 1000000
    table_open_cache = 1024
    table_definition_cache = 1024
    thread_stack = 512K
    sort_buffer_size = 4M
    join_buffer_size = 4M
    read_buffer_size = 8M
    read_rnd_buffer_size = 4M
    bulk_insert_buffer_size = 64M
    thread_cache_size = 768
    interactive_timeout = 600
    wait_timeout = 600
    tmp_table_size = 32M
    max_heap_table_size = 32M

Secret

配置->保密字典,找到 mysql-secret,點擊右側的三個豎點,點擊編輯 YAML

現網的 mysql-secret.yaml

kind: Secret
apiVersion: v1
metadata:
  name: mysql-secret
  namespace: lstack
  annotations:
    kubesphere.io/creator: lstack
data:
  MYSQL_ROOT_PASSWORD: UEA4OHcwcmQ=
type: Opaque

修改後的 mysql-secret.yaml

kind: Secret
apiVersion: v1
metadata:
  name: mysql-secret
  namespace: lstack
data:
  MYSQL_ROOT_PASSWORD: UEA4OHcwcmQ=
type: Opaque


這裏要說一句,Secret 裏的值是用 base64 方式加密的,所以這裏的 MYSQL_ROOT_PASSWORD,要用實際的密碼用 base64 的方式加密。

  • base64 解密。

    [root@ks-k8s-master-0 ~]# echo "UEA4OHcwcmQ=" | base64 -d
    P@88w0rd
    
  • base 加密。

    [root@ks-k8s-master-0 ~]# echo -n "P@88w0rd" | base64
    UEA4OHcwcmQ=
    

StatefulSet

應用負載->工作負載->有狀態副本集,找到 mysql,點擊右側的三個豎點,點擊編輯 YAML

現網的 mysql-sts.yaml

kind: StatefulSet
apiVersion: apps/v1
metadata:
  name: mysql
  namespace: lstack
  labels:
    app: mysql
  annotations:
    kubesphere.io/creator: lstack
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: mysql
      annotations:
        logging.kubesphere.io/logsidecar-config: '{}'
    spec:
      volumes:
        - name: host-time
          hostPath:
            path: /etc/localtime
            type: ''
        - name: volume-rca2zx
          configMap:
            name: mysql-cnf
            items:
              - key: custom.cnf
                path: custom.cnf
            defaultMode: 420
      containers:
        - name: lstack-mysql
          image: 'mysql:5.7.38'
          ports:
            - name: tcp-mysql
              containerPort: 3306
              protocol: TCP
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: MYSQL_ROOT_PASSWORD
          resources:
            limits:
              cpu: '2'
              memory: 4000Mi
            requests:
              cpu: 500m
              memory: 500Mi
          volumeMounts:
            - name: host-time
              mountPath: /etc/localtime
            - name: data
              mountPath: /var/lib/mysql
            - name: volume-rca2zx
              readOnly: true
              mountPath: /etc/mysql/conf.d/custom.cnf
              subPath: custom.cnf
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
          imagePullPolicy: IfNotPresent
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
      dnsPolicy: ClusterFirst
      serviceAccountName: default
      serviceAccount: default
      securityContext: {}
      schedulerName: default-scheduler
  volumeClaimTemplates:
    - kind: PersistentVolumeClaim
      apiVersion: v1
      metadata:
        name: data
        namespace: lstack
        creationTimestamp: null
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 5Gi
        storageClassName: glusterfs
        volumeMode: Filesystem
      status:
        phase: Pending
  serviceName: mysql-1dpr
  podManagementPolicy: OrderedReady
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 0
  revisionHistoryLimit: 10

修改後的 mysql-sts.yaml

kind: StatefulSet
apiVersion: apps/v1
metadata:
  name: mysql
  namespace: lstack
  labels:
    app: mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      volumes:
        - name: host-time
          hostPath:
            path: /etc/localtime
            type: ''
        - name: volume-cnf
          configMap:
            name: mysql-cnf
            items:
              - key: custom.cnf
                path: custom.cnf
            defaultMode: 420
      containers:
        - name: lstack-mysql
          image: 'mysql:5.7.38'
          ports:
            - name: tcp-mysql
              containerPort: 3306
              protocol: TCP
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: MYSQL_ROOT_PASSWORD
          resources:
            limits:
              cpu: '2'
              memory: 4000Mi
            requests:
              cpu: 500m
              memory: 500Mi
          volumeMounts:
            - name: host-time
              mountPath: /etc/localtime
            - name: data
              mountPath: /var/lib/mysql
            - name: volume-cnf
              mountPath: /etc/mysql/conf.d/custom.cnf
              subPath: custom.cnf
  volumeClaimTemplates:
    - metadata:
        name: data
        namespace: lstack
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 5Gi
        storageClassName: glusterfs
  serviceName: mysql-headless

Service

先創建 Headless 服務,應用負載->服務->,找到 mysql-xxxx(mysql),點擊右側的三個豎點,點擊編輯 YAML

現網的 mysql-headless.yaml

kind: Service
apiVersion: v1
metadata:
  name: mysql-1dpr
  namespace: lstack
  labels:
    app: mysql
  annotations:
    kubesphere.io/alias-name: mysql
    kubesphere.io/creator: lstack
    kubesphere.io/serviceType: statefulservice
spec:
  ports:
    - name: tcp-mysql
      protocol: TCP
      port: 3306
      targetPort: 3306
  selector:
    app: mysql
  clusterIP: None
  clusterIPs:
    - None
  type: ClusterIP
  sessionAffinity: None
  ipFamilies:
    - IPv4
  ipFamilyPolicy: SingleStack

修改後的 mysql-headless.yaml

kind: Service
apiVersion: v1
metadata:
  name: mysql-headless
  namespace: lstack
  labels:
    app: mysql
spec:
  ports:
    - name: tcp-mysql
      protocol: TCP
      port: 3306
      targetPort: 3306
  selector:
    app: mysql
  clusterIP: None
  type: ClusterIP

再看看自定義的 mysql-external 服務 ,應用負載->服務->,找到 mysql-external,點擊右側的三個豎點,點擊編輯 YAML

現網的 mysql-external.yaml

kind: Service
apiVersion: v1
metadata:
  name: mysql-external
  namespace: lstack
  labels:
    app: mysql-external
  annotations:
    kubesphere.io/creator: lstack
spec:
  ports:
    - name: tcp-mysql-external
      protocol: TCP
      port: 3306
      targetPort: 3306
      nodePort: 32529
  selector:
    app: mysql
  clusterIP: 10.233.36.71
  clusterIPs:
    - 10.233.36.71
  type: NodePort
  sessionAffinity: None
  externalTrafficPolicy: Cluster
  ipFamilies:
    - IPv4
  ipFamilyPolicy: SingleStack

這裏有一點要說明 nodePort 這個參數,如果 K8s 集羣可控,建議規劃一套服務端口使用規範,每個需要 nodePort 的服務都指定固定的端口,這樣有利於運維的標準化。

修改後的 mysql-external.yaml(注意 nodePort 參數沒有指定)。

kind: Service
apiVersion: v1
metadata:
  name: mysql-external
  namespace: lstack
  labels:
    app: mysql-external
spec:
  ports:
    - name: tcp-mysql-external
      protocol: TCP
      port: 3306
      targetPort: 3306
  selector:
    app: mysql
  type: NodePort

將 MySQL 資源配置清單提交到 Git 倉庫。

通過上面的操作,我們獲取了 MySQL 的資源配置清單。

本人強迫症,喜歡分類存放,所以我用了 4 個文件,mysql-headless.yamlmysql-sts.yaml 合併在一個文件當然你也可以放到一個配置文件裏。

  • mysql-external.yaml
  • mysql-sts.yaml
  • mysql-secret.yaml
  • mysql-cfm.yaml

將資源配置清單提交到 Git 倉庫

選擇 GitHub 作爲主倉庫,Gitee 作爲同步倉庫 (人工)。

本系列文檔所有 k8s 的資源配置清單文件使用了一個公共倉庫,生產環境建議每種服務創建一個配置倉庫,有利於更精細化的版本控制。

本文爲了演示主備倉庫的使用,所有選擇了 Github 和 Gitee 兩種 Git 服務,實際使用中爲了更好的使用體驗建議選擇 Gitee。

在 GitHub 新建一個倉庫,倉庫名稱k8s-yaml,添加一個 README 文件初始化倉庫,點擊Create repository,確認創建。

將代碼倉庫 Clone 回本地。

$ git clone [email protected]:devops/k8s-yaml.git
$ ls k8s-yaml 
README.md

新創建一個文件夾,用自己喜歡的文本編輯器 (推薦 vscode) 編輯 MySQL 的資源配置清單,並將文件放入新創建的文件夾。

爲了以後的擴展性,這裏創建了一個 single 命名的二級目錄,存放單實例的資源配置清單文件。

$ mkdir -p k8s-yaml/mysql/single
$ ls -l k8s-yaml/mysql/single
total 32
-rw-r--r--  1 z  staff   646  5 11 19:23 mysql-cfm.yaml
-rw-r--r--  1 z  staff   266  5 11 19:31 mysql-external.yaml
-rw-r--r--  1 z  staff   134  5 11 19:23 mysql-secret.yaml
-rw-r--r--  1 z  staff  1911  5 11 19:31 mysql-sts.yaml

將編輯好的資源配置文件清單,提交到 GitHub。

$ cd k8s-yaml
$ git add .
$ git commit -am '添加MySQL single資源配置清單'
$ git push

在 GitHub 上查看,確認代碼是否提交。

接下來將資源配置清單同步到 Gitee 備份倉庫。

  • 本文采用了手工推送同步的方式 (個人習慣)
  • Gitee 也支持自動同步 GitHub 的倉庫 (更便捷)

在 Gitee 新建一個倉庫,倉庫名稱k8s-yaml,類型默認私有,點擊創建

創建完成後可去倉庫設置中修改爲開源。

創建完成後,因爲我們創建的時候,沒選擇初始化倉庫的配置,所以,默認會顯示一個幫助頁面,告訴你該如何提交代碼到倉庫。

因爲,我們已經有了代碼倉庫,所以我們選擇已有倉庫的配置方法,將已有代碼提交到 Gitee。

根據幫助提示操作,要注意 origin 我們要換成 gitee

$ git remote add gitee https://gitee.com/zdevops/k8s-yaml.git
$ git push -u gitee

在 Gitee 上查看,確認代碼是否提交。

修改 Gitee 倉庫爲開源 (可選)。

Gitee 倉庫->管理->倉庫設置->基本信息,最後面是否開源,選擇開源倉庫公開須知,三個都勾選,點擊保存

修改後,你的代碼倉庫就是開源,所有人可見的了。

GitOps 初體驗-在 K8s 集羣上部署 MySQL

MySQL 資源配置清單已經存放到了 Git 在線倉庫,接下來開啓我們的 GitOps 體驗之旅。

登錄 k8s 的 master 節點,執行後面的操作任務。

生產環境建議打造獨立的運維管理節點進行整個集羣的管理 , 可以參考《基於 KubeSphere 玩轉 k8s-運維管理節點打造手記》

安裝 Git。

$ yum install git -y

創建 devops 目錄,我選擇 /opt 目錄作爲 devops 的根目錄。

$ mkdir /opt/devops
$ cd /opt/devops/

從 Gitee 下載 k8s-yaml 倉庫的代碼。

$ git clone https://gitee.com/zdevops/k8s-yaml.git
$ ls k8s-yaml/
mysql  README.md

由於是同一個測試環境,先清理掉現有的 MySQL 服務。

$ kubectl get secrets -n lstack 
NAME                  TYPE                                  DATA   AGE
default-token-x2gzv   kubernetes.io/service-account-token   3      31d
mysql-secret          Opaque                                1      2d20h

$ kubectl get configmaps -n lstack 
NAME               DATA   AGE
kube-root-ca.crt   1      31d
mysql-cnf          1      47h

$ kubectl get service -n lstack 
NAME                                                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
glusterfs-dynamic-afe88cf4-86b1-4215-833a-534c5f779a22   ClusterIP   10.233.13.188   <none>        1/TCP            2d
mysql-1dpr                                               ClusterIP   None            <none>        3306/TCP         2d
mysql-external                                           NodePort    10.233.36.71    <none>        3306:32529/TCP   47h

$ kubectl get statefulsets -n lstack 
NAME    READY   AGE
mysql   1/1     2d

# 清理
$ kubectl delete statefulsets mysql -n lstack
statefulset.apps "mysql" deleted
$ kubectl delete service mysql-external -n lstack
service "mysql-external" deleted
$ kubectl delete service mysql-1dpr -n lstack
service "mysql-1dpr" deleted
$ kubectl delete secrets mysql-secret -n lstack
secret "mysql-secret" deleted
$ kubectl delete configmaps mysql-cnf -n lstack
configmap "mysql-cnf" deleted

利用資源配置清單一鍵部署 MySQL。

$ cd /opt/devops/k8s-yaml/
$ ls
mysql  README.md

$ kubectl apply -f mysql/single/

驗證結果,發現 StatefulSet 沒有創建,分析問題。

$ kubectl get statefulsets -n lstack
No resources found in lstack namespace.

# 一開始我以爲我遺漏了配置文件,ls看一眼,發現文件都在
$ ls
mysql  README.md
$ cd mysql/
$ ls
single
$ cd single/
$ ls
mysql-cfm.yaml  mysql-external.yaml  mysql-secret.yaml  mysql-sts.yaml

# 確認一下文件內容,發現文件也有內容
$ vi mysql-sts.yaml

# 再次執行,發現了端倪,爲啥只有service/mysql-headless 的資源配置,沒有statefulset
$ kubectl apply -f mysql-sts.yaml 
service/mysql-headless unchanged

# 再次確認,發現編輯文件的時候遺漏了一點,當一個配置文件有多種資源定義時,不同資源的配置直接需要用"---"分隔。修改配置文件再次執行,發現執行成功。
$ vi mysql-sts.yaml
$ cd ..

$ kubectl apply -f single/

$ kubectl get statefulsets -n lstack -o wide
NAME    READY   AGE   CONTAINERS     IMAGES
mysql   1/1     31s   lstack-mysql   mysql:5.7.38
$ kubectl get pods -n lstack -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP              NODE              NOMINATED NODE   READINESS GATES
mysql-0   1/1     Running   0          35s   10.233.116.59   ks-k8s-master-2   <none>           <none>

回到我們的 KubeSphere 的管理控制檯,發現 mysql 的工作負載也能在界面中顯示,這也驗證了在原生 k8s 上的操作也會直接反應到 KubeSphere 的管理控制檯。

二次體驗 GitOps

正好藉着上面出現的問題,二次體驗一下 GitOps。我們直接在部署服務器上修改了 mysql-sts.yaml,且修改後的結果驗證成功。

爲了演示 GitOps 的更多場景,直接在部署服務器上修改,然後提交到在線代碼倉庫。

實際工作中我都是在自己的辦公電腦上修改,提交到在線代碼倉庫,然後部署服務器拉取更新代碼。

修改後的 mysql-sts.yaml,由於篇幅問題這裏只演示關鍵部分,StatefulSet 的完整配置見 Gitee 倉庫或是前文。

---
kind: StatefulSet
apiVersion: apps/v1
metadata:
  name: mysql
  namespace: lstack
  labels:
    app: mysql
...

---
kind: Service
apiVersion: v1
metadata:
  name: mysql-headless
  namespace: lstack
  labels:
    app: mysql
spec:
  ports:
    - name: tcp-mysql
      protocol: TCP
      port: 3306
      targetPort: 3306
  selector:
    app: mysql
  clusterIP: None
  type: ClusterIP

提交修改後的代碼到代碼倉庫。

# 修改後查看git倉庫的變化
$ git diff 
diff --git a/mysql/single/mysql-sts.yaml b/mysql/single/mysql-sts.yaml
index f775920..1eded9c 100644
--- a/mysql/single/mysql-sts.yaml
+++ b/mysql/single/mysql-sts.yaml
@@ -1,3 +1,4 @@
+---
 kind: StatefulSet
 apiVersion: apps/v1
 metadata:
@@ -68,6 +69,7 @@ spec:
         storageClassName: glusterfs
   serviceName: mysql-headless
 
+---
 kind: Service
 apiVersion: v1
 metadata:

# 本地提交代碼變更
$ git commit -am '修復mysql statefulset配置不生效問題'

# push到在線代碼倉庫,有一個warning可以忽略,也可以按提示執行
$ git push

查看 Gitee 在線代碼倉庫是否有變更。

在個人的辦公電腦上,同步更新後的代碼。

# 更新代碼
$ git pull

# 同步更新後的代碼到Github
$ git push -u origin

查看 GitHub 在線代碼倉庫是否有變更。

再次體驗 GitOps

模擬一個業務場景,再次體驗一下 GitOps。

  • MySQL 上線運行後,由於業務量上漲,初始配置參數中的 max_connections 太小了,需要增大。

  • 配置參數調整完成後,更新線上配置,並重啓服務 (生產環境數據庫不要輕易重啓,這種需求可以用臨時修改解決)。

  • 這裏只是模擬一個簡單的例子,帶大家體驗 GitOps,實際使用中所有的配置文件都建議使用 Git 進行版本控制。

編輯本地 Git 倉庫 MySQL 資源配置清單中的 mysql-cfm.yaml 文件,修改 max_connections,從 512 變成 1024。

提交修改到 Git 在線倉庫。

# 提交本地修改
$ git commit -am '修改mysql-cnf中max_connections的值'

# 提交到Github
$ git push

# 同步到Gitee
$ git push -u gitee

登錄運維管理節點,更新 Git 代碼,並重新運行。

$ git pull

$ kubectl apply -f mysql/single/

# 查看ConfigMap的變化
$ kubectl get configmaps mysql-cnf -n lstack -o yaml
apiVersion: v1
data:
  custom.cnf: |-
    [mysqld]
    #performance setttings
    lock_wait_timeout = 3600
    open_files_limit    = 65535
    back_log = 1024
    max_connections = 1024
    max_connect_errors = 1000000
    table_open_cache = 1024
    table_definition_cache = 1024
    thread_stack = 512K
    sort_buffer_size = 4M
    join_buffer_size = 4M
    read_buffer_size = 8M
    read_rnd_buffer_size = 4M
    bulk_insert_buffer_size = 64M
    thread_cache_size = 768
    interactive_timeout = 600
    wait_timeout = 600
    tmp_table_size = 32M
    max_heap_table_size = 32M
kind: ConfigMap
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","data":{"custom.cnf":"[mysqld]\n#performance setttings\nlock_wait_timeout = 3600\nopen_files_limit    = 65535\nback_log = 1024\nmax_connections = 1024\nmax_connect_errors = 1000000\ntable_open_cache = 1024\ntable_definition_cache = 1024\nthread_stack = 512K\nsort_buffer_size = 4M\njoin_buffer_size = 4M\nread_buffer_size = 8M\nread_rnd_buffer_size = 4M\nbulk_insert_buffer_size = 64M\nthread_cache_size = 768\ninteractive_timeout = 600\nwait_timeout = 600\ntmp_table_size = 32M\nmax_heap_table_size = 32M"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"mysql-cnf","namespace":"lstack"}}
  creationTimestamp: "2022-05-12T07:20:07Z"
  name: mysql-cnf
  namespace: lstack
  resourceVersion: "8928391"
  uid: 1b7322cf-f11e-445d-a2ba-b42a90ade469

# 重啓mysql pod使配置生效
$ kubectl delete -f mysql/single/mysql-sts.yaml 

$ kubectl apply -f mysql/single/mysql-sts.yaml 

# 查看mysql容器內部配置是否更新
$ kubectl exec  mysql-0  -n lstack -- cat /etc/mysql/conf.d/custom.cnf
[mysqld]
#performance setttings
lock_wait_timeout = 3600
open_files_limit    = 65535
back_log = 1024
max_connections = 1024
max_connect_errors = 1000000
table_open_cache = 1024
table_definition_cache = 1024
thread_stack = 512K
sort_buffer_size = 4M
join_buffer_size = 4M
read_buffer_size = 8M
read_rnd_buffer_size = 4M
bulk_insert_buffer_size = 64M
thread_cache_size = 768
interactive_timeout = 600
wait_timeout = 600
tmp_table_size = 32M

切記! 上面的例子只是讓大家體驗 GitOps,生產環境不要輕易重啓數據庫服務器,除非你知道自己在幹什麼。

現在經過驗證,我們的 MySQL 的配置可用且比較穩定,我們把這個好的狀態記錄下來,避免以後修改變更弄壞了,再找不回原來正確的配置。

在我們的個人電腦上給當前的 Git 代碼打個 Tag,記錄當前的狀態 (也可以通過在線倉庫的管理界面操作)。

# 打tag -a tag名字 -m tag描述
$ git tag -a v0.1  -m 'mysql version v0.1'

# 查看現有tag
$ git tag -l
v0.1

# 查看tag詳細信息
$ git show v0.1
tag v0.1
Tagger: devops <[email protected]>
Date:   Thu May 12 18:15:34 2022 +0800

mysql version v0.1

commit 180f97ac96da504a0b46eb4871ef423f64fde093 (HEAD -> main, tag: v0.1, origin/main, origin/HEAD, gitee/main)
Author: devops <[email protected]>
Date:   Thu May 12 17:48:18 2022 +0800

    修改mysql-cnf中max_connections的值

diff --git a/mysql/single/mysql-cfm.yaml b/mysql/single/mysql-cfm.yaml
index e24d96d..50d1778 100644
--- a/mysql/single/mysql-cfm.yaml
+++ b/mysql/single/mysql-cfm.yaml
@@ -10,7 +10,7 @@ data:
     lock_wait_timeout = 3600
     open_files_limit    = 65535
     back_log = 1024
-    max_connections = 512
+    max_connections = 1024
     max_connect_errors = 1000000
     table_open_cache = 1024
     table_definition_cache = 1024
     
# 將tag推送到遠程服務器
$ git push -u origin --tags
$ git push -u gitee --tags

# 線上服務器驗證(圖略)

運維管理服務器更新代碼,並切換到指定 tag(注意!使用 Git 一定要養成每次操作前 git pull 這種習慣)。

## 更新代碼
$ git pull

## 切換到v0.1
$ git checkout -b v0.1

通過上面的幾波操作,我們可以看到,我們所有的配置變更都採用了 Git 管理,完整的記錄了配置的全生命週期管理,通過給倉庫打分支或是 tag,可以方便我們切換到任意已記錄狀態。

高可用部署 MySQL(預留佔坑)

暫時沒有高可用部署的需求,因此不涉及高可用模式的 MySQL 的部署,但是有一些思考留着佔坑。

目前的做法

  • 不給自己找麻煩,有高可用需求直接買雲服務商的 RDS。
  • 實在需要自己搭建,在 K8s 集羣之外部署主從。

以後可能的方向

  • K8s 上的 MySQL 主從部署
  • Operator
  • Helm

遺留問題

此部分內容也是運維 MySQL 必備的技能,有些內容我也沒有經驗無法分享,有些內容會在 << 基於 KubeSphere 的 K8s 生產實踐之路 >> 系列文檔中介紹。

  • MySQL 數據庫備份
  • MySQL 高可用部署
  • MySQL 安全加固
  • MySQL 調優

MySQL 性能 (基準) 測試

運維一定要做到對自己的運維環境心中有數,MySQL 上線前一定要進行性能 (基準測試),有助於瞭解我們的數據庫服務器能達到的理想狀態。本次介紹的只是皮毛,只是告訴大家一些基本入門的知識,更細節、更深入的內容請參考其他更專業的文檔。

性能 (基準) 測試工具安裝

工具選型 (sysbench)

  • 雲廠商展示自家數據庫產品性能都用這個工具
  • 據說很多 DBA 也喜歡用

sysbench 工具安裝

  • 安裝工具
# 導入軟件源
$ curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.rpm.sh | sudo bash

# 安裝sysbench
$ yum install sysbench -y
  • 驗證-執行命令查看版本
$ sysbench --version
sysbench 1.0.20

性能 (基準) 測試

測試方案

  • 測試參數

  • 指標
    線程數 8/16/32
    單表數據量 100000
    表數量 16

    性能指標

    指標 說明
    TPS Transactions Per Second ,即數據庫每秒執行的事務數,以 commit 成功次數爲準。
    QPS Queries Per Second ,即數據庫每秒執行的 SQL 數(含 insert、select、update、delete 等)。
    RT Response Time ,響應時間。包括平均響應時間、最小響應時間、最大響應時間、每個響應時間的查詢佔比。比較需要重點關注的是,前 95-99% 的最大響應時間。因爲它決定了大多數情況下的短板。
    Concurrency Threads 併發量,每秒可處理的查詢請求的數量。

準備測試數據

使用我們在 k8s 上創建的數據庫,涉及數據庫操作命令,需要終端登錄到容器內運行。

提前創建測試用數據庫 sbtest,並賦予 root 從任意 IP 遠程管理所有數據庫的權限。

生產環境千萬不要這麼搞,一定要遵循最小化原則!

# bash
root@mysql-0:/# mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.7.38 MySQL Community Server (GPL)

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> create database sbtest;
Query OK, 1 row affected (0.02 sec)

mysql> grant all privileges on *.* to 'root'@'%' identified by 'P@88w0rd' with grant option;
Query OK, 0 rows affected, 1 warning (0.02 sec)
  • 測試數據庫是否能連接
# 安裝mysql客戶端,下面的示例是在k8s節點上安裝的,由於系統是最小化安裝,所有會安裝很多依賴。實際測試可以起一個mysql的pod或是用其他的mysql客戶端工具。

$ yum install mysql -y

# 測試MySQL服務連通性 -h 是k8s節點的IP -P 是mysql外部服務的端口號

$ mysql -h 192.168.9.91 -P 32529 -u root -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 5
Server version: 5.7.38 MySQL Community Server (GPL)

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> 
  • 準備測試數據
$ sysbench --db-driver=mysql --mysql-host=192.168.9.91 --mysql-port=32529 --mysql-user=root --mysql-password=P@88w0rd --mysql-db=sbtest --table-size=100000 --tables=16 --threads=8 --events=999999999 --report-interval=10 --time=100 /usr/share/sysbench/oltp_common.lua prepare
sysbench 1.0.20 (using bundled LuaJIT 2.1.0-beta2)

Initializing worker threads...

Creating table 'sbtest6'...
Creating table 'sbtest2'...
Creating table 'sbtest8'...
Creating table 'sbtest3'...
Creating table 'sbtest7'...
Creating table 'sbtest5'...
Creating table 'sbtest1'...
Creating table 'sbtest4'...
Inserting 100000 records into 'sbtest3'
Inserting 100000 records into 'sbtest6'
Inserting 100000 records into 'sbtest1'
Inserting 100000 records into 'sbtest4'
Inserting 100000 records into 'sbtest7'
Inserting 100000 records into 'sbtest5'
Inserting 100000 records into 'sbtest2'
Inserting 100000 records into 'sbtest8'
Creating a secondary index on 'sbtest3'...
Creating table 'sbtest11'...
Inserting 100000 records into 'sbtest11'
Creating a secondary index on 'sbtest5'...
Creating a secondary index on 'sbtest1'...
Creating a secondary index on 'sbtest6'...
Creating a secondary index on 'sbtest4'...
Creating a secondary index on 'sbtest7'...
Creating a secondary index on 'sbtest2'...
Creating a secondary index on 'sbtest8'...
Creating table 'sbtest13'...
Inserting 100000 records into 'sbtest13'
Creating table 'sbtest9'...
Inserting 100000 records into 'sbtest9'
Creating table 'sbtest14'...
Creating table 'sbtest12'...
Inserting 100000 records into 'sbtest14'
Inserting 100000 records into 'sbtest12'
Creating table 'sbtest15'...
Inserting 100000 records into 'sbtest15'
Creating table 'sbtest16'...
Creating table 'sbtest10'...
Inserting 100000 records into 'sbtest16'
Inserting 100000 records into 'sbtest10'
Creating a secondary index on 'sbtest11'...
Creating a secondary index on 'sbtest13'...
Creating a secondary index on 'sbtest9'...
Creating a secondary index on 'sbtest12'...
Creating a secondary index on 'sbtest14'...
Creating a secondary index on 'sbtest15'...
Creating a secondary index on 'sbtest10'...
Creating a secondary index on 'sbtest16'...
  • 執行測試-8 線程測試
$ sysbench --db-driver=mysql --mysql-host=192.168.9.91 --mysql-port=32529 --mysql-user=root --mysql-password=P@88w0rd --mysql-db=sbtest --table-size=100000 --tables=16 --threads=8 --events=999999999 --report-interval=10 --time=100  /usr/share/sysbench/oltp_read_write.lua run
sysbench 1.0.20 (using bundled LuaJIT 2.1.0-beta2)

Running the test with following options:
Number of threads: 8
Report intermediate results every 10 second(s)
Initializing random number generator from current time

Initializing worker threads...

Threads started!

[ 10s ] thds: 8 tps: 88.46 qps: 1782.38 (r/w/o: 1249.19/355.46/177.73) lat (ms,95%): 267.41 err/s: 0.00 reconn/s: 0.00
[ 20s ] thds: 8 tps: 84.31 qps: 1678.47 (r/w/o: 1173.42/336.43/168.62) lat (ms,95%): 277.21 err/s: 0.00 reconn/s: 0.00
[ 30s ] thds: 8 tps: 70.20 qps: 1413.82 (r/w/o: 990.21/283.20/140.40) lat (ms,95%): 369.77 err/s: 0.00 reconn/s: 0.00
[ 40s ] thds: 8 tps: 47.30 qps: 946.00 (r/w/o: 662.20/189.20/94.60) lat (ms,95%): 484.44 err/s: 0.00 reconn/s: 0.00
[ 50s ] thds: 8 tps: 43.80 qps: 875.99 (r/w/o: 613.19/175.20/87.60) lat (ms,95%): 484.44 err/s: 0.00 reconn/s: 0.00
[ 60s ] thds: 8 tps: 60.70 qps: 1213.08 (r/w/o: 849.69/242.00/121.40) lat (ms,95%): 411.96 err/s: 0.00 reconn/s: 0.00
[ 70s ] thds: 8 tps: 53.90 qps: 1078.22 (r/w/o: 754.42/216.00/107.80) lat (ms,95%): 376.49 err/s: 0.00 reconn/s: 0.00
[ 80s ] thds: 8 tps: 56.49 qps: 1127.98 (r/w/o: 790.11/224.88/112.99) lat (ms,95%): 397.39 err/s: 0.00 reconn/s: 0.00
[ 90s ] thds: 8 tps: 50.60 qps: 1014.59 (r/w/o: 709.56/203.82/101.21) lat (ms,95%): 434.83 err/s: 0.00 reconn/s: 0.00
[ 100s ] thds: 8 tps: 54.70 qps: 1093.12 (r/w/o: 765.22/218.50/109.40) lat (ms,95%): 390.30 err/s: 0.00 reconn/s: 0.00
SQL statistics:
    queries performed:
        read:                            85582
        write:                           24452
        other:                           12226
        total:                           122260
    transactions:                        6113   (61.10 per sec.)
    queries:                             122260 (1221.96 per sec.)
    ignored errors:                      0      (0.00 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          100.0494s
    total number of events:              6113

Latency (ms):
         min:                                   35.63
         avg:                                  130.89
         max:                                  951.86
         95th percentile:                      390.30
         sum:                               800129.59

Threads fairness:
    events (avg/stddev):           764.1250/4.14
    execution time (avg/stddev):   100.0162/0.01
  • 執行測試-16 線程測試
$ sysbench --db-driver=mysql --mysql-host=192.168.9.91 --mysql-port=32529 --mysql-user=root --mysql-password=P@88w0rd --mysql-db=sbtest --table-size=100000 --tables=16 --threads=16 --events=999999999 --report-interval=10 --time=100  /usr/share/sysbench/oltp_read_write.lua run
sysbench 1.0.20 (using bundled LuaJIT 2.1.0-beta2)

Running the test with following options:
Number of threads: 16
Report intermediate results every 10 second(s)
Initializing random number generator from current time

Initializing worker threads...

Threads started!

[ 10s ] thds: 16 tps: 114.41 qps: 2310.22 (r/w/o: 1621.18/458.63/230.41) lat (ms,95%): 369.77 err/s: 0.00 reconn/s: 0.00
[ 20s ] thds: 16 tps: 106.35 qps: 2111.86 (r/w/o: 1474.74/424.41/212.71) lat (ms,95%): 383.33 err/s: 0.00 reconn/s: 0.00
[ 30s ] thds: 16 tps: 80.40 qps: 1612.01 (r/w/o: 1129.21/322.00/160.80) lat (ms,95%): 623.33 err/s: 0.00 reconn/s: 0.00
[ 40s ] thds: 16 tps: 63.40 qps: 1266.80 (r/w/o: 886.80/253.20/126.80) lat (ms,95%): 539.71 err/s: 0.00 reconn/s: 0.00
[ 50s ] thds: 16 tps: 57.20 qps: 1145.91 (r/w/o: 802.74/228.78/114.39) lat (ms,95%): 549.52 err/s: 0.00 reconn/s: 0.00
[ 60s ] thds: 16 tps: 69.91 qps: 1408.31 (r/w/o: 987.57/280.92/139.81) lat (ms,95%): 511.33 err/s: 0.00 reconn/s: 0.00
[ 70s ] thds: 16 tps: 78.00 qps: 1547.22 (r/w/o: 1080.51/310.70/156.00) lat (ms,95%): 484.44 err/s: 0.00 reconn/s: 0.00
[ 80s ] thds: 16 tps: 79.50 qps: 1599.87 (r/w/o: 1122.58/318.29/159.00) lat (ms,95%): 520.62 err/s: 0.00 reconn/s: 0.00
[ 90s ] thds: 16 tps: 67.80 qps: 1354.83 (r/w/o: 947.62/271.61/135.60) lat (ms,95%): 539.71 err/s: 0.00 reconn/s: 0.00
[ 100s ] thds: 16 tps: 73.90 qps: 1474.10 (r/w/o: 1030.80/295.50/147.80) lat (ms,95%): 502.20 err/s: 0.00 reconn/s: 0.00
SQL statistics:
    queries performed:
        read:                            110950
        write:                           31700
        other:                           15850
        total:                           158500
    transactions:                        7925   (79.00 per sec.)
    queries:                             158500 (1580.05 per sec.)
    ignored errors:                      0      (0.00 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          100.3103s
    total number of events:              7925

Latency (ms):
         min:                                   41.24
         avg:                                  202.44
         max:                                 1198.81
         95th percentile:                      511.33
         sum:                              1604328.52

Threads fairness:
    events (avg/stddev):           495.3125/4.03
    execution time (avg/stddev):   100.2705/0.03
  • 執行測試-32 線程測試
$ sysbench --db-driver=mysql --mysql-host=192.168.9.91 --mysql-port=32529 --mysql-user=root --mysql-password=P@88w0rd --mysql-db=sbtest --table-size=100000 --tables=16 --threads=32 --events=999999999 --report-interval=10 --time=100  /usr/share/sysbench/oltp_read_write.lua run
sysbench 1.0.20 (using bundled LuaJIT 2.1.0-beta2)

Running the test with following options:
Number of threads: 32
Report intermediate results every 10 second(s)
Initializing random number generator from current time

Initializing worker threads...

Threads started!

[ 10s ] thds: 32 tps: 140.10 qps: 2825.04 (r/w/o: 1981.25/560.39/283.39) lat (ms,95%): 450.77 err/s: 0.00 reconn/s: 0.00
[ 20s ] thds: 32 tps: 124.41 qps: 2515.49 (r/w/o: 1763.43/503.24/248.82) lat (ms,95%): 549.52 err/s: 0.00 reconn/s: 0.00
[ 30s ] thds: 32 tps: 95.90 qps: 1887.10 (r/w/o: 1316.70/378.60/191.80) lat (ms,95%): 733.00 err/s: 0.00 reconn/s: 0.00
[ 40s ] thds: 32 tps: 81.80 qps: 1656.59 (r/w/o: 1164.89/328.10/163.60) lat (ms,95%): 707.07 err/s: 0.00 reconn/s: 0.00
[ 50s ] thds: 32 tps: 82.60 qps: 1638.41 (r/w/o: 1143.51/329.70/165.20) lat (ms,95%): 657.93 err/s: 0.00 reconn/s: 0.00
[ 60s ] thds: 32 tps: 94.34 qps: 1905.84 (r/w/o: 1336.62/380.65/188.58) lat (ms,95%): 623.33 err/s: 0.00 reconn/s: 0.00
[ 70s ] thds: 32 tps: 87.86 qps: 1739.86 (r/w/o: 1215.31/348.73/175.82) lat (ms,95%): 634.66 err/s: 0.00 reconn/s: 0.00
[ 80s ] thds: 32 tps: 84.40 qps: 1705.48 (r/w/o: 1196.49/340.20/168.80) lat (ms,95%): 759.88 err/s: 0.00 reconn/s: 0.00
[ 90s ] thds: 32 tps: 80.50 qps: 1580.71 (r/w/o: 1101.70/318.00/161.00) lat (ms,95%): 612.21 err/s: 0.00 reconn/s: 0.00
[ 100s ] thds: 32 tps: 81.40 qps: 1661.90 (r/w/o: 1167.00/332.10/162.80) lat (ms,95%): 707.07 err/s: 0.00 reconn/s: 0.00
SQL statistics:
    queries performed:
        read:                            133924
        write:                           38264
        other:                           19132
        total:                           191320
    transactions:                        9566   (95.33 per sec.)
    queries:                             191320 (1906.56 per sec.)
    ignored errors:                      0      (0.00 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          100.3457s
    total number of events:              9566

Latency (ms):
         min:                                   51.94
         avg:                                  335.14
         max:                                 1405.78
         95th percentile:                      657.93
         sum:                              3205913.85

Threads fairness:
    events (avg/stddev):           298.9375/5.15
    execution time (avg/stddev):   100.1848/0.14

MySQL 容器性能監控圖。

kubesphere-projects-lstack-statefulsets-mysql-74

清理測試數據 (爲了保證數據更精準,建議每次測試前都清理數據,準備數據,測試)。

$ sysbench --db-driver=mysql --mysql-host=192.168.9.91 --mysql-port=32529 --mysql-user=root --mysql-password=P@88w0rd --mysql-db=sbtest --table-size=100000 --tables=16 --threads=32 --events=999999999 --report-interval=10 --time=100  /usr/share/sysbench/oltp_read_write.lua cleanup
sysbench 1.0.20 (using bundled LuaJIT 2.1.0-beta2)

Dropping table 'sbtest1'...
Dropping table 'sbtest2'...
Dropping table 'sbtest3'...
Dropping table 'sbtest4'...
Dropping table 'sbtest5'...
Dropping table 'sbtest6'...
Dropping table 'sbtest7'...
Dropping table 'sbtest8'...
Dropping table 'sbtest9'...
Dropping table 'sbtest10'...
Dropping table 'sbtest11'...
Dropping table 'sbtest12'...
Dropping table 'sbtest13'...
Dropping table 'sbtest14'...
Dropping table 'sbtest15'...
Dropping table 'sbtest16'...

測試結果

結果彙總對比。

壓測線程數量 TPS QPS 延遲
8 61 1221 130
16 79 1580 202
32 95 1906 335

建議根據測試結果,調優!

總結

本文詳細介紹了 Git 常用操作、如何將代碼在多個在線代碼倉庫中存儲並保持同步,還介紹了 GitOps 的基本概念並演示瞭如何用 GitOps 理念在原生 K8s 上部署 MySQL 服務。最後,演示了 MySQL 常用性能測試工具 sysbench 的安裝和基礎使用。

我多年的一些運維經驗和運維思路貫穿了全文。

本文由博客一文多發平臺 OpenWrite 發佈!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章