MySQL InnoDB ClusterSet の、複数の Kubernetes クラスターへのデプロイは骨の折れるタスクです。MySQL Operator for Kubernetes は、単一の Kubernetes クラスター内では MySQL InnoDB Cluster を効率的に管理できますが、複数のクラスターにまたがる InnoDB ClusterSet のデプロイには、現在の MySQL Operator リリースではまだサポートされていない、クラスター間通信が必要となります。

ここで Cilium が役立ちます。Cilium とは、高度なネットワーク機能を活用し、安全で効率的なクラスター間通信を可能にする強力なソリューションです。Cilium の統合によって、ネイティブな Kubernetes のサポート、簡素化された管理、そして MySQL のようにステートフルアプリケーションでの、パフォーマンス向上の恩恵を受けることができます。

複数クラスターの Kubernetes 環境にまたがって MySQL InnoDB ClusterSet デプロイを行うと、スケーラビリティの向上、ダウンタイムの最小化、データ一貫性の確保、そしてプライマリクラスター障害時のビジネス継続性が得られます。加えて、MySQL Operator によって管理される InnoDB Cluster デプロイメントの回復力も強化します。下図は、Cilium によってクラスター間通信が強化された 2 つの Kubernetes クラスターが、それぞれプライマリクラスターとレプリカクラスターとして MySQL InnoDB ClusterSet をデプロイ構成されている状態を示しています。

Cilium を用いたマルチクラスター Kubernates 環境での InnoDB ClusterSet
Cilium を用いたマルチクラスター Kubernates 環境での InnoDB ClusterSet

複数の Kubernetes クラスターにまたがる InnoDB ClusterSet のセットアップは、現在のところは以下に示すように手動でのプロセスです。このセットアップ手順は、オンプレミスの Kubernetes クラスター、あるいは公共クラウド上のフルマネージド Kubernetes サービスで実行されている、MySQL コンテナイメージを用いた MySQL InnoDB ClusterSet に対して実施できます。CI/CD ツールを用いることで、この手順はスクリプト化、自動化できます。

Cilium クラスターメッシュのデプロイ

すでに Cilium がインストールされている場合はこの章をスキップしてください。PROD クラスターと DR クラスターを区別するためのコンテキストを取得します。

$ kubectl config get-contexts
CURRENT   NAME                 CLUSTER              AUTHINFO           NAMESPACE
          context-cabqjcrx6nq  cluster-cabqjcrx6nq  user-cabqjcrx6nq  

*         context-che34bk2roa  cluster-che34bk2roa  user-che34bk2roa

使われている環境に対応するコンテキスト名で、context-cabqjcrx6nq と context-che34bk2roa の値を置き換えてください。手順内での参照を容易にするために、PROD クラスターのkubeconfigコンテキストを CONTEXT1 に、DR クラスターを CONTEXT2 に保存しましょう。

$ export CONTEXT1=context-che34bk2roa
$ export CONTEXT2=context-cabqjcrx6nq

Helm を使用して Cilium をインストールするため、Cilium helm リポジトリを追加し、編集用のマニフェストをローカルに設定します:

$ helm repo add cilium https://helm.cilium.io/
$ helm show values cilium/cilium > prod-cilium.yaml
$ helm show values cilium/cilium > dr-cilium.yaml

prod-cilium.yaml を編集し、デフォルト値を以下のように変更します。

cluster:
   name: prod
   
id: 1
hubble:
   tls:
     enabled: false
hubble:
   relay:
     enabled: true
hubble:
   ui:
     enabled: true
ipam:
   mode: “kubernetes”

その後、Helm を用いて PROD Kubernetes クラスターに適用します:

$ helm install cilium cilium/cilium –namespace=kube-system -f prod-cilium.yaml –kube-context $CONTEXT1  

dr-cilium.yaml ファイルを prod-cilium.yaml の設定と一致するように設定しますが、クラスター名を「dr」に、クラスターIDを「2」とする点だけ変更します。そして、それを Helm を用いて DR Kubernetes クラスターに適用します。

$ helm install cilium cilium/cilium –namespace=kube-system -f dr-cilium.yaml –kube-context $CONTEXT2 

続いて各クラスターで、Cilium が管理していないすべてのポッドを削除します。

kubectl config use-context $CONTEXT1
kubectl delete pod –namespace kube-system -l k8s-app=kube-dns
kubectl delete pod –namespace kube-system -l k8s-app=hubble-relay
kubectl delete pod –namespace kube-system -l k8s-app=hubble-ui
kubectl delete pod –namespace kube-system -l k8s-app=kube-dns-autoscaler


kubectl config use-context $CONTEXT2
kubectl delete pod –namespace kube-system -l k8s-app=kube-dns
kubectl delete pod –namespace kube-system -l k8s-app=hubble-relay
kubectl delete pod –namespace kube-system -l k8s-app=hubble-ui
kubectl delete pod –namespace kube-system -l k8s-app=kube-dns-autoscaler

各クラスターで「cilium status」を実行して、Cilium のデプロイを確認します。Cilium デプロイメントが成功すると、PROD クラスターでは次のようになります:

Cilium-Cluster-Mesh-1

DR クラスターでは次のようになります:

cilium-cluster-mesh-2

この状態では、PRODクラスターでクラスターメッシュは無効になっています。有効にするには、prod-cilium.yamlファイルを以下のように変更します:

clusterMesh:
   useAPIServer: true
ciliumEndpointSlice:
   enabled: true

そして、Helm を用いて Cilium を再デプロイします。

$ helm upgrade cilium cilium/cilium –namespace=kube-system -f prod-cilium.yaml –kube-context $CONTEXT1

ここで PROD クラスターでクラスターメッシュのステータスを確認すると、有効になっています。

$ cilium clustermesh status –context $CONTEXT1  
⚠️  Service type NodePort detected! Service may fail when nodes are removed from the cluster!
Service “clustermesh-apiserver” of type “NodePort” found
Cluster access information is available:
  – 158.178.237.122:32379
Deployment clustermesh-apiserver is ready
ℹ️ KVStoreMesh is enabled
🔌 No cluster connected
🔀 Global services: [ min:0 / avg:0.0 / max:0 ]

同じ手順を DR クラスターでも繰り返します。クラスターメッシュを有効にするため、dr-cilium.yaml ファイルを prod-cilium.yaml と同様に変更し、続けて以下を実行します:

$ helm upgrade cilium cilium/cilium –namespace=kube-system -f dr-cilium.yaml –kube-context $CONTEXT2
$ cilium clustermesh status –context $CONTEXT2  

クラスター間通信を有効にするため、次のコマンドを実行し、PROD クラスターと DR クラスターを接続します:

$ cilium clustermesh connect –context $CONTEXT1 –destination-context $CONTEXT2

PROD クラスターからクラスターメッシュのステータスを確認します。成功したクラスターメッシュは次のように見えます。

$ cilium clustermesh status –context $CONTEXT1
⚠️  Service type NodePort detected! Service may fail when nodes are removed from the cluster!
Service “clustermesh-apiserver” of type “NodePort” found
Cluster access information is available:
  – 158.178.237.122:32379
Deployment clustermesh-apiserver is ready
ℹ️  KVStoreMesh is enabled
All 3 nodes are connected to all clusters [min:1 / avg:1.0 / max:1]
All 1 KVStoreMesh replicas are connected to all clusters [min:1 / avg:1.0 / max:1]
🔌 Cluster Connections:
  – dr: 3/3 configured, 3/3 connected – KVStoreMesh: 1/1 configured, 1/1 connected

同様に、DR クラスター側からクラスターメッシュのステータスを確認します。

$ cilium clustermesh status –context $CONTEXT2
⚠️  Service type NodePort detected! Service may fail when nodes are removed from the cluster!
Service “clustermesh-apiserver” of type “NodePort” found
Cluster access information is available:
  – 134.185.86.75:32379
Deployment clustermesh-apiserver is ready
ℹ️  KVStoreMesh is enabled
All 3 nodes are connected to all clusters [min:1 / avg:1.0 / max:1]
All 1 KVStoreMesh replicas are connected to all clusters [min:1 / avg:1.0 / max:1]
🔌 Cluster Connections:
  – prod: 3/3 configured, 3/3 connected – KVStoreMesh: 1/1 configured, 1/1 connected
🔀 Global services: [ min:0 / avg:0.0 / max:0 ]

MySQL InnoDB ClusterSet では、各ポッドの FQDN が各クラスターに認識される必要があります。kubectl を用いて以下のマニフェストを PROD クラスターと DR クラスターに適用し、coreDNS を NodePort として公開します。

apiVersion: v1
kind: Service
metadata:
  labels:
    k8s-app: core-dns-nodeport
    kubernetes.io/name: CoreDNS
  name: core-dns-nodeport
  namespace: kube-system
spec:
  ports:
  – name: dns
    port: 53
    protocol: UDP
    targetPort: 53
    nodePort: 30053
  – name: dns-tcp
    port: 53
    protocol: TCP
    targetPort: 53
    nodePort: 30053
  selector:
    k8s-app: kube-dns
  sessionAffinity: None
  type: NodePort

上記のマニフェストが coreDNS_nodeport.yaml として保存されたと仮定して、以下のコマンドを用いてそれを両方のクラスターに適用します:

$ kubectl apply -f coreDNS_nodeport.yaml –context=$CONTEXT1
$ kubectl apply -f coreDNS_nodeport.yaml –context=$CONTEXT2

PROD と DR で InnoDB Clusters を実行するための環境準備

もしすでに完了している場合、この章をスキップしてください。

PROD クラスターに InnoDB Cluster 用の名前空間とシークレットを作成します。ClusterSet 内の InnoDB Cluster には各々一意な名前空間名が必要です。以下では、PROD InnoDB Cluster(「PRD」クラスターと命名)の名前空間として mysql-prod を使用し、MySQL の root パスワードを root に設定しています。

$ kubectl create ns mysql-prod –context=$CONTEXT1
$ kubectl -n mysql-prod create secret generic mypwds –from-literal=rootUser=root –from-literal=rootHost=% –from-literal=rootPassword=”root” –context=$CONTEXT1

DR InnoDB Cluster(「DR」クラスターと命名)の名前空間として mysql-dr を使用し、MySQL の root パスワードを PROD InnoDB Cluster と同じ root に設定しています。

$ kubectl create ns mysql-dr –context=$CONTEXT2
$ kubectl -n mysql-dr create secret generic mypwds –from-literal=rootUser=root –from-literal=rootHost=% –from-literal=rootPassword=”root” –context=$CONTEXT2

PROD クラスターで、kube-system 名前空間の coreDNS 設定を編集して、ターゲット DNS が mysql-dr.svc.cluster.local の場合に接続トラフィックをすべての DR クラスターワーカーノードの IP アドレスとポート 30053 に転送します。

$ kubectl -n kube-system edit configmap coredns –context=$CONTEXT1

configmap-coredns-1

同様に DR クラスターで、kube-system 名前空間の coreDNS 設定を編集して、ターゲット DNS が mysql-prd.svc.cluster.local の場合に接続トラフィックをすべての PROD クラスターワーカーノードの IP アドレスとポート 30053 に転送します。

$ kubectl -n kube-system edit configmap coredns –context=$CONTEXT2

configmap-coredns-2

MySQL Operator のデプロイ

すでにインストールされている場合、この章をスキップしてください。両方のクラスターにオペレーターが使用する CRD をインストールします。

wgetを使用してマニフェストファイル: deploy-operator.yamlをダウンロードします。

デフォルトの Cilium クラスタードメインに設定するために、MYSQL_OPERATOR_K8S_CLUSTER_DOMAIN 環境変数を追加し、deploy-operator.yaml を更新します。以下を参照してください。

env:
    – name: MYSQLSH_USER_CONFIG_HOME
      value: /mysqlsh
    – name: MYSQLSH_CREDENTIAL_STORE_SAVE_PASSWORDS
      value: never
    – name: MYSQL_OPERATOR_K8S_CLUSTER_DOMAIN
      value: cluster.local

deploy-operator.yaml を各クラスターに適用します:

$ kubectl apply -f deploy-operator.yaml –context=$CONTEXT1
$ kubectl apply -f deploy-operator.yaml –context=$CONTEXT2

PROD クラスターでの、MySQL Operator のデプロイを確認します:

$ kubectl -n mysql-operator get pod –context=$CONTEXT1
NAME                              READY   STATUS    RESTARTS   AGE
mysql-operator-7f84cb7784-x2cjg   1/1     Running   0          41s

DR クラスターでの、MySQL Operator のデプロイを確認します:

$ kubectl -n mysql-operator get pod –context=$CONTEXT2
NAME                              READY   STATUS    RESTARTS   AGE
mysql-operator-79bccbddcd-wj96l   1/1     Running   0          51s

MySQL Operator での MySQL InnoDB Cluster のデプロイ

以下のように、PROD クラスターに InnoDB Cluster をデプロイするためのマニフェストファイル(PRD_cluster.yaml)を用意し、名前空間として mysql-prod を用います:

apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: prd
  namespace: mysql-prod
spec:
  secretName: mypwds
  baseServerId: 100
  tlsUseSelfSigned: true
  instances: 3
  router:
    instances: 1

同様に、DR クラスターに InnoDB Cluster をデプロイするための別のマニフェストファイル(DR_cluster.yaml)を用意し、名前空間として mysql-dr を用います:

apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: dr
  namespace: mysql-dr
spec:
  secretName: mypwds
  baseServerId: 200
  tlsUseSelfSigned: true
  instances: 3
  router:
    instances: 1

InnoDB ClusterSet では、各インスタンスに一意な server_id が必要です。したがって、YAML 設定内の baseServerId パラメータは各クラスターで異なる必要があります。以下のコマンドを使用して、双方の YAML を各クラスターに適用します:

$ kubectl apply -f PRD_cluster.yaml –context=$CONTEXT1
$ kubectl apply -f DR_cluster.yaml –context=$CONTEXT2

約 2~3 分待ち、以下のコマンドを用いて PROD クラスターでの InnoDB Cluster のデプロイ状態を確認します。

$ kubectl -n mysql-prod get ic –context=$CONTEXT1
NAME   STATUS   ONLINE   INSTANCES   ROUTERS   AGE
prd    ONLINE   3        3           1         3m40s

以下のコマンドを用いて、DR クラスターでの InnoDB Cluster のデプロイ状態を確認します。

$ kubectl -n mysql-dr get ic –context=$CONTEXT2
NAME   STATUS   ONLINE   INSTANCES   ROUTERS   AGE
dr     ONLINE   3        3           1         3m40s

双方の InnoDB Cluster は、MySQL インスタンス用に 3 つのポッドと、MySQL Router 用に 1 つのポッドで実行されています。以下のコマンドを用いて、MySQL Shell で InnoDB Cluster のステータスを確認してみます:

$ kubectl -n mysql-prod –-context=$CONTEXT1 exec -it prd-0 –- mysqlsh root:root@localhost:3306 –- cluster status

ic-k8s-1

$ kubectl -n mysql-dr –context=$CONTEXT2 exec -it dr-0 — mysqlsh root:root@localhost:3306 — cluster status

ic-k8s-2

両方の InnoDB Cluster が、正常な状態で実行されています。

クラスター間通信のためには、MySQL ポッドでクラスター間サービスルーティング設定(global service affinity)を有効にする必要があります。PROD クラスターで有効にするには、以下のコマンドを用います。

$ kubectl -n mysql-prod annotate svc prd-instances service.cilium.io/affinity=”remote” –context=$CONTEXT1
$ kubectl -n mysql-prod annotate svc prd-instances service.cilium.io/global=”true” –context=$CONTEXT1
$ kubectl -n mysql-prod annotate svc prd-instances service.cilium.io/global-sync-endpoint-slices=”true” –context=$CONTEXT1
$ kubectl -n mysql-prod annotate svc prd-instances service.cilium.io/shared=”true” –context=$CONTEXT1

次に、DR クラスターで実行されている MySQL ポッドでもクラスター間サービスルーティング設定を有効にするには、以下のコマンドを使用します。

$ kubectl -n mysql-dr annotate svc dr-instances service.cilium.io/affinity=”remote” –context=$CONTEXT2
$ kubectl -n mysql-dr annotate svc dr-instances service.cilium.io/global=”true” –context=$CONTEXT2
$ kubectl -n mysql-dr annotate svc dr-instances service.cilium.io/global-sync-endpoint-slices=”true” –context=$CONTEXT2
$ kubectl -n mysql-dr annotate svc dr-instances service.cilium.io/shared=”true” –context=$CONTEXT2

FQDN を用いて、PROD クラスターの MySQL ポッドから DR クラスターの MySQL ポッドへのログインをテストしてみましょう:

$ kubectl -n mysql-prod –context=$CONTEXT1 exec -it prd-0 — mysqlsh root:root@dr-0.dr-instances.mysql-dr.svc.cluster.local:3306

同様に、FQDN を用いて DR クラスターの MySQL ポッドから PROD クラスターの MySQL ポッドへのログインを試みます:

$ kubectl -n mysql-dr –context=$CONTEXT2 exec -it dr-0 — mysqlsh root:root@prd-0.prd-instances.mysql-prod.svc.cluster.local:3306

PROD と DR、双方のクラスターにある InnoDB Cluster は、InnoDB ClusterSet を設定するのに必要な FQDN を通じてリモートの Kubernetes クラスターからアクセスできます。これは、リモート FQDN トラフィックを NodePort として公開されているリモートの coreDNS に転送するために、coreDNS を編集したためです。

PROD クラスターでの InnoDB ClusterSet の設定

以下のコマンドを用いて、PROD クラスターに InnoDB ClusterSet を作成し、「mycs」と名付けます。

$ kubectl –context=$CONTEXT1 -n mysql-prod exec -it prd-0 — mysqlsh root:root@localhost:3306 — cluster createClusterSet mycs

クラスター・メタデータの変更を適用するために、PROD クラスターで MySQL Router を再デプロイします:

$ kubectl –context=$CONTEXT1 -n mysql-prod rollout restart deployment prd-router

PROD クラスターで InnoDB ClusterSet の状態を確認すると、メンバーとして表示される InnoDB Cluster は 1 つのみです。

$ kubectl –context=$CONTEXT1 -n mysql-prod exec -it prd-0 — mysqlsh root:root@localhost:3306 — clusterset status

icset-1

DR クラスターから InnoDB ClusterSet へのレプリカクラスターの追加

まず、DR クラスターの既存の InnoDB Cluster を解散し、DR クラスターのプライマリノードをレプリカクラスターの最初のメンバーとして用いて、「myreplica」という名前のレプリカクラスターを作成します。

DR クラスターを解散します:

$ kubectl –context=$CONTEXT2 -n mysql-dr exec -it dr-0 — mysqlsh root:root@localhost:3306 — cluster dissolve

レプリカクラスターを作成します:

$ kubectl –context=$CONTEXT1 -n mysql-prod exec -it prd-0 — mysqlsh root:root@localhost:3306 — clusterset createReplicaCluster root:root@dr-0.dr-instances.mysql-dr.svc.cluster.local:3306 myreplica –recoveryMethod=clone

PROD クラスターで InnoDB ClusterSet の状態を確認すると、今度は ClusterSet には 2 つの InnoDB Cluster が確認できます。

$ kubectl –context=$CONTEXT1 -n mysql-prod exec -it prd-0 — mysqlsh root:root@localhost:3306 — clusterset status

icset-k8s-2

続いて、PROD クラスターの Kubernetes シークレットに保存されているバックアップパスワード、クラスター管理者パスワード、およびルーターパスワードをエクスポートし、それらを DR クラスターに適用します。

PROD クラスターから InnoDB Cluster のユーザーパスワードをエクスポートします:

$ export routerPassword=`kubectl –context=$CONTEXT1 -n mysql-prod get secret prd-router -o json | jq -r ‘.data[“routerPassword”]’`

$ export clusterAdminPassword=`kubectl –context=$CONTEXT1 -n mysql-prod get secret prd-privsecrets -o json | jq -r ‘.data[“clusterAdminPassword”]’`

$ export backupPassword=`kubectl –context=$CONTEXT1 -n mysql-prod get secret prd-backup -o json | jq -r ‘.data[“backupPassword”]’`

InnoDB Cluster のユーザーパスワードを DR クラスターにインポートします:

$ kubectl –context=$CONTEXT2 -n mysql-dr get secret dr-backup -o json | jq –arg x `echo $backupPassword` ‘.data[“backupPassword”]=$x’ | kubectl –context=$CONTEXT2 apply -f –

$ kubectl –context=$CONTEXT2 -n mysql-dr get secret dr-privsecrets -o json | jq –arg x `echo $clusterAdminPassword` ‘.data[“clusterAdminPassword”]=$x’ | kubectl –context=$CONTEXT2 apply -f –

$ kubectl –context=$CONTEXT2 -n mysql-dr get secret dr-router -o json | jq –arg x `echo $routerPassword` ‘.data[“routerPassword”]=$x’ | kubectl –context=$CONTEXT2 apply -f –

第三に、DR クラスターのレプリカクラスターにすべてのセカンダリノードを戻します:

$ kubectl –context=$CONTEXT2 -n mysql-dr exec -it dr-0 — mysqlsh root:root@localhost:3306 — cluster add-instance root:root@dr-1.dr-instances.mysql-dr.svc.cluster.local:3306 –recoveryMethod=clone

$ kubectl –context=$CONTEXT2 -n mysql-dr exec -it dr-0 — mysqlsh root:root@localhost:3306 — cluster add-instance root:root@dr-2.dr-instances.mysql-dr.svc.cluster.local:3306 –recoveryMethod=clone

クラスター・メタデータの変更を適用するために、DR クラスターで MySQL Router を再デプロイします:

$ kubectl –context=$CONTEXT2 -n mysql-dr rollout restart deployment dr-router

まとめ

おめでとうございます!これで複数の Kubernetes クラスターにまたがる MySQL InnoDB ClusterSet が正常にデプロイされました。Cilium をデプロイすることで、2 つ以上の Kubernetes クラスターをシームレスに接続し、それらを同じ Kubernetes クラスターの一部であるかのように機能させられるようになりました。これにより、ある Kubernetes クラスターで実行されている InnoDB Cluster が、リモートの Kubernetes クラスターにある別の InnoDB Cluster を検出し、接続できるようになります。それにより、複数の Kubernetes クラスターにまたがる 2 つ以上の InnoDB Cluster 間で、データを複製するための InnoDB ClusterSet を設定できるようになりました。

(訳者註: 原記事は2025年3月10日に公開されました。)