14
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Ansible の ec2 モジュールでインスタンスを起動してそのままタスクを続行

Posted at

Ansible の ec2 モジュールを使えば Ansible で ec2 インスタンスを起動することができますが、起動するインスタンスの Public IP は起動してみなければわからないので、同じプレイブックで通常のタスクをそのまま続行することはできません。

ec2 モジュールの結果を set_fact でホストの ansible_ssh_host 変数に登録してやればそのまま ssh で接続してタスクを続行することができます。

インベントリ

次のようなインベントリファイルを使います。変数は必要に応じて group_varshost_vars に分けると良いです。

ec2.ini
################################################################################
# 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 も指定します。

プレイブック

次のようなプレイブックを使います。

site.yaml
---
- 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 を作成してホスト鍵のチェックを無効にします。

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つのプレイブックでインスタンスの起動からプロビジョニングまでできましたが、以下のような方法の方が使いやすい気がするのであまりメリットは無いかもしれません。

  1. ec2 モジュールで user_data を指定して clout-init でプロビジョニング
    • 大抵の場合は独自の AMI から起動すると思うので clout-init で十分
    • user_datagroup_varshost_vars で書くと良い
  2. インスタンス起動のプレイブックとプロビジョニングのプレイブックを分ける
    • 静的なインベントリで ec2 インスタンスを起動
    • ダイナミックインベントリでインスタンスの情報を拾ってきてプロビジョニング
14
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?