はじめに
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の構成図はこんな感じです。
ディレクトリ構成
├── 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を利用して動的インベントリを使用しています。
詳細は以下のサイトを参考にしてください。
- Ansible Dynamic Inventoryを使ってAWSのEC2インスタンスに付与されたタグでプロビジョニング対象を決める
- Dynamic Inventory — Ansible Documentation
以下の様に、site.ymlでEC2インスタンス作成後にRefresh inventoryメタタスクを定義しています。
このメタタスクを実行することでEC2インスタンス作成後に動的インベントリの再取得が実行され、
作成後のEC2インスタンスに対して環境構築を継続することが可能になります。
---
- 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で作成しています。
---
- 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が既に存在することになるので作成されません。
- 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
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)に含まれているのでこれを利用します。
- 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
aws:
common:
region: ap-northeast-1
vpc:
igw:
tags:
Name: AnsibleIGW
3. サブネットを作成
ec2_vpc_subnetモジュールでサブネットを構築します。
サブネットではVPC, Availability Zone, CIDRを指定します。
今回は1つのサブネットしか作成していませんが、
サブネットは複数作成することが想定されるため、
with_dict構文を使用することで変数定義のリスト要素追加で複数サブネットに対応できるようにしています。
- 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
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側に流れるように定義しています。
- 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
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を取得しています。
---
- 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
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を入れるようにしています。
---
- 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 }}'
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
---
my_vars:
ec2:
key_name: XXXX # AWSに登録したキーペア名を入力
その後の構築
作成したEC2インスタンスに対してyum,wordpressロールで、
WordPress環境を構築していますが詳細は割愛します。
playbookの内容読んだらだいたいわかると思います。
まとめ
AnsibleでAWSの環境構築を自動化できるようPlaybookを作成してみましたが、
yaml形式でまとめることでとても見通しが良くなっていると思います。
まだAWS純正のCloudFormationは使ったことないのでどちらが管理しやすいか比較ができていないので、そのうちCloudFormationも使って比較してみたいと思います。