AnsibleによるEC2インスタンスの構築
渡辺です。
過去にも同様のエントリーが存在するのですが、AWSといえばEC2ということで、最新のAWSモジュールを利用してEC2インスタンスを作成してみます。
グループ変数でインスタンス定義を明確にする
可読性の高いグループ変数は、このシリーズのテーマです。 今回は、次のようなグループ変数でEC2インスタンスを定義したいと思います。
ec2: - name: FrontWebA instance_type: t2.small image: ami-1a15c77b instance_profile_name: web key_name: dev-key subnet_name: FrontA group: - Internal - Mainte root_volume_size: 8 assign_public_ip: false - name: FrontWebC instance_type: t2.small image: ami-1a15c77b instance_profile_name: web key_name: dev-key subnet_name: FrontC group: - Internal - Mainte root_volume_size: 8 assign_public_ip: false
EC2を作成するPlaybook
Playbookはこんな感じです。
- hosts: localhost connection: local gather_facts: False become: False tasks: - name: Facts subnet ec2_vpc_subnet_facts: profile: "{{ profile }}" region: "{{ region }}" register: _subnet_facts - debug: var=_subnet_facts verbosity=1 - name: "EC2" ec2: image: "{{ item.image }}" instance_profile_name: "{{ item.instance_profile_name }}" instance_type: "{{ item.instance_type }}" key_name: "{{ item.key_name }}" vpc_subnet_id: "{{ _subnet_facts.subnets | selectattr('tags.Name', 'defined') | selectattr('tags.Name', 'equalto', item.subnet_name) | map(attribute='id') | first }}" group: "{{ item.group }}" volumes: - device_name: /dev/xvda volume_type: gp2 volume_size: "{{ item.root_volume_size }}" delete_on_termination: true instance_tags: Name: "{{ item.name }}" count_tag: Name: "{{ item.name }}" exact_count: 1 assign_public_ip: "{{ item.assign_public_ip }}" profile: "{{ profile }}" region: "{{ region }}" with_items: "{{ ec2 }}"
ec2_vpc_subnet_factsモジュールでサブネットIDを収集する
はじめに、EC2インスタンスを配置するサブネットの情報を取得するため、ec2_vpc_subnet_factsモジュールを実行します。
Ansibleでは多くの_facts
とサフィックスがついたモジュールが提供されており、様々な情報を取得するために利用できます。
AWS CLIであれば、describe
系のコマンドと考えて良いでしょう。
- name: Facts subnet ec2_vpc_subnet_facts: profile: "{{ profile }}" region: "{{ region }}" register: _subnet_facts
ここでは、filter
などのオプションは指定せず、指定したリージョンの全サブネットを取得し、register
で変数に格納しておきます。
実行結果は次のような形式です。
TASK [debug] ******************************************************************* ok: [localhost] => { "_subnet_facts": { "changed": false, "subnets": [ { "availability_zone": "ap-northeast-1c", "available_ip_address_count": 250, "cidr_block": "172.31.101.0/24", "default_for_az": "false", "id": "subnet-xxxxxxxx1", "map_public_ip_on_launch": "false", "state": "available", "tags": { "Name": "FrontC" }, "vpc_id": "vpc-xxxxxxxx" }, { "availability_zone": "ap-northeast-1a", "available_ip_address_count": 250, "cidr_block": "172.31.100.0/24", "default_for_az": "false", "id": "subnet-xxxxxxxx2", "map_public_ip_on_launch": "false", "state": "available", "tags": { "Name": "FrontA" }, "vpc_id": "vpc-xxxxxxxx" } ] } }
ec2モジュール
ec2モジュールはEC2インスタンスの構成管理だけでなく、stop/startなどもサポートするモジュールです。 設定次第で色々とできてしまう点は注意してください。
- name: "EC2" ec2: image: "{{ item.image }}" instance_profile_name: "{{ item.instance_profile_name }}" instance_type: "{{ item.instance_type }}" key_name: "{{ item.key_name }}" vpc_subnet_id: "{{ _subnet_facts.subnets | selectattr('tags.Name', 'defined') | selectattr('tags.Name', 'equalto', item.subnet_name) | map(attribute='id') | first }}" group: "{{ item.group }}" volumes: - device_name: /dev/xvda volume_type: gp2 volume_size: "{{ item.root_volume_size }}" delete_on_termination: true instance_tags: Name: "{{ item.name }}" count_tag: Name: "{{ item.name }}" exact_count: 1 assign_public_ip: "{{ item.assign_public_ip }}" profile: "{{ profile }}" region: "{{ region }}" with_items: "{{ ec2 }}"
ec2モジュールでは、instance_profile_name
にはプロファイル名を、instance_type
にはインスタンスタイプを、と想像通りのパラメータを持ちます。
ポイントとなるパラメータに絞って解説しますので、それ以外のパラメータはドキュメントを参照してください。
image
image
にはイメージIDを指定します。
image: "{{ item.image }}"
今回はイメージIDをグループ変数に直接定義しましたが、最新のAmazon Linuxなど動的にイメージIDを取得することもできます。 取得する場合は、AWS CLIを実行してもよいですが、ec2_ami_findモジュールも利用できます。
vpc_subnet_id
vpc_subnet_id
はサブネットIDを指定します。
IDであるため、グループ変数でIDを指定するか、動的に取得するかの二択ですが、今回はec2_vpc_subnet_factsモジュールを使って動的に取得しています。
Jinja2のフィルタ機能を利用し、タグ名から要素をフィルタし、IDのみを抽出しました。
_subnet_facts.subnets | selectattr('tags.Name', 'defined') | selectattr('tags.Name', 'equalto', item.subnet_name) | map(attribute='id') | first
group
group
にはセキュリティグループ名のリストを指定します。
group: "{{ item.group }}"
グループ変数に定義された名前でセキュリティグループを適用するので、IDを指定する必要がありません。
なお、group_id
を利用してID指定も可能です。
count_tag, exact_count
count_tag
とexact_count
はセットで利用します。
このパラメータは、AnsibleからEC2インスタンスを識別するために利用します。
count_tag: Name: "{{ item.name }}" exact_count: 1
count_tag
がEC2を識別するためのタグを定義します。
ここでは、Nameタグとその値を指定しました。
count_tag
で識別したインスタンスが存在する場合、EC2インスタンスを新しく作成しません。
exact_count
は識別したインスタンスが幾つ存在すべきかを定義します。
ここでは、1を指定しているため、識別したインスタンスが1つあれば、それ以上のEC2インスタンスを作成しません。
通常、Nameタグで指定したEC2インスタンスの名前でインスタンスが識別できれば良いので、この設定を覚えておくと良いでしょう。
まとめ
ec2モジュールでEC2インスタンスを作成する時には、count_tag
とexact_count
を利用し、EC2インスタンスの識別方法を定義することがポイントです。
また、可読性の高いグループ変数の定義があれば、EC2インスタンスの仕様書となります。
宣言的で解りやすい仕様書があれば、構築作業もスムーズに進むことでしょう。