Virtualbox + Vagrant + AnsibleでWeb, DBサーバ環境をつくる

# 本エントリの目的
ローカル環境で開発をしているとデータベースやその他諸々のサービスをlocalhostで参照できて設定が楽というメリットはありますが、WebサーバとDBサーバの分かれた本番環境と構成が異なるのが気になります。

せめて本番にデプロイする前にステージング環境で確認をしておきたいところですが、個人開発ではお金を節約したいのでわざわざ本番サーバと同じVPSやAWSは使いたくありません。そこでVirtualboxの仮想マシンにVagrantとAnsibleを使って環境を構築することにします。

* Virtualbox、Vagrant、Ansibleのインストール方法、使い方などは説明しません。
* 使用したファイルはGithubに上げています。

https://github.com/seihmd/vagrant-multiserver-example

# 環境

- ホストマシン: MacOSX El Capitan
- Vagrant v1.9.1
- Ansible v2.0.2.0
- Virtualbox v5.0.24 r108355

# 仮想マシン構成・要件 

## 仮想マシン構成

- Webサーバ
    - DBサーバにアクセス可能
- DBサーバ
    - mysqlを稼働
    - Webサーバからアクセス可能

上記２サーバを"ゲスト"と呼ぶのに対し、仮想環境を構築するマシンを"ホスト"と呼びます。


## 要件

- ホストはゲストにSSH接続可能
    - DBサーバのDBにもアクセス可能
- ゲストWebサーバのポート80はポートフォワーディングでホストから接続可能
    - 要はWebサーバで動かしているアプリをホストのブラウザから見られるようにする
- サーバ構築はAnsibleで行う

## ディレクトリ構成

- :page_facing_up: Vagrantfile
- :file_folder: groupvars
    - :page_facing_up: all.yml
- :file_folder: roles
    - :file_folder: mysql
- :page_facing_up: staging　　　// inventoryファイル
- :page_facing_up: site.yml　　// vagrantから実行するplaybook
- :page_facing_up: dbservers.yml　　// DBサーバ用playbook

Vagrantfileはディレクトリで`vagrant init`コマンド実行で作成されるものです
その他はansibleで使用するファイルですが、このあたりの構成は人によって異なると思います。

今回ansibleはDBサーバにmysqlを立てるためにしか使わないのでだいぶ簡略化しています。
[groupvars/all.yml](https://github.com/seihmd/vagrant-multiserver-example/blob/master/group_vars/all.yml)にはmysqlのユーザ作成に使用するための値を設定しています。
roles/mysqlは[こちら](https://github.com/geerlingguy/ansible-role-mysql)のリポジトリから拝借して自分用に適当に改変したものです。
ともかく、ansibleファイルの構成はinventoryファイルを本番環境用のものに差し替えればそのまま同じように実行できることが重要だと思います。

## VagrantFileの記述

```ruby:Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

    VAGRANT_VM_PROVIDER = "virtualbox"
    machineNames = ["example.com.web", "example.com.db"]
    config.vm.box = "centos/7"

    N = 1

    (0..N).each do |index|
        config.vm.define machineNames[index] do |machine|

            # 仮想マシンのネットワーク設定
            machine.vm.hostname = machineNames[index]
            machine.vm.network "forwarded_port", guest: 80, host: 10020 + index, guest_ip: "192.168.33.#{10+index}"
            machine.vm.network "private_network", ip: "192.168.33.#{10+index}",  virtualbox__intnet: "example.com"
            machine.vm.network "private_network", ip: "192.168.33.#{10+index}"

            # ansibleでのprovisioning. ループの最後で実行される
            if index == N
                machine.vm.provision :ansible do |ansible|
                    ansible.limit = "all"
                    ansible.playbook = "site.yml"
                    ansible.inventory_path = "staging"
                end
            end
        end
    end
end
```

[Vagrantのドキュメント](https://www.vagrantup.com/docs/provisioning/ansible.html)を参考にループを回してWeb, DBサーバそれぞれに異なるIPアドレスを割り当てています。

仮想マシンのネットワーク設定の部分ではゲストで実行するアプリをホスト側で確認するためのforwarded_port、仮想マシン間で通信するためのprivate_networkを設定しています。

## Vagrant up してみる

ansible以外の仮想マシン構築タスクがうまく動作確認するためにとりあえず `vagrant up` してみましょう。Vagrantとansibleを使うと途中でエラーになっても再実行が簡単なので楽です。

Vagrantによる仮想マシン構築はうまく行ったのでansibleのタスクだけ実行したいという場合は `vagrant provision` コマンドが使えます。

なんか変になったので仮想マシンごと削除したい場合は `vagrant destroy` コマンドです。一回のコマンドで各サーバについて削除するか聞かれます。

```bash
    example.com.db: Are you sure you want to destroy the 'example.com.db' VM? [y/N] y
==> example.com.db: Forcing shutdown of VM...
==> example.com.db: Destroying VM and associated drives...
    example.com.web: Are you sure you want to destroy the 'example.com.web' VM? [y/N] y
==> example.com.web: Forcing shutdown of VM...
==> example.com.web: Destroying VM and associated drives...
```

`vagrant destroy example.com.web` のように指定することも可能です。

## ホストでWebアプリの稼働を確認する

疎通確認のためワンライナーでサーバを起動します。
ポート80を指定しているのはVagrantfileでforwarded_portにしているからです。

```bash
# Webサーバで実行
sudo python -m SimpleHTTPServer 80
```

ホストのブラウザで http://localhost:10020 にアクセスしてみます。

![Screen Shot 0029-01-29 at 19.32.26.png](https://qiita-image-store.s3.amazonaws.com/0/51303/0822cd25-0058-c79b-bd20-22fb7681cb0a.png)

Webサーバ側でもアクセスが来ているのがわかります。
![Screen Shot 0029-01-29 at 19.37.08.png](https://qiita-image-store.s3.amazonaws.com/0/51303/87e95537-1852-239f-aeb2-bf252e11874f.png)


## ホストからDBに接続する

Sequel ProからDBに接続するときの設定です。
SSH Keyには.vagrantフォルダ配下にあるキーを指定します。
`.vagrant/machines/example.com.db/virtualbox/private_key`

![Screen Shot 0029-01-29 at 14.06.08.png](https://qiita-image-store.s3.amazonaws.com/0/51303/b2672f32-f1cf-1d90-500a-9a6e6cc8311b.png)

## ゲストサーバ間でアクセスする

Vagrantfileのprivate_networkに指定したipアドレスでアクセスすることができます。
とりあえず簡易的にpingが通るかどうかで確認してみます。

![Screen Shot 0029-01-29 at 21.04.58.png](https://qiita-image-store.s3.amazonaws.com/0/51303/844d9d5e-0f3d-8562-2b64-71b640445dbf.png)

## トラブルシューティング

本エントリの元にした個人プロジェクト用環境をセットアップしたときはなかったのですが、記事を執筆するために手順を再現しているとうまくいかないことがありました。詳しくないので原因ははっきりしていません。

### 1. ホストのブラウザから localhost:10020 につながらない

まずVirtualboxのマシン一覧からWebサーバ → setting → network → Adaptor3 を確認します。
![Screen Shot 0029-01-29 at 21.13.50.png](https://qiita-image-store.s3.amazonaws.com/0/51303/938dbca7-be13-1b63-5d90-315d763aa819.png)

ここで指定されているNetwork Adaptor(ここではvboxnet4)をVirtualboxの環境設定で確認します。
![Screen Shot 0029-01-29 at 21.16.10.png](https://qiita-image-store.s3.amazonaws.com/0/51303/a2b6609e-3dac-738b-b165-c1a184c532ab.png)

上の画像は問題が解決したあとの設定です。解決する前はvboxnet4のIPv4 Addressが別の値になっていました（記録し忘れました）。

### 2. ゲストサーバ間でpingが通らない

原因ははっきりしませんが再起動をかけると直ったりします。