LoginSignup
7
5

More than 5 years have passed since last update.

MySQL の準同期レプリケーション + マルチスレッドレプリケーションを ansible で組む

Posted at

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 の中身に依存しているので変更に弱い問題がある。

site.yml
---
- hosts: mysql
  roles:
    - mysql
  become: yes
  serial: 1

group_vars/all.yml

内部 IP とレプリカのマスター IP のマッピング情報を格納する。

group_vars/all.yml
internal_ip_map:
  mysql-01: "(マスターとなる内部の IP に差し替えること)"
  mysql-02: "(レプリカとなる内部の IP に差し替えること)"

replication_master_map:
  mysql-02: "(マスターとなる内部の IP に差し替えること)"

inventories/mysql

ホスト名 (mysql-01mysql-02)は適当につけてるので、変更してよいが、[mysql-master][mysql-replica] の名前部分、[mysql:children] の名前部分及びその中身はいじらないこと(動かなくなる)。また、ansible_host の部分の書き換えもお忘れなく。

inventories/mysql
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 使って外部化するべきである。

roles/mysql/defaults/main.yml
mysql_user_uid: 10000
mysql_user_gid: 10000

mysql_root_password: root
mysql_repl_password: repl

roles/mysql/handlers/main.yml

systemd を使って MySQL の再起動を行う

roles/mysql/handlers/main.yml
---
- 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 デプロイ
  • マスター及びレプリカの処理に分岐
roles/mysql/tasks/main.yml
---
- 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

マスター側は以下の処理を行う

  • レプリケーションユーザの作成
  • 匿名ユーザとテスト用データベースの削除
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 を発行
  • レプリケーション再開
roles/mysql/tasks/replica.yml
---
- 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 ユーザでログインするための機密情報を格納している

roles/mysql/templates/client-my.cnf.j2
# {{ 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

あくまで準同期レプリケーションとマルチスレッドレプリケーションを有効にするための設定なので特にチューニングしていない

roles/mysql/templates/server-my.cnf.j2
# {{ 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
7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5