はじめに
MySQL Operator for Kubernetesは、MySQLをKubernetes上で稼働させるために各種存在するOperatorの1つで、Oracleにより開発されています。MySQL 8.0.29のリリースと共に、MySQL Operator for KubernetesがGAとなったことが発表されました。
MySQL Operator for Kubernetesを使用すると、InnoDBコンテナを複数個起動しInnoDBクラスターを構成すると共に、アプリケーションとInnoDBクラスタを接続するMySQL Routerもコンテナとして稼働させて、全体で高可用性環境を実現します。この環境をKubernetesのコンテナ管理とOperatorの機能を用いることで、環境の自動的なプロビジョニングが可能となります。
MySQL Operatorのインストール方法は、MySQL Operator GitHub Project PageのREADME.mdで説明されていますが、この公式ガイドそのままではOpenShiftでPodとして起動できないため、ここではOpenShiftにMySQL Operatorをインストールする追加の手順を含めて説明します。
前提
OpenShiftは、CodeReady Containersの2.4.1(Linux版)を使用します。これはOpenShift4.10.14に相当しますが、MySQL Operatorには前提とするKubernetesバージョンやOpenShiftバージョンは指定されていないので、より古いバージョンでも手順は同じかと思います。
$ crc version
CRC version: 2.4.1+b877358
OpenShift version: 4.10.14
Podman version: 4.0.2
$ oc get clusterversion
NAME VERSION AVAILABLE PROGRESSING SINCE STATUS
version 4.10.14 True False 20d Cluster version is 4.10.14
インストール
MySQL Operatorのインストールは、Operator自身のインストールとInnoDBクラスターのインストールの2段階で行います。
また公式ガイドでは、インストール方法としてHelmを使用する方法と、kubectlコマンドを使用する方法それぞれ説明されていますが、ここではkubectl(正確にはoc)コマンドを使用した方法に沿って説明します。
MySQL OperatorとInnoDBクラスターは、それぞれ異なるProjectにインストールすることとします。MySQL Operator用にmysql-operator
、InnoDBクラスター用にmysql
Projectを使用します。インストール手順の中で使用するdeploy-operator.yaml
ファイルには、MySQL Operatorが使用するnamespaceとしてmysql-operator
が直書きされているので、そのまま使用します。
Operatorインストール
CRDインストール
最初にMySQL Operatorが使用するCRD(Custom Resource Definition)をインストールします。公式ガイドのコマンドパラメータをそのまま使用します。
$ oc apply -f https://raw.githubusercontent.com/mysql/mysql-operator/trunk/deploy/deploy-crds.yaml
customresourcedefinition.apiextensions.k8s.io/innodbclusters.mysql.oracle.com created
customresourcedefinition.apiextensions.k8s.io/mysqlbackups.mysql.oracle.com created
customresourcedefinition.apiextensions.k8s.io/clusterkopfpeerings.zalando.org created
customresourcedefinition.apiextensions.k8s.io/kopfpeerings.zalando.org created
MySQL Operator用Projectの作成
公式ガイドが提供するdeploy-operator.yaml
には、Operatorの使用するClusterRoleとServiceAccountおよびそのロールへのClusterRoleBinding、そしてDeploymentとNamespaceが定義されています。Projectは作成してくれないので、このyamlをapplyする前にmysql-operator
Projectを作成します。
$ oc new-project mysql-operator
Now using project "mysql-operator" on server "https://api.crc.testing:6443".
You can add applications to this project with the 'new-app' command. For example, try:
oc new-app rails-postgresql-example
to build a new example application in Ruby. Or use kubectl to deploy a simple Kubernetes application:
kubectl create deployment hello-node --image=k8s.gcr.io/e2e-test-images/agnhost:2.33 -- /agnhost serve-hostname
MySQL Operatorインストール
この後、手順の通りdeploy-operator.yaml
をapplyします。
$ oc apply -f https://raw.githubusercontent.com/mysql/mysql-operator/trunk/deploy/deploy-operator.yaml
clusterrole.rbac.authorization.k8s.io/mysql-operator created
clusterrole.rbac.authorization.k8s.io/mysql-sidecar created
clusterrolebinding.rbac.authorization.k8s.io/mysql-operator-rolebinding created
clusterkopfpeering.zalando.org/mysql-operator created
Warning: resource namespaces/mysql-operator is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by oc apply. oc apply should only be used on resources created declaratively by either oc create --save-config or oc apply. The missing annotation will be patched automatically.
namespace/mysql-operator configured
serviceaccount/mysql-operator-sa created
deployment.apps/mysql-operator created
先にProjectを作成済みであるため、NamespaceのところでWarningが出ていますが、気にせず進みます。これで、mysql-operator Deploymentが作成されたので、operator podが立ち上がるはずですが、いつまでたってもPodが起動する気配がありません。何が起きているかを知るためにEventを見てみましょう。
$ oc get event
LAST SEEN TYPE REASON OBJECT MESSAGE
7s Warning FailedCreate replicaset/mysql-operator-586f9f5d5b Error creating: pods "mysql-operator-586f9f5d5b-" is forbidden: unable to validate against any security context constraint: [provider "anyuid": Forbidden: not usable by user or serviceaccount, spec.containers[0].securityContext.runAsUser: Invalid value: 2: must be in the ranges: [1000640000, 1000649999], provider "nonroot": Forbidden: not usable by user or serviceaccount, provider "hostmount-anyuid": Forbidden: not usable by user or serviceaccount, provider "machine-api-termination-handler": Forbidden: not usable by user or serviceaccount, provider "hostnetwork": Forbidden: not usable by user or serviceaccount, provider "hostaccess": Forbidden: not usable by user or serviceaccount, provider "privileged": Forbidden: not usable by user or serviceaccount]
2m50s Normal ScalingReplicaSet deployment/mysql-operator Scaled up replica set mysql-operator-586f9f5d5b to 1
Operator Podが使用するServiceAccountに適切なSCCが設定されていないため、Podの作成でFailedCreateになっていました。これを解消するために、Podが使用するServiceAccountにSCCを付与します。各種SCCについてのエラーが出ていますが、ここではanyuidのみ設定すれば問題は解消します。
$ oc get serviceaccount
NAME SECRETS AGE
builder 2 9m30s
default 2 9m30s
deployer 2 9m30s
mysql-operator-sa 2 8m7s
$ oc get deployment mysql-operator -o custom-columns=NAME:metadata.name,ACCOUNT:spec.template.spec.serviceAccountName
NAME ACCOUNT
mysql-operator mysql-operator-sa
$ oc adm policy add-scc-to-user anyuid -z mysql-operator-sa
clusterrole.rbac.authorization.k8s.io/system:openshift:scc:anyuid added: "mysql-operator-sa"
しばらく待っていると、Podの作成が成功して、Operator Podが起動します。
$ oc get event
LAST SEEN TYPE REASON OBJECT MESSAGE
21s Normal Scheduled pod/mysql-operator-586f9f5d5b-hsqck Successfully assigned mysql-operator/mysql-operator-586f9f5d5b-hsqck to crc-m5qn9-master-0
19s Normal AddedInterface pod/mysql-operator-586f9f5d5b-hsqck Add eth0 [10.217.0.131/23] from openshift-sdn
19s Normal Pulling pod/mysql-operator-586f9f5d5b-hsqck Pulling image "mysql/mysql-operator:8.0.29-2.0.4"
7m57s Warning FailedCreate replicaset/mysql-operator-586f9f5d5b Error creating: pods "mysql-operator-586f9f5d5b-" is forbidden: unable to validate against any security context constraint: [provider "anyuid": Forbidden: not usable by user or serviceaccount, spec.containers[0].securityContext.runAsUser: Invalid value: 2: must be in the ranges: [1000640000, 1000649999], provider "nonroot": Forbidden: not usable by user or serviceaccount, provider "hostmount-anyuid": Forbidden: not usable by user or serviceaccount, provider "machine-api-termination-handler": Forbidden: not usable by user or serviceaccount, provider "hostnetwork": Forbidden: not usable by user or serviceaccount, provider "hostaccess": Forbidden: not usable by user or serviceaccount, provider "privileged": Forbidden: not usable by user or serviceaccount]
21s Normal SuccessfulCreate replicaset/mysql-operator-586f9f5d5b Created pod: mysql-operator-586f9f5d5b-hsqck
13m Normal ScalingReplicaSet deployment/mysql-operator Scaled up replica set mysql-operator-586f9f5d5b to 1
$ oc get pod
NAME READY STATUS RESTARTS AGE
mysql-operator-586f9f5d5b-hsqck 1/1 Running 0 82s
InnoDBクラスターインストール
Operatorが立ち上がったので、続いてInnoDBクラスターをインストールします。ここでは公式ガイドに従い、3個のInnoDB Podと1個のMySQL Router Podを起動します。
クラスター用Projectの作成
InnoDBクラスターは、mysql Projectに作成することにしたので、ここでそのProjectを作成します。この後、管理者パスワード用のSecretや、InnoDBClusterリソースをこのProjectに作成します。
$ oc new-project mysql
Now using project "mysql" on server "https://api.crc.testing:6443".
You can add applications to this project with the 'new-app' command. For example, try:
oc new-app rails-postgresql-example
to build a new example application in Ruby. Or use kubectl to deploy a simple Kubernetes application:
kubectl create deployment hello-node --image=k8s.gcr.io/e2e-test-images/agnhost:2.33 -- /agnhost serve-hostname
Secretの作成
まず、管理者用のパスワードをSecretに指定します。ガイドの通り管理ユーザをroot、パスワードをsakilaとしていますが、少なくともパスワードは変更すべきです。
$ oc create secret generic mypwds --from-literal=rootUser=root --from-literal=rootHost=% --from-literal=rootPassword="sakila"
secret/mypwds created
クラスターPodとrouter Podのインストール
InnoDBクラスターPodは、InnoDBClusterリソースの作成によりインストールできます。yaml定義は公式ガイドで提供されているものをそのまま使用し、InnoDB Pod3個、MySQL Router Pod1個含み、管理ユーザを先ほど作成したSecretの内容としたmycluster
クラスターが作成されます。
ガイドでは一旦ファイルmycluster.yaml
に保存した後、そのファイルをoc apply
コマンドに渡していますが、ここでは標準入力を使います。-fオプションのパラメータとしてハイフン '-'を指定することで、入力は標準入力となります。ガイドのyamlをコピー&ペーストして、Ctrl-Dで入力を終了します。
$ oc apply -f -
apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
name: mycluster
spec:
secretName: mypwds
tlsUseSelfSigned: true
instances: 3
router:
instances: 1
^D
innodbcluster.mysql.oracle.com/mycluster created
これで、InnoDB PodのStatefulSet。MySQL RouterのDeploymentとReplicaSetおよびそれらのServiceが作成されますが、ここでもPodが立ち上がる気配はありません。MySQL Operatorのインストールと同様にEventを確認します。
$ oc get events --field-selector "type!=Normal"
LAST SEEN TYPE REASON OBJECT MESSAGE
1s Warning FailedCreate statefulset/mycluster create Pod mycluster-0 in StatefulSet mycluster failed error: pods "mycluster-0" is forbidden: unable to validate against any security context constraint: [spec.initContainers[2].securityContext.capabilities.add: Invalid value: "DAC_OVERRIDE": capability may not be added, spec.initContainers[2].securityContext.capabilities.add: Invalid value: "SETGID": capability may not be added, spec.initContainers[2].securityContext.capabilities.add: Invalid value: "SETUID": capability may not be added, spec.initContainers[2].securityContext.capabilities.add: Invalid value: "SYS_NICE": capability may not be added, spec.initContainers[2].securityContext.capabilities.add: Invalid value: "SYS_RESOURCE": capability may not be added, spec.containers[1].securityContext.capabilities.add: Invalid value: "DAC_OVERRIDE": capability may not be added, spec.containers[1].securityContext.capabilities.add: Invalid value: "SETGID": capability may not be added, spec.containers[1].securityContext.capabilities.add: Invalid value: "SETUID": capability may not be added, spec.containers[1].securityContext.capabilities.add: Invalid value: "SYS_NICE": capability may not be added, spec.containers[1].securityContext.capabilities.add: Invalid value: "SYS_RESOURCE": capability may not be added, provider restricted: .spec.securityContext.fsGroup: Invalid value: []int64{27}: 27 is not an allowed group, spec.initContainers[0].securityContext.runAsUser: Invalid value: 0: must be in the ranges: [1000680000, 1000689999], spec.initContainers[1].securityContext.runAsUser: Invalid value: 27: must be in the ranges: [1000680000, 1000689999], spec.initContainers[2].securityContext.runAsUser: Invalid value: 27: must be in the ranges: [1000680000, 1000689999], spec.containers[0].securityContext.runAsUser: Invalid value: 27: must be in the ranges: [1000680000, 1000689999], spec.containers[1].securityContext.runAsUser: Invalid value: 27: must be in the ranges: [1000680000, 1000689999], provider "nonroot": Forbidden: not usable by user or serviceaccount, provider "hostmount-anyuid": Forbidden: not usable by user or serviceaccount, provider "machine-api-termination-handler": Forbidden: not usable by user or serviceaccount, provider "hostnetwork": Forbidden: not usable by user or serviceaccount, provider "hostaccess": Forbidden: not usable by user or serviceaccount, provider "privileged": Forbidden: not usable by user or serviceaccount]
Operatorと同様にSCCの問題がStatefulSet mycluster
にあることがわかります。使用されているServiceAccountが存在することを確認し、SCCを付与します。ここでは、privileged SCCが必要となります。
$ oc get serviceaccount
NAME SECRETS AGE
builder 2 25m
default 2 25m
deployer 2 25m
mycluster-sidecar-sa 2 21m
$ oc get statefulset mycluster -o custom-columns=NAME:metadata.name,ACCOUNT:spec.template.spec.serviceAccountName
NAME ACCOUNT
mycluster mycluster-sidecar-sa
$ oc adm policy add-scc-to-user privileged -z mycluster-sidecar-sa
clusterrole.rbac.authorization.k8s.io/system:openshift:scc:anyuid added: "mycluster-sidecar-sa"
これで、Podが起動します。
InnoDB Podは、デフォルトで100GBのRWO PVを使用します。つまり100GBのPVが合計3個必要となります。ここで使用しているCodeReady Containersでは、開発用途のPVがあらかじめ用意されているため特に手当は必要ありませんが、実際の環境では別途ストレージが必要となります。Podが使用するPVCがPVとbindできないとPodはPendingのままとなります。
$ oc get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
datadir-mycluster-0 Bound pv0019 100Gi RWO,ROX,RWX 22m
datadir-mycluster-1 Bound pv0012 100Gi RWO,ROX,RWX 21m
datadir-mycluster-2 Bound pv0010 100Gi RWO,ROX,RWX 21m
最初は、InnoDB Podが3個。これが正常に起動した後、MySQL RouterのPodが1個上がります。InnoDB Podは、READINESS GATEを持っているので、これが2/2
になると正常に稼働していることがわかります。その確認のためには、oc get pod
コマンドに-o wide
オプションが必要です。
$ oc get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mycluster-0 2/2 Running 0 99m 10.217.0.165 crc-m5qn9-master-0 <none> 2/2
mycluster-1 2/2 Running 0 99m 10.217.0.166 crc-m5qn9-master-0 <none> 2/2
mycluster-2 2/2 Running 0 99m 10.217.0.167 crc-m5qn9-master-0 <none> 2/2
mycluster-router-8498dd4468-6lx6m 1/1 Running 0 97m 10.217.0.169 crc-m5qn9-master-0 <none> <none>
$ oc get statefulset mycluster -o wide
NAME READY AGE CONTAINERS IMAGES
mycluster 3/3 100m sidecar,mysql mysql/mysql-operator:8.0.29-2.0.4,mysql/mysql-server:8.0.29
これで、MySQL OperatorおよびOperatorが制御するInnoDBクラスターがインストールできました。
稼働確認
クラスターが立ち上がったので、MySQLが機能していることを確認しましょう。
クラスターの状態
インストールの中で、InnoDB Podが3個RUNNING
であり、READY=2/2
かつREADINESS GATE=2/2
となりました。Podとしての正常性はこれで確認できるので、MySQL内部の状態を調べます。
クラスターの状態確認は、mysqlshコマンドを使用します。公式のガイドでは、mysqlsh用のPodを個別に立ち上げています。ガイドの中にも記載されている通りMySQLコンテナの中にもmysqlshはインストールされているので、ここではそれを使用しましょう。
InnoDB Podでmysqlshを実行して、クラスターのstatus()
出力を確認します。InnoDB Podには、MySQLのコンテナとsidecarコンテナが稼働しているため、MySQLコンテナを指定する必要がある点に注意してください。まず、mycluster-0 Podのmysqlコンテナでmysqlshを実行します。
$ oc get pod
NAME READY STATUS RESTARTS AGE
mycluster-0 2/2 Running 0 32m
mycluster-1 2/2 Running 0 32m
mycluster-2 2/2 Running 0 32m
mycluster-router-8498dd4468-6lx6m 1/1 Running 0 29m
$ oc exec -it mycluster-0 -c mysql -- mysqlsh
Cannot set LC_ALL to locale en_US.UTF-8: No such file or directory
MySQL Shell 8.0.29
Copyright (c) 2016, 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 '\?' for help; '\quit' to exit.
MySQL JS >
クラスターに管理者IDで接続し、dba.getCluster().status()
を実行します。ユーザrootのパスワードは、InnoDBクラスターのインストールで作成したSecretに記載したものを使用します。
MySQL JS > \c root@mycluster
Creating a session to 'root@mycluster'
Please provide the password for 'root@mycluster': ******
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 4555 (X protocol)
Server version: 8.0.29 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.
MySQL mycluster:33060+ ssl JS > dba.getCluster().status()
{
"clusterName": "mycluster",
"defaultReplicaSet": {
"name": "default",
"primary": "mycluster-0.mycluster-instances.mysql.svc.cluster.local:3306",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"mycluster-0.mycluster-instances.mysql.svc.cluster.local:3306": {
"address": "mycluster-0.mycluster-instances.mysql.svc.cluster.local:3306",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.29"
},
"mycluster-1.mycluster-instances.mysql.svc.cluster.local:3306": {
"address": "mycluster-1.mycluster-instances.mysql.svc.cluster.local:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.29"
},
"mycluster-2.mycluster-instances.mysql.svc.cluster.local:3306": {
"address": "mycluster-2.mycluster-instances.mysql.svc.cluster.local:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.29"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "mycluster-0.mycluster-instances.mysql.svc.cluster.local:3306"
}
MySQL mycluster:33060+ ssl JS > \q
Bye!
bash-4.4$ exit
exit
クラスターの中では、各InnoDB Podはインスタンスと認識されています。StatefulSetであるPodは、pod DNSを介して<pod名>.<headless service名>.<namespace名>.svc.cluster.local
として識別されます。
この出力から、
- クラスター全体のstatusがOKであること
- クラスターには3つのインスタンスが登録され、それぞれONLINEであること
- mycluster-0 PodがPRIMARYロールを持ちR/Wモードで稼働していること
- それ以外のPodはSECONDARYロールを持ちR/Oモードで稼働していること
などがわかります。
MySQL DBへのアクセス
Podの状態とmysqlshを使用した確認では、外形的にMySQLが正常動作しているように見えました。ここでは実際にDBへのアクセスを行い、DBとしての機能が正常であることを確認しましょう。
ガイドでは、3306ポートをforwardして、OpenShiftの外部からDBにアクセスする方法が紹介されていましたが、ここではMySQL Router Podのmysqlコマンドを使用します。
$ oc get pod
NAME READY STATUS RESTARTS AGE
mycluster-0 2/2 Running 0 52m
mycluster-1 2/2 Running 0 52m
mycluster-2 2/2 Running 0 52m
mycluster-router-8498dd4468-6lx6m 1/1 Running 0 49m
$ oc exec -it mycluster-router-8498dd4468-6lx6m -- /bin/bash
bash-4.4$ mysql -uroot -p
Enter password: ******
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 15077
Server version: 8.0.29 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>
performance_schema DBでは、InnoDBクラスター関連のデータを管理しているので、そこからインスタンスメンバーについて調べてみましょう。
mysql> show databases;
+-------------------------------+
| Database |
+-------------------------------+
| information_schema |
| mysql |
| mysql_innodb_cluster_metadata |
| performance_schema |
| sys |
+-------------------------------+
5 rows in set (0.00 sec)
mysql> use performance_schema;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select MEMBER_HOST,MEMBER_STATE,MEMBER_ROLE from replication_group_members;
+---------------------------------------------------------+--------------+-------------+
| MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE |
+---------------------------------------------------------+--------------+-------------+
| mycluster-1.mycluster-instances.mysql.svc.cluster.local | ONLINE | SECONDARY |
| mycluster-0.mycluster-instances.mysql.svc.cluster.local | ONLINE | PRIMARY |
| mycluster-2.mycluster-instances.mysql.svc.cluster.local | ONLINE | SECONDARY |
+---------------------------------------------------------+--------------+-------------+
3 rows in set (0.00 sec)
mysql> quit
Bye
DBの内容が表示され、DBが正常に機能していることがわかりました。
おわりに
MySQLの高可用性オプションとして提供されているInnoDBクラスターをOpenShift上に構築し、健全性の確認まで行いました。コンテナを使用することで、複数のInnoDBインスタンスの作成が一度に完了し、Operatorで自動管理されます。ここでは取り上げませんでしたが、InnoDB Podの配置を適切にノード上に分散させれば、仮想サーバで組んだInnoDBクラスターと同等の耐障害性も得られます。
Kubernetes上では主にstatelessなアプリケーションを中心に稼働が進んでいますが、こうしたコンテナ稼働を意識したDatabaseが普及して、statefulなアプリケーションでも利用が広がることを期待しています。