Debuginfo

思考とアウトプット

AnsibleでMySQLのリプリケーションする

この数日はインフラエンジニアやってます。

冪等性を保証するためにスピードが遅くなるのはスタートアップでは致命的かもしれませんが、万が一の再構築のケースを考えるとここで一生懸命作っておくのが将来のリスクに備えることかと思います。

時間を決めて、ここまでにうまくいかなかったら直で書くという選択が必要だと思ってます。

さて、MySQLのリプリケーションです。Master/Slaveですね。 負荷分散やサーバダウンに備えるためにもProductionでは必須です。

元はhttps://github.com/bennojoy/mysqlです。

私の環境でも試行錯誤して動くようになりました。

https://github.com/shohey1226/Ansible にあげておきますが、下記のような感じ。

---
- name: install mysql 
  yum: name={{ item }} state=latest
  with_items:
    - mysql
    - mysql-server
    - MySQL-python

- name: Copy the my.cnf file 
  template: src=my.cnf.j2 dest=/etc/my.cnf
  notify: 
   - restart mysqld

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

- name: update mysql root password for all root accounts
  mysql_user: name=root host={{ item }} password={{ mysql_root_db_pass }}
  with_items:
   - "{{ ansible_hostname }}"
   - 127.0.0.1
   - ::1
   - localhost
  when: ansible_hostname != 'localhost' 

- name: copy .my.cnf file with root password credentials
  template: src=.my.cnf.j2 dest=~/.my.cnf mode=0600

- name: ensure anonymous users are not in the database
  mysql_user: name='' host={{ item }} state=absent
  with_items:
    - localhost

- name: remove the test database
  mysql_db: name=test state=absent

- name: Create the database's
  mysql_db: name={{ item.name }} state=present
  with_items: mysql_db
  when: mysql_db|lower() != 'none'

- name: Create the database users
  mysql_user: name={{ item.name }}  password={{ item.pass|default("foobar") }}  
                priv={{ item.priv|default("*.*:ALL") }} state=present host={{ item.host | default("localhost") }}
  with_items: mysql_users
  when: mysql_users|lower() != 'none'

- name: Create the replication users
  mysql_user: name={{ item.name }}  host="%" password={{ item.pass|default("foobar") }}  
                priv=*.*:"REPLICATION SLAVE" state=present
  with_items: mysql_repl_user
  when: mysql_repl_role == 'master'

- name: Check if slave is already configured for replication
  mysql_replication: mode=getslave
  ignore_errors: true
  register: slave
  when: mysql_repl_role == 'slave'

- name: Get the current master servers replication status
  mysql_replication: mode=getmaster
  delegate_to: "{{ mysql_repl_master }}"
  register: repl_stat
  when: slave|failed and mysql_repl_role == 'slave' and mysql_repl_master is defined

- name: Change the master in slave to start the replication
  mysql_replication: mode=changemaster master_host={{ mysql_repl_master }} master_log_file={{ repl_stat.File }} master_log_pos={{ repl_stat.Position }} master_user={{ mysql_repl_user[0].name }} master_password={{ mysql_repl_user[0].pass }}
  when: slave|failed and mysql_repl_role == 'slave' and mysql_repl_master is defined

これを使うためにはマスターは下記のように呼び出し、

---
- hosts: db-master-hosts
  remote_user: root
  roles:
    - mysql 
  vars:
    mysql_root_db_pass: root_pass
    mysql_port: 3306
    mysql_bind_address: "0.0.0.0"
    mysql_db_id: 1
    mysql_repl_role: master
    mysql_db: 
      - name: hogehoge 
        replicate: yes
    mysql_users:
      - name: a_user
        pass: a_user_pass
        priv: "hogehoge.*:ALL"
        host: app1
      - name: a_user2 
        pass: a_user_pass2 
        priv: "hogehgoe.*:ALL"
        host: app2
    mysql_repl_user:
      - name: repel_user
        pass: repel_pass

スレーブは、

---
- hosts: db-slave-hosts
  remote_user: root
  roles:
   - mysql 
  vars:
    mysql_root_db_pass: root_pass 
    mysql_port: 3306
    mysql_bind_address: "0.0.0.0"
    mysql_db_id: 2
    mysql_repl_role: slave
    mysql_repl_master: master_host_name 
    mysql_users:
      - name: a_user 
        pass: a_user_pass 
        priv: "hogehoge.*:ALL"
        host: app1
      - name: a_user2 
        pass: a_user_pass2
        priv: "hogehoge.*:ALL"
        host: app2
    mysql_db:
      - name: hogehoge 
    mysql_repl_user:
      - name: repel_user
        pass: repel_pass

これで必要に応じてガンガンSlaveを作れそうですね!!!