AnsibleでMySQLの環境を作成する（おまけ付き）

## やりたいこと
**Ansibleでこの辺を自動化する**
- MySQLのインストール
- DBユーザの作成
- DBの作成
- 別のDBからデータを取得してリストア

## 実施環境

* OS: OS X Yosemite (10.11.6)
* Vagrant: 1.8.5
* Virtualbox: 5.1.6 r110634
* Ansible: 2.1.2.0

## 1. 各ツールの準備（インストール済みの場合不要）

(省略)　[この辺](http://qiita.com/negibouze/items/8ff4deb2e25b890d7827#1-%E5%90%84%E3%83%84%E3%83%BC%E3%83%AB%E3%81%AE%E6%BA%96%E5%82%99%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E6%B8%88%E3%81%BF%E3%81%AE%E5%A0%B4%E5%90%88%E4%B8%8D%E8%A6%81)に書いてあります

#### step1. Homebrewをインストールする
#### step2. Homebrew Caskをインストールする
#### step3. VirtualBoxをインストールする
#### step4. Vagrantをインストールする
#### step5. Vagrant-vbguest pluginをインストールする
#### step6. Ansibleをインストールする

## 2. Vagrantfileの作成

(省略)　[この辺](http://qiita.com/negibouze/items/8ff4deb2e25b890d7827#2-vagrantfile%E3%81%AE%E4%BD%9C%E6%88%90)とか[この辺](http://qiita.com/negibouze/items/df2ad24e1477833bece6#2-vagrantfile%E3%81%AE%E7%B7%A8%E9%9B%86)に書いてあります

ここまででこんな感じ

```
Vagrant.configure("2") do |config|

  config.vm.box = "puppetlabs/centos-6.6-64-nocm"
  config.vm.box_url = "https://atlas.hashicorp.com/puppetlabs/boxes/centos-6.6-64-nocm/versions/1.0.3/providers/virtualbox.box"

  config.ssh.insert_key = false
  config.hostmanager.enabled = true
  config.hostmanager.manage_host = true

  # create db server
  config.vm.define :"db" do |host|
    host.vm.hostname = "db"
    host.vm.network :private_network, ip: "192.168.34.21", netmask: "255.255.255.0"
    host.vm.network :private_network, ip: "192.168.33.21", virtualbox__intnet: "mv"
    # ansible
    host.vm.provision "ansible" do |ansible|
      ansible.playbook = "provisioning/dbservers.yml"
      ansible.inventory_path = "provisioning/hosts"
      ansible.limit = 'all'
    end
  end
end
```

## 3. Ansibleのタスクを作成
#### step1. ベストプラクティスを参考にディレクトリとファイルを作成する
```
roles/
   hosts
   dbservers.yml
   common/
      tasks/
         main.yml
   database/
      handlers/
         main.yml
      tasks/
         main.yml
         mysql.yml
         restore.yml
      vars/
         main.yml
```

## 4. AnsibleのPlaybookを記述する
#### step1. hosts

```
[dbservers]
127.0.0.1 ansible_ssh_private_key_file=.vagrant/machines/db/virtualbox/private_key ansible_ssh_user=vagrant
```

#### step2. dbservers.yml

```
---
- hosts: dbservers
  become: true
  roles:
    - common
    - database
```

#### step3. common/tasks/main.yml

```
---
- name: change timezone
  command: cp -p /usr/share/zoneinfo/Japan /etc/localtime
- name: remove all rules from iptables
  command: /sbin/iptables -F
- name: iptables stop
  service: name=iptables state=stopped
- name: iptables off
  command: /sbin/chkconfig iptables off
```

#### step4. database/handlers/main.yml

```
---
- name: enable mysql launch settings
  command: /sbin/chkconfig mysqld on
```

#### step5. database/tasks/main.yml

```
---
- include: mysql.yml
- include: restore.yml
```

#### step6. database/tasks/mysql.yml

```
---
- name: MySQL5.1（デフォルト）削除
  yum: name=mysql* state=absent
- name: install repository
  yum: name=http://dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm state=present
- name: MySQL5.6インストール
  yum: name={{ item }} state=present
  with_items:
    - mysql
    - mysql-devel
    - mysql-server
    - mysql-utilities
    - MySQL-python
  notify:
    - enable mysql launch settings
- name: start MySQL
  service: name=mysqld state=started enabled=yes
- name: データベース作成
  mysql_db: db={{ item }} state=present encoding=utf8
  with_items: "{{ dbnames }}"
- name: データベースのユーザ作成（権限も付与する）
  mysql_user: >
    name={{ dbuser }}
    password="{{ dbpass }}"
    host={{ item[0] }}
    priv={{ item[1] }}.*:ALL
    append_privs=yes
    state=present
  with_nested:
    - "{{ hosts }}"
    - "{{ dbnames }}"
```

#### step7. database/tasks/restore.yml

```
---
- name: ダンプファイル作成(データは別のDBから取ってくる)
  mysql_db: >
    login_host={{ remotehost }}
    login_user={{ dbuser }}
    login_password={{ dbpass }}
    name={{ item }}
    target=/tmp/{{ item }}.dump
    state=dump
  with_items: "{{ dbnames }}"
- name: リストア
  mysql_db: name={{ item }} target=/tmp/{{ item }}.dump state=import
  with_items: "{{ dbnames }}"
- name: ダンプファイル削除
  file: path=/tmp/{{ item }}.dump state=absent
  with_items: "{{ dbnames }}"
```

#### step7. database/vars/main.yml

```
---
dbuser: <DBユーザ名>
dbpass: <DBユーザのパスワード>
remotehost: <データ取得元のIP>
hosts:
  - "localhost"
  - "192.168.33.20"
dbnames:
  - <restoreするDB名>
  - <restoreするDB名>
```

## 5. 動作確認
#### step1. 仮想マシンを作成する

```
$ vagrant up
```

#### step2. 動作確認
DBに接続できればOK

## 6. おまけ1　--no-data（-d）を併用する
`mysql_db`モジュールはdumpのオプションが指定できないようなので`shell`で実行する
！`command`だと`>`が使えないので注意

```
- name: create no-data dump files
  shell: mysqldump -h {{ remotehost }} -u {{ dbuser }} -p{{ dbpass }} {{ item }} -d > /tmp/{{ item }}.dump
  with_items: "{{ nodatadbs }}"
```

リストアとかする時は「Jinja2 filters」の`union`を使うと良い感じに書けます

```
- name: restore databases
  mysql_db: name={{ item }} target=/tmp/{{ item }}.dump state=import
  with_items: "{{ dbnames | union(nodatadbs) }}"
```

## 7. おまけ2　Viewを含むDBに対応する
対象のDBにViewが含まれている場合、DEFINERの設定によってはアクセスできないので、リストア前に置換しておく（置換しても問題ない場合に限る）

```
- name: edit dump files
  replace: dest=/tmp/{{ item }}.dump regexp='<置換前>`@`%' replace={{ dbuser }}`@`localhost
  with_items: "{{ dbnames }}"
```

## 8. おまけ3　Windowsに対応する
WindowsにはAnsibleがインストールできないのでVM側で実行する  

#### step1. $scriptを追記

```
$script = <<SCRIPT

if ! [ `which ansible` ]; then
    yum update -y
    yum install -y http://ftp.iij.ad.jp/pub/linux/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm
    sed -i -e "s/enabled *= *1/enabled=0/g" /etc/yum.repos.d/epel.repo
    yum install --enablerepo=epel -y ansible
fi

ansible-playbook -i /vagrant/provisioning/hosts /vagrant/provisioning/dbservers.yml

SCRIPT
```

#### step2. provisionを編集する

編集前

```
host.vm.provision "ansible" do |ansible|
  ansible.playbook = "provisioning/dbservers.yml"
  ansible.inventory_path = "provisioning/hosts"
  ansible.limit = 'all'
end
```

編集後

```
config.vm.provision "shell", inline: $script
```

**mysql_db**モジュール便利ですね。