Ansible は、構成管理ツールとして認知されていますが、AWS 関連のモジュールが多数実装されており、各コンポーネントの起動や設定ができます。

このエントリでは、Ansible で、検証環境用の EC2 インスタンスを起動して、その Public DNS をRoute 53 に登録してみます。
以前書いたこのエントリの内容 を Ansible で自動化するイメージですね。
準備
今回は、AWS を操作するので、Python の AWS SDK である boto をインストールしておきます。boto は、pip なり、yum なりでインストールできます。
- OSX
$ pip install boto
- RHEL / CentOS
$ rpm -ivh http://ftp.riken.jp/Linux/fedora/epel/6/i386/epel-release-6-8.noarch.rpm $ yum -y install python-boto
AWS 認証情報
AWS 認証情報を設定します。
playbook に直接記載する方法もあるのですが、ここでは、boto の設定ファイルである ~/.boto に記述をします。こうしておけば、playbook では、認証情報を指定する必要がありません。
[Credentials] aws_access_key_id = xxxxxxxxxxxxxxx aws_secret_access_key = xxxxxxxxxxxxxxx
EC2 インスタンスの起動
Ansible で EC2 インスタンスを起動するには、ec2 モジュールを使います。
AWS 関連のモジュールを使う場合、対象のインベントリが指定できない場合がある(対象のホストをこのタスクで生成するので)ので、local connection として実行します。
ec2 モジュールには、EC2 インスタンスを起動するためのパラメータを指定します。下記では、VPC で、t2.micro インスタンスを指定しています。各パラメータについては、EC2 インスタンスを起動する際は、お馴染みのものなので、値を指定していきます。あとで識別できるように Name タグに ansible1 を設定しておきます。
ここで起動したインスタンスの Public DNS を、Route 53 に登録するので、register を使って、処理結果をec2という変数に格納しておきます。
- hosts: localhost
gather_facts: no
connection: local
tasks:
- name: Create ec2 instanse
ec2:
key_name: keyA
instance_type: t2.micro
image: ami-0xxxxx
monitoring: yes
wait: yes
region: ap-northeast-1
group_id: sg-xxxxxx
vpc_subnet_id: subnet-xxxxx
assign_public_ip: yes
instance_tags:
Name: ansible1
register: ec2
Route 53 に Public DNS を CNAME に登録
あらかじめ決められた FQDN でアクセスできるように、先ほど起動したインスタンスの Public DNS を CNAME として Route 53 に登録します。
Route 53 の操作には、route53 モジュールを使います。
EC2 インスタンスの情報は、ec2.instances に格納されているので、これを利用します。下記では、ansible1.dev.example.com という FQDN に対して、ec2 の Public DNS を CNAME で割り当てています。
- name: Set Public DNS to CNAME in Route53
route53:
command: create
zone: dev.example.com
type: CNAME
value: "{{ item.public_dns_name }}"
overwrite: yes
record: ansible1.dev.example.com
ttl: 300
with_items: ec2.instances
playbook の実行
この playbook を ansible-playbook コマンドで実行します。
local connection を使うのですが、インベントリファイルが必要になるので、作成しておきます。
$ cat > hosts <EOF 127.0.0.1 EOF
では、実行してみましょう。ansible-playbook コマンドを実行すると、2 つのタスクが処理されました。
$ ansible-playbook -i hosts aws.yml
PLAY [localhost] **************************************************************
TASK: [Create ec2 instanse] ***************************************************
changed: [localhost]
TASK: [Set Public DNS to CNAME in Route53] *********************************************
changed: [localhost] => (item={......})
PLAY RECAP ********************************************************************
localhost : ok=2 changed=2 unreachable=0 failed=0
AWS の Management Console を確認すると、Name タグに ansible1 が設定されたインスタンスが生成されていました。
![]()
Route 53 を見ると、想定したいた FQDN の CNAME に EC2 インスタンスの Public DNS が設定されていました。
![]()
起動済の EC2 を破棄
EC2 インスタンスを起動して、Route 53 に登録するという流れはできました。
ただ、このままだと、この playbook を実行する度に新しいインスタンスが作成されるので、不要なインスタンスが残り続けます。そこで、今回は検証環境ということで、古いインスタンスは破棄した後に、新しいインスタンスを作るという流れにします。
起動済のインスタンスを破棄するには、稼働中のインスタンス ID を取得する必要があります。インスタンス ID はec2_factsモジュールを実行します。このモジュールは、インスタンス内で実行するので、インスタンスをインベントリファイルに追加する必要があります。
これを手で行うと、インスタンスを起動するたびにインベントリファイルを書き換えることになるので、Dynamic Inventory を利用します。Dynamic Inventory は、インベントリ情報をスクリプト等で動的に生成することができる仕組みです。これを使うことで、AWS から稼働中のインスタンス情報を取得して、各インスタンスをインベントリとして、タスクを実行することができます。
Ansible のソースコードには、稼働中の EC2 インスタンス情報を取得するスクリプト(plugins/inventory/ec2.py, plugins/inventory/ec2.ini)が含まれているので、これを利用します。
https://github.com/ansible/ansible/blob/devel/plugins/inventory/ec2.py
https://github.com/ansible/ansible/blob/devel/plugins/inventory/ec2.ini
ec2.py はインベントリを取得するスクリプトで、ec2.ini がその設定ファイルです。
まず、ec2.ini の設定を変更します。ec2.py は、デフォルトでは取得した情報をキャッシュ仕組みになっているのですが、今回は実行時に最新の情報を取得したいので、このキャッシュを無効にします。
$ vim ec2.ini cache_max_age = 300 # デフォルトは 300 秒なので、0 にする ↓ cache_max_age = 0
次に、playbook にインスタンス情報を取得するタスクを追加します。このタスクは、先頭に記述しておきます。hosts には、tag_Name_ansible1を指定しています。これは ec2.py で動的に取得したインベントリの内、タグ Name の値が ansible1 のホストを対象にするということです。このように ec2.py では稼働中の全インスタンスを利用するだけでなく、タグやインスタンスタイプなど様々な切り口でインベントリを絞り込むことができます。
---
- hosts: tag_Name_ansible1
gather_facts: no
user: root
tasks:
- ec2_facts:
つづいて、取得したインスタンス情報を使って、インスタンスを破棄します。インスタンスの破棄には ec2 モジュールを利用します。state=absentを指定することで、instance_idsで指定したインスタンスが破棄されます。
下記では、with_items で、インスタンスID を指定しているので、タグ名が ansible1 のインスタンスは全て破棄されます。
- hosts: tag_Name_ansible1
gather_facts: no
connection: local
tasks:
- name: Remove ec2 previous instances
ec2: state=absent
region=ap-northeast-1
instance_ids={{ item }}
wait=true
with_items: ansible_ec2_instance_id
完成した playbook の実行
playbook が完成しました。実行してみましょう。
-iオプションで、ec2.py を指定して、下記のように実行します。
実行すると下記の流れでタスクが実行されていきます。これで、何度実行しても起動しているインスタンスは一つのみになりました。
- タグ名=ansible1 の EC2 インスタンス情報取得
- 1 で取得した EC2 インスタンスを破棄
- EC2 インスタンス作成
- 3 で作成したインスタンスの Public DNS を Route 53 に登録
$ ansible-playbook -i ec2.py aws.yml
PLAY [tag_Name_ansible1] ******************************************************
TASK: [ec2_facts ] ************************************************************
ok: [xxx.xxx.xxx.xxx]
PLAY [tag_Name_ansible1] ******************************************************
TASK: [Remove ec2 previous instances] *****************************************
changed: [xxx.xxx.xxx.xxx] => (item=i-xxxxxx)
PLAY [localhost] **************************************************************
TASK: [Create ec2 instanse] ***************************************************
changed: [localhost]
TASK: [Set Public DNS to CNAME in Route53] *********************************************
changed: [localhost] => (item={...})
PLAY RECAP ********************************************************************
xxx.xxx.xxx.xxx : ok=2 changed=1 unreachable=0 failed=0
localhost : ok=2 changed=2 unreachable=0 failed=0
最終的な playbook は以下です。
---
- hosts: tag_Name_ansible1
gather_facts: no
user: root
tasks:
- ec2_facts:
- hosts: tag_Name_ansible1
gather_facts: no
connection: local
tasks:
- name: Remove ec2 previous instances
ec2: state=absent
region=ap-northeast-1
instance_ids={{ item }}
wait=true
with_items: ansible_ec2_instance_id
- hosts: localhost
gather_facts: no
connection: local
tasks:
- name: Create ec2 instanse
ec2:
key_name: keyA
instance_type: t2.micro
image: ami-0xxxxx
monitoring: yes
wait: yes
region: ap-northeast-1
group_id: sg-xxxxxx
vpc_subnet_id: subnet-xxxxx
assign_public_ip: yes
instance_tags:
Name: ansible1
register: ec2
- name: Set Public DNS to CNAME in Route53
route53:
command: create
zone: dev.example.com
type: CNAME
value: "{{ item.public_dns_name }}"
overwrite: yes
record: ansible1.dev.example.com
ttl: 300
with_items: ec2.instances
さいごに
Ansible で AWS を操作してみました。
インスタンス情報の取得やその情報の利用(インスタンス破棄)などは少しコツが必要ですが、それさえ分かれば、思ったとおりに動作しました。はじめは同じことを Terraform で行っていたのですが、プロビジョンには Ansible を使っていたので、どうせなら Ansible で完結させようと思い、試してみました。
このエントリでは、AWS の操作のみ行っていますが、実際は、インスタンス生成後にプロビジョンやデプロイを行う playbook を挟む形になります。こうすれば、EC2 インスタンス生成、プロビジョン、デプロイが Ansible だけで行うことができます。
- Newer: 「Ansibleではじめるサーバ作業の自動化」を発表してきました
- Older: [書評] CakePHPで学ぶ継続的インテグレーション

