3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Docker】InnoDB Clusterを使ったDBサーバの冗長化手順書

3
Last updated at Posted at 2026-06-14

はじめに

こんにちは、@y-428です。
今回はMySQLの標準ストレージエンジンであるInno DBを利用して、フェイルオーバ可能な可用性の高いDBサーバの構築手順についてまとめてみました。
間違いがあれば都度ご指摘いただけますと幸いです。

また、併せてこちらのドキュメントもご参照ください。

では早速行きましょう。

執筆の背景

以前Proxmox上のCentOS Stream 10でMySQL InnoDB Clusterを構築しました。
今回はその構成をDockerで簡単に再現できるか気になり、検証も込めて執筆に至りました。

VM環境では当たり前にできることがDockerではうまくいかないケースが何箇所かあったので、ハマりポイントも含めてまとめます。

DockerでMySQL 8.4 InnoDB Cluster + MySQL Routerを構築してWordPressと繋ぐ

構成

3台のDBコンテナでInnoDB Clusterを組み、MySQL Routerを経由してWordPressから接続します。

[WordPress] → router:6446 → [MySQL Router]
                                   ↓
                   ┌───────────────┼───────────────┐
                [db01]          [db02]          [db03]
               PRIMARY         SECONDARY       SECONDARY
               (R/W)            (R/O)           (R/O)
コンテナ名 役割 ホスト側ポート
db01 MySQL 8.4 / PRIMARY 13306
db02 MySQL 8.4 / SECONDARY 13307
db03 MySQL 8.4 / SECONDARY 13308
mysql-router MySQL Router 8.4 6446(RW) / 6447(RO)
wordpress WordPress latest 8080

クラスター構築を先に完了させてからWordPressを起動する必要があるため、DBクラスターとWordPress側でComposeファイルを分けています。

ディレクトリ構成

db_server_test/
├── db/
│   ├── Dockerfile
│   ├── docker-compose.yml
│   └── conf/
│       ├── db01.cnf
│       ├── db02.cnf
│       └── db03.cnf
└── wp/
    ├── docker-compose.yml
    └── router/
        ├── Dockerfile
        └── entrypoint.sh

Step 1: DBコンテナの準備

Dockerfile

mysql:8.4 はOracle Linuxベースだったので、パッケージマネージャーは microdnf を使ってます。
イメージには既にMySQLの公式リポジトリが含まれているため、リポジトリの追加設定なしで mysql-shell をインストールできます。

FROM mysql:8.4

RUN microdnf install -y mysql-shell && microdnf clean all

docker-compose.yml

networks:
  innodb-net:
    name: innodb-net
    driver: bridge

services:
  db01:
    build: .
    container_name: db01
    hostname: db01
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_ROOT_HOST: "%"
    volumes:
      - ./conf/db01.cnf:/etc/mysql/conf.d/cluster.cnf:ro
      - db01_data:/var/lib/mysql
    networks:
      - innodb-net
    ports:
      - "13306:3306"
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-prootpassword"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 30s

  db02:
    build: .
    container_name: db02
    hostname: db02
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_ROOT_HOST: "%"
    volumes:
      - ./conf/db02.cnf:/etc/mysql/conf.d/cluster.cnf:ro
      - db02_data:/var/lib/mysql
    networks:
      - innodb-net
    ports:
      - "13307:3306"
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-prootpassword"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 30s

  db03:
    build: .
    container_name: db03
    hostname: db03
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_ROOT_HOST: "%"
    volumes:
      - ./conf/db03.cnf:/etc/mysql/conf.d/cluster.cnf:ro
      - db03_data:/var/lib/mysql
    networks:
      - innodb-net
    ports:
      - "13308:3306"
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-prootpassword"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 30s

volumes:
  db01_data:
  db02_data:
  db03_data:

各DBでMYSQL_ROOT_HOST: "%" を設定しておかないと、rootユーザーがlocalhost経由でしか接続できず、後のMySQL Shellによるコンテナ間操作で失敗します。

conf/db01.cnf / db02.cnf / db03.cnf

最低限 server_id(各ノードで一意の値)と report_host(コンテナのホスト名)を設定してます。
GTIDやbinlogなどの設定は、後で実行するdba.configureInstance() が自動で書き込んでくれます。便利ですね。

conf/db01.cnf

[mysqld]
server_id   = 1
report_host = db01

conf/db02.cnf

[mysqld]
server_id   = 2
report_host = db02

conf/db03.cnf

[mysqld]
server_id   = 3
report_host = db03

report_host はコンテナ名と一致させる必要があります。
未設定の場合、コンテナIDがホスト名として使われてしまうため、ノードからの名前解決に失敗してしまいます。

Step 2: DBコンテナを起動する

cd db_server_test/db
docker compose up -d --build

全コンテナのhealthcheckが通るまで1〜2分待ちましょう。

起動確認のために以下のコマンドを実行します。

docker compose ps

全台 (healthy) になってることを確認したら次に進みましょう。

Step 3: InnoDB Clusterを構築する

各インスタンスをクラスター用に設定する

db01のMySQL Shellから3台分を順番に設定していきます。

docker exec -it db01 mysqlsh --js "root:rootpassword@db01:3306"

Note: 自分がMySQL Shellのパスワード入力でタイプミスが起きまくったのでURIに直接含めてますが、普通に対話入力で大丈夫です。

MySQL Shell内で実行:

dba.configureInstance('root:rootpassword@db01:3306')

途中でいくつか確認が出ますが、すべて y で進めます。
最後の「MySQLを再起動するか」という項目で y を入力しますが、以下のエラーが出ます。

image.png

これはDocker環境では想定内の挙動です。
DockerではSQLの RESTART コマンドが使えないため、設定ファイルへの書き込みは完了していても再起動だけ失敗してしまいます。

ここまで完了したら、MySQL Shellを抜けてコンテナを手動再起動していきます。

# mysqlshを抜ける
\quit

# コンテナ再起動
docker restart db01

db02・db03も同様に実施します(db01のmysqlshで行ってください)。

docker exec -it db01 mysqlsh --js "root:rootpassword@db01:3306"

追記
「リカバリ方法(Clone または Incremental)」を聞かれた場合(基本聞かれます)は、今回はCloneを選択してもらえれば大丈夫です。

db02

dba.configureInstance('root:rootpassword@db02:3306')
docker restart db02

db03

dba.configureInstance('root:rootpassword@db03:3306')
docker restart db03

クラスターを作成してメンバーを追加

全台の再起動が完了したら、db01のMySQL Shellでクラスターを作成します。

docker exec -it db01 mysqlsh --js "root:rootpassword@db01:3306"
var cluster = dba.createCluster('wpCluster')
cluster.addInstance('root:rootpassword@db02:3306')
cluster.addInstance('root:rootpassword@db03:3306')

cluster.status()で以下のような出力が得られれば成功です。
image.png

"can tolerate up to ONE failure." = 1台が落ちてもクラスターが継続できる状態です。

WordPress用のDBとユーザーを作成する

docker exec -it db01 mysql -u root -p rootpassword 

CREATE DATABASE wordpress DEFAULT CHARACTER SET utf8mb4;
CREATE USER 'wp_user'@'%' IDENTIFIED BY 'wp_password';
GRANT ALL PRIVILEGES ON wordpress.* TO 'wp_user'@'%';
FLUSH PRIVILEGES;

Step 4: WordPress + MySQL Routerを起動する

router/Dockerfile

wpコンテナにmysql-router をインストールします。

FROM mysql:8.4

RUN microdnf install -y mysql-router && \
    microdnf clean all && \
    useradd -r -s /sbin/nologin mysqlrouter 2>/dev/null || true

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

EXPOSE 6446 6447 6448 6449

ENTRYPOINT ["/entrypoint.sh"]

router/entrypoint.sh

#!/bin/bash
set -e

CONF_DIR="/etc/mysqlrouter"
CLUSTER_HOST="${CLUSTER_HOST:-db01}"
CLUSTER_PORT="${CLUSTER_PORT:-3306}"

echo "[router] Bootstrapping against ${CLUSTER_HOST}:${CLUSTER_PORT} ..."

until mysqlrouter \
    --bootstrap "root:${MYSQL_ROOT_PASSWORD}@${CLUSTER_HOST}:${CLUSTER_PORT}" \
    --user=root \
    --directory="${CONF_DIR}" \
    --force; do
    echo "[router] Bootstrap failed, retrying in 10s..."
    sleep 10
done

echo "[router] Bootstrap complete."
exec mysqlrouter -c "${CONF_DIR}/mysqlrouter.conf"

このスクリプトのポイントは後述のハマりポイントで解説します。

docker-compose.yml

networks:
  innodb-net:
    external: true
    name: innodb-net

services:
  router:
    build: ./router
    container_name: mysql-router
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      CLUSTER_HOST: db01
      CLUSTER_PORT: "3306"
    networks:
      - innodb-net
    ports:
      - "6446:6446"
      - "6447:6447"
    restart: unless-stopped

  wordpress:
    image: wordpress:latest
    container_name: wordpress
    environment:
      WORDPRESS_DB_HOST: router:6446
      WORDPRESS_DB_USER: wp_user
      WORDPRESS_DB_PASSWORD: wp_password
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wp_data:/var/www/html
    networks:
      - innodb-net
    ports:
      - "8080:80"
    depends_on:
      - router
    restart: unless-stopped

volumes:
  wp_data:

innodb-netexternal: true にすることで、DBコンテナが作成したネットワークをWordPress側でも使い回します。
WordPressの接続先はDBサーバーのIPではなく router:6446(MySQL Router)にします。

起動

cd db_server_test/wp
docker compose up -d --build

docker logs mysql-router でBootstrapの完了ログが確認できます。

[router] Bootstrapping against db01:3306 ...
# Bootstrapping MySQL Router 8.4.x instance at '/etc/mysqlrouter'...
...
# MySQL Router configured for the InnoDB Cluster 'wpCluster'
[router] Bootstrap complete.

http://localhost:8080 にアクセスしてWordPressのインストール画面が表示されれば完成です。

ハマったポイント

1. mysql:8.4はOracle Linuxベース(apt-getが使えない)

mysql:8.4 の公式イメージはDebianではなくOracle Linuxベースに変わっており、apt-get を使うとビルドエラーになるので microdnf を使いました。

# NG
RUN apt-get install -y mysql-shell

# OK
RUN microdnf install -y mysql-shell

2. dba.configureInstance() の再起動はDockerでは使えない

dba.configureInstance() は設定完了後にMySQLを再起動しようとしますが、DockerではPID 1のプロセスがsupervisorではないため RESTART SQLコマンドが失敗します。

ERROR: mysqld is not managed by supervisor process.

設定ファイルへの書き込み自体は完了しているので、docker restart で手動再起動すれば問題ありません。

3. RouterのBootstrapはファイル存在チェックをしてはいけない

mysqlrouter.conf の有無でBootstrapをスキップする実装を最初に書いたのですが、うまく動きませんでした。

原因は、Oracle Linux版の mysql-router RPMパッケージがインストール時にデフォルトの mysqlrouter.conf/etc/mysqlrouter/ に配置するためです。
ファイルがあると判定されるとBootstrapがスキップされ、ルーティング設定のない空のconfでRouterが起動してしまいました。

# NG: デフォルトファイルが存在するのでBootstrapがスキップされる
if [ ! -f "/etc/mysqlrouter/mysqlrouter.conf" ]; then
    mysqlrouter --bootstrap ...
fi

# OK: 毎回Bootstrapを実行(--force で上書き)
mysqlrouter --bootstrap ... --force

4. rootでBootstrapするには --user=root が必要

rootユーザーでBootstrapを実行するには --user=root の明示が必要でした。
省略するとエラーになります。

Error: You are bootstrapping as a superuser.
Please use --user=username option.
Use --user=root if this really should be the superuser.

まとめ

DockerでもMySQL InnoDB Cluster + MySQL RouterによるHA構成は動作することを確認できました。

VM環境との主な違いは以下の3点です。

項目 VM環境(CentOS) Docker
パッケージマネージャー dnf microdnf
MySQLの再起動 systemctl restart mysqld または自動 docker restart で手動
Router Bootstrapのデフォルトconf なし RPMがテンプレートを配置するため注意

次のステップとして、db01を停止してdb02がPRIMARYに昇格するフェイルオーバーの検証もやってみると理解が深まります。

おわりに

最後までご拝読ありがとうございました。
いかがだったでしょうか。
かなり詰め込んだので、何度か見返していただけるとありがたいです。

もし間違い等気づきましたらご指摘ください!
ありがとうございました!

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?