docker-compose で MySQL レプリケーション環境をサクッと用意する

本番環境は MySQL のマスターとリードレプリカのスレーブで構成されているものの、ローカル環境はシングル構成なこと、よくあります。

そういうとき、レプリケーション遅延が原因による不自然な表示・・不具合が生じることがあったりするので、docker-compose なローカル環境でもサクッとレプリケーション環境を用意する方法。

Docker Hub のオフィシャルの MySQL イメージならコンテナの /docker-entrypoint-initdb.d/ にシェルスクリプトを置いておけば初回開始時に自動的に実行されるので、これを利用します。

docker-compose.yml

環境変数 MYSQL_REPLICATION_USER MYSQL_REPLICATION_PASSWORD でレプリケーションのユーザー名・パスワードを指定します。MYSQL_REPLICATION_HOST でマスターのホストを指定しますがこれはスレーブでのみ設定します。

version: '3.7'

services:

  mysql-master:
    image: mysql:8
    command:
      - --default_authentication_plugin=mysql_native_password
      - --gtid_mode=ON
      - --enforce_gtid_consistency=ON
      - --relay-log=relay-bin
      - --server_id=1
    environment: &environment
      TZ: Asia/Tokyo
      MYSQL_ALLOW_EMPTY_PASSWORD: 1
      MYSQL_DATABASE: test
      MYSQL_USER: test
      MYSQL_PASSWORD: pass
      MYSQL_REPLICATION_USER: repl
      MYSQL_REPLICATION_PASSWORD: pass
    volumes:
      - ./init.sh:/docker-entrypoint-initdb.d/init.sh:ro

  mysql-slave:
    image: mysql:8
    depends_on:
      - mysql-master
    command:
      - --default_authentication_plugin=mysql_native_password
      - --gtid_mode=ON
      - --enforce_gtid_consistency=ON
      - --relay-log=relay-bin
      - --server_id=2
    environment:
      <<: *environment
      MYSQL_REPLICATION_HOST: mysql-master
    volumes:
      - ./init.sh:/docker-entrypoint-initdb.d/init.sh:ro

init.sh

コンテナの /docker-entrypoint-initdb.d/init.sh にマウントされるスクリプトです。レプレーションユーザーを作成したり、スレーブを開始したりします。

オフィシャルの MySQL イメージだと entrypoint でユーザーを作成したりデータベースを作成したりいろいろ行われているので、そのままだとレプリケーションの開始後に更新が競合します。

なので、一通り共通の準備が終わったところで RESET MASTER でバイナリログをふっとばします。

#!/bin/bash

if [ -v MYSQL_REPLICATION_USER -a -v MYSQL_REPLICATION_PASSWORD ]; then
  mysql -u root -v mysql <<SQL
    CREATE USER '$MYSQL_REPLICATION_USER'@'%' IDENTIFIED BY '$MYSQL_REPLICATION_PASSWORD';
    GRANT REPLICATION SLAVE ON *.* TO '$MYSQL_REPLICATION_USER'@'%';
SQL
fi

mysql -u root -v mysql <<SQL
  RESET MASTER;
SQL

if [ -v MYSQL_REPLICATION_USER -a -v MYSQL_REPLICATION_PASSWORD -a -v MYSQL_REPLICATION_HOST ]; then
  mysqladmin ping --wait=5 -h "$MYSQL_REPLICATION_HOST"
  mysql -u root -v <<SQL
    CHANGE MASTER TO
      MASTER_HOST = '$MYSQL_REPLICATION_HOST',
      MASTER_USER = '$MYSQL_REPLICATION_USER',
      MASTER_PASSWORD = '$MYSQL_REPLICATION_PASSWORD',
      MASTER_AUTO_POSITION = 1,
      MASTER_DELAY = 10;
    START SLAVE;
SQL
fi

動作確認

docker-compose up -d

# レプリケーション状態 -> Slave_IO_Running と Slave_SQL_Running ともに Yes
docker-compose exec mysql-slave mysql -e 'show slave status \G'

# マスターにテーブルを作ってデータを入れる
docker-compose exec mysql-master mysql test -e 'create table t (id int not null primary key)'
docker-compose exec mysql-master mysql test -e 'insert into t values (1)'

# レプリケーション状態 -> Retrieved_Gtid_Set や Executed_Gtid_Set がちょっと進む
docker-compose exec mysql-slave mysql -e 'show slave status \G'

# レプリケーションされたデータが表示される
docker-compose exec mysql-slave mysql test -e 'select * from t'