Dynamic InventoryでAnsibleの実行先ホストを自動で生成する


AWSの場合、EC2インスタンスが入れ替わる度に、実行先ホストを更新しなければなりません。
毎回実行先を更新するのは大変なので、予め用意されているEC2 external inventoryスクリプトを利用して、自動でEC2の実行先を生成してみました。

# Dynamic Inventoryとは

- Ansibleの対象となるホスト一覧を動的に生成することが出来る仕組み 
- コマンド実行時に実行権限が付与されたファイルをインベントリファイルに指定した場合、そのファイルに記載されたスクリプトを実行した結果を対象ホストとして認識してくれる 

# 参考URL
http://docs.ansible.com/ansible/intro_dynamic_inventory.html

# 準備 
※事前にwas configureを実行しておくか、AWS_ACCESS_KEY_ID等の環境変数を設定し、AWS CLIの初期設定は済ませておきます。

```bash 
## botoをインストール
$ sudo pip install boto

## EC2 external inventoryスクリプトをダウンロード
$ curl -O https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/ec2.py 
$ curl -O https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/ec2.ini 

$ chmod +x ec2.py 
``` 

# AWSの情報を収集

ec2.pyを実行すると、全リージョンのEC2の情報が収集され、結果がJSON形式で返ってきます。
Public IP、リージョン、Tag、セキュリティグループなど、自動でグループ分けがされていることがわかります。

※複数Profileがある場合は「--profile プロファイル名」を指定する

```bash 
$ ./ec2.py --list  
{ 
  "_meta": { 
    "hostvars": { 
      "54.xxx.xxx.xxx": { 
        "ec2__in_monitoring_element": false, 
        "ec2_ami_launch_index": "0", 
        "ec2_architecture": "x86_64", 
        "ec2_client_token": "", 
        "ec2_dns_name": "ec2-54-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com", 
        "ec2_ebs_optimized": false, 
        "ec2_eventsSet": "", 
        "ec2_group_name": "", 
        "ec2_hypervisor": "xen", 
        "ec2_id": "i-xxxxxxxx", 
        "ec2_image_id": "ami-xxxxxxxx", 
        "ec2_instance_type": "t2.micro", 
        "ec2_ip_address": "54.xxx.xxx.xxx", 
        "ec2_item": "", 
        "ec2_kernel": "", 
        "ec2_key_name": "mykey", 
        "ec2_launch_time": "2015-12-06T15:12:15.000Z", 
        "ec2_monitored": false, 
        "ec2_monitoring": "", 
        "ec2_monitoring_state": "disabled", 
        "ec2_persistent": false, 
        "ec2_placement": "ap-northeast-1a", 
        "ec2_platform": "", 
        "ec2_previous_state": "", 
        "ec2_previous_state_code": 0, 
        "ec2_private_dns_name": "ip-172-31-31-65.ap-northeast-1.compute.internal", 
        "ec2_private_ip_address": "172.31.31.65", 
        "ec2_public_dns_name": "ec2-54-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com", 
        "ec2_ramdisk": "", 
        "ec2_reason": "", 
        "ec2_region": "ap-northeast-1", 
        "ec2_requester_id": "", 
        "ec2_root_device_name": "/dev/xvda", 
        "ec2_root_device_type": "ebs", 
        "ec2_security_group_ids": "sg-xxxxxxxx", 
        "ec2_security_group_names": "launch-wizard-1", 
        "ec2_sourceDestCheck": "true", 
        "ec2_spot_instance_request_id": "", 
        "ec2_state": "running", 
        "ec2_state_code": 16, 
        "ec2_state_reason": "", 
        "ec2_subnet_id": "subnet-xxxxxxxx", 
        "ec2_tag_Memo": "This is test server.", 
        "ec2_tag_Name": "test-server", 
        "ec2_virtualization_type": "hvm", 
        "ec2_vpc_id": "vpc-xxxxxxxx" 
      } 
    } 
  }, 
  "ami_xxxxxxxx": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "ap-northeast-1": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "ap-northeast-1a": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "ec2": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "i-xxxxxxxx": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "key_mykey": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "security_group_launch_wizard_1": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "tag_Memo_This_is_test_server_": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "tag_Name_test_server": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "type_t2_micro": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "vpc_id_vpc_xxxxxxxx": [ 
    "54.xxx.xxx.xxx" 
  ] 
} 
``` 

キャッシュファイルは以下に配置されています。

```bash 
$ ls ~/.ansible/tmp/ 
ansible-ec2.cache	ansible-ec2.index 
``` 

キャッシュファイルの中を見てみると、先ほど収集した情報が記載されています。

```bash 
$ cat ~/.ansible/tmp/ansible-ec2.cache 
{ 
  "_meta": { 
    "hostvars": { 
      "54.xxx.xxx.xxx": { 
        "ec2__in_monitoring_element": false, 
        "ec2_ami_launch_index": "0", 
        "ec2_architecture": "x86_64", 
        "ec2_client_token": "", 
        "ec2_dns_name": "ec2-54-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com", 
        "ec2_ebs_optimized": false, 
        "ec2_eventsSet": "", 
        "ec2_group_name": "", 
        "ec2_hypervisor": "xen", 
        "ec2_id": "i-xxxxxxxx", 
        "ec2_image_id": "ami-xxxxxxxx", 
        "ec2_instance_type": "t2.micro", 
        "ec2_ip_address": "54.xxx.xxx.xxx", 
        "ec2_item": "", 
        "ec2_kernel": "", 
        "ec2_key_name": "mykey", 
        "ec2_launch_time": "2015-12-06T15:12:15.000Z", 
        "ec2_monitored": false, 
        "ec2_monitoring": "", 
        "ec2_monitoring_state": "disabled", 
        "ec2_persistent": false, 
        "ec2_placement": "ap-northeast-1a", 
        "ec2_platform": "", 
        "ec2_previous_state": "", 
        "ec2_previous_state_code": 0, 
        "ec2_private_dns_name": "ip-172-31-31-65.ap-northeast-1.compute.internal", 
        "ec2_private_ip_address": "172.31.31.65", 
        "ec2_public_dns_name": "ec2-54-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com", 
        "ec2_ramdisk": "", 
        "ec2_reason": "", 
        "ec2_region": "ap-northeast-1", 
        "ec2_requester_id": "", 
        "ec2_root_device_name": "/dev/xvda", 
        "ec2_root_device_type": "ebs", 
        "ec2_security_group_ids": "sg-xxxxxxxx", 
        "ec2_security_group_names": "launch-wizard-1", 
        "ec2_sourceDestCheck": "true", 
        "ec2_spot_instance_request_id": "", 
        "ec2_state": "running", 
        "ec2_state_code": 16, 
        "ec2_state_reason": "", 
        "ec2_subnet_id": "subnet-xxxxxxxx", 
        "ec2_tag_Memo": "This is test server.", 
        "ec2_tag_Name": "test-server", 
        "ec2_virtualization_type": "hvm", 
        "ec2_vpc_id": "vpc-xxxxxxxx" 
      } 
    } 
  }, 
  "ami_xxxxxxxx": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "ap-northeast-1": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "ap-northeast-1a": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "ec2": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "i-xxxxxxxx": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "key_mykey": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "security_group_launch_wizard_1": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "tag_Memo_This_is_test_server_": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "tag_Name_test_server": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "type_t2_micro": [ 
    "54.xxx.xxx.xxx" 
  ], 
  "vpc_id_vpc_xxxxxxxx": [ 
    "54.xxx.xxx.xxx" 
  ] 
} 
``` 

# コマンドの実行

とりあえずグループ分けされた内容を指定して、pingコマンドを実行してみます。
いずれも成功しています。

## host指定の場合

```bash 
$ ansible -i ec2.py 54.xxx.xxx.xxx -m ping --private-key="~/.ssh/mykey.pem" -u ec2-user 
54.xxx.xxx.xxx | success >> { 
    "changed": false, 
    "ping": "pong" 
} 
``` 

## タグ名指定 

```bash 
$ ansible -i ec2.py "tag_Name_test_server" -m ping --private-key="~/.ssh/mykey.pem" -u ec2-user 
54.xxx.xxx.xxx | success >> { 
    "changed": false, 
    "ping": "pong" 
} 
``` 

# Playbookの実行

続いて、適当なPlaybookを実行してみます。
今回は、試しにApache httpd,MySQL Client,PHPをインストールするPlaybookを用意してみました。

- testserver.yml

```bash
- name: testserver package install
  hosts: all
  sudo: yes
  remote_user: ec2-user
  gather_facts: no
  roles:
    - testserver
```

- roles/testserver/tasks/main.yml

```bash
- name: yum package install for testserver
  yum:
    name={{ item }}
    state=present
  with_items:
    - httpd24
    - mysql
    - php55

- name: package are running and enabled
  service:
    name={{ item }}
    state=running
    enabled=yes
  with_items:
    - httpd
```

では、Playbookを実行します。

```bash 
$ ansible-playbook -i inventory/ec2.py -l "tag_Name_test_server" testserver.yml --private-key="~/.ssh/mykey.pem" -u ec2-user 

PLAY [testserver install] ****************************************************** 

TASK: [testserver | yum package install for testserver] ************************* 
changed: [54.xxx.xxx.xxx] => (item=httpd24,mysql,php55) 

TASK: [testserver | package are running and enabled] *************************** 
changed: [54.xxx.xxx.xxx] => (item=httpd) 

PLAY RECAP ******************************************************************** 
54.xxx.xxx.xxx               : ok=2    changed=2    unreachable=0    failed=0 
```

成功したようです。
本当にインストールされたか、確認してみます。

```bash
$ ansible -i inventory/ec2.py "tag_Name_test_server" -m shell -a "rpm -qa | grep -e httpd -e mysql -e php" --private-key="~/.ssh/mykey.pem" -u ec2-user 
54.xxx.xxx.xxx | success | rc=0 >> 
httpd24-tools-2.4.16-1.62.amzn1.x86_64 
php55-process-5.5.30-1.110.amzn1.x86_64 
php-pear-1.9.5-2.17.amzn1.noarch 
mysql-config-5.5.46-1.10.amzn1.x86_64 
httpd24-2.4.16-1.62.amzn1.x86_64 
mysql55-libs-5.5.46-1.10.amzn1.x86_64 
php55-cli-5.5.30-1.110.amzn1.x86_64 
php55-common-5.5.30-1.110.amzn1.x86_64 
php55-pecl-jsonc-1.3.6-1.13.amzn1.x86_64 
mysql-5.5-1.6.amzn1.noarch 
mysql55-5.5.46-1.10.amzn1.x86_64 
php55-xml-5.5.30-1.110.amzn1.x86_64 
php55-5.5.30-1.110.amzn1.x86_64 
``` 

```bash 
$ ansible -i inventory/ec2.py "tag_Name_test_server" -m shell -a "ps -ef | grep [h]ttpd" --private-key="~/.ssh/mykey.pem" -u ec2-user 
54.xxx.xxx.xxx | success | rc=0 >> 
root      6856     1  0 05:55 ?        00:00:00 /usr/sbin/httpd 
apache    6859  6856  0 05:55 ?        00:00:00 /usr/sbin/httpd 
apache    6860  6856  0 05:55 ?        00:00:00 /usr/sbin/httpd 
apache    6861  6856  0 05:55 ?        00:00:00 /usr/sbin/httpd 
apache    6862  6856  0 05:55 ?        00:00:00 /usr/sbin/httpd 
apache    6863  6856  0 05:55 ?        00:00:00 /usr/sbin/httpd 
``` 

インストールされたことが確認できました。