Ansibleで本番レベルのMySQLレプリケーションを構築して見よう。


## はじまる前に

この記事では主にAnsibleの機能を紹介したいと思います。
playbookに記述したMySQLの詳細を知りたい方は次の記事を参照してください。

* [CentOS 7.3でMySQL 5.7を設置、かなり勉強になりました。](https://qiita.com/park-jh/items/1e118580ae1deab70f99)
* [MySQLのSQLモードをstrictモードで設定する。](https://qiita.com/park-jh/items/32b21d7b8d24b0ab3dba)

## やりたいこと

実際のサービス環境でMySQLを運用する場合、このくらいは必要だと思ったことを書いてみた。

* master1台とslave2台のレプリケーション構成
* スロークエリを残す。
* MySQLをstrictモードで使う。

これらをAnsibleのplaybookで作成する。

## ファイル構成

```sh
# mysqlのrole
roles/mysql
├── handlers
│   └── main.yml
├── tasks
│   └── main.yml
└── vars
    └── main.yml

# dbグループの変数設定
group_vars
└── db.yml

# 各dbの変数設定
# master: db1
# slave:  db2, db3
host_vars
├── db1.yml
├── db2.yml
└── db3.yml

# hosts.ini
[db]
db[1:3]

# /etc/hosts
192.168.33.32 db1
192.168.33.33 db2
192.168.33.34 db3
```

## playbook

MySQLを初期化する方法には二つがある。

* mysqld --initialize
* mysqld --initialize-insecure

詳細は「はじまる前に」にて紹介した記事を参照しよう。
最初はインタラクティブで実行されるmysql_secure_installationを使わないために「mysqld --instialize-insecure」で初期化し、mysql_secure_installationと同等の処理を行うようにplaybookを作成した。
その後、mysql_secure_installationをインタラクティブではない自動で実行する方法を知り、それに合わせてplaybookを修正した。
二つのplaybookを全部みて見よう。

### mysqld --initialize-insecureの場合

insecureでmysqlを初期化すると、昔のように「mysql -u root」だけで接続ができる。
後で、mysql_secure_installationと同等な処理を行えばいい。

```ruby:roles/mysql/tasks/main.yml
- name: install the mysql rpm
  yum:
    name: "{{ mysql_yum_repository }}"
    state: present

- name: install the mysql
  yum: name={{ item }} state=present
  with_items: "{{ mysql_yum_packages }}"

- name: check if mysql log file size is 0 or not
  stat: path={{ mysql_log_file }}
  register: log_file
  changed_when: false

- name: initialize insecure mysql
  command: mysqld --initialize-insecure --user=mysql
  changed_when: false
  when: log_file.stat.size == 0

- name: start the mysqld
  service: name=mysqld state=started enabled=yes

- name: change the mysql from insecure to secure
  command: mysql -u root -e "{{ mysql_secure_installation_query }}"
  changed_when: false
  when: log_file.stat.size == 0

- name: create a log file to write the slow query
  file:
    path: "{{ mysql_slow_log_file }}"
    state: touch
    owner: mysql
    group: mysql
    mode: 0644
  when: log_file.stat.size == 0

- name: setting up to my.cnf
  blockinfile:
    path: /etc/my.cnf
    block: |
      {{ item.content }}
    marker: "# {mark} {{ item.name }}"
    validate: mysqld --defaults-file=%s --verbose --help
  with_items: "{{ mysql_my_cnf }}"
  register: config_file

- name: restart the mysqld
  service: name=mysqld state=restarted
  when: config_file.changed

- name: create replication user to master
  mysql_user:
    login_user: "{{ mysql_user_name }}"
    login_password: "{{ mysql_user_password }}"
    name: "{{ mysql_replication_user.name }}"
    host: "{{ mysql_replication_user.host | default('%') }}"
    password: "{{ mysql_replication_user.password }}"
    priv: "{{ mysql_replication_user_priv | default('*.*:REPLICATION SLAVE,REPLICATION CLIENT') }}"
    state: present
  when: >
    mysql_replication_role == 'master'
    and mysql_replication_master is defined
    and mysql_replication_user is defined

- name: get replication slave status to slave
  mysql_replication:
    mode: getslave
    login_user: "{{ mysql_user_name }}"
    login_password: "{{ mysql_user_password }}"
  register: slave
  when: >
    mysql_replication_role == 'slave'
    and mysql_replication_master is defined
    and mysql_replication_user is defined

- debug: msg={{ slave }}

- name: change replication master to slave
  mysql_replication:
    mode: changemaster
    login_user: "{{ mysql_user_name }}"
    login_password: "{{ mysql_user_password }}"
    master_host: "{{ mysql_replication_master.host }}"
    master_port: "{{ mysql_replication_master.port | default(3306) }}"
    master_user: "{{ mysql_replication_user.name }}"
    master_password: "{{ mysql_replication_user.password }}"
    master_auto_position: 1
  when: >
    mysql_replication_role == 'slave'
    and slave.Is_Slave is defined and not slave.Is_Slave
    and mysql_replication_master is defined
    and mysql_replication_user is defined

- name: start replication to slave
  mysql_replication:
    mode: startslave
    login_user: "{{ mysql_user_name }}"
    login_password: "{{ mysql_user_password }}"
  when: >
    mysql_replication_role == 'slave'
    and slave.Is_Slave is defined and not slave.Is_Slave
    and mysql_replication_master is defined
    and mysql_replication_user is defined
```

* install the mysql rpm

yum repositoryをインストールする。

* install the mysql

mysql_yum_packagesという変数にListで記述したパッケージをインストールする。

* check if mysql log file size is 0 or not

最初1回だけ実行したいタスクがあって、書いた。
mysqldが起動される前にログファイルのstat情報を取得して保存しておく必要がある。
他のタスクの実行可否を決めるための情報なので、ターゲットノードには影響を与えていないタスクだ。
changedになる必要がないので、「changed_when: false」で上書きした。
この件についてもっと知りたい人は次の記事が役に立つと思う。

[Ansibleのcommand系（command、shell）を使う時の冪等性を保つ方法について](https://qiita.com/park-jh/items/4fe4eb111da442e15d85)

* initialize insecure mysql

insecureでmysqlを初期化する。
最初1回だけ実行すればいい。

* start the mysqld

mysqld起動する。
mysqlクライアントを使って何かの処理をしなければならないから。

* change the mysql from insecure to secure

insecureのまま運用するのはセキュリティ上問題があるので、mysql_secure_installationと同等のクエリを実行してsecureな状態を作る。
最初1回だけ実行すればいい。

* create a log file to write the slow query

遅いクエリが実行された場合は、それをログとして残さないと障害が発生して原因を探るとき面倒になる。
「state: touch」はLinuxコマンドのtouchと同様。
最初1回だけ実行すればいい。

* setting up to my.cnf

最初からある程度設定されたmy.cnfファイルを用意した方がいいかも。
ここではMySQLの設定を一つずつ適用してみたいと思って、わざと変数にListとして保存した。

* restart the mysqld

my.cnfファイルを変更したので、mysqldを再起動する必要がある。
handlerにタスクを用意してなぜnotifyを使って通知していないのかについて説明する。
__Ansibleのhandlerは全てのタスクが終了した後、1回だけ実行されるようになっている。__
__もう一度言う。playbookの中にある全てのタスクが終了した後だ。__
__roleやinclude_tasksで読み込んでいる全てのタスクが終了しないとhandlerは実行されない。__
my.cnfは変更されて次のタスクが残されている場合は、そのタスクを実行する前に再起動しないといけない。

* create replication user to master

レプリケーション構成でslaveからmasterへ接続するユーザーを生成する。
以前のタスクでrootパスワードを変更したので、login_userとlogin_passwordを指定する必要がある。
「host: "{{ mysql_replication_user.host | default('%') }}"」にてdefault filterが使われている。
mysql_replication_user.hostが定義されていない場合はdefault filterで定義された'%'が割り当てられる。
全てのサーバーから接近できるようになるということだ。
privは特権を設定する部分だ。
詳細はAnsibleドキュメントのmysql_userモジュールを参照にしよう。
__条件式の先頭にある&gt;だが、|との違いは改行コード(\n)の有無だ。__
__&gt;は改行して書いても改行コードが入らない。|は改行コードが入る。__
mysql_replication_roleという変数はサーバー別に定義されている。
masterサーバーの場合は'master'で、slaveサーバーの場合は'slave'になる。
mysql_replication_masterとmysql_replication_userという変数は「group_vars/db.yml」に定義されている。
このタスクはmasterサーバーでレプリケーション関連変数が定義されている場合に実行される。

* get replication slave status to slave

slaveサーバーからslaveの状態を取得してregisterでslaveという名前で保存する。
debugでslave変数を確認してみるとどんな情報が取得されるのかすぐ分かる。
最初実行した時と２度目以降の値が異なるので、是非確認して見よう。
このタスクはslaveサーバーでレプリケーション関連変数が定義されている場合に実行される。

* change replication master to slave

slaveサーバーからchange masterを実行する。
masterサーバーの情報をslaveに記録する。
詳細は「はじまる前に」のMySQL関連記事を参照にしよう。
このタスクはslaveサーバーでレプリケーション関連変数が定義されていて先ほど取得したslave状態からIs_Slaveが定義されていてその値がfalseの場合に実行される。
__このタスクでは処理結果をslaveという変数に保存することがポイントだ。__

* start replication to slave

slaveサーバーからレプリケーションのslaveモードを実行する。
このタスクの実行条件は「change replication master to slave」タスクと同じだ。

### mysqld --initializeの場合

こっちがMySQLサーバー設置の正式ルートであろう。
既存playbookとの差分を用意した。

```diff:roles/mysql/tasks/main.yml
diff --git a/roles/mysql/tasks/main.yml b/roles/mysql/tasks/main.yml
index b680ff4..b3eb029 100644
--- a/roles/mysql/tasks/main.yml
+++ b/roles/mysql/tasks/main.yml
@@ -12,16 +12,23 @@
   register: log_file
   changed_when: false

-- name: initialize insecure mysql
-  command: mysqld --initialize-insecure --user=mysql
+- name: start the mysqld
+  service: name=mysqld state=started enabled=yes
+
+- name: get the temporary root password
+  shell: cat {{ mysql_log_file }} | grep 'temporary password'
+  register: temporary_password
   changed_when: false
   when: log_file.stat.size == 0

-- name: start the mysqld
-  service: name=mysqld state=started enabled=yes
+- name: change the root password
+  command: >
+    mysqladmin password '{{ mysql_user_password }}' -u root -p'{{ temporary_password.stdout.split(" ")[-1] }}'
+  changed_when: false
+  when: log_file.stat.size == 0

-- name: change the mysql from insecure to secure
-  command: mysql -u root -e "{{ mysql_secure_installation_query }}"
+- name: run mysql_secure_installation
+  command: mysql_secure_installation -u root -p'{{ mysql_user_password }}' -D
   changed_when: false
   when: log_file.stat.size == 0
```

MySQLを初期化しない状態でmysqldを起動すると、自動的にsecureで初期化してしまう。
それでmysqldを起動する前にinsecureで初期化が必要だった。
今度はmysql_secure_installationを使うので、こんな処理は要らない。

__代わりに初期化のとき割り当てられたrootパスワードを取得する必要がある。__
__初期rootパスワードを改めて決めたrootパスワードで変更後、変更したrootパスワードを用いてmysql_secure_installationを実行する。__
このタスクは最初1回だけ走ればいい。
それで「log_file.stat.size == 0」の条件を各タスクに入れて置いた。

__注意を払わなければならないのは、rootパスワードをsingle quotationで囲む必要があるということだ。__
その理由については「はじまる前に」の記事内コメントに書かれている。

## variables

このplaybookを作成することでどこにどんな変数を書けばいいのかしっかりと掴んだ。
group_vars、host_vars、vars順で説明する。

### group_vars

MySQLサーバー群で共通的に使われる変数を設定する。

```ruby:group_vars/db.yml
mysql_user_name: root
mysql_user_password: <PASSWORD>
mysql_replication_user:
  name: repl
  host: 192.168.33.%
  password: <PASSWORD>
mysql_replication_master:
  host: 192.168.33.32
  port: 3306
```

上記の変数らはmasterとslaveのようなサーバーの役割に関係なく共通的に使われる。
group_varsとhost_varsはパスワードのような重要情報を書く時に有用だ。
バージョン管理の対象から外せて運用しやすい。
勿論暗号化ができれば暗号化した内容を書いた方がいいだろう。

### host_vars

各サーバー別情報を保存する変数を設定する。

```ruby:host_vars/db1.yml
mysql_server_id: 1
mysql_replication_role: 'master'
```

```ruby:host_vars/db2.yml
mysql_server_id: 2
mysql_replication_role: 'slave'
```

```ruby:host_vars/db3.yml
mysql_server_id: 3
mysql_replication_role: 'slave'
```

mysql_server_idは各サーバーの識別番号になる。
MySQLはいろんなレプリケーション構成ができる。
その構成に合わせて各サーバーの変数を設定すればいい。

### vars

このplaybookで使われる変数を設定する。

```ruby:roles/mysql/vars/main.yml
mysql_yum_repository: https://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm
mysql_yum_packages:
  - MySQL-python
  - mysql-community-server
mysql_secure_installation_query: >
  ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '{{ mysql_user_password }}';
  DELETE FROM mysql.user WHERE User='';
  DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
  DROP DATABASE IF EXISTS test;
  DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
  FLUSH PRIVILEGES;
mysql_log_file: /var/log/mysqld.log
mysql_slow_log_file: /var/log/mysqld-slow.log
mysql_my_cnf:
  - name: replication
    content: |
      server-id={{ mysql_server_id }}
      gtid_mode=on
      enforce-gtid-consistency
      log-bin=mysql-bin
      log_slave_updates
  - name: slow query log
    content: |
      slow_query_log=1
      slow_query_log_file={{ mysql_slow_log_file }}
      long_query_time=1
      log_queries_not_using_indexes
      log_slow_admin_statements
  - name: enable strict sql mode
    content: |
      sql_mode=TRADITIONAL,NO_AUTO_VALUE_ON_ZERO,ONLY_FULL_GROUP_BY
```

playbookの中から変更される可能性があるものについては変数にした方がいい。
mysql_yum_repositoryとmysql_yum_packagesはバージョンによって十分変更される可能性が高いため、変数にして置いた。
mysql_secure_installation_queryはmysql_secure_installationと同等な処理を行うためのクエリ文である。
__&gt;で定義されているので、playbookでは1行で処理される。__
mysql_secure_installationを使う場合は削除してもいい。
mysql_log_fileはデフォルトのログファイルで、mysql_slow_log_fileはスロークエリを残すためのログファイルである。
mysql_my_cnfでは各global設定をListで書いた。
my.cnfファイルに書く時はblockinfileモジュールを使っている。
Ansibleで設定ファイルを書く方法については次の記事を参照しよう。

[Ansible ファイルを書き換える方法](https://qiita.com/park-jh/items/8676ec76b8313357194f)

## 参考

* [ansible-role-mysql](https://github.com/geerlingguy/ansible-role-mysql)

## 結果

おまけで処理結果をコピペして置いた。
最初実行する時と２度目以降実行する時の違いに目を通しておこう。
変更事項がない場合は２度目実行した時にchanaged=0でなければならない。

```sh
 ansible (master) ✗ vagrant destroy -f db1 db2 db3; vagrant up db1 db2 db3; ansible-playbook db.yml
==> db3: Forcing shutdown of VM...
==> db3: Destroying VM and associated drives...
==> db2: Forcing shutdown of VM...
==> db2: Destroying VM and associated drives...
==> db1: Forcing shutdown of VM...
==> db1: Destroying VM and associated drives...
Bringing machine 'db1' up with 'virtualbox' provider...
Bringing machine 'db2' up with 'virtualbox' provider...
Bringing machine 'db3' up with 'virtualbox' provider...
==> db1: Importing base box 'bento/centos-7.3'...
==> db1: Matching MAC address for NAT networking...
==> db1: Checking if box 'bento/centos-7.3' is up to date...
==> db1: Setting the name of the VM: db1
==> db1: Clearing any previously set network interfaces...
==> db1: Preparing network interfaces based on configuration...
    db1: Adapter 1: nat
    db1: Adapter 2: hostonly
==> db1: Forwarding ports...
    db1: 22 (guest) => 2232 (host) (adapter 1)
==> db1: Booting VM...
==> db1: Waiting for machine to boot. This may take a few minutes...
    db1: SSH address: 127.0.0.1:2232
    db1: SSH username: vagrant
    db1: SSH auth method: private key
    db1: Warning: Remote connection disconnect. Retrying...
    db1: Warning: Connection reset. Retrying...
    db1: Warning: Remote connection disconnect. Retrying...
    db1: Warning: Connection reset. Retrying...
    db1: Warning: Remote connection disconnect. Retrying...
    db1: Warning: Connection reset. Retrying...
    db1: Warning: Remote connection disconnect. Retrying...
    db1: Warning: Connection reset. Retrying...
    db1: Warning: Remote connection disconnect. Retrying...
    db1: Warning: Connection reset. Retrying...
    db1: Warning: Remote connection disconnect. Retrying...
==> db1: Machine booted and ready!
==> db1: Checking for guest additions in VM...
==> db1: Setting hostname...
==> db1: Configuring and enabling network interfaces...
    db1: SSH address: 127.0.0.1:2232
    db1: SSH username: vagrant
    db1: SSH auth method: private key
==> db1: Mounting shared folders...
    db1: /vagrant => /Users/devtopia
==> db2: Importing base box 'bento/centos-7.3'...
==> db2: Matching MAC address for NAT networking...
==> db2: Checking if box 'bento/centos-7.3' is up to date...
==> db2: Setting the name of the VM: db2
==> db2: Clearing any previously set network interfaces...
==> db2: Preparing network interfaces based on configuration...
    db2: Adapter 1: nat
    db2: Adapter 2: hostonly
==> db2: Forwarding ports...
    db2: 22 (guest) => 2233 (host) (adapter 1)
==> db2: Booting VM...
==> db2: Waiting for machine to boot. This may take a few minutes...
    db2: SSH address: 127.0.0.1:2233
    db2: SSH username: vagrant
    db2: SSH auth method: private key
    db2: Warning: Connection reset. Retrying...
    db2: Warning: Remote connection disconnect. Retrying...
    db2: Warning: Connection reset. Retrying...
    db2: Warning: Remote connection disconnect. Retrying...
    db2: Warning: Connection reset. Retrying...
    db2: Warning: Remote connection disconnect. Retrying...
    db2: Warning: Connection reset. Retrying...
    db2: Warning: Remote connection disconnect. Retrying...
==> db2: Machine booted and ready!
==> db2: Checking for guest additions in VM...
==> db2: Setting hostname...
==> db2: Configuring and enabling network interfaces...
    db2: SSH address: 127.0.0.1:2233
    db2: SSH username: vagrant
    db2: SSH auth method: private key
==> db2: Mounting shared folders...
    db2: /vagrant => /Users/devtopia
==> db3: Importing base box 'bento/centos-7.3'...
==> db3: Matching MAC address for NAT networking...
==> db3: Checking if box 'bento/centos-7.3' is up to date...
==> db3: Setting the name of the VM: db3
==> db3: Clearing any previously set network interfaces...
==> db3: Preparing network interfaces based on configuration...
    db3: Adapter 1: nat
    db3: Adapter 2: hostonly
==> db3: Forwarding ports...
    db3: 22 (guest) => 2234 (host) (adapter 1)
==> db3: Booting VM...
==> db3: Waiting for machine to boot. This may take a few minutes...
    db3: SSH address: 127.0.0.1:2234
    db3: SSH username: vagrant
    db3: SSH auth method: private key
    db3: Warning: Remote connection disconnect. Retrying...
    db3: Warning: Connection reset. Retrying...
    db3: Warning: Remote connection disconnect. Retrying...
    db3: Warning: Connection reset. Retrying...
    db3: Warning: Remote connection disconnect. Retrying...
    db3: Warning: Connection reset. Retrying...
    db3: Warning: Remote connection disconnect. Retrying...
    db3: Warning: Connection reset. Retrying...
==> db3: Machine booted and ready!
==> db3: Checking for guest additions in VM...
==> db3: Setting hostname...
==> db3: Configuring and enabling network interfaces...
    db3: SSH address: 127.0.0.1:2234
    db3: SSH username: vagrant
    db3: SSH auth method: private key
==> db3: Mounting shared folders...
    db3: /vagrant => /Users/devtopia

PLAY [db] ****************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************************************************************************************************************
ok: [db2]
ok: [db3]
ok: [db1]

TASK [base : include_tasks] **********************************************************************************************************************************************************************************************************************
included: /Users/devtopia/Apps/ansible/roles/base/tasks/common.yml for db1, db2, db3

TASK [base : install common yum packages] ********************************************************************************************************************************************************************************************************
changed: [db1] => (item=[u'libselinux-python', u'epel-release', u'bind-utils', u'telnet'])
changed: [db3] => (item=[u'libselinux-python', u'epel-release', u'bind-utils', u'telnet'])
changed: [db2] => (item=[u'libselinux-python', u'epel-release', u'bind-utils', u'telnet'])

TASK [base : add mappings to /etc/hosts] *********************************************************************************************************************************************************************************************************
changed: [db3] => (item={u'ip': u'192.168.33.10', u'name': u'jumpbox'})
changed: [db2] => (item={u'ip': u'192.168.33.10', u'name': u'jumpbox'})
changed: [db1] => (item={u'ip': u'192.168.33.10', u'name': u'jumpbox'})
changed: [db3] => (item={u'ip': u'192.168.33.21', u'name': u'dev'})
changed: [db2] => (item={u'ip': u'192.168.33.21', u'name': u'dev'})
changed: [db1] => (item={u'ip': u'192.168.33.21', u'name': u'dev'})
changed: [db3] => (item={u'ip': u'192.168.33.22', u'name': u'test'})
changed: [db2] => (item={u'ip': u'192.168.33.22', u'name': u'test'})
changed: [db1] => (item={u'ip': u'192.168.33.22', u'name': u'test'})
changed: [db3] => (item={u'ip': u'192.168.33.23', u'name': u'live'})
changed: [db2] => (item={u'ip': u'192.168.33.23', u'name': u'live'})
changed: [db1] => (item={u'ip': u'192.168.33.23', u'name': u'live'})
changed: [db3] => (item={u'ip': u'192.168.33.24', u'name': u'gembox'})
changed: [db1] => (item={u'ip': u'192.168.33.24', u'name': u'gembox'})
changed: [db2] => (item={u'ip': u'192.168.33.24', u'name': u'gembox'})
changed: [db1] => (item={u'ip': u'192.168.33.31', u'name': u'proxy'})
changed: [db3] => (item={u'ip': u'192.168.33.31', u'name': u'proxy'})
changed: [db2] => (item={u'ip': u'192.168.33.31', u'name': u'proxy'})
changed: [db1] => (item={u'ip': u'192.168.33.32', u'name': u'db1'})
changed: [db3] => (item={u'ip': u'192.168.33.32', u'name': u'db1'})
changed: [db2] => (item={u'ip': u'192.168.33.32', u'name': u'db1'})
changed: [db1] => (item={u'ip': u'192.168.33.33', u'name': u'db2'})
changed: [db2] => (item={u'ip': u'192.168.33.33', u'name': u'db2'})
changed: [db3] => (item={u'ip': u'192.168.33.33', u'name': u'db2'})
changed: [db1] => (item={u'ip': u'192.168.33.34', u'name': u'db3'})
changed: [db3] => (item={u'ip': u'192.168.33.34', u'name': u'db3'})
changed: [db2] => (item={u'ip': u'192.168.33.34', u'name': u'db3'})
changed: [db1] => (item={u'ip': u'192.168.33.41', u'name': u'redmine'})
changed: [db3] => (item={u'ip': u'192.168.33.41', u'name': u'redmine'})
changed: [db2] => (item={u'ip': u'192.168.33.41', u'name': u'redmine'})
changed: [db1] => (item={u'ip': u'192.168.33.42', u'name': u'chat'})
changed: [db3] => (item={u'ip': u'192.168.33.42', u'name': u'chat'})
changed: [db2] => (item={u'ip': u'192.168.33.42', u'name': u'chat'})
changed: [db3] => (item={u'ip': u'192.168.33.51', u'name': u'gitlab'})
changed: [db2] => (item={u'ip': u'192.168.33.51', u'name': u'gitlab'})
changed: [db1] => (item={u'ip': u'192.168.33.51', u'name': u'gitlab'})
changed: [db3] => (item={u'ip': u'192.168.33.52', u'name': u'runner'})
changed: [db2] => (item={u'ip': u'192.168.33.52', u'name': u'runner'})
changed: [db1] => (item={u'ip': u'192.168.33.52', u'name': u'runner'})

TASK [base : include_tasks] **********************************************************************************************************************************************************************************************************************
included: /Users/devtopia/Apps/ansible/roles/base/tasks/locale.yml for db1, db2, db3

TASK [base : set up the locale to LANG="ja_JP.UTF-8" if os is CentOS 6.x] ************************************************************************************************************************************************************************
skipping: [db1]
skipping: [db2]
skipping: [db3]

TASK [base : set up the locale to LANG="ja_JP.UTF-8" if os is CentOS 7.x] ************************************************************************************************************************************************************************
ok: [db3]
ok: [db1]
ok: [db2]

TASK [base : include_tasks] **********************************************************************************************************************************************************************************************************************
included: /Users/devtopia/Apps/ansible/roles/base/tasks/timezone.yml for db1, db2, db3

TASK [base : set up the timezone to Asia/Tokyo if os is CentOS 6.x] ******************************************************************************************************************************************************************************
skipping: [db1]
skipping: [db2]
skipping: [db3]

TASK [base : modify the symbolick link to the Asia/Tokyo] ****************************************************************************************************************************************************************************************
skipping: [db1]
skipping: [db2]
skipping: [db3]

TASK [base : set up the timezone to Asia/Tokyo if os is CentOS 7.x] ******************************************************************************************************************************************************************************
ok: [db1]
ok: [db2]
ok: [db3]

TASK [base : include_tasks] **********************************************************************************************************************************************************************************************************************
included: /Users/devtopia/Apps/ansible/roles/base/tasks/ntp.yml for db1, db2, db3

TASK [base : install the ntp] ********************************************************************************************************************************************************************************************************************
changed: [db1]
changed: [db3]
changed: [db2]

TASK [base : copy the ntp's config file] *********************************************************************************************************************************************************************************************************
changed: [db3]
changed: [db2]
changed: [db1]

TASK [base : ntp is running and enabled] *********************************************************************************************************************************************************************************************************
changed: [db3]
changed: [db2]
changed: [db1]

TASK [base : include_tasks] **********************************************************************************************************************************************************************************************************************
included: /Users/devtopia/Apps/ansible/roles/base/tasks/sudoers.yml for db1, db2, db3

TASK [base : create administrators] **************************************************************************************************************************************************************************************************************
changed: [db3]
changed: [db1]
changed: [db2]

TASK [base : grant sudo permission to the admin user] ********************************************************************************************************************************************************************************************
changed: [db2]
changed: [db1]
changed: [db3]

TASK [base : copy a public key to the target node] ***********************************************************************************************************************************************************************************************
changed: [db1]
changed: [db2]
changed: [db3]

TASK [base : copy a private key to the target node] **********************************************************************************************************************************************************************************************
changed: [db1]
changed: [db2]
changed: [db3]

TASK [mysql : install the mysql rpm] *************************************************************************************************************************************************************************************************************
changed: [db1]
changed: [db2]
changed: [db3]

TASK [mysql : install the mysql] *****************************************************************************************************************************************************************************************************************
changed: [db3] => (item=[u'MySQL-python', u'mysql-community-server'])
changed: [db2] => (item=[u'MySQL-python', u'mysql-community-server'])
changed: [db1] => (item=[u'MySQL-python', u'mysql-community-server'])

TASK [mysql : check if mysql log file size is 0 or not] ******************************************************************************************************************************************************************************************
ok: [db3]
ok: [db1]
ok: [db2]

TASK [mysql : start the mysqld] ******************************************************************************************************************************************************************************************************************
changed: [db2]
changed: [db3]
changed: [db1]

TASK [mysql : get the temporary root password] ***************************************************************************************************************************************************************************************************
ok: [db1]
ok: [db2]
ok: [db3]

TASK [mysql : change the root password] **********************************************************************************************************************************************************************************************************
ok: [db2]
ok: [db1]
ok: [db3]

TASK [mysql : run mysql_secure_installation] *****************************************************************************************************************************************************************************************************
ok: [db1]
ok: [db2]
ok: [db3]

TASK [mysql : create a log file to write the slow query] *****************************************************************************************************************************************************************************************
changed: [db2]
changed: [db3]
changed: [db1]

TASK [mysql : setting up to my.cnf] **************************************************************************************************************************************************************************************************************
changed: [db1] => (item={u'content': u'server-id=1\ngtid_mode=on\nenforce-gtid-consistency\nlog-bin=mysql-bin\nlog_slave_updates\n', u'name': u'replication'})
changed: [db2] => (item={u'content': u'server-id=2\ngtid_mode=on\nenforce-gtid-consistency\nlog-bin=mysql-bin\nlog_slave_updates\n', u'name': u'replication'})
changed: [db3] => (item={u'content': u'server-id=3\ngtid_mode=on\nenforce-gtid-consistency\nlog-bin=mysql-bin\nlog_slave_updates\n', u'name': u'replication'})
changed: [db1] => (item={u'content': u'slow_query_log=1\nslow_query_log_file=/var/log/mysqld-slow.log\nlong_query_time=1\nlog_queries_not_using_indexes\nlog_slow_admin_statements\n', u'name': u'slow query log'})
changed: [db2] => (item={u'content': u'slow_query_log=1\nslow_query_log_file=/var/log/mysqld-slow.log\nlong_query_time=1\nlog_queries_not_using_indexes\nlog_slow_admin_statements\n', u'name': u'slow query log'})
changed: [db3] => (item={u'content': u'slow_query_log=1\nslow_query_log_file=/var/log/mysqld-slow.log\nlong_query_time=1\nlog_queries_not_using_indexes\nlog_slow_admin_statements\n', u'name': u'slow query log'})
changed: [db1] => (item={u'content': u'sql_mode=TRADITIONAL,NO_AUTO_VALUE_ON_ZERO,ONLY_FULL_GROUP_BY\n', u'name': u'enable strict sql mode'})
changed: [db2] => (item={u'content': u'sql_mode=TRADITIONAL,NO_AUTO_VALUE_ON_ZERO,ONLY_FULL_GROUP_BY\n', u'name': u'enable strict sql mode'})
changed: [db3] => (item={u'content': u'sql_mode=TRADITIONAL,NO_AUTO_VALUE_ON_ZERO,ONLY_FULL_GROUP_BY\n', u'name': u'enable strict sql mode'})

TASK [mysql : restart the mysqld] ****************************************************************************************************************************************************************************************************************
changed: [db2]
changed: [db1]
changed: [db3]

TASK [mysql : create replication user to master] *************************************************************************************************************************************************************************************************
skipping: [db2]
skipping: [db3]
changed: [db1]

TASK [mysql : get replication slave status to slave] *********************************************************************************************************************************************************************************************
skipping: [db1]
ok: [db2]
ok: [db3]

TASK [mysql : debug] *****************************************************************************************************************************************************************************************************************************
ok: [db1] => {
    "msg": {
        "changed": false,
        "skip_reason": "Conditional result was False",
        "skipped": true
    }
}
ok: [db2] => {
    "msg": {
        "Is_Slave": false,
        "changed": false,
        "failed": false,
        "msg": "Server is not configured as mysql slave"
    }
}
ok: [db3] => {
    "msg": {
        "Is_Slave": false,
        "changed": false,
        "failed": false,
        "msg": "Server is not configured as mysql slave"
    }
}

TASK [mysql : change replication master to slave] ************************************************************************************************************************************************************************************************
skipping: [db1]
changed: [db2]
changed: [db3]

TASK [mysql : start replication to slave] ********************************************************************************************************************************************************************************************************
skipping: [db1]
changed: [db2]
changed: [db3]

RUNNING HANDLER [base : restart ntpd] ************************************************************************************************************************************************************************************************************
changed: [db3]
changed: [db2]
changed: [db1]

PLAY RECAP ***************************************************************************************************************************************************************************************************************************************
db1                        : ok=30   changed=17   unreachable=0    failed=0
db2                        : ok=32   changed=18   unreachable=0    failed=0
db3                        : ok=32   changed=18   unreachable=0    failed=0

 ansible (master) ✗ ansible-playbook db.yml -t mysql

PLAY [db] ****************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************************************************************************************************************
ok: [db2]
ok: [db3]
ok: [db1]

TASK [mysql : install the mysql rpm] *************************************************************************************************************************************************************************************************************
ok: [db2]
ok: [db1]
ok: [db3]

TASK [mysql : install the mysql] *****************************************************************************************************************************************************************************************************************
ok: [db1] => (item=[u'MySQL-python', u'mysql-community-server'])
ok: [db2] => (item=[u'MySQL-python', u'mysql-community-server'])
ok: [db3] => (item=[u'MySQL-python', u'mysql-community-server'])

TASK [mysql : check if mysql log file size is 0 or not] ******************************************************************************************************************************************************************************************
ok: [db1]
ok: [db2]
ok: [db3]

TASK [mysql : start the mysqld] ******************************************************************************************************************************************************************************************************************
ok: [db2]
ok: [db3]
ok: [db1]

TASK [mysql : get the temporary root password] ***************************************************************************************************************************************************************************************************
skipping: [db1]
skipping: [db2]
skipping: [db3]

TASK [mysql : change the root password] **********************************************************************************************************************************************************************************************************
skipping: [db1]
skipping: [db2]
skipping: [db3]

TASK [mysql : run mysql_secure_installation] *****************************************************************************************************************************************************************************************************
skipping: [db1]
skipping: [db2]
skipping: [db3]

TASK [mysql : create a log file to write the slow query] *****************************************************************************************************************************************************************************************
skipping: [db1]
skipping: [db2]
skipping: [db3]

TASK [mysql : setting up to my.cnf] **************************************************************************************************************************************************************************************************************
ok: [db3] => (item={u'content': u'server-id=3\ngtid_mode=on\nenforce-gtid-consistency\nlog-bin=mysql-bin\nlog_slave_updates\n', u'name': u'replication'})
ok: [db1] => (item={u'content': u'server-id=1\ngtid_mode=on\nenforce-gtid-consistency\nlog-bin=mysql-bin\nlog_slave_updates\n', u'name': u'replication'})
ok: [db2] => (item={u'content': u'server-id=2\ngtid_mode=on\nenforce-gtid-consistency\nlog-bin=mysql-bin\nlog_slave_updates\n', u'name': u'replication'})
ok: [db1] => (item={u'content': u'slow_query_log=1\nslow_query_log_file=/var/log/mysqld-slow.log\nlong_query_time=1\nlog_queries_not_using_indexes\nlog_slow_admin_statements\n', u'name': u'slow query log'})
ok: [db3] => (item={u'content': u'slow_query_log=1\nslow_query_log_file=/var/log/mysqld-slow.log\nlong_query_time=1\nlog_queries_not_using_indexes\nlog_slow_admin_statements\n', u'name': u'slow query log'})
ok: [db2] => (item={u'content': u'slow_query_log=1\nslow_query_log_file=/var/log/mysqld-slow.log\nlong_query_time=1\nlog_queries_not_using_indexes\nlog_slow_admin_statements\n', u'name': u'slow query log'})
ok: [db2] => (item={u'content': u'sql_mode=TRADITIONAL,NO_AUTO_VALUE_ON_ZERO,ONLY_FULL_GROUP_BY\n', u'name': u'enable strict sql mode'})
ok: [db1] => (item={u'content': u'sql_mode=TRADITIONAL,NO_AUTO_VALUE_ON_ZERO,ONLY_FULL_GROUP_BY\n', u'name': u'enable strict sql mode'})
ok: [db3] => (item={u'content': u'sql_mode=TRADITIONAL,NO_AUTO_VALUE_ON_ZERO,ONLY_FULL_GROUP_BY\n', u'name': u'enable strict sql mode'})

TASK [mysql : restart the mysqld] ****************************************************************************************************************************************************************************************************************
skipping: [db1]
skipping: [db2]
skipping: [db3]

TASK [mysql : create replication user to master] *************************************************************************************************************************************************************************************************
skipping: [db2]
skipping: [db3]
ok: [db1]

TASK [mysql : get replication slave status to slave] *********************************************************************************************************************************************************************************************
skipping: [db1]
ok: [db3]
ok: [db2]

TASK [mysql : debug] *****************************************************************************************************************************************************************************************************************************
ok: [db1] => {
    "msg": {
        "changed": false,
        "skip_reason": "Conditional result was False",
        "skipped": true
    }
}
ok: [db2] => {
    "msg": {
        "Auto_Position": 1,
        "Channel_Name": "",
        "Connect_Retry": 60,
        "Exec_Master_Log_Pos": 647,
        "Executed_Gtid_Set": "6a579e67-beab-11e7-b99e-08002737f846:1-2",
        "Is_Slave": true,
        "Last_Errno": 0,
        "Last_Error": "",
        "Last_IO_Errno": 0,
        "Last_IO_Error": "",
        "Last_IO_Error_Timestamp": "",
        "Last_SQL_Errno": 0,
        "Last_SQL_Error": "",
        "Last_SQL_Error_Timestamp": "",
        "Master_Bind": "",
        "Master_Host": "192.168.33.32",
        "Master_Info_File": "/var/lib/mysql/master.info",
        "Master_Log_File": "mysql-bin.000001",
        "Master_Port": 3306,
        "Master_Retry_Count": 86400,
        "Master_SSL_Allowed": "No",
        "Master_SSL_CA_File": "",
        "Master_SSL_CA_Path": "",
        "Master_SSL_Cert": "",
        "Master_SSL_Cipher": "",
        "Master_SSL_Crl": "",
        "Master_SSL_Crlpath": "",
        "Master_SSL_Key": "",
        "Master_SSL_Verify_Server_Cert": "No",
        "Master_Server_Id": 1,
        "Master_TLS_Version": "",
        "Master_UUID": "6a579e67-beab-11e7-b99e-08002737f846",
        "Master_User": "repl",
        "Read_Master_Log_Pos": 647,
        "Relay_Log_File": "db2-relay-bin.000002",
        "Relay_Log_Pos": 860,
        "Relay_Log_Space": 1065,
        "Relay_Master_Log_File": "mysql-bin.000001",
        "Replicate_Do_DB": "",
        "Replicate_Do_Table": "",
        "Replicate_Ignore_DB": "",
        "Replicate_Ignore_Server_Ids": "",
        "Replicate_Ignore_Table": "",
        "Replicate_Rewrite_DB": "",
        "Replicate_Wild_Do_Table": "",
        "Replicate_Wild_Ignore_Table": "",
        "Retrieved_Gtid_Set": "6a579e67-beab-11e7-b99e-08002737f846:1-2",
        "SQL_Delay": 0,
        "SQL_Remaining_Delay": null,
        "Seconds_Behind_Master": 0,
        "Skip_Counter": 0,
        "Slave_IO_Running": "Yes",
        "Slave_IO_State": "Waiting for master to send event",
        "Slave_SQL_Running": "Yes",
        "Slave_SQL_Running_State": "Slave has read all relay log; waiting for more updates",
        "Until_Condition": "None",
        "Until_Log_File": "",
        "Until_Log_Pos": 0,
        "changed": false,
        "failed": false
    }
}
ok: [db3] => {
    "msg": {
        "Auto_Position": 1,
        "Channel_Name": "",
        "Connect_Retry": 60,
        "Exec_Master_Log_Pos": 647,
        "Executed_Gtid_Set": "6a579e67-beab-11e7-b99e-08002737f846:1-2",
        "Is_Slave": true,
        "Last_Errno": 0,
        "Last_Error": "",
        "Last_IO_Errno": 0,
        "Last_IO_Error": "",
        "Last_IO_Error_Timestamp": "",
        "Last_SQL_Errno": 0,
        "Last_SQL_Error": "",
        "Last_SQL_Error_Timestamp": "",
        "Master_Bind": "",
        "Master_Host": "192.168.33.32",
        "Master_Info_File": "/var/lib/mysql/master.info",
        "Master_Log_File": "mysql-bin.000001",
        "Master_Port": 3306,
        "Master_Retry_Count": 86400,
        "Master_SSL_Allowed": "No",
        "Master_SSL_CA_File": "",
        "Master_SSL_CA_Path": "",
        "Master_SSL_Cert": "",
        "Master_SSL_Cipher": "",
        "Master_SSL_Crl": "",
        "Master_SSL_Crlpath": "",
        "Master_SSL_Key": "",
        "Master_SSL_Verify_Server_Cert": "No",
        "Master_Server_Id": 1,
        "Master_TLS_Version": "",
        "Master_UUID": "6a579e67-beab-11e7-b99e-08002737f846",
        "Master_User": "repl",
        "Read_Master_Log_Pos": 647,
        "Relay_Log_File": "db3-relay-bin.000002",
        "Relay_Log_Pos": 860,
        "Relay_Log_Space": 1065,
        "Relay_Master_Log_File": "mysql-bin.000001",
        "Replicate_Do_DB": "",
        "Replicate_Do_Table": "",
        "Replicate_Ignore_DB": "",
        "Replicate_Ignore_Server_Ids": "",
        "Replicate_Ignore_Table": "",
        "Replicate_Rewrite_DB": "",
        "Replicate_Wild_Do_Table": "",
        "Replicate_Wild_Ignore_Table": "",
        "Retrieved_Gtid_Set": "6a579e67-beab-11e7-b99e-08002737f846:1-2",
        "SQL_Delay": 0,
        "SQL_Remaining_Delay": null,
        "Seconds_Behind_Master": 0,
        "Skip_Counter": 0,
        "Slave_IO_Running": "Yes",
        "Slave_IO_State": "Waiting for master to send event",
        "Slave_SQL_Running": "Yes",
        "Slave_SQL_Running_State": "Slave has read all relay log; waiting for more updates",
        "Until_Condition": "None",
        "Until_Log_File": "",
        "Until_Log_Pos": 0,
        "changed": false,
        "failed": false
    }
}

TASK [mysql : change replication master to slave] ************************************************************************************************************************************************************************************************
skipping: [db1]
skipping: [db2]
skipping: [db3]

TASK [mysql : start replication to slave] ********************************************************************************************************************************************************************************************************
skipping: [db1]
skipping: [db2]
skipping: [db3]

PLAY RECAP ***************************************************************************************************************************************************************************************************************************************
db1                        : ok=8    changed=0    unreachable=0    failed=0
db2                        : ok=8    changed=0    unreachable=0    failed=0
db3                        : ok=8    changed=0    unreachable=0    failed=0

```