OSSとして公開されているOracle MySQL Operator for Kubernetesを利用して、Kubernetesクラスタ上にWordPressとMySQL InnoDBクラスタ環境を構築する過程を辿りたいと思います。
Oracle MySQL Operator for Kubernetes
Oracle MySQL Operator for Kubernetesは、MySQLの高可用構成を実現するInnoDBクラスタをKubernetes上に構築、管理を容易化するオペレーターです。
OracleがOSSとして開発、ソースコードはGitHubで公開されています。
OPERATOR CAPABILITY LEVELSのLevel3です。(2021年12月)
2021年12月時点での、主な機能は以下となります。
- MySQL Server/Routerの自動デプロイと管理
- セルフヒーリング
- バックアップ
- MySQL Server/Routerのスケールアウト/イン
- ローリングアップデート
MySQL InnoDB Cluster
MySQL InnoDB Clusterは、グループレプリケーション、MySQL Router、MySQL Shell の3つの技術要素を組み合わせて実現するMySQLサーバの高可用性構成です。
MySQL InnoDB Clusterには、シングルプライマリ型とマルチプライマリ型があります。Oracle MySQL Operator for Kubernetesでは、シングルプライマリのみ対応しています。(2021年12月)
シングルプライマリ型での高可用性は、以下のように機能します。
WordPress + Oracle MySQL Operator (Scale Out/In) on Kubernetes
実際に、Kubernetesクラスタ上に、WordPressとOracle MySQL Operatorの環境を構築して、InnoDBクラスタやWordPressのスケールイン・アウトを実行してみたいと思います。
環境としては、3ノードの1Kubernetesクラスタ上に、WordPressとOracle MySQL Operatorを構築します。
環境構築の流れ
- Installation of the MySQL Operator
- Using the MySQL Operator to setup a MySQL InnoDB Cluster
- Connecting to the MYSQL InnoDB Cluster
- Installation of WordPress
- Connect to MySQL
- MySQL Scale Out/In
- WordPress Scale
環境
Public Cloud: Oracle Cloud Infrastructure
Kubernetes : Oracle Container Engine for Kubernetes (OKE)
- Cluster:1
- Node:3
- Shape:VM.Standard.E3.Flex
- OCPU:1 x 3
- MEM:16Gi x 3
Installation of the MySQL Operator
Namespace mysql-operator
を作成します。
kubectl create ns mysql-operator
コマンド結果
namespace/mysql-operator created
MySQL Operator をインストールします。
kubectl 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
kubectl apply -f https://raw.githubusercontent.com/mysql/mysql-operator/trunk/deploy/deploy-operator.yaml
コマンド結果
serviceaccount/mysql-sidecar-sa created
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: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
namespace/mysql-operator configured
serviceaccount/mysql-operator-sa created
deployment.apps/mysql-operator created
Namespace mysql-operator
に MySQL Operator がインストールされたことを確認します。
kubectl get deployment -n mysql-operator mysql-operator
コマンド結果
NAME READY UP-TO-DATE AVAILABLE AGE
mysql-operator 1/1 1 1 30s
Using the MySQL Operator to setup a MySQL InnoDB Cluster
InnoDBクラスタを作成する上で必要となる、MySQLのrootユーザ情報(パスワード)をSecretに登録します。
kubectl create secret generic mypwds --from-literal=rootPassword="mysqlp@ssword"
コマンド結果
secret/mypwds created
サンプルクラスタのマニフェストを作成します。このサンプルクラスタは、3つのMySQLサーバーインスタンスと1つのMySQLルーターインスタンスを持つInnoDBクラスタです。
vim sample-cluster.yaml
apiVersion: mysql.oracle.com/v2alpha1
kind: InnoDBCluster
metadata:
name: mycluster
spec:
secretName: mypwds
instances: 3
version: "8.0.25"
router:
instances: 1
version: "8.0.25"
マニフェストを適用します。
kubectl apply -f sample-cluster.yaml
コマンド結果
innodbcluster.mysql.oracle.com/mycluster created
InnoDBクラスタの状況を確認します。
kubectl get innodbcluster --watch
NAME STATUS ONLINE INSTANCES ROUTERS AGE
・
・
・
mycluster ONLINE 3 3 1 7m7s
Connecting to the MYSQL InnoDB Cluster
InnoDBクラスタに接続する、KubernetesクラスタのServiceを確認します。後のWordPressと連携する際に必要となります。
kubectl get service mycluster
コマンド結果
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mycluster ClusterIP 10.96.201.29 <none> 6446/TCP,6448/TCP,6447/TCP,6449/TCP 8m13s
エクスポートされたポートは、MySQLプロトコルおよびXプロトコルの読み取り/書き込み、読み取り専用ポートです。
kubectl describe service mycluster
コマンド結果
Name: mycluster
Namespace: default
Labels: mysql.oracle.com/cluster=mycluster
tier=mysql
Annotations: <none>
Selector: component=mysqlrouter,mysql.oracle.com/cluster=mycluster,tier=mysql
Type: ClusterIP
IP Families: <none>
IP: 10.43.203.248
IPs: <none>
Port: mysql 6446/TCP
TargetPort: 6446/TCP
Endpoints: <none>
Port: mysqlx 6448/TCP
TargetPort: 6448/TCP
Endpoints: <none>
Port: mysql-ro 6447/TCP
TargetPort: 6447/TCP
Endpoints: <none>
Port: mysqlx-ro 6449/TCP
TargetPort: 6449/TCP
Endpoints: <none>
Session Affinity: None
Events: <none>
Installation of WordPress
WordPress環境を構築します。
NFS Server
NFSサーバを構築します。最初にPersistentVolumeClaimを作成します。
vim nfs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
storageClassName: "oci-bv"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
マニフェストを適用します。
kubectl apply -f nfs-pvc.yaml
コマンド結果
persistentvolumeclaim/nfs-pvc created
NFSサーバを作成します。
vim nfs-server.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-server
spec:
replicas: 1
selector:
matchLabels:
role: nfs-server
template:
metadata:
labels:
role: nfs-server
spec:
containers:
- name: nfs-server
image: gcr.io/google_containers/volume-nfs:0.8
ports:
- name: nfs
containerPort: 2049
- name: mountd
containerPort: 20048
- name: rpcbind
containerPort: 111
securityContext:
privileged: true
volumeMounts:
- mountPath: /exports
name: nfs-local-storage
volumes:
- name: nfs-local-storage
persistentVolumeClaim:
claimName: nfs-pvc
マニフェストを適用します。
kubectl apply -f nfs-server.yaml
コマンド結果
deployment.apps/nfs-server created
WordPressのPersistentVolumeから指定するNFSのServiceを作成します。
vim nfs-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nfs-service
spec:
ports:
- name: nfs
port: 2049
- name: mountd
port: 20048
- name: rpcbind
port: 111
selector:
role: nfs-server
マニフェストを適用します。
kubectl apply -f nfs-service.yaml
コマンド結果
service/nfs-service created
WordPressのPersistentVolumeから指定するNFSのServiceのClusterIPを確認します。
kubectl get services
コマンド結果
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 16m
mycluster ClusterIP 10.96.198.46 <none> 6446/TCP,6448/TCP,6447/TCP,6449/TCP 9m33s
mycluster-instances ClusterIP None <none> 3306/TCP,33060/TCP,33061/TCP 9m33s
nfs-service ClusterIP 10.96.189.130 <none> 2049/TCP,20048/TCP,111/TCP 14s
WordPress
WordPressのPersistentVolumeを作成します。
vim wordpress-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: wordpress-pv
labels:
type: local
spec:
persistentVolumeReclaimPolicy: Delete
storageClassName: wordpress
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
nfs:
server: xx.xx.xx.xx #「nfs-service」のCLUSTER-IPを定義
path: /
マニフェストを適用します。
kubectl apply -f wordpress-pv.yaml
コマンド結果
persistentvolume/wordpress-pv created
WordPressのPersistentVolumeClaimを作成します。
vim wordpress-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wordpress-pvc
labels:
app: wordpress
tier: wordpress
spec:
storageClassName: wordpress
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
マニフェストを適用します。
kubectl apply -f wordpress-pvc.yaml
コマンド結果
persistentvolumeclaim/wordpress-pvc created
WordPressのPersistentVolumeとPersistentVolumeClaimが連携していることを確認します。
kubectl get persistentvolumes,persistentvolumeclaims
コマンド結果
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/csi-343c6a85-ceac-41ad-9ec3-5a9c66c16be2 50Gi RWO Delete Bound default/nfs-pvc oci-bv 116s
persistentvolume/ocid1.volume.oc1.ap-osaka-1.abvwsljr4ub2x5hdai6hrobi2c742b7cogbzttaln2cv7tvvn6efcd4zukga 50Gi RWO Delete Bound default/datadir-mycluster-2 oci 6m
persistentvolume/ocid1.volume.oc1.ap-osaka-1.abvwsljr7zgyejcsd3gg5vbzlzgpr72nkxn2x4qtwax5lj4tskpsnnstnm2q 50Gi RWO Delete Bound default/datadir-mycluster-0 oci 10m
persistentvolume/ocid1.volume.oc1.ap-osaka-1.abvwsljreozgjd5kz4zei24w47nee4raeef33e7o56auoqilcificgvugtqq 50Gi RWO Delete Bound default/datadir-mycluster-1 oci 8m19s
persistentvolume/wordpress-pv 50Gi RWX Delete Bound default/wordpress-pvc wordpress 26s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/datadir-mycluster-0 Bound ocid1.volume.oc1.ap-osaka-1.abvwsljr7zgyejcsd3gg5vbzlzgpr72nkxn2x4qtwax5lj4tskpsnnstnm2q 50Gi RWO oci 10m
persistentvolumeclaim/datadir-mycluster-1 Bound ocid1.volume.oc1.ap-osaka-1.abvwsljreozgjd5kz4zei24w47nee4raeef33e7o56auoqilcificgvugtqq 50Gi RWO oci 8m30s
persistentvolumeclaim/datadir-mycluster-2 Bound ocid1.volume.oc1.ap-osaka-1.abvwsljr4ub2x5hdai6hrobi2c742b7cogbzttaln2cv7tvvn6efcd4zukga 50Gi RWO oci 6m11s
persistentvolumeclaim/nfs-pvc Bound csi-343c6a85-ceac-41ad-9ec3-5a9c66c16be2 50Gi RWO oci-bv 2m30s
persistentvolumeclaim/wordpress-pvc Bound wordpress-pv 50Gi RWX wordpress 12s
WordPressのマニフェストを作成します。
vim wordpress.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
labels:
app: wordpress
spec:
replicas: 1
selector:
matchLabels:
app: wordpress
template:
metadata:
labels:
app: wordpress
spec:
containers:
- image: wordpress:5.6.2
name: wordpress
env:
#Service名「mycluster:6446」を定義
- name: WORDPRESS_DB_HOST
value: mycluster:6446
#MySQLのデータベースパスワードを参照する定義
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mypwds
key: rootPassword
ports:
- containerPort: 80
name: wordpress
#Podのマウントパス定義
volumeMounts:
- name: wordpress-local-storage
mountPath: /var/www/html
#「wordpress-pvc」を指定する定義
volumes:
- name: wordpress-local-storage
persistentVolumeClaim:
claimName: wordpress-pvc
マニフェストを適用します。
kubectl apply -f wordpress.yaml
コマンド結果
deployment.apps/wordpress created
WordPressのPodがRunnninngであることを確認します。
kubectl get pods
コマンド結果
NAME READY STATUS RESTARTS AGE
mycluster-0 2/2 Running 0 13m
mycluster-1 2/2 Running 0 11m
mycluster-2 2/2 Running 0 8m45s
mycluster-router-gwvpr 1/1 Running 0 11m
nfs-server-788c45b6f5-chp4k 1/1 Running 0 4m47s
wordpress-598746d47b-wfffq 1/1 Running 0 31s
WordPress Serviceのマニフェストを作成します。
vim wordpress-service.yaml
apiVersion: v1
kind: Service
metadata:
name: wordpress-service
labels:
app: wordpress
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: wordpress
マニフェストを適用します。
kubectl apply -f wordpress-service.yaml
コマンド結果
service/wordpress-service created
EXTERNAL-IPが表示されます。実際にブラウザでアクセスすると、WordPressのセットアップ画面が表示されます。
kubectl get services
コマンド結果
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 21m
mycluster ClusterIP 10.96.198.46 <none> 6446/TCP,6448/TCP,6447/TCP,6449/TCP 14m
mycluster-instances ClusterIP None <none> 3306/TCP,33060/TCP,33061/TCP 14m
nfs-service ClusterIP 10.96.189.130 <none> 2049/TCP,20048/TCP,111/TCP 5m11s
wordpress-service LoadBalancer 10.96.93.51 140.xx.xx.xx 80:32606/TCP 31s
Connect to MySQL
1.MySQL Operator Pod に接続してMySQL Shellを実行する場合
MySQL Operator Pod からMySQL Shellを利用して mycluster-0 に接続します。
パスワードが要求されるので、MySQLのSecretに設定したパスワード( mysqlp@ssword )を入力します。
接続後、抜ける場合は、「\q」を実行します。
kubectl exec -it -n mysql-operator mysql-operator-xxxxxxxxxx-xxxxx -- mysqlsh root@mycluster-0.mycluster-instances.default.svc.cluster.local
Cannot set LC_ALL to locale en_US.UTF-8: No such file or directory
Please provide the password for 'root@mycluster-0.mycluster-instances.default.svc.cluster.local': *************
MySQL Shell 8.0.26
Copyright (c) 2016, 2021, 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.
Creating a session to 'root@mycluster-0.mycluster-instances.default.svc.cluster.local'
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 2115 (X protocol)
Server version: 8.0.25 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.
MySQL mycluster-0.mycluster-instances.default.svc.cluster.local:33060+ ssl JS > \q
Bye!
2. mycluster-0 に接続する場合
パスワードが要求されるので、MySQLのSecretに設定したパスワード( mysqlp@ssword )を入力します。
接続後、抜ける場合は、「\q」を実行します。
kubectl exec -it mycluster-0 -- mysqlsh --mysql localroot@localhost
Defaulting container name to sidecar.
Use 'kubectl describe pod/mycluster-0 -n default' to see all of the containers in this pod.
Cannot set LC_ALL to locale en_US.UTF-8: No such file or directory
Please provide the password for 'localroot@localhost': *************
MySQL Shell 8.0.26
Copyright (c) 2016, 2021, 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.
Creating a Classic session to 'localroot@localhost'
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 2262
Server version: 8.0.25 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.
MySQL localhost JS > \q
Bye!
MySQL Scale Out/In
Scale Out
sample-cluster.yaml のinstancesを以下に変更
instances: 3 ⇒ 5
instances: 1 ⇒ 2
vim sample-cluster.yaml
apiVersion: mysql.oracle.com/v2alpha1
kind: InnoDBCluster
metadata:
name: mycluster
spec:
secretName: mypwds
instances: 5
version: "8.0.25"
router:
instances: 2
version: "8.0.25"
kubectl apply -f sample-cluster.yaml
コマンド結果
innodbcluster.mysql.oracle.com/mycluster configured
InnoDBクラスタの状況を確認します。
kubectl get innodbcluster --watch
NAME STATUS ONLINE INSTANCES ROUTERS AGE
・
・
・
mycluster ONLINE 5 5 2 55m
Podも確認するとmyclusterが5、mycluster-routerが2となっていることを確認できます。
kubectl get pods
コマンド結果
NAME READY STATUS RESTARTS AGE
mycluster-0 2/2 Running 0 25m
mycluster-1 2/2 Running 0 22m
mycluster-2 2/2 Running 0 20m
mycluster-3 2/2 Running 0 4m51s
mycluster-4 2/2 Running 0 2m41s
mycluster-router-6j2t2 1/1 Running 0 4m51s
mycluster-router-gwvpr 1/1 Running 0 22m
nfs-server-788c45b6f5-chp4k 1/1 Running 0 16m
wordpress-598746d47b-wfffq 1/1 Running 0 12m
OCIコンソールで、Block Volumeが追加されていることも確認できます。
WordPressで使用しているNFSのものを含めて、6 Block Volumeとなります。
Scale In
sample-cluster.yaml のinstancesを以下に変更
instances: 3
instances: 1
vim sample-cluster.yaml
apiVersion: mysql.oracle.com/v2alpha1
kind: InnoDBCluster
metadata:
name: mycluster
spec:
secretName: mypwds
instances: 3
version: "8.0.25"
router:
instances: 1
version: "8.0.25"
kubectl apply -f sample-cluster.yaml
コマンド結果
innodbcluster.mysql.oracle.com/mycluster configured
kubectl get innodbcluster --watch
NAME STATUS ONLINE INSTANCES ROUTERS AGE
・
・
・
mycluster ONLINE 3 3 1 60m
kubectl get pods
NAME READY STATUS RESTARTS AGE
mycluster-0 2/2 Running 0 28m
mycluster-1 2/2 Running 0 25m
mycluster-2 2/2 Running 0 23m
mycluster-router-gwvpr 1/1 Running 0 25m
nfs-server-788c45b6f5-chp4k 1/1 Running 0 19m
wordpress-598746d47b-wfffq 1/1 Running 0 14m
現時点(2021年12月)では、myclusterとmycluster-routerのPodは、指定した数に自動で戻りますが、PVC、PV、Block Volumeは自動で削除されないので、手動でPVCを削除します。(同時にPV、Block Volumeも削除されます。ReclaimPolicyでDeleteを指定しているため。)
kubectl delete pvc datadir-mycluster-4
コマンド結果
persistentvolumeclaim "datadir-mycluster-4" deleted
kubectl delete pvc datadir-mycluster-3
コマンド結果
persistentvolumeclaim "datadir-mycluster-3" deleted
OCIコンソールで、Block Volumeが終了されていることも確認できます。
WordPress Scale
WordPressのPod数を10に変更してスケールします。
kubectl scale deployment wordpress --replicas 10
コマンド結果
deployment.apps/wordpress scaled
kubectl get pods
コマンド結果
NAME READY STATUS RESTARTS AGE
mycluster-0 2/2 Running 0 31m
mycluster-1 2/2 Running 0 28m
mycluster-2 2/2 Running 0 26m
mycluster-router-gwvpr 1/1 Running 0 28m
nfs-server-788c45b6f5-chp4k 1/1 Running 0 22m
wordpress-598746d47b-2r776 1/1 Running 0 37s
wordpress-598746d47b-4rx94 1/1 Running 0 37s
wordpress-598746d47b-626x7 1/1 Running 0 37s
wordpress-598746d47b-bv2c2 1/1 Running 0 37s
wordpress-598746d47b-jd9sh 1/1 Running 0 37s
wordpress-598746d47b-l7t4d 1/1 Running 0 37s
wordpress-598746d47b-lhsws 1/1 Running 0 37s
wordpress-598746d47b-qht84 1/1 Running 0 37s
wordpress-598746d47b-vdf8s 1/1 Running 0 37s
wordpress-598746d47b-wfffq 1/1 Running 0 18m
実際にWordPressにアクセスして、初期セットアップして問題ないことを確認します。「wordpress-service」の「EXTERNAL-IP」を確認して、ブラウザでアクセスします。
kubectl get services
コマンド結果
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 64m
mycluster ClusterIP 10.96.159.155 <none> 6446/TCP,6448/TCP,6447/TCP,6449/TCP 48m
mycluster-instances ClusterIP None <none> 3306/TCP,33060/TCP,33061/TCP 48m
nfs-service ClusterIP 10.96.49.232 <none> 2049/TCP,20048/TCP,111/TCP 39m
wordpress-service LoadBalancer 10.96.79.114 152.xxx.xxx.xxx 80:30769/TCP 26m
次回は、疑似障害を起こした場合のInnoDBクラスタの挙動について試してみたいと思います。