Edited at

AnsibleでAWS環境を自動構築する

More than 1 year has passed since last update.


はじめに

Ansibleで環境構築の自動化に凝っています。

AWSの環境構築も含めて全て自動化できたらもっと楽になるんじゃないかなーと思って、

Ansibleの標準モジュールでAWS環境構築ができるので試してみました。

この記事で作成したplaybookはgithubで公開しています。

https://github.com/rednes/ansible-aws-sample

(AWS01フォルダ)


AWSの何を作るか

以下の内容をAnsibleで自動構築できるようにします。


  • VPCの作成

  • Internet Gatewayの作成

  • サブネットの作成

  • ルートテーブルの作成

  • セキュリティグループの作成

  • EC2インスタンスの作成

  • EC2インスタンスにWordPress環境の構築


前提


  • ansible, botoをインストールすること

  • AWSのサーバー構築に使用するIAMユーザが作成されていること

  • AWSマネジメントコンソールでキーペア登録していること


AWS構成図

今回Ansibleで構築するAWSの構成図はこんな感じです。

AWS構成図


ディレクトリ構成

├── ansible.cfg

├── host_vars
│   └── localhost.yml
├── hosts
│   ├── ec2.ini
│   └── ec2.py
├── roles
│   ├── ec2
│   │   ├── defaults
│   │   │   └── main.yml
│   │   └── tasks
│   │   ├── ec2.yml
│   │   ├── main.yml
│   │   ├── security_group.yml
│   │   └── vpc.yml
│   ├── wordpress
│   │   ├── defaults
│   │   │   └── main.yml
│   │   └── tasks
│   │   └── main.yml
│   └── yum
│   ├── defaults
│   │   └── main.yml
│   └── tasks
│   └── main.yml
└── site.yml


inventory

AWSリソース関連モジュールはすべてlocalhostで実行するのですが、

EC2インスタンスは作成するまでどのIPになるかわかりません。

そのためhostsフォルダのec2.ini,ec2.pyを利用して動的インベントリを使用しています。

詳細は以下のサイトを参考にしてください。

以下の様に、site.ymlでEC2インスタンス作成後にRefresh inventoryメタタスクを定義しています。

このメタタスクを実行することでEC2インスタンス作成後に動的インベントリの再取得が実行され、

作成後のEC2インスタンスに対して環境構築を継続することが可能になります。


site.yml

---

- name: create Amazon Web Services
hosts: localhost
connection: local
gather_facts: False
roles:
- ec2
tasks:
- name: Refresh inventory
meta: refresh_inventory

- name: build wordpress
hosts: tag_Name_WordPress
user: ec2-user
become: True
gather_facts: true
roles:
- yum
- wordpress



Playbookの解説

AWS関連は全てec2のroleで作成しています。


roles/ec2/tasks/main.yml

---

- include_tasks: vpc.yml
- include_tasks: security_group.yml
- include_tasks: ec2.yml

vpc.ymlでVPCからルートテーブルまで、

security_group.ymlでセキュリティグループ、

ec2.ymlでec2インスタンスを作成しています。


1. VPCの作成

ec2_vpc_netモジュールでVPCを構築します。

VPC名とCIDRブロックを指定してVPCを定義します。

作成したVPCの情報は変数vpc_netに格納してInternetGateway作成時等にVPC IDを利用します。

Ansibleは冪等性を確保しており、新規に作成すべきか既に作成されているかをVPC名で識別しています。

そのため、2回目の実行を行った場合、同名のVPCが既に存在することになるので作成されません。


roles/ec2/tasks/vpc.ymlの一部

- name: VPC作成

ec2_vpc_net:
name: "{{ aws.vpc.name }}"
cidr_block: "{{ aws.vpc.cidr_block }}"
region: "{{ aws.common.region }}"
tags: "{{ aws.vpc.tags }}"
tenancy: default
register: vpc_net


roles/ec2/defaults/main.ymlの一部

aws:

common:
region: ap-northeast-1
vpc:
name: AnsibleVPC
cidr_block: 10.0.0.0/16
tags:
Name: AnsibleVPC


2. Internet Gatewayの作成&VPCにアタッチ

ec2_vpc_igwモジュールでInternet Gatewayを構築します。

VPC IDでアタッチするVPCを定義します。

VPC IDはVPC作成時の結果変数(vpc_net)に含まれているのでこれを利用します。


roles/ec2/tasks/vpc.ymlの一部

- name: Internet Gateway作成

ec2_vpc_igw:
region: "{{ aws.common.region }}"
vpc_id: "{{ vpc_net.vpc.id }}"
tags: "{{ aws.vpc.igw.tags }}"
state: present
register: vpc_igw


roles/ec2/defaults/main.ymlの一部

aws:

common:
region: ap-northeast-1
vpc:
igw:
tags:
Name: AnsibleIGW


3. サブネットを作成

ec2_vpc_subnetモジュールでサブネットを構築します。

サブネットではVPC, Availability Zone, CIDRを指定します。

今回は1つのサブネットしか作成していませんが、

サブネットは複数作成することが想定されるため、

with_dict構文を使用することで変数定義のリスト要素追加で複数サブネットに対応できるようにしています。


roles/ec2/tasks/vpc.ymlの一部

- name: subnet作成

ec2_vpc_subnet:
region: "{{ aws.common.region }}"
state: present
vpc_id: "{{ vpc_net.vpc.id }}"
az: "{{ aws.common.region }}{{ item.value.zone }}"
cidr: "{{ item.value.cidr }}"
map_public: "{{ item.value.map_public|default(True) }}"
tags: "{{ item.value.tags }}"
with_dict: "{{ aws.vpc.subnet }}"
register: vpc_subnet


roles/ec2/defaults/main.ymlの一部

aws:

common:
region: ap-northeast-1
vpc:
subnet:
subnet1:
tags:
Name: public-a
cidr: 10.0.1.0/24
zone: a


4. ルートテーブルを設定

ec2_vpc_route_tableモジュールでルートテーブルを構築します。

ルートテーブルでは主に対象となるサブネットとInternetGatewayと内向けのCIDRを指定します。

subnetsに対象とするサブネットをSubnet ID,Subnet名またはCIDRで定義します。

今回は、サブネット作成時の結果変数(vpc_subnet)からSubnet IDを指定しています。

routesでルートの定義をしています。

任意のIP全て(0.0.0.0/0)に対してはInternetGatewayの方向に流れるようにして、

VPC内部(10.0.0.0/16)に対してはlocal側に流れるように定義しています。


roles/ec2/tasks/vpc.ymlの一部

- name: route table作成

ec2_vpc_route_table:
vpc_id: "{{ vpc_net.vpc.id }}"
region: "{{ aws.common.region }}"
tags: "{{ aws.vpc.route_table.tags }}"
subnets: "{{ vpc_subnet.results | map(attribute='subnet.id') | list }}"
routes:
- dest: 0.0.0.0/0
gateway_id: "{{ vpc_igw.gateway_id }}"
- dest: "{{ vpc_net.vpc.cidr_block }}"
gateway_id: local
register: route_table


roles/ec2/defaults/main.ymlの一部

aws:

common:
region: ap-northeast-1
vpc:
name: AnsibleVPC
cidr_block: 10.0.0.0/16
tags:
Name: AnsibleVPC
route_table:
tags:
Name: public-route-table


5. セキュリティグループを作成

ec2_groupモジュールでセキュリティグループを構築します。

セキュリティグループでは主にVPCとインバウンドルールを指定します。

VPC IDを取得するためにec2_vpc_net_factsモジュールを実行して、

VPC名からVPC IDを取得しています。


roles/ec2/tasks/security_group.yml

---

- name: vpc_id取得
ec2_vpc_net_facts:
region: "{{ aws.common.region }}"
filters:
"tag:Name": "{{ aws.vpc.name }}"
register: vpc_net_fact
check_mode: no

- name: security group作成
ec2_group:
name: "{{ aws.vpc.security_group.name }}"
description: "{{ aws.vpc.security_group.description }}"
tags:
Name: "{{ aws.vpc.security_group.name }}"
vpc_id: "{{ vpc_net_fact.vpcs[0].id }}"
region: "{{ aws.common.region }}"
purge_rules: "{{ aws.vpc.security_group.purge_rules|default(False) }}"
rules: "{{ aws.vpc.security_group.rules }}"
register: security_group



roles/ec2/defaults/main.ymlの一部

aws:

common:
region: ap-northeast-1
vpc:
name: AnsibleVPC
cidr_block: 10.0.0.0/16
tags:
Name: AnsibleVPC
security_group:
name: AnsibleWeb
description: EC2 group
rules:
- proto: tcp
ports:
- 22
cidr_ip: 0.0.0.0/0
- proto: tcp
ports:
- 80
- 443
cidr_ip: 0.0.0.0/0


6. EC2の作成&セキュリティグループの設定

ec2モジュールでEC2インスタンスを構築します。

VPCやサブネット等ネットワーク周りを*_factsモジュールを利用して、

名称からIDを引っ張ってきて定義しています。

キーペア名だけはhost_vars/localhost.ymlに定義を分けているので、

自分の環境にあわせて書き換えてください。

インスタンスの作成にはある程度時間がかかるため、

wait_forモジュールを利用してssh接続できるようになるまでWaitを入れるようにしています。


roles/ec2/tasks/ec2.yml

---

- name: security group取得
ec2_group_facts:
region: "{{ aws.common.region }}"
filters:
"tag:Name": "{{ aws.vpc.security_group.name }}"
register: ec2_group_facts
check_mode: no

- name: subnet取得
ec2_vpc_subnet_facts:
region: "{{ aws.common.region }}"
filters:
"tag:Name": "{{ aws.vpc.subnet.subnet1.tags.Name }}"
register: ec2_subnet_facts
check_mode: no

- name: Provision a set of instance
ec2:
key_name: "{{ my_vars.ec2.key_name }}"
group_id: "{{ ec2_group_facts.security_groups[0].group_id }}"
vpc_subnet_id: "{{ ec2_subnet_facts.subnets[0].id }}"
assign_public_ip: yes
instance_type: "{{ aws.vpc.ec2.instance_type }}"
region: "{{ aws.common.region }}"
image: "{{ aws.vpc.ec2.image }}"
wait: yes
wait_timeout: 300
count_tag:
Name: "{{ aws.vpc.ec2.name }}"
exact_count: 1
instance_tags:
Name: "{{ aws.vpc.ec2.name }}"
register: ec2

- name: wait for ssh
wait_for:
host: '{{ item.public_ip }}'
port: 22
timeout: 300
with_items: '{{ ec2.tagged_instances }}'



roles/ec2/defaults/main.ymlの一部

aws:

common:
region: ap-northeast-1
vpc:
subnet:
subnet1:
tags:
Name: public-a
cidr: 10.0.1.0/24
zone: a
security_group:
name: AnsibleWeb
ec2:
name: WordPress
instance_type: t2.micro
image: ami-2a69be4c


host_vars/localhost.yml

---

my_vars:
ec2:
key_name: XXXX # AWSに登録したキーペア名を入力


その後の構築

作成したEC2インスタンスに対してyum,wordpressロールで、

WordPress環境を構築していますが詳細は割愛します。

playbookの内容読んだらだいたいわかると思います。


まとめ

AnsibleでAWSの環境構築を自動化できるようPlaybookを作成してみましたが、

yaml形式でまとめることでとても見通しが良くなっていると思います。

まだAWS純正のCloudFormationは使ったことないのでどちらが管理しやすいか比較ができていないので、そのうちCloudFormationも使って比較してみたいと思います。