るいすときのこの物語

オタクエンジニアの雑記

AnsibleとMySQL 5.7で準同期レプリケーションを張る


覚書。 初めてAnsibleを触ってて少し躓いたところがあったので。

 

環境

・Ansible 2.3.0 ・Percona server 5.7 (MySQL 5.7_

 

 

my.cnfを配置

Master用とSlave用のmy.cnfをリモート先へ送る。

- name: Copy my.cnf to master
  copy: src="roles/dbtier/files/my.cnf.master" dest="/etc/percona-server.conf.d/mysqld.cnf" owner=root group=root mode=0644
  when: hostname.stdout.find('db-master') == 0

- name: Copy my.cnf to slave
  copy: src="roles/dbtier/files/my.cnf.slave" dest="/etc/percona-server.conf.d/mysqld.cnf" owner=root group=root mode=0644
  when: hostname.stdout.find('db-slave') == 0

 

 

 

rootパスワードを取得

MySQL 5.7から/var/log/mysqld.logにパスワードが記載されているので これを使って初期設定を行うらしい。

- name: Get percona server password
  shell: cat /var/log/mysqld.log | grep "temporary password" | awk '{print $11}'
  register: mysql_temp_pass

 

 

 

.my.cnfを/rootに設置

Ansibleのmysqlモジュールを使うために/root/.my.cnfにファイルを設置

- name: Copy .my.cnf file
  template: src=temp_my.cnf.j2 dest=/root/.my.cnf owner=root group=root mode=0644

temp_my.cnf.j2

connect-expired-passwordが地味に大事

[client]
user=root
password={{ mysql_temp_pass.stdout }}
connect-expired-password

 

 

 

rootパスワードを設定する

一時的なパスワードでは覚えられないので変更します。 パスワードの要件が厳しくなって大文字小文字記号数字で8桁以上とかだった気がする(覚えてない)

- name: Set root password
  mysql_user: name=root host=localhost password="{{ mysql_root_pass }}"
  ignore_errors: True

 

 

 

新しい.my.cnfを/rootに設置

- name: Override .my.cnf file
  template: src=new_my.cnf.j2 dest=/root/.my.cnf owner=root group=root mode=0644

new_my.cnf.j2

connect-expired-passwordを削除したバージョン

[client]
user=root
password={{ mysql_root_pass }}

 

 

 

anonymousユーザーを削除

- name: Remove all anonymous user
  mysql_user: name='' host=localhost state=absent

 

 

 

レプリケーションユーザーを作成

- name: Semi-synchronous - Create replication user
  mysql_user: >
    name="{{ mysql_rep_user }}"
    password="{{ mysql_rep_pass }}"
    priv="{{ mysql_rep_priv }}"
    host="{{ mysql_rep_host }}"
  when: hostname.stdout.find('db-master') == 0

 

 

 

FlushコマンドってAnsibleじゃ使えないよね...?

- name: Semi-synchronous - Flush
  shell: mysql --defaults-file=/root/.my.cnf -e "FLUSH PRIVILEGES";
  shell: mysql --defaults-file=/root/.my.cnf -e "FLUSH TABLES WITH READ LOCK";
  when: hostname.stdout.find('db-master') == 0

 

 

 

マスターDBからダンプして、スレーブにインポート

- name: Semi-synchronous - Dump all database
  mysql_db: state=dump name=all target=/root/all_db_dump.sql
  when: hostname.stdout.find('db-master') == 0

- name: Semi-synchronous - Send all_db_dump.sql
  shell: sshpass -p '' scp -o StrictHostKeyChecking=no all_db_dump.sql root@db-slave.local:~/
  when: hostname.stdout.find('db-master') == 0

- name: Semi-synchronous - Stop slave if running
  mysql_replication: mode=stopslave
  when: hostname.stdout.find('db-slave') == 0

- name: Semi-synchronous - Import all_db_dump.sql
  shell: mysql --defaults-file=/root/.my.cnf < /root/all_db_dump.sql
  when: hostname.stdout.find('db-slave') == 0

 

 

 

マスターのファイル名とポジションの取得

ここが一番詰まった。 Ansibleは異なるホスト間で変数の参照ができないので ファイル名とポジションの取得はスレーブのときに実行して、実際に実行するコマンドはマスター側で処理をする。

- name: Semi-synchronous - Get the master status
  mysql_replication: mode=getmaster
  delegate_to: "{{ mysql_master }}"
  register: binlog
  when: hostname.stdout.find('db-slave') == 0

 

Change master

- name: Semi-synchronous - Change the master
  mysql_replication: >
    mode=changemaster
    master_host="{{ mysql_master }}"
    master_user="{{ mysql_rep_user }}"
    master_password="{{ mysql_rep_pass }}"
    master_log_file={{ binlog.File }}
    master_log_pos={{ binlog.Position }}
  when: hostname.stdout.find('db-slave') == 0

 

 

 

スレーブの開始

- name: Semi-synchronous - Start slave
  mysql_replication: mode=startslave
  when: hostname.stdout.find('db-slave') == 0

 

 

 

スレーブの状況確認

Slave_IO_Running と Slave_SQL_RunningがどっちもYesであることを確認

- name: Semi-synchronous - Get Slave_IO_Running
  shell: mysql --defaults-file=/root/.my.cnf -e "SHOW SLAVE STATUS\G" | grep "Slave_IO_Running" | awk '{print $2}'
  register: SIOR
  failed_when: SIOR.stdout not in ["Yes"]
  when: hostname.stdout.find('db-slave') == 0

- name: Semi-synchronous - Get Slave_SQL_Running
  shell: mysql --defaults-file=/root/.my.cnf -e "SHOW SLAVE STATUS\G" | grep "Slave_SQL_Running:" | awk '{print $2}'
  register: SSQLR
  failed_when: SSQLR.stdout not in ["Yes"]
  when: hostname.stdout.find('db-slave') == 0

 

 

 

マスターでロックしていたのを解除

- name: Semi-synchnorous - Unlock tables
  shell: mysql --defaults-file=/root/.my.cnf -e "UNLOCK TABLES"
  when: hostname.stdout.find('db-master') == 0

- name: Semi-synchronous - Flush privileges
  shell: mysql --defaults-file=/root/.my.cnf -e "FLUSH PRIVILEGES"
  when: hostname.stdout.find('db-master') == 0