Dynamic Inventory を使って EC2 動的ホストに Ansible を適用する

# バージョン＆記載ソースリポジトリ

| version | 
|:---|
| ansible 1.9.2 |
| boto 2.38.0 |

ポイントとなるソースのみ載せています。全てのソースが見たい場合は、以下のリポジトリを参照してください。

* https://github.com/katsuhiko/ec2-playbooks


# 概要

EC2 インスタンスを作るたびにグローバルIP を確認して playbook を実行するのは大変です。
ec2.py を使った Dyamic Inventory を利用して、タグをつけた EC2 インスタンスに対して playbook を実行できるようにします。

先にすべての手順を実施したあとの実行方法について記載します。

## 実行方法

* hosts の確認

```shell-session
$ ansible-playbook -i production ec2-rails.example.yml --list-hosts
```

* ansible の実行

```shell-session
$ ansible-playbook -i production ec2-rails.example.yml
```


# 準備

## EC2 インスタンスのタグ付け

ansible を実行する EC2 インスタンスにタグ付けをします。
env と role の２つのタグを作ります。

| キー  | 値 | 
|:---  |:---|
| env  | production |
| role | rails |


## boto のインストール

ec2.py を利用するためには boto が必要になります。

```shell-session
$ pip install boto
```

上記でインストールできますが、ansible 実行環境を仮想環境に作る場合は boto のインストールを ansible で行うこともできます。

```yaml
- name: Add repository for ansible
  apt_repository: repo='ppa:ansible/ansible' state=present

- name: Install ansible and pip
  apt: name={{ item }} state=latest
  with_items:
    - ansible
    - python-pip

- name: Install boto
  pip: name={{ item }} state=latest
  with_items:
    - boto
```

## Shared Credentials の作成

AWS への接続は Shared Credentials を利用します。いろいろな設定方法がありますが現時点では、Shared Credentials が良いと思います。

~/.aws/credentials を作成し Key 情報を設定します。

Key 情報を作成するのは以下サイトが参考になると思います。
http://dev.classmethod.jp/cloud/aws-cli-credential-config/

```ini:~/.aws/credentials
[default]
aws_access_key_id = XXXXXXXX
aws_secret_access_key = XXXXXXXX
```

## ec2.py と ec.ini の用意

http://docs.ansible.com/ansible/intro_dynamic_inventory.html#example-aws-ec2-external-inventory-script

こちらが参考にしました。
ec2.py と ec2.ini は以下から取得し production 配下に配置します。

ec2.py : https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/ec2.py
ec2.ini : https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/ec2.ini

ディレクトリ構成については、
http://docs.ansible.com/ansible/playbooks_best_practices.html#directory-layout
こちらを参考にして
https://github.com/katsuhiko/ec2-playbooks
のように作っています。

## ec2.ini のカスタマイズ１

変更した箇所のみを記載します。

* Tokyo Region を指定します。

```ini:production/ec2.ini
regions = ap-northeast-1
```

* 利用しない AWS のサービスを利用しないようにします。IAM で作成したユーザーに権限がない場合、Forbidden が発生したためです。Forbidden が発生した場合、権限を確認するのが良さそうです。

```ini:prodcution/ec2.ini
rds = False
elasticache = False
```

* 常に最新の情報を取得するようにします。

```ini:production/ec2.ini
cache_max_age = 0
```

## ec2.ini のカスタマイズ２

ec2.ini の最後に記載されている例を参考に instance_filters を定義します。
ec2.ini で指定する instance_filters は OR 条件になりますが、playbook の hosts でもタグの絞り込みをすることで AND 条件を作ることはできます。

ec2.ini では env, role タグのうち、env タグを「production」で絞り込みます。

```ini:production/ec2.ini
instance_filters = tag:env=production
```

## private-key の指定

amazon の pem ファイルを毎回指定しなくても良いようにするために ansible.cfg を作成し、pem ファイルを指定します。

```ini:ansible.cfg
[defaults]
private_key_file=~/.ssh/xxx.pem
```

# 実行

## playbook のサンプル

playbook の hosts で role タグを「rails」で絞り込みます。
記述方法は tag_[タグ名]_[タグ値] です。
これで env, role タグの各値を AND 条件で絞り込めます。

remote_user も playbook で指定します。ansible.cfg でも指定できるようですが、どのユーザーで ssh 接続しているかがすぐにわかるので playbook で指定したほうが良いと思います。

```yml:ec2-rails.example.yml
- hosts: tag_role_rails
  remote_user: ec2-user
  sudo: yes
  vars:
    rbenv_user: ec2-user
    rbenv_ruby_version: 2.2.2
    nginx_includes: ["/var/apps/*/shared/config/nginx.conf"]
  roles:
    - ec2-yum-update
    - ec2-rbenv
    - ec2-nodejs
    - ec2-railsenv
    - ec2-nginx
    - ec2-appenv
```

playbook の詳細は
[Ansible を使って EC2 に Railsサーバーを立ち上げる](http://qiita.com/katsuhiko/items/c84a282f3a56d71c404f)
を参照してください。

## hosts の確認

--list-hosts オプションを指定すると ansible を実行する hosts を確認できます。

```shell-session
$ ansible-playbook -i production ec2-rails.example.yml --list-hosts
```

## ansible の実行

```shell-session
$ ansible-playbook -i production ec2-rails.example.yml
```

# 感想

これで EC2 インスタンスを作ったあと、毎回 IP アドレスを確認しなくても実行できるようになりました。
試していませんが、Auto Scale にも対応できるのではないかと思っています。

EC2 インスタンスの Auto Scale については、別途試してみたいです。