はじめに #
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 Operator for Kubernetes
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かを気にせずアクセスできるようになります。 |
3. MySQL Operatorとは #
k8sには公式で紹介されているオペレーターパターンというものがあります。 これは簡単にいうとk8sへの追加のplugin的な要素であり、kube-apiserverが提供するAPIを拡張し独自定義したDeploymentを使うことが可能になります。
実際に使われているhelm chartを見てみるとapiVersion: mysql.oracle.com/v2
やkind: 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にする方法を紹介します。