メインコンテンツへスキップ
  1. Blogs/

MySQL InnoDB Cluster1 -k8sにdeploy-

·3736 文字·
Blog Kubernetes Mysql
hiroki
著者
hiroki
クラウドを作るお仕事をしてます。
目次
mysql-innodb-cluster - 関連記事
1: << この記事 >>

はじめに
#

k8s上にアプリケーションをdeployする際には、みなさんDBはどうしていますでしょうか? PaaSサービスを利用する場合も多いと思いますが、今回はk8s上にMySQLを構築する方法を紹介しようと思います。

今回解説する際に使用しているk8s環境はオンプレで構築しています。(CNI: carico, driver: nfs-csi-driver)

root@v0-dev-01:~/blog/hirohirolab# kubectl version
Client Version: v1.28.8
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.28.2

root@v0-dev-01:~/blog/hirohirolab# kubectl get nodes
NAME        STATUS   ROLES           AGE    VERSION
v0-k8s-01   Ready    control-plane   222d   v1.28.2
v0-k8s-02   Ready    <none>          222d   v1.28.2
v0-k8s-03   Ready    <none>          222d   v1.28.2

1. bitnamiのMySQL Helm Chartを利用する
#

とにかく手軽にMySQLをk8s上にデプロイしたい場合は、bitnamiのMySQL Helm Chartを利用するのがおすすめです。 あまりにも手軽で、helmを使える方であれば特に迷うこともないので解説は割愛します。

k8s上にMySQL(もしくはStatefulSetのpod)を構築したことがない場合は、まずこれがdeployできるか確認しておくとトラブルシュートが簡単になるのでおすすめです。

筆者はPersistentVolumeの動的な払い出しができておらず躓いたので…

2. MySQL InnoDB Cluster
#

上記のbitnamiのMySQLでは冗長構成が取れていないので、本番用として使用するには不十分です。 そこで本命のMySQL InnoDB Clusterが出てきます。公式docs

mysql/mysql-operator

MySQL Operator for Kubernetes

Python
771
126

InnoDBは複数のコンポーネントで構成されており、

  • Primary/Secondaryを使った冗長構成
  • MySQL-Routerを使ったDBへの透過的な(Primary/Secondaryを気にしない)アクセス
  • MySQL-Operator + helmを使用した簡単なdeploy
  • DBのbackupする機能の同梱

とMySQLに必要な機能を全て兼ね備えつつ、k8sに最適化されているProductになっています。

名前 詳細
MySQL-Operator InnoDBのdeployは複雑なため、各コンポーネントを自分で作成しません。
Operatorを先にdeployし、Operatorに対して構築用のParameterを提供することで自動でInnoDBの作成を行ないます。
これにより後述するServerの冗長構成やRouterによるDBへのroutingをuserが設定せずに実現できます。
MySQL-Server 実際にデータの保存をするpodです。
k8sといえばpodが自動生成され、どのpodにアクセスしても同じ結果になることが特徴ですが、今回は「Primary/Secondaryの概念」、「データの永続化が必要」という観点から特定のpodにアクセスすることが必要になります。
従ってStatefulSetという特殊なpodでdeployされます。
MySQL-Router MySQL-Serverへの接続を仲介してくれるLB的なpodです。
k8sのServiceでもpodへのLBはできますが、「Primary/Secondaryの概念」等のためk8sにpodへのルーティングを任せずに独自のRouterを構築します。これによりuserはどれがPrimary/Secondaryかを気にせずアクセスできるようになります。

img001
Figure by MySQL

3. MySQL Operatorとは
#

k8sには公式で紹介されているオペレーターパターンというものがあります。 これは簡単にいうとk8sへの追加のplugin的な要素であり、kube-apiserverが提供するAPIを拡張し独自定義したDeploymentを使うことが可能になります。

実際に使われているhelm chartを見てみるとapiVersion: mysql.oracle.com/v2kind: InnoDBClusterが独自のものになっていることがわかります。さらにMySQL-Routerをカスタマイズできる、spec.router.というParameterも存在します。

apiVersion: mysql.oracle.com/v2  # 独自のapi
kind: InnoDBCluster # 独自のkind
metadata:
  name: {{ $cluster_name }}
  namespace: {{ .Release.Namespace }}
spec:
  instances: {{ required "serverInstances is required" .Values.serverInstances }}
  tlsUseSelfSigned: {{ $use_self_signed }}
  router: # MySQL Routerをカスタマイズできる項目
    instances: {{ required "router.instances is required" $routerInstances }}

このOperatorを使用することで、複雑なInnoDBの構築や初期設定を全て自動で実施してくれるようになります。

Operatorの実装が気になったので何をしているか更に詳しく見てみました。

InnoDB用のyamlを作成

Operatorに提供したyamlから、InnoDB用のyaml(以下例はService)の作成

def prepare_router_service(spec: InnoDBClusterSpec) -> dict:
    tmpl = f"""
apiVersion: v1
kind: Service
metadata:
  name: {spec.name}
  namespace: {spec.namespace}
  labels:
    tier: mysql
    mysql.oracle.com/cluster: {spec.name}
spec:
  ports:
  - name: mysql
    port: {spec.mysql_port}
    protocol: TCP
    targetPort: {spec.service.get_default_port_number(spec)}
  - name: mysqlx
    port: {spec.mysql_xport}
    protocol: TCP
    targetPort: {spec.router_rwxport}
  - name: mysql-alternate
    port: {spec.router_rwport}
    protocol: TCP

MySQLの初期構築

初回起動のために以下のことを自動で実施してくれています。

これを自分で実施すると、非常に大変なのでMySQL-Operatorがあることによってかなり便利になっている様子。

# Bootstrap sequence:

# 1 - initconf (initContainer)
# 1.1 - initializes MySQL configuration files
#
# 2 - initmysql (initContainer) 
# 2.1 - initializes MySQL datadir
# 2.2 - create default root account
# 2.3 - create localroot
#
# 3 - mysql - via container entrypoint:
# 3.1 - start mysqld
#
# 4 - sidecar:
# 4.1 - configureInstance()
# 4.2 - initialize db (clone, loadDump etc)
# 4.3 - restart (optional)
# 4.4 - mark as ready
# # TODO move some stuff to initdatadir?

などなど

4. MySQL Operatorのdeploy
#

deployはhelmなので非常に簡単です。githubのREADMEや、公式docsを参考にdeployしましょう。

helm repo add mysql-operator https://mysql.github.io/mysql-operator/
helm repo update

helm install mysql-operator mysql-operator/mysql-operator --namespace mysql-operator --create-namespace

5. InnoDBのdeploy
#

こちらもdeployはhelmなので非常に簡単です。githubのREADMEや、公式docsを参考にdeployしましょう。

tls.useSelfSigned=trueが無いと、独自で作成した証明書を使用する必要があるので注意しましょう。自分で作成した証明書を使いたい場合はこちら

helmなので他にも色々なParameterでカスタマイズできますが、長くなるので第3回で紹介したいと思います。

helm install mycluster mysql-operator/mysql-innodbcluster \
        --namespace default \
        --create-namespace \
        --set credentials.root.user='root' \
        --set credentials.root.password='supersecret' \
        --set credentials.root.host='%' \
        --set serverInstances=3 \
        --set routerInstances=1 \
        --set tls.useSelfSigned=true

MySQLはDBの内容を保存する必要がある + 先に述べたようにStatefulSetでdeployされます。従ってstorage classを使用して動的にPersistentVolumeを作成できる必要があります。

またmysql-operatorとinnodbのhelm-versionは合わせないとdeployできないので注意です。

helm list -A | grep mysql
mycluster          mysql-innodbcluster-2.1.3         8.4.0      
mysql-operator     mysql-operator-2.1.3              8.4.0-2.1.3

6. deploy後check
#

まずdeployした後にrouterがdeployできているか確認しましょう。routerのpodがdeployできていればdeploy成功と考えてOKです。(多分。。。)

また実際にDBにアクセスしたいのですが、残念ながら公式で提供しているhelm chartではNodePortやLoadBalancerを指定できません。こちらの設定方法は第3回に紹介します。

root@v0-dev-01:~/blog/hirohirolab# kubectl get pods,pvc,service
NAME                                    READY   STATUS    RESTARTS   AGE
pod/mycluster-0                         2/2     Running   0          5m59s
pod/mycluster-1                         2/2     Running   0          5m59s
pod/mycluster-2                         2/2     Running   0          5m59s
pod/mycluster-router-74d4c64d8f-xrjzt   1/1     Running   0          4m34s

NAME                                        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/datadir-mycluster-0   Bound    pvc-a2664dd5-ae7d-4650-9974-3747900da5ba   2Gi        RWO            nfs-csi        5m59s
persistentvolumeclaim/datadir-mycluster-1   Bound    pvc-5a2b435a-e6cd-4833-9285-0ff7c9fa2b77   2Gi        RWO            nfs-csi        5m59s
persistentvolumeclaim/datadir-mycluster-2   Bound    pvc-8ebadee2-3054-4bc5-849b-05b4d9df41bf   2Gi        RWO            nfs-csi        5m59s

NAME                          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                                                                    AGE
service/kubernetes            ClusterIP   10.96.0.1       <none>        443/TCP                                                                    222d
service/mycluster             ClusterIP   10.104.161.97   <none>        3306/TCP,33060/TCP,6446/TCP,6448/TCP,6447/TCP,6449/TCP,6450/TCP,8443/TCP   5m59s
service/mycluster-instances   ClusterIP   None            <none>        3306/TCP,33060/TCP,33061/TCP                                               5m59s

今回はgithubのREADMEにも紹介されているport-forwardでアクセスします。他のserverからもアクセスしたいので--address 0.0.0.0を追加します。

root@v0-k8s-01:~# kubectl port-forward service/mycluster mysql --address 0.0.0.0
Forwarding from 0.0.0.0:3306 -> 6446
Handling connection for 3306

これによって他のserverからk8s内部のmysqlにアクセスすることができます。 デフォルトの:3306では必ずMySQL-RouterによってPrimaryのMySQL-Server(今回はmycluster-0のpod)にroutingされます。

root@v0-dev-01:~/blog/hirohirolab# mysql -h 192.168.1.215 -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 0
Server version: 8.4.0-router MySQL Community Server - GPL

Copyright (c) 2000, 2024, 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> show variables like 'hostname';
+---------------+-------------+
| Variable_name | Value       |
+---------------+-------------+
| hostname      | mycluster-0 |
+---------------+-------------+
1 row in set (0.01 sec)

mysql> 

7. 再deploy時の注意
#

InnoDBを再度deployする際には必ずPVCを削除してから再構築してください。(DBのデータは消えるので注意)

これを実施しないとOperatorがMySQL-ServerにアクセスできなくなりRouterがdeployされなくなります。

この原因はMySQL-ServerがStatefulSetであるため、再deployした際には以前使っていたPVが再度割り当てされます。この結果Operatorによる初期化が発生せずオペレータがー疎通不可になります。

正確には以下Operatorのlogのように、Operatorがアクセスするuser mysqladminのpasswordが、Operator側: 新しく作ったパスワード、Server側: 以前使ってたパスワードとなり不一致で接続不可になる模様。

kubectl logs mysql-operator-98d6fc64b-746bs -n mysql-operator
[INFO    ] Error executing mysqlsh.connect_dba, retrying after 2s: MySQL Error (1045): mysqlsh.connect_dba: Access denied for user 'mysqladmin'@'192-168-2-129.mysql-operator.mysql-operator.svc.cluster.local' (using password: YES)

おわりに
#

今回はInnoDBの概要とk8sにdeployするところまでを解説しました。今回はdeployだけだったので高機能なInnoDBについてあまり感じれなかったと思いますが、次回以降で色々な機能について紹介できたらと思います。

第2回はMySQL-Routerによって、Primary/Secondaryのrouting手法の解説

第3回はHelm Chartをカスタマイズして、NodePortやLoadBalancerにする方法を紹介します。

mysql-innodb-cluster - 関連記事
1: << この記事 >>

Related

pyvmomiで自動化7 -HoLを使った検証環境の用意-
·1189 文字
Blog VMware VSphere Pyvmomi Python
iphoneで特定APPのみVPNを有効化する
·684 文字
Blog IPhone Homelab
Bashで簡易的なlogging
·292 文字
Blog ShellScript