Vagrant + Ansibleで複数のEC2インスタンスの起動、プロビジョニング

コマンド1発で理想の環境ができあがるとうれしいですよね！僕は、嬉しいです！！

開発環境や、本番環境など用途は色々ですが、同時にサーバーを幾つか建てて、動かして、削除する際にはVagrantと構成管理ツールが便利です。Chefとかpupetとか色々とありますが、手軽に定義ファイルを作って利用できる[Ansible](http://www.ansible.com/home)を今回は利用します。


今回は、同じアプリケーションが動作しているけど参照しているDBが異なる5つのインスタンスの起動を行います。アプリケーションのデプロイが完了したamiからインスタンスを起動し、設定ファイルをホスト毎に切り替えます。

## Vagrantfileを作る

EC2を利用するので、[vagrant-aws](https://github.com/mitchellh/vagrant-aws)プラグインを事前に入れておいてください。


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

# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  access_key = ENV['AWS_ACCESS_KEY']
  access_secret = ENV['AWS_SECRET_KEY']
  config.vm.box = "dummy"
  config.vm.box_url = "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"


  config.vm.define :ap1 do |ap1|
    ap1.vm.provider :aws do |aws, override|
      aws.access_key_id = access_key
      aws.secret_access_key = access_secret
      aws.keypair_name = "AWS_KEYPAIR_NAME"
      aws.region = "ap-northeast-1"
      aws.ami = "ami-xxxxxx"
      aws.user_data = "#!/bin/bash\nexport TARGET_ENV=test;"
      aws.availability_zone = "ap-northeast-1a"
      aws.instance_type = "m1.small"
      aws.security_groups = ["aws_security_group"]
      override.ssh.username = "aws_user"
      override.ssh.private_key_path = "~/.ssh/aws_private_key"
      aws.tags = {
        'Name' => 'ap01',
      }
    end
    ap1.vm.provision "ansible" do |ansible|
      ansible.groups = {
        "ap1" => ["ap1"]
      }
      ansible.extra_vars = {
        "db_host" => db_host,
        "db_name" => "db_01"
      }
      ansible.playbook = "playbook.yaml"
    end
  end

  config.vm.define :ap2 do |ap2|
    ap2.vm.provider :aws do |aws, override|
      aws.access_key_id = access_key
      aws.secret_access_key = access_secret
      aws.keypair_name = "AWS_KEYPAIR_NAME"
      aws.region = "ap-northeast-1"
      aws.ami = "ami-xxxxxx"
      aws.user_data = "#!/bin/bash\nexport TARGET_ENV=test;"
      aws.availability_zone = "ap-northeast-1a"
      aws.instance_type = "m1.small"
      aws.security_groups = ["aws_security_group"]
      override.ssh.username = "aws_user"
      override.ssh.private_key_path = "~/.ssh/aws_private_key"
      aws.tags = {
        'Name' => 'ap02',
      }
    end
    ap2.vm.provision "ansible" do |ansible|
      ansible.groups = {
        "ap2" => ["ap2"]
      }
      ansible.extra_vars = {
        "db_host" => db_host,
        "db_name" => "db_02"
      }
      ansible.playbook = "playbook.yaml"
    end
  end

  config.vm.define :ap3 do |ap3|
    ap3.vm.provider :aws do |aws, override|
      aws.access_key_id = access_key
      aws.secret_access_key = access_secret
      aws.keypair_name = "AWS_KEYPAIR_NAME"
      aws.region = "ap-northeast-1"
      aws.ami = "ami-xxxxxx"
      aws.user_data = "#!/bin/bash\nexport TARGET_ENV=test;"
      aws.availability_zone = "ap-northeast-1a"
      aws.instance_type = "m1.small"
      aws.security_groups = ["aws_security_group"]
      override.ssh.username = "aws_user"
      override.ssh.private_key_path = "~/.ssh/aws_private_key"
      aws.tags = {
        'Name' => 'ap03',
      }
    end
    ap3.vm.provision "ansible" do |ansible|
      ansible.groups = {
        "ap3" => ["ap3"]
      }
      ansible.extra_vars = {
        "db_host" => db_host,
        "db_name" => "db_03"
      }
      ansible.playbook = "playbook.yaml"
    end
  end

  config.vm.define :ap4 do |ap4|
    ap4.vm.provider :aws do |aws, override|
      aws.access_key_id = access_key
      aws.secret_access_key = access_secret
      aws.keypair_name = "AWS_KEYPAIR_NAME"
      aws.region = "ap-northeast-1"
      aws.ami = "ami-xxxxxx"
      aws.user_data = "#!/bin/bash\nexport TARGET_ENV=test;"
      aws.availability_zone = "ap-northeast-1a"
      aws.instance_type = "m1.small"
      aws.security_groups = ["aws_security_group"]
      override.ssh.username = "aws_user"
      override.ssh.private_key_path = "~/.ssh/aws_private_key"
      aws.tags = {
        'Name' => 'ap04',
      }
    end
    ap4.vm.provision "ansible" do |ansible|
      ansible.groups = {
        "ap4" => ["ap4"]
      }
      ansible.extra_vars = {
        "db_host" => db_host,
        "db_name" => "db_04"
      }
      ansible.playbook = "playbook.yaml"
    end
  end

  config.vm.define :ap5 do |ap5|
    ap5.vm.provider :aws do |aws, override|
      aws.access_key_id = access_key
      aws.secret_access_key = access_secret
      aws.keypair_name = "AWS_KEYPAIR_NAME"
      aws.region = "ap-northeast-1"
      aws.ami = "ami-xxxxxx"
      aws.user_data = "#!/bin/bash\nexport TARGET_ENV=test;"
      aws.availability_zone = "ap-northeast-1a"
      aws.instance_type = "m1.small"
      aws.security_groups = ["aws_security_group"]
      override.ssh.username = "aws_user"
      override.ssh.private_key_path = "~/.ssh/aws_private_key"
      aws.tags = {
        'Name' => 'ap05',
      }
    end
    ap5.vm.provision "ansible" do |ansible|
      ansible.groups = {
        "ap5" => ["ap5"]
      }
      ansible.extra_vars = {
        "db_host" => db_host,
        "db_name" => "db_05"
      }
      ansible.playbook = "playbook.yaml"
    end
  end

end
```

## playbookを作る

Ansibleの構成定義ファイル、playbookを作ります。yamlで書けるので新しいDSLを覚えたりしなくて良いよ！！ってのがウリらしいですね。



```yaml
---
- name: Setup ecs
  hosts: all
  remote_user: aws_user
  sudo: yes
  tasks:
    
    - name: upload config
      template: src=config.ini dest=/var/www/Application/common/config/config.ini

    - name: start httpd
      service: name=httpd state=restarted enabled=yes

```


## テンプレートを用意する

設定ファイルはテンプレートを利用し、Vagrantfileの`ansible.extra_values`でそれぞれ異なる値を代入しています。そのため、


```
database.host 	= {{ db_host }}
database.name   = {{ db_name }}
```


`{{}}`で囲った中身に変数が展開されます。

## ファイルの配置

今回はこんな感じ。


```
├── Vagrantfile
├── config.ini
└── playbook.yaml
```

規模に応じて[Ansible Best Practices](http://docs.ansible.com/playbooks_best_practices.html)を採用したり。

## 起動、終了

あとは、Vagrantfileのある場所で`vagrant up --provide aws`をやります。環境を消すには`vagrant destroy`。お好みで`-f`も。

## おわり

VagrantとAnsibleの連携方法がドキュメントを読んでてもよく分からなかったので自分用にまとめました。やっぱりAnsibleは手軽に使えて便利だなぁ。