Ansible の ec2 モジュールを使えば Ansible で ec2 インスタンスを起動することができますが、起動するインスタンスの Public IP は起動してみなければわからないので、同じプレイブックで通常のタスクをそのまま続行することはできません。
ec2 モジュールの結果を set_fact
でホストの ansible_ssh_host
変数に登録してやればそのまま ssh で接続してタスクを続行することができます。
インベントリ
次のようなインベントリファイルを使います。変数は必要に応じて group_vars
や host_vars
に分けると良いです。
################################################################################
# Role groups
[web]
web01
web02
[ap]
ap01
ap02
################################################################################
# Subnet groups
[zone-a]
ap01
web01
[zone-c]
ap02
web02
################################################################################
# EC2 groups
[ec2:children]
ap
web
################################################################################
# Default vars
[all:vars]
image=ami-4985b048 # Amazon Linux AMI 2014.09.1 (HVM)
tag_env=prod
################################################################################
# Role vars
[ap:vars]
tag_role=ap
security_group=sg-12345678
[web:vars]
tag_role=web
security_group=sg-xxxxxxxx
################################################################################
# Subnet vars
[zone-a:vars]
vpc_subnet_id=subnet-12345678
[zone-c:vars]
vpc_subnet_id=subnet-xxxxxxxx
合計4台のインスタンスを起動します。
ap と web が2台ずつで、Availability Zone を別々にするために Subnet も指定します。
プレイブック
次のようなプレイブックを使います。
---
- hosts: all
gather_facts: no
connection: local
tasks:
- name: create ec2 instances
register: ec2
ec2:
region: ap-northeast-1
key_name: oreore
instance_type: t2.micro
image: "{{ image }}"
vpc_subnet_id: "{{ vpc_subnet_id }}"
group: "{{ security_group }}"
assign_public_ip: yes
instance_profile_name: ec2-prod
wait: yes
wait_timeout: 300
exact_count: 1
count_tag:
Name: "{{ inventory_hostname }}"
Env: "{{ tag_env }}"
Role: "{{ tag_role }}"
instance_tags:
Name: "{{ inventory_hostname }}"
Env: "{{ tag_env }}"
Role: "{{ tag_role }}"
- name: set instances vars
set_fact:
ec2_instance: "{{ ec2.tagged_instances[0] }}"
- name: wait for ssh
wait_for: host="{{ ec2_instance.public_ip }}" port=22
- hosts: all
gather_facts: no
remote_user: ec2-user
sudo: yes
vars:
ansible_ssh_host: "{{ ec2_instance.public_ip }}"
tasks:
- shell: uname -a
register: result
changed_when: no
- debug: var=result.stdout
前半がブートストラッピングで、後半がプロビジョニング(コンフィグレーション?)です。
プロビジョニングは仮に uname -a
を実行するだけにしています(なので実質なにもしていない)。
ansible.cfg
起動したばかりのインスタンスのホスト鍵は不明なので、カレントディレクトリに ansible.cfg
を作成してホスト鍵のチェックを無効にします。
[defaults]
host_key_checking = False
実行
プレイブックを実行すると ec2 インスタンスを起動した後に ssh 経由で接続してプロビジョニングできます(この例では uname -a コマンドをシェルモジュールで実行しているだけですが)。
$ ansible-playbook -i ec2.ini site.yaml
PLAY [all] ********************************************************************
TASK: [create ec2 instances] **************************************************
changed: [ap02]
changed: [ap01]
changed: [web01]
changed: [web02]
TASK: [set instances vars] ****************************************************
ok: [web02]
ok: [web01]
ok: [ap02]
ok: [ap01]
TASK: [wait for ssh] **********************************************************
ok: [ap02]
ok: [ap01]
ok: [web01]
ok: [web02]
PLAY [all] ********************************************************************
TASK: [shell uname -a] ********************************************************
ok: [web01]
ok: [ap02]
ok: [ap01]
ok: [web02]
TASK: [debug var=result.stdout] ***********************************************
ok: [web01] => {
"result.stdout": "Linux ip-10-0-1-244 3.14.20-20.44.amzn1.x86_64 #1 SMP Mon Oct 6 22:52:46 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux"
}
ok: [ap01] => {
"result.stdout": "Linux ip-10-0-1-123 3.14.20-20.44.amzn1.x86_64 #1 SMP Mon Oct 6 22:52:46 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux"
}
ok: [web02] => {
"result.stdout": "Linux ip-10-0-3-158 3.14.20-20.44.amzn1.x86_64 #1 SMP Mon Oct 6 22:52:46 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux"
}
ok: [ap02] => {
"result.stdout": "Linux ip-10-0-3-186 3.14.20-20.44.amzn1.x86_64 #1 SMP Mon Oct 6 22:52:46 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux"
}
PLAY RECAP ********************************************************************
ap01 : ok=5 changed=1 unreachable=0 failed=0
ap02 : ok=5 changed=1 unreachable=0 failed=0
web01 : ok=5 changed=1 unreachable=0 failed=0
web02 : ok=5 changed=1 unreachable=0 failed=0
プレイブックの内容
プレイブックの内容をざっくり説明します。
- hosts: all
gather_facts: no
connection: local
ec2 モジュールはローカルで実行する必要があるので connection: local
を指定します。
- name: create ec2 instances
register: ec2
ec2:
ec2 モジュールでインスタンスを起動してその結果を ec2 変数に入れます。
wait: yes
wait_timeout: 300
インスタンスが起動するまで待機します。
exact_count: 1
count_tag:
Name: "{{ inventory_hostname }}"
Env: "{{ tag_env }}"
Role: "{{ tag_role }}"
instance_tags:
Name: "{{ inventory_hostname }}"
Env: "{{ tag_env }}"
Role: "{{ tag_role }}"
instance_tags
はインスタンスに付与するタグです。
exact_count
は起動するインスタンスの数です。count_tag
に指定されたタグが付与されたインスタンスが既に存在する場合、その分は起動済と判断されます(なので2回実行してもインスタンスが追加で起動することはありません)。
なお、今回の使い方だとインベントリで記述したホスト1つに付きインスタンスも1つでなければならないので exact_count
に 1 以外の値を指定することはできません(そうしないで 2 個目以降のインスタンスでプロビジョニングのタスクが実行されません)。
- name: set instances vars
set_fact:
ec2_instance: "{{ ec2.tagged_instances[0] }}"
ec2 モジュールが取得したインスタンスの情報を ec2_instance
変数に登録します。ec2.instances[0]
にも同じ値が入っていますが、インスタンスが起動しなかったとき(既に同じタグのインスタンスが起動済のとき)は空になるので ec2.tagged_instances[0]
を使います。
- name: wait for ssh
wait_for: host="{{ ec2_instance.public_ip }}" port=22
上の方で wait: yes
でインスタンスが起動するまで待機していますが、インスタンスが起動しても sshd が起動していなければ後続のタスクを実行することができないので、インスタンスの 22 番ポートが利用可能になるまで待機します。
- hosts: all
gather_facts: no
remote_user: ec2-user
sudo: yes
これ以降のタスクは ssh でインスタンスに接続して実行するので connection
は指定しません。
vars:
ansible_ssh_host: "{{ ec2_instance.public_ip }}"
ssh で接続できるようにするためにインスタンスの Public IP を ansible_ssh_host
変数に設定します。
さいごに
1つのプレイブックでインスタンスの起動からプロビジョニングまでできましたが、以下のような方法の方が使いやすい気がするのであまりメリットは無いかもしれません。
- ec2 モジュールで
user_data
を指定して clout-init でプロビジョニング- 大抵の場合は独自の AMI から起動すると思うので clout-init で十分
-
user_data
はgroup_vars
やhost_vars
で書くと良い
- インスタンス起動のプレイブックとプロビジョニングのプレイブックを分ける
- 静的なインベントリで ec2 インスタンスを起動
- ダイナミックインベントリでインスタンスの情報を拾ってきてプロビジョニング