MySQL 5.6 から入った以下の2つの機能を試すための環境構築を自動化するために ansible で書いた。
- 準同期レプリケーション
- マルチスレッドレプリケーション
CentOS7 向けに書いてるので、CentOS7 が予め入った環境に site.yml
を流すことで出来るだろう。なお、Ansible 2.3.2 を使ったが、もしかしたらもうちょっと低いバージョンでもいけるかもしれない。
# 必要に応じて --user と --private-key を入れる
ansible-playbook -i inventories/mysql site.yml
以下 ansible のコードを解説付きで記述する。
site.yml
MySQL ホストに MySQL ロールを流す。inventories/mysql
の中身に依存しているので変更に弱い問題がある。
---
- hosts: mysql
roles:
- mysql
become: yes
serial: 1
group_vars/all.yml
内部 IP とレプリカのマスター IP のマッピング情報を格納する。
internal_ip_map:
mysql-01: "(マスターとなる内部の IP に差し替えること)"
mysql-02: "(レプリカとなる内部の IP に差し替えること)"
replication_master_map:
mysql-02: "(マスターとなる内部の IP に差し替えること)"
inventories/mysql
ホスト名 (mysql-01
と mysql-02
)は適当につけてるので、変更してよいが、[mysql-master]
と [mysql-replica]
の名前部分、[mysql:children]
の名前部分及びその中身はいじらないこと(動かなくなる)。また、ansible_host
の部分の書き換えもお忘れなく。
mysql-01 mysql_server_id=1 ansible_host=(マスターとなる IP アドレスに差し替えること)
mysql-02 mysql_server_id=2 ansible_host=(レプリカとなる IP アドレスに差し替えること)
[mysql-master]
mysql-01
[mysql-replica]
mysql-02
[mysql:children]
mysql-master
mysql-replica
roles/mysql/defaults/main.yml
パスワードはここではべた書きしてるが、本来は Ansible Vault 使って外部化するべきである。
mysql_user_uid: 10000
mysql_user_gid: 10000
mysql_root_password: root
mysql_repl_password: repl
roles/mysql/handlers/main.yml
systemd を使って MySQL の再起動を行う
---
- name: restart mysql
systemd:
name: mysqld.service
state: restarted
daemon_reload: yes
roles/mysql/tasks/main.yml
メインとなる部分。これは以下のフローで MySQL サーバを構築する。
- MySQL ユーザとグループの作成
- yum レポジトリ経由で MySQL 5.7 系の最新版のクライアント及びサーバをインストール
- 最初から内蔵されてる MariaDB とのパッケージ衝突対策のため予め MariaDB のライブラリを削除する
- MySQL のデータディレクトリを確認し、初期化されてなければ初期化する
- パッケージデフォルトの
/var/lib/mysql
直指定
- パッケージデフォルトの
- サーバ側の my.cnf デプロイ
- MySQL サーバ起動
- root パスワード変更
- クライアント側の my.cnf デプロイ
- マスター及びレプリカの処理に分岐
---
- name: create mysql group
group:
name: mysql
gid: "{{ mysql_user_gid }}"
state: present
- name: create mysql user
user:
name: mysql
uid: "{{ mysql_user_uid }}"
group: mysql
shell: /bin/false
state: present
- name: fetch yum repository
yum:
name: https://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm
- name: remove mariadb library due to confliction
yum:
name: mariadb-libs
state: absent
- name: install mysql packages
yum:
name: "{{ item }}"
state: installed
with_items:
- mysql-community-client
- mysql-community-libs
- mysql-community-libs-compat
- mysql-community-server
- MySQL-python
- name: check mysql database is initialized
find:
paths: /var/lib/mysql
register: mysql_database_directory
- name: set initialization state
set_fact:
mysql_has_been_initialized: "{{ mysql_database_directory.files | length > 0 }}"
- name: deploy server my.cnf
template:
src: server-my.cnf.j2
dest: /etc/my.cnf
mode: 0600
owner: mysql
group: mysql
notify: restart mysql
- name: ensure mysql database initialized
shell: mysqld --user=mysql --initialize-insecure
when: 'not mysql_has_been_initialized'
- name: start mysql server
systemd:
name: mysqld.service
state: started
daemon_reload: yes
- name: change root user password
mysql_user:
name: root
password: "{{ mysql_root_password }}"
state: present
login_host: localhost
login_user: root
host_all: yes
- name: deploy client my.cnf for current user
template:
src: client-my.cnf.j2
dest: "/home/{{ ansible_user }}/.my.cnf"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: 0600
- name: deploy client my.cnf for root user
template:
src: client-my.cnf.j2
dest: "/root/.my.cnf"
owner: root
group: root
mode: 0600
- include: master.yml
when: '"mysql-master" in group_names'
- include: replica.yml
when: '"mysql-replica" in group_names'
roles/mysql/tasks/master.yml
マスター側は以下の処理を行う
- レプリケーションユーザの作成
- 匿名ユーザとテスト用データベースの削除
---
- name: create replication user
mysql_user:
name: repl
password: "{{ mysql_repl_password }}"
host: "{{ internal_ip_map[item] }}"
priv: '*.*:SUPER,REPLICATION SLAVE'
state: present
with_items:
- "{{ groups['mysql-replica'] }}"
- name: remove anonymous mysql user
mysql_user:
name: ""
state: absent
host_all: yes
- name: remove test database
mysql_db:
name: test
state: absent
roles/mysql/tasks/replica.yml
レプリカ(スレーブとも)側は以下の処理を行う
- レプリケーション停止
- マスターのバイナリログのファイル名と位置を保存
- 上記の情報から
CHANGE MASTER
を発行 - レプリケーション再開
---
- name: stop replication
mysql_replication:
mode: stopslave
- name: get replication master status
mysql_replication:
mode: getmaster
login_host: "{{ replication_master_map[inventory_hostname] }}"
login_user: repl
login_password: "{{ mysql_repl_password }}"
register: replication_master_info
- name: change replication master
mysql_replication:
mode: changemaster
master_host: "{{ replication_master_map[inventory_hostname] }}"
master_log_file: "{{ replication_master_info.File }}"
master_log_pos: "{{ replication_master_info.Position }}"
master_user: repl
master_password: "{{ mysql_repl_password }}"
- name: start replication
mysql_replication:
mode: startslave
roles/mysql/templates/client-my.cnf.j2
root ユーザでログインするための機密情報を格納している
# {{ ansible_managed }}
[client]
user = root
password = {{ mysql_root_password }}
roles/mysql/templates/server-my.cnf.j2
今回のキモとなる部分。
準同期レプリケーションのために以下の項目を入れている。http://qiita.com/fetaro/items/f56c3f4e5efc66692705 を参考にした。
plugin_load = "rpl_semi_sync_master=semisync_master.so"
-
rpl_semi_sync_master_enabled
- マスターでの準同期レプリケーションを有効かつ
plugin_load
により構築時から有効にする
- マスターでの準同期レプリケーションを有効かつ
plugin_load = "rpl_semi_sync_slave=semisync_slave.so"
-
rpl_semi_sync_slave_enabled
- レプリカでの準同期レプリケーションを有効かつ
plugin_load
により構築時から有効にする
- レプリカでの準同期レプリケーションを有効かつ
マルチスレッドレプリケーションのために以下の項目を入れている。マルチスレッドレプリケーションについての情報は http://qiita.com/smallpalace/items/e9fd924078b663d7616b がまとまってる。
-
slave_parallel_workers
- マルチスレッドレプリケーションが有効になる
slave_parallel_type
slave_preserve_commit_order
log_slave_updates
gtid_mode
あくまで準同期レプリケーションとマルチスレッドレプリケーションを有効にするための設定なので特にチューニングしていない
# {{ ansible_managed }}
[mysqld]
server_id = {{ mysql_server_id }}
gtid_mode = on
enforce-gtid-consistency = on
skip_name_resolve = on
symbolic_links = 0
{% if 'mysql-master' in group_names %}
# replication master
log_bin = mysql-bin
expire_logs_days = 7
binlog_format = ROW
master_info_repository = TABLE
# semi-sync replication
plugin_load = "rpl_semi_sync_master=semisync_master.so"
rpl_semi_sync_master_enabled = 1
rpl_semi_sync_master_timeout = 10000
{% elif 'mysql-replica' in group_names %}
# replication replica
read_only = on
relay_log = relay-bin
relay_log_index = relay-bin.index
relay_log_info_repository = TABLE
relay_log_recovery = on
# semi-sync replication
plugin_load = "rpl_semi_sync_slave=semisync_slave.so"
rpl_semi_sync_slave_enabled = 1
# multi-threaded replication
slave_parallel_workers = 2
slave_parallel_type = LOGICAL_CLOCK
slave_preserve_commit_order = on
log_slave_updates = on
{% endif %}
# innodb
innodb_large_prefix = on
innodb_file_per_table = on
# performance schema
performance_schema = on