docker-composeでmysqlコンテナーを立てて、replicationをかける（mac）

#DBレプリケーションとは
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566437/2bb6e957-4d5a-1097-9e67-e695e47f133d.png)
Master DBのデータがSlave DBに同期化されて、同じデータをSlave DBから確認できます。
データの安定性を確保するかつ、書き込みはMaster DB, 読み込みはSlave DBを利用してDBの負荷を下げるために使います。
（最近はMasterやSlaveなどの言葉は使わない方向に進んでいますが）

## ディレクトリ構造
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566437/f72a1bff-bbcb-23ed-6bbc-d5bf50b5b1d9.png)

### docker-compose.yml
```yml:docker-compose.yml
version: '3.3'

services:
  db-master:
    image: mymaster
    volumes:
      - ./mymaster/mysql-init-files/:/docker-entrypoint-initdb.d/
      - ./mymaster/mysql-config-files/:/etc/mysql/conf.d
    restart: always
    container_name: mymaster
    environment:
      - MYSQL_ALLOW_EMPTY_PASSWORD="true"
    ports:
      - '3333:3306'
    expose:
      - '3333'

  db-slave:
    image: myslave
    volumes:
      - ./myslave/mysql-init-files/:/docker-entrypoint-initdb.d/
      - ./myslave/mysql-config-files/:/etc/mysql/conf.d
    restart: always
    container_name: myslave
    environment:
      - MYSQL_ALLOW_EMPTY_PASSWORD="true"
    ports:
      - '2222:3306'
    expose:
      - '2222'
 ```
imageとしてmymasterを利用していますが、dockerfileでビルドしたものです。大した内容はありませんのでmysqlのimageを使用しても構いません。

## Masterの設定
### dockerfile
```dockerfile:dockerfile
FROM mysql:5.7

EXPOSE 3306

CMD ["mysqld"]
```

### my.cnf
```
[mysqld]
log-bin=mysql-bin
server-id=1
```
log-binはクエリを実行するとき残すログです。
server-idを1として指定します。

### create.sql
```sql:create.sql
CREATE DATABASE repl;

CREATE TABLE repl.user(
  id VARCHAR(100) PRIMARY KEY,
  name VARCHAR (100)
);

create user master@'%' identified by 'master';
grant all privileges on *.* to master@'%' identified by 'master';

grant replication slave on *.* to 'slave'@'%' identified by 'slave';
```
重要なのは
`grant replication slave on *.* to 'slave'@'%' identified by 'slave';`
でSlaveからレプリケーションをかけるユーザーを作成することです。

## Slave設定
### dockerfile
```dockerfile:dockerfile
FROM mysql:5.7

EXPOSE 3306

CMD ["mysqld"]
```
Masterと同じです。

### my.cnf
```
[mysqld]
server-id=2
replicate-do-db='repl'
```
server-idを2に指定します。
レプリケーションするDBを指定します。

### create.sql
```sql:create.sql
CREATE DATABASE repl;

CREATE TABLE repl.user(
  id VARCHAR(100) PRIMARY KEY,
  name VARCHAR (100)
);

create user slave@'%' identified by 'slave';
grant all privileges on repl.* to slave@'%' identified by 'slave';
```
レプリケーションのため、Masterと同じテーブルを作成することが必要です。

最後に、シェルスクリプトでレプリケーションを実行します。

## レプリケーションの実行
### init.sh
```sh:init.sh
#!/bin/sh

#make mymaster image
docker build --no-cache -t mymaster ./mymaster/

#make myslave image
docker build --no-cache -t myslave ./myslave/
docker-compose -f ./docker-compose.yml up -d --remove-orphans

sleep 15s

# macのコマンドですので、Windowsなら他のIPアドレス取得方法が必要です。
myaddress=`ipconfig getifaddr en0`
myaddress=${myaddress}

# Masterのログファイルの場所を取得します。
master_log_file=`mysql -h127.0.0.1 --port 3333 -uroot -e "show master status\G" | grep mysql-bin`
master_log_file="${master_log_file}"

re="[a-z]*-bin.[0-9]*"

if [[ ${master_log_file} =~ $re ]];then
    master_log_file=${BASH_REMATCH[0]}
fi

# Masterのポジションも取得します。
master_log_pos=`mysql -h127.0.0.1 --port 3333 -uroot -e "show master status\G" | grep Position`
master_log_pos="${master_log_pos}"

re="[0-9]+"

if [[ ${master_log_pos} =~ $re ]];then
    master_log_pos=${BASH_REMATCH[0]}
fi

query="change master to master_host='${myaddress}', master_user='slave', master_password='slave', master_log_file='${master_log_file}', master_log_pos=${master_log_pos}, master_port=3333"

mysql -h127.0.0.1 --port 2222 -uroot -e "${query}"
mysql -h127.0.0.1 --port 2222 -uroot -e "start slave"
```
init.shを実行するとdockerのimageをビルドして、コンテナー間のレプリケーションをかけます。
これでレプリケーションをかけたmysqlコンテナーができました。