最近、一気に寒くなったせいで、
寒暖差疲労 なるものに襲われ
調子が悪いアルバイトの本田 です😷
それはさておき、
先日のブログご覧いただけたでしょうか?
エイチームさん主催の勉強会 で
オンラインマイグレーションツール gh-ost
について紹介したと思います。
labs.spookies.co.jp
すると、弊社CEOとアンバサダー @masayuki14 より ↓こんなメッセージが!
ということで、
今回は、実際にスプーキーズの開発に導入してみた話です。
(ローカル環境で動作確認したところまでのお届け🎅🤶🦌)
gh-ost とは
先日のブログを読んでいない方もおられると思うので(ぜひ読みに行ってください!→先日のブログ )
まずは、簡単なgh-ostの紹介↓
オンラインマイグレーションツール
正式名称:GitHub's Online Schema Migrations for MySQL
OSS として開発が進む
本番で動いているテーブルのコピー(ゴーストテーブル)を作成し、そっちにmigrateかけて、できあがったら本番のものと入れ替え
本番用のテーブル内のデータが更新されたときは、非同期でゴーストテーブルも更新
Slave DBのbinary log を監視することにより実現
ローカル環境で動かしてみた
導入にあたり、まずは、ローカル環境でgh-ostの動作を試してみました。
ここからの作業は
エイチームさんの勉強会で、gh-ostについてお話されていた
s2terminal さんが書かれたQiita記事↓をもとに進めていきます。
qiita.com
テスト環境
macOS(Ver. 10.13.4)
Docker for Mac(Ver. 2.0.0.0-mac78 (28905))
gh-ost(Ver. 1.0.47)
1. gh-ostインストール
GitHub からgh-ostをダウンロード
ダウンロードした圧縮ファイルを解凍し、実行ファイルgh-ost
を取り出す
gh-ost
の実行ファイルをパスが通っているところに移動
動作チェック
$ gh-ost -version
1.0.47
OK!
2. Docker上でMySQL 環境を構築
Docker公式サイト からDockerをダウンロード(アカウント作成必須)
Dockerfile一式をクローン
$ git clone https://github.com/s2terminal/mysql-repl.git
mysql-slave.cnf
の記述を下記のとおり修正
[mysqld]
server-id=2
log-bin
log_slave_updates
3.docker-compose up
で起動
3. DB起動確認
以下コマンド実行してMySQLに入れたらOK
$ mysql -u root -h 127.0.0.1 -P 13306
$ mysql -u root -h 127.0.0.1 -P 23306
4. Master/Slave の設定
Master側での作業
$ mysql -u root -h 127.0.0.1 -P 13306 -e 'FLUSH TABLES WITH READ LOCK'
$ mysqldump --all-databases -u root -h 127.0.0.1 -P 13306 --master-data --single-transaction --order-by-primary -r backup.sql
$ MASTER_LOG_FILE=`mysql -u root -h 127.0.0.1 -P 13306 -e 'SHOW MASTER STATUS\G' | grep File | awk '{ print $2 }'`
$ MASTER_LOG_POSITION=`mysql -u root -h 127.0.0.1 -P 13306 -e 'SHOW MASTER STATUS\G' | grep Position | awk '{ print $2 }'`
$ mysql -u root -h 127.0.0.1 -P 13306 -e 'UNLOCK TABLES'
2.Slave側でレプリケーションスタート
$ mysql -u root -h 127.0.0.1 -P 23306 -e 'STOP SLAVE'
$ mysql -u root -h 127.0.0.1 -P 23306 -e 'SOURCE backup.sql'
$ mysql -u root -h 127.0.0.1 -P 23306 -e "CHANGE MASTER TO MASTER_HOST='db-master', MASTER_PORT=3306, MASTER_USER='root', MASTER_PASSWORD='', MASTER_LOG_FILE='${MASTER_LOG_FILE}', MASTER_LOG_POS=${MASTER_LOG_POSITION};"
$ mysql -u root -h 127.0.0.1 -P 23306 -e 'START SLAVE'
$ mysql -u root -h 127.0.0.1 -P 23306 -e 'SHOW SLAVE STATUS\G'
$ mysql -u root -h 127.0.0.1 -P 23306 -e "SET GLOBAL binlog_format = 'ROW'"
これで Master/Slaveの設定は完了です。
実際にMasterにDBを作成して確認してみます。
【Master DBで実行するSQL例】
CREATE DATABASE test_db;
USE test_db;
CREATE TABLE books(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20),
INDEX(id)
);
INSERT into test_db.books (name) VALUES ('Spookies News');
INSERT into test_db.books (name) VALUES ('Story of Spookies');
上記のクエリで適当なDBとテーブル、テストデータをMasterに作成します。
【Master DBで実行】
mysql> select * from books;
+----+-------------------+
| id | name |
+----+-------------------+
| 1 | Spookies News |
| 2 | Story of Spookies |
+----+-------------------+
2 rows in set (0.00 sec)
ちゃんとMasterにデータが入っていますね。
次にSlaveを確認します。
【Slave DBで実行】
mysql> select * from books;
+----+-------------------+
| id | name |
+----+-------------------+
| 1 | Spookies News |
| 2 | Story of Spookies |
+----+-------------------+
2 rows in set (0.00 sec)
MasterのデータがSlaveにレプリケーションされていますね!
4. 準備は整った。いざ、gh-ost始動!
今回は
Slaveのbooks
テーブルに新しいカラムを追加してみます。
gh-ostの動作モードは3つあります。→ 参照
今回は、上記参照のa. Connect to replica
とc. test-on-replica(テスト)
のモードで動かします。
まずは、テストモードで実行します。
テストモードでは、ゴーストテーブルが作成されるだけで、
MasterおよびSlaveの内容が変更されることはありません。
4.1. テストモードで動かしてみる
--test-on-replica
がテストモードであることを示します。
$ gh-ost --user="root" --password="" --host="127.0.0.1" --port="23306" --database="test_db" \
--table="books" \
--alter="ADD COLUMN price INT DEFAULT 100, ADD COLUMN created_at DATETIME" \
--test-on-replica \
--gcp \
--assume-master-host="127.0.0.1:13306" \
--execute
上記コマンドを実行すると、
_books_gho
テーブルが作られています。
中身を確認すると
【Slaveで実行】
mysql> select * from _books_gho;
+----+-------------------+-------+------------+
| id | name | price | created_at |
+----+-------------------+-------+------------+
| 1 | Spookies News | 100 | NULL |
| 2 | Story of Spookies | 100 | NULL |
+----+-------------------+-------+------------+
2 rows in set (0.00 sec)
確かに、price
とcreated_at
カラムが追加されています!
では、次は実際にSlaveに反映→Masterに反映というプロセスをやっていきます。
さきほどお作成したゴーストテーブルは削除しておきましょう。
mysql> DROP TABLE _books_gho;
Query OK, 0 rows affected (0.00 sec)
4.2. 「a. Connect to replica」モードで動かしてみる
1. Migration実行後に自動でMasterに反映するバージョン
gh-ost --user="root" --password="" --host="127.0.0.1" --port="23306" --database="test_db" \
--table="books" \
--alter="ADD COLUMN price INT DEFAULT 100, ADD COLUMN created_at DATETIME" \
--gcp \
--assume-master-host="127.0.0.1:13306" \
--execute
上記コマンドを実行後にMaster DBを確認すると…
【Masterで実行】
mysql> show tables;
+-------------------+
| Tables_in_test_db |
+-------------------+
| _books_del |
| books |
+-------------------+
2 rows in set (0.00 sec)
mysql> select * from books;
+----+-------------------+-------+------------+
| id | name | price | created_at |
+----+-------------------+-------+------------+
| 1 | Spookies News | 100 | NULL |
| 2 | Story of Spookies | 100 | NULL |
+----+-------------------+-------+------------+
2 rows in set (0.00 sec)
できてますね!
先述しておりますが、
gh-ostでは、Masterテーブルの複製(ゴーストテーブル)を作成し、
そのゴーストテーブルに対してMigrationを行います。
そして、Migrationが完了次第、
本番稼働してるテーブルと名称を入れ替える形で切り替えを行います。
したがって、_books_del
テーブルは置き換える前に本番で動いていたテーブルです。
2. Migration実行後に手動でMasterに反映するバージョン
Masterに先程作成されたテーブルは消しておきましょう。
【Master DBで実行】
mysql> drop tables _books_del;
Query OK, 0 rows affected (0.01 sec)
まずは、手動切り替える用のフラグファイルを作成します。
$ touch /tmp/ghost.postpone.flag
(↑gh-ostコマンドを実行するPC上での実行)
それから、
--postpone-cut-over-flag-file
オプションにより
切り替え用フラグファイルを指定。
$ gh-ost --user="root" --password="" --host="127.0.0.1" --port="23306" --database="test_db" \
--table="books" \
--alter="ADD COLUMN updated_at DATETIME" \
--gcp \
--assume-master-host="127.0.0.1:13306" \
--postpone-cut-over-flag-file=/tmp/ghost.postpone.flag \
--execute
上記コマンドを実行すると、
2018/12/18 12:09:19 binlogsyncer.go:79: [info] create BinlogSyncer with config {99999 mysql 127.0.0.1 23306 root false false <nil>}
2018/12/18 12:09:19 binlogsyncer.go:246: [info] begin to sync binlog from position (9ef9788f80f5-bin.000004, 3333278)
2018/12/18 12:09:19 binlogsyncer.go:139: [info] register slave for master server 127.0.0.1:23306
2018/12/18 12:09:19 binlogsyncer.go:573: [info] rotate to (9ef9788f80f5-bin.000004, 3333278)
このようなログが出力され続けると思います。これでOKです!
Migrationが終わり、いつでも切り替えられる状況です。
この状況のままで Master を確認します。
【Masterで実行】
mysql> show tables;
+-------------------+
| Tables_in_test_db |
+-------------------+
| _books_ghc |
| _books_gho |
| books |
+-------------------+
3 rows in set (0.01 sec)
mysql> describe books;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(20) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
mysql> describe _books_gho;
+------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(20) | YES | | NULL | |
| price | int(11) | YES | | 100 | |
| created_at | datetime | YES | | NULL | |
+------------+-------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)
確かに、books
テーブルは変化なしですが、
_books_gho
テーブル(ゴーストテーブル)にはカラムが追加されている!
では、実際に本番DBに反映します。
本番DBに反映するための切り替え作業はフラグファイルを消してあげるだけです。
$ mv /tmp/ghost.postpone.flag /tmp/ghost.postpone.flag.unpostpone
(↑gh-ostコマンドを実行するPC上での実行)
実行すると、
ログ出力が止まります。
では、Master DBを見てみましょう。
【Masterで実行】
mysql> show tables;
+-------------------+
| Tables_in_test_db |
+-------------------+
| _books_ghc |
| _books_gho |
| books |
+-------------------+
3 rows in set (0.00 sec)
mysql> select * from books;
+----+-------------------+-------+------------+------------+
| id | name | price | created_at | updated_at |
+----+-------------------+-------+------------+------------+
| 1 | Spookies News | 100 | NULL | NULL |
| 2 | Story of Spookies | 100 | NULL | NULL |
+----+-------------------+-------+------------+------------+
2 rows in set (0.00 sec)
反映できてますね!
すごい!
ちなみに、
手動切替を待つ状態で
Masterのデータに変更があった場合を試してみましょう。
切り替え用フラグをまた作成し、
$ touch /tmp/ghost.postpone.flag
下記コマンド実行
$ gh-ost --user="root" --password="" --host="127.0.0.1" --port="23306" --database="test_db" \
--table="books" \
--alter="ADD COLUMN favorite B DEFAULT 0, ADD COLUMN created_at DATETIME" \
--gcp \
--assume-master-host="127.0.0.1:13306" \
--execute
また、ログが出力され続ける状態になったと思います。
では、この状態でMaster DBの方にデータを追加してみましょう。
【Masterで実行】
mysql> insert into books (name, price) values ('Spo Paper', 200);
Query OK, 1 row affected (0.01 sec)
mysql> select * from books;
+----+-------------------+-------+------------+------------+
| id | name | price | created_at | updated_at |
+----+-------------------+-------+------------+------------+
| 1 | Spookies News | 100 | NULL | NULL |
| 2 | Story of Spookies | 100 | NULL | NULL |
| 3 | Spo Paper | 200 | NULL | NULL |
+----+-------------------+-------+------------+------------+
3 rows in set (0.00 sec)
この変更はもちろんSlaveに反映されています。
【Slaveで実行】
mysql> select * from books;
+----+-------------------+-------+------------+------------+
| id | name | price | created_at | updated_at |
+----+-------------------+-------+------------+------------+
| 1 | Spookies News | 100 | NULL | NULL |
| 2 | Story of Spookies | 100 | NULL | NULL |
| 3 | Spo Paper | 200 | NULL | NULL |
+----+-------------------+-------+------------+------------+
3 rows in set (0.00 sec)
問題はMasterに作成されいるゴーストテーブルにも反映されているのか否か…。
では、手動切替を行った後にMasterを確認した結果がこれです↓
【Masterで実行】
mysql> select * from books;
+----+-------------------+-------+------------+------------+----------+
| id | name | price | created_at | updated_at | favorite |
+----+-------------------+-------+------------+------------+----------+
| 1 | Spookies News | 100 | NULL | NULL | 0 |
| 2 | Story of Spookies | 100 | NULL | NULL | 0 |
| 3 | Spo Paper | 200 | NULL | NULL | 0 |
+----+-------------------+-------+------------+------------+----------+
3 rows in set (0.00 sec)
反映されています!
こちらも先述したとおり、
gh-ostではSlaveのバイナリログを見て、
非同期 でデータを同期しています。
したがって、常に本番で動作しているMaster DBとの同期が取れています 。
最後に
このようにオンラインマイグレーションツール gh-ost
を使用することで
サービスを止めることなくマイグレーションが
安全(サービス的にも肉体的にも精神的にも!)に行えることができます!
Spookiesでも早速導入しました。
みなさんも機会があればぜひ使ってみてください👩💻👨💻