EC2をAnsibleで管理する

  • 0
    いいね
  • 0
    コメント

    はじめに

    AnsibleにはAWSのリソースを操作できるモジュールが豊富に用意されています。

    今回は、定番のEC2をAnsibleで管理してみます。

    やること

    • EC2インスタンス作成

    ポイント

    ec2モジュールは、セキュリティグループについては名前で指定できるのですが、サブネットはIDで指定する必要があります。

    しかし、サブネットIDをAnsibleのYAMLに書きたくないので、サブネット名からIDを取得する実装とします。

    前提

    AWS関連のモジュール実行にはbotoが必要です。
    credential情報は環境変数かaws configureでセットしてある必要があります。

    下記リソースを前提に進めます。

    • VPC
      • AnsibleVPC
    • キーペア
      • keypair
    • サブネット
      • public-a
      • public-c
    • セキュリティグループ
      • common
      • web_server

    sample

    以下のようなEC2インスタンスを作成します。

    • testinstance1
      • AmazonLinux
      • アベイラビリティゾーンA
      • セキュリティグループ
        • common,web_server
    • testinstance2
      • AmazonLinux
      • アベイラビリティゾーンC
      • セキュリティグループ
        • common

    ディレクトリ構成

    ディレクトリ構成
    site.yml
    roles/
    |--ec2/
    |  |--tasks/
    |  |  |--main.yml
    hosts/aws    #inventory
    host_vars/
    |--localhost.yml
    

    inventory

    AWSリソース関連モジュールはすべてlocalhostで実行するので、下記のようなインベントリファイルを用意します。

    hosts/aws
    [aws]
    localhost
    

    vars

    こんな感じに変数を定義します。今回はhost_varsで定義しました。

    host_vars/localhost.yml
    ---
    my_vars:
      aws:
        common:
          region: ap-northeast-1
        vpc:
          name: AnsibleVPC    # ターゲットのVPC名
        ec2:
          testinstance1:
            ami_image: ami-56d4ad31 # Amazon Linux
            key_name: keypair # キーペア名
            security_group:
              - common
              - web_server
            instance_type: t2.micro
            device_name: /dev/xvda
            device_type: gp2
            volume_size: 8 # EBSのディスクサイズ(GB)
            subnet: public-a # サブネット名
            assign_public_ip: yes
            tags:
              Name: testinstance1
              Role: test
          testinstance2:
            ami_image: ami-56d4ad31 # Amazon Linux
            key_name: keypair # キーペア名
            security_group:
              - common
            instance_type: t2.micro
            device_name: /dev/xvda
            device_type: gp2
            volume_size: 8 # EBSのディスクサイズ(GB)
            subnet: public-c # サブネット名
            assign_public_ip: yes
            tags:
              Name: testinstance2
              Role: test
    

    Role

    まずVPCを特定するためにidが必要ですが、こちらと同様、VPC名でidを取得します。

    今回はリストではなくディクショナリとしてEC2インスタンスを定義しましたので、with_dictでループさせます。

    前述のように、サブネットはIDで指定する必要があるので、ec2_vpc_subnet_factsモジュールでIDを取得します。

    定義されたEC2インスタンスの全てのサブネット名とIDのディクショナリを作成し、後続taskで参照します。

    あとはec2モジュールで作成しますが、exact_count: 1を指定することで重複作成を防止します(stop状態だと作成されてしまいますが)。

    roles/vpc/tasks/main.yml
    ---
    - name: vpc_id取得
      ec2_vpc_net_facts:
        region: "{{ my_vars.aws.common.region }}"
        filters:
          "tag:Name": "{{ my_vars.aws.vpc.name }}"
      register: vpc_net_fact
    
    - debug: var=vpc_net_fact
    
    - name: subnet id取得
      ec2_vpc_subnet_facts:
        region: "{{ my_vars.aws.common.region }}"
        filters:
          vpc_id: "{{ vpc_net_fact.vpcs[0].id }}"
          "tag:Name": "{{ item.value.subnet }}"
      with_dict: "{{ my_vars.aws.ec2 }}"
      register: subnet_fact
      when: my_vars.aws.ec2 is defined
    
    - name: subnet dict作成
      set_fact:
        subnet_dict: >-
          {%- set dict = {} -%}
          {%- for i in range(subnet_fact.results|length) -%}
          {%-   set _ = dict.update({subnet_fact.results[i].subnets[0].tags.Name: subnet_fact.results[i].subnets[0].id}) -%}
          {%- endfor -%}
          {{ dict }}
      when: my_vars.aws.ec2 is defined
    
    - name: EC2インスタンスを作成
      ec2:
        image: "{{ item.value.ami_image }}"
        instance_type: "{{ item.value.instance_type }}"
        region: "{{ my_vars.aws.common.region }}"
        key_name: "{{ item.value.key_name }}"
        group: "{{ item.value.security_group }}"
        vpc_subnet_id: >-
          {%- set id = subnet_dict[item.value.subnet] -%}
          {{ id }}
        instance_tags: "{{ item.value.tags }}"
        assign_public_ip: "{{ item.value.assign_public_ip }}"
        private_ip: "{{ item.value.private_ip }}"
        wait: yes
        wait_timeout: 300
        volumes:
          - device_name: "{{ item.value.device_name }}"
            device_type: "{{ item.value.device_type }}"
            volume_size: "{{ item.value.volume_size }}"
            delete_on_termination: true
        count_tag:
          Name: "{{ item.value.tags.Name }}"
        exact_count: 1
        user_data: |
          #!/bin/bash
          # 初期設定スクリプトなど
      with_dict: "{{ my_vars.aws.ec2 }}"
      register: ec2
      when: my_vars.aws.ec2 is defined
    
    - debug: var=ec2
    

    site.yml

    site.yml
    ---
    - name: ec2
      hosts: localhost
      connection: local
      roles:
        - role: ec2
    

    実行

    Command
    $ ansible-playbook -i hosts/aws -l localhost site.yml
    

    まとめ

    ネット上のサンプルでもサブネットIDを指定している例がほとんどですが、実際YAMLで管理する場合、IDではなく名前の方が分かりやすいと思います。
    参考になれば幸いです。

    参考