2025年7月4日金曜日

MySQL8.4.5でInnoDBClusterを構築してみた

MySQL8.4.5でInnoDBClusterを構築してみた

概要

前回 MySQL8.4.5 で簡単なレプリケーション構成を組んでみました
今回は少し複雑にして Group Replication を使った InnoDB Cluster を構築してみます

なお検証なので docker compose で一発で InnoDB Cluster が構築できるようしています

環境

  • macOS 15.5
  • docker 28.2.2
  • MySQL 8.4.5

compose.yaml

まずは3台の MySQL を Group Replication に必要なオプションを付与して起動します
InnoDB Cluster を構築するには mysqlshell コンテナがクラスタを構築するための Python スクリプトを実行することで構築されます

MySQL の起動オプションのみで InnoDB Cluster を構築できるわけではないので注意しましょう

services:
  mysql1:
    image: mysql:8.4.5
    environment:
      - MYSQL_ROOT_PASSWORD=rootpass
    ports:
      - "33061:3306"
    command: >
      --mysql-native-password=ON
      --server-id=1
      --gtid-mode=ON
      --enforce-gtid-consistency=ON
      --binlog-format=ROW
      --log-bin=mysql-bin
      --relay-log=relay-bin
      --log-replica-updates=ON
      --read-only=OFF
    volumes:
      - mysql1_data:/var/lib/mysql
    restart: always
    networks:
      - mysqlnet

  mysql2:
    image: mysql:8.4.5
    environment:
      - MYSQL_ROOT_PASSWORD=rootpass
    ports:
      - "33062:3306"
    command: >
      --mysql-native-password=ON
      --server-id=2
      --gtid-mode=ON
      --enforce-gtid-consistency=ON
      --binlog-format=ROW
      --log-bin=mysql-bin
      --relay-log=relay-bin
      --log-replica-updates=ON
      --read-only=OFF
    volumes:
      - mysql2_data:/var/lib/mysql
    restart: always
    networks:
      - mysqlnet

  mysql3:
    image: mysql:8.4.5
    environment:
      - MYSQL_ROOT_PASSWORD=rootpass
    ports:
      - "33063:3306"
    command: >
      --mysql-native-password=ON
      --server-id=3
      --gtid-mode=ON
      --enforce-gtid-consistency=ON
      --binlog-format=ROW
      --log-bin=mysql-bin
      --relay-log=relay-bin
      --log-replica-updates=ON
      --read-only=OFF
    volumes:
      - mysql3_data:/var/lib/mysql
    restart: always
    networks:
      - mysqlnet

  mysqlshell:
    image: mysql:8.4.5
    depends_on:
      - mysql1
      - mysql2
      - mysql3
    networks:
      - mysqlnet
    volumes:
      - ./scripts:/scripts
    command: >
      bash -c "
      echo 'Waiting for MySQL to be ready...' &&
      sleep 15 &&
      mysqlsh --host=mysql1 --port=3306 --user=root --password=rootpass --py < /scripts/setup_cluster.py
      "

networks:
  mysqlnet:

volumes:
  mysql1_data:
  mysql2_data:
  mysql3_data:

./scripts/setup_cluster.py

mysqlsh という機能を使って Python スクリプトを実行します
Python の他に JavaScript でも InnoDB Cluster を操作できます

# setup_cluster.py

import time


# 簡単なリトライ(実際はもっと robust に作ったほうがよい)
# dba は import しなくても使えるようになっている
def wait_and_configure(uri):
    for i in range(10):
        try:
            # cluster管理用のユーザを各ノードに作成する
            dba.configure_instance(
                uri, {"clusterAdmin": "admin", "clusterAdminPassword": "adminpass"}
            )
            return
        except Exception as e:
            print(f"Retry configuring {uri} in 5 sec: {e}")
            time.sleep(5)
    raise RuntimeError(f"Failed to configure instance: {uri}")


wait_and_configure("root:rootpass@mysql1:3306")
wait_and_configure("root:rootpass@mysql2:3306")
wait_and_configure("root:rootpass@mysql3:3306")

# Cluster 作成
cluster = dba.create_cluster("testCluster")
cluster.add_instance("root:rootpass@mysql2:3306", {"recoveryMethod": "clone"})
cluster.add_instance("root:rootpass@mysql3:3306", {"recoveryMethod": "clone"})

print("Cluster status:")
print(cluster.status())

流れとしては

  1. 各ノードにクラスタ構築用のユーザを設定 (configure_instance)
  2. 各ノードの追加 (add_instance)
  3. クラスタの状態を確認 (status)

という感じになっています
今回は一発で構築するためスクリプトにしましたが各 MySQL を起動したあとに手動で mysqlsh を実行し InnoDB Cluster を構築しても OK です

動作確認

  • docker compose up -d

でまずは各種コンテンを起動します
今回は上記一発で InnoDB Cluster が構築できるのであとは構築できたかを mysqlsh を使って確認します

  • docker compose exec -it mysql1 mysqlsh --py

でますは mysqlsh を起動しましょう root ユーザのパスワードが必要になります
そして Python を使ってクラスタの状況を確認します

dba.get_cluster().status() 

以下のようにクラスタが ONLINE で構築されていれば OK です

{
  "clusterName": "testCluster",
  "defaultReplicaSet": {
    "name": "default",
    "primary": "65d9833167a3:3306",
    "ssl": "REQUIRED",
    "status": "OK",
    "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
    "topology": {
      "65d9833167a3:3306": {
        "address": "65d9833167a3:3306",
        "memberRole": "PRIMARY",
        "mode": "R/W",
        "readReplicas": {},
        "replicationLag": "applier_queue_applied",
        "role": "HA",
        "status": "ONLINE",
        "version": "8.4.5"
      },
      "6892d7a7d916:3306": {
        "address": "6892d7a7d916:3306",
        "memberRole": "SECONDARY",
        "mode": "R/O",
        "readReplicas": {},
        "replicationLag": "applier_queue_applied",
        "role": "HA",
        "status": "ONLINE",
        "version": "8.4.5"
      },
      "bcbe386a9df7:3306": {
        "address": "bcbe386a9df7:3306",
        "memberRole": "SECONDARY",
        "mode": "R/O",
        "readReplicas": {},
        "replicationLag": "applier_queue_applied",
        "role": "HA",
        "status": "ONLINE",
        "version": "8.4.5"
      }
    },
    "topologyMode": "Single-Primary"
  },
  "groupInformationSourceMember": "65d9833167a3:3306"
}

どのノードがどのホスト名なのかわかない場合は add_instance 時に label というオプションが指定できるのでそれを使ってホスト名などを指定するといいかなと思います

FailOver の確認

PRIMARY なノードを停止して他のノードが PRIMARY になることを確認しましょう

  • docker compose stop mysql1

しばらくしたら再度起動します

  • docker compose start mysql1

これで再度クラスタの状態を確認すると PRIMARY が変わっていることが確認できると思います

{
    "clusterName": "testCluster", 
    "defaultReplicaSet": {
        "name": "default", 
        "primary": "bcbe386a9df7:3306", 
        "ssl": "REQUIRED", 
        "status": "OK", 
        "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", 
        "topology": {
            "65d9833167a3:3306": {
                "address": "65d9833167a3:3306", 
                "memberRole": "SECONDARY", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "replicationLag": "applier_queue_applied", 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.4.5"
            }, 
            "6892d7a7d916:3306": {
                "address": "6892d7a7d916:3306", 
                "memberRole": "SECONDARY", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "replicationLag": "applier_queue_applied", 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.4.5"
            }, 
            "bcbe386a9df7:3306": {
                "address": "bcbe386a9df7:3306", 
                "memberRole": "PRIMARY", 
                "mode": "R/W", 
                "readReplicas": {}, 
                "replicationLag": "applier_queue_applied", 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.4.5"
            }
        }, 
        "topologyMode": "Single-Primary"
    }, 
    "groupInformationSourceMember": "bcbe386a9df7:3306"
}

mysql クライアントを使うときには

基本は普通に使えば OK ですが必ず PRIMARY に接続するようにしましょう

  • mysql -u root -h 192.168.1.100 -p --port=33063

InnoDB Cluster の場合マルチマスター構成なのでどのノードもマスタなので書き込みが出来そうなのですが実際はできずにエラーになります

mysql> create database test;
ERROR 1290 (HY000): The MySQL server is running with the --super-read-only option so it cannot execute this statement

なので Failover 時にはアプリ側で書き込み先を変更しなければいけないのですがそれを自動で行う MySQL Router という機能があるので次回はそれを組み合わせてみます

最後に

MySQL8.4.5 で InnoDB Cluster を構築してみました
構築自体は非常に簡単で MySQL を3台用意して mysqlsh でクラスタを構築するための API をコールするだけでした
これで MySQL の冗長構成 + 自動 FailOver な環境は構築できました
次回は MySQL Router を組み合わせてみます

参考サイト

0 件のコメント:

コメントを投稿