aws上に作成したansibleサーバーでテストサイトを構築する(3) EC2インスタンスの作成

# はじめに
[前回](https://qiita.com/yoshino_dd/items/d9be528837803ebb4bd3)までで、AWSのテストサイト用のVPCとネットワーク、キーペアの作成までできました。
今回はEC2インスタンスを登録、SSHにて接続するまでを実装します。

- aws上に作成したansibleサーバーでテストサイトを構築する
  1. [Ansibleサーバー構築](https://qiita.com/yoshino_dd/items/1e0969fd6dab1b51e1e0)
  1. [AWSネットワーク構成](https://qiita.com/yoshino_dd/items/d9be528837803ebb4bd3)
  1. [EC2インスタンスの作成](https://qiita.com/yoshino_dd/items/4368e468c6aa5353bf3c)
  1. [サーバー共通の設定](https://qiita.com/yoshino_dd/items/3fa56fc09e9162d6ad4a)
  1. [Webサーバーの構成](https://qiita.com/yoshino_dd/items/1292d6b3beeae26433f7)
  1. [RDSの構成-1](https://qiita.com/yoshino_dd/items/8d559b60d5b9f316d8bb)
  1. [RDSの構成-2](https://qiita.com/yoshino_dd/items/1da42dc2d1446ec20526)
  1. [WordPressの設定](https://qiita.com/yoshino_dd/items/d41694d468c5c64d78a0)
  1. 参考にしたサイトなど

# インベントリファイル作成
まずは作成するインスタンスを記載したインベントリファイルを作成します。
とりあえず、Webサーバーを1台登録。

```ini:inventories/hosts
[webapps]
web01 type="t2.micro" image="ami-2a69be4c" secgrp="SG_webapps" subnet="webapp1"
```

ここで指定している変数は以下の通り。

| 変数名 | ここでの設定値 | 備考 |
|:--|:--|:--|
|type| t2.micro | EC2インスタンスの種類<br/>無料枠で使用できるものを選択 |
|image|ami-2a69be4c| 構成に使用するAMIの指定<br/>同じく無料枠で使用できるものを選択 |
|secgrp|SG_webapps|インスタンスに適用するセキュリティグループ<br/>前回作成したセキュリティグループを指定|
|subnet|webapp1|インスタンスが所属するサブネット名<br/>こちらも前回作成したもの|

`ansible`コマンドに毎回`-i`オプションを指定しなくてもいいように、`ansible.cfg`を作成し、以下の設定を行います。

```ini:ansible.cfg
[defaults]
inventory = ./inventories
```

# EC2インスタンス作成
EC2インスタンスを作成するロール`aws_ec2`を新しく作成することにします。

まず、呼び出す`aws_infra.yml`を修正。
このロールはインベントリに記載した全ホストに対して実行したいので`hosts: all`のPlayとして追加します。
AWSに対するEC2作成要求はAnsibleサーバー(localhost)から実行しなければいけないので、'connection: local'を設定。

```yaml:aws_infta.yml
- hosts: localhost
  roles:
  - { role: aws_network, tags: [aws_network] }

- hosts: all
  connection: local
  roles:
  - { role: aws_ec2, tags: [aws_ec2] }
```

インスタンス配置先のサブネットIDが必要となるため、`ec2_vpc_subnet_facts`モジュールを使用して情報を取得します。

```yaml:roles/aws_ec2/tasks/main.yml
---
- name: get subnet id
  ec2_vpc_subnet_facts:
    region: "{{ vpc.region }}"
    filters:
      "tag:Name": "{{ subnet }}"
  register: aws_get_subnet
```

取得したサブネットIDと、インベントリファイルに記載した情報を使ってEC2インスタンスを作成するタスクを作成します。
`exact_count`と`count_tag`を使用して、同じインスタンスが複数起動されることのないようにします。

```yaml:roles/aws_ec2/tasks/main.yml(追記)
- name: create ec2 instance
  ec2:
    instance_tags:
      Name: "{{ inventory_hostname }}"
    key_name: "{{ keypair.name }}"
    instance_type: "{{ type }}"
    image: "{{ image }}"
    wait: yes
    group: "{{ secgrp }}"
    vpc_subnet_id: "{{ aws_get_subnet.subnets[0].id }}"
    region: "{{ vpc.region }}"
    assign_public_ip: yes
    count_tag:
      Name: "{{ inventory_hostname }}"
    exact_count: 1
  register: aws_create_ec2
```

実行結果

```shell-session
$ ansible-playbook site.yml

PLAY [localhost] ***********************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************
ok: [localhost]

TASK [aws_network : create vpc] ********************************************************************************************
ok: [localhost]

TASK [aws_network : create vpc subnets] ************************************************************************************
ok: [localhost] => (item={u'cidr': u'172.22.1.0/24', u'az': u'ap-northeast-1a', u'name': u'webapp1'})
ok: [localhost] => (item={u'cidr': u'172.22.2.0/24', u'az': u'ap-northeast-1c', u'name': u'webapp2'})

TASK [aws_network : create internet gateway] *******************************************************************************
ok: [localhost]

TASK [aws_network : make subnet list] **************************************************************************************
ok: [localhost]

TASK [aws_network : Set up public subnet route table] **********************************************************************
ok: [localhost]

TASK [aws_network : create security group] *********************************************************************************
ok: [localhost] => (item={u'rules': [{u'to_port': 80, u'from_port': 80, u'cidr_ip': u'0.0.0.0/0', u'proto': u'tcp'}, {u'to_port': 443, u'from_port': 443, u'cidr_ip': u'0.0.0.0/0', u'proto': u'tcp'}, {u'to_port': 22, u'from_port': 22, u'cidr_ip': u'0.0.0.0/0', u'proto': u'tcp'}], u'name': u'SG_webapps', u'description': u'for WebApplication Servers'})

TASK [aws_network : create keypair] ****************************************************************************************
ok: [localhost]

TASK [aws_network : create keypair file] ***********************************************************************************
skipping: [localhost]

TASK [aws_network : keypair write] *****************************************************************************************
skipping: [localhost]

PLAY [all] *****************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************
ok: [web01]

TASK [aws_ec2 : get subnet id] *********************************************************************************************
ok: [web01]

TASK [aws_ec2 : create ec2 instance] ***************************************************************************************
changed: [web01]

PLAY RECAP *****************************************************************************************************************
localhost                  : ok=8    changed=0    unreachable=0    failed=0
web01                      : ok=3    changed=1    unreachable=0    failed=0
```

# Ansibleアクセス用hostsエントリ追加
作成したEC2にansibleサーバーからインベントリファイル上のホスト名でアクセスさせるため、EC2のグローバルIPをansibleサーバー上のhostsファイルに追加。
(ローカルのhostsをいじるより、もっといい方法があるはずだとは思いますが…)

```yaml:roles/aws_ec2/tasks/main.yml(追記)
---
- name: write host list
  run_once: true
  become: true
  lineinfile:
    path: "/etc/hosts"
    create: true
    regexp: '{{ item }}.{{ vpc.name }}'
    state: present
    line: "{{ hostvars[item].aws_create_ec2.tagged_instances[0].public_ip }}    {{ item }} {{ item }}.{{ vpc.vpc_name }}"
  with_items: "{{ play_hosts }}"
```

ちなみに、`hosts: all`なPlayなので、それぞれのホストのタスクで書き込ませたほうがわかりやすいのですが、当初そのように実装したところ`/etc/hosts`への書き込みが競合したのか一部のホストにて書き込まれない事象が発生したため、`run_once`と`with_items: play_hosts`を使用して1回のタスクですべてのホスト情報を書き込ませています。

```yaml:roles/aws_ec2/tasks/main.yml(失敗版)
---
- name: write host list
  become: true
  lineinfile:
    path: "/etc/hosts"
    create: true
    state: present
    regexp: '{{ inventory_hostname }}.{{ vpc.name }}'
    line: "{{ aws_create_ec2.tagged_instances[0].public_ip }}    {{ inventory_hostname }} {{ inventory_hostname }}.{{ vpc.vpc_name }}"
```

実行結果。

```shell-session:command
$ ansible-playbook site.yml

PLAY [localhost] ***********************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************
ok: [localhost]

TASK [aws_network : create vpc] ********************************************************************************************
ok: [localhost]

TASK [aws_network : create vpc subnets] ************************************************************************************
ok: [localhost] => (item={u'cidr': u'172.22.1.0/24', u'az': u'ap-northeast-1a', u'name': u'webapp1'})
ok: [localhost] => (item={u'cidr': u'172.22.2.0/24', u'az': u'ap-northeast-1c', u'name': u'webapp2'})

TASK [aws_network : create internet gateway] *******************************************************************************
ok: [localhost]

TASK [aws_network : make subnet list] **************************************************************************************
ok: [localhost]

TASK [aws_network : Set up public subnet route table] **********************************************************************
ok: [localhost]

TASK [aws_network : create security group] *********************************************************************************
ok: [localhost] => (item={u'rules': [{u'to_port': 80, u'from_port': 80, u'cidr_ip': u'0.0.0.0/0', u'proto': u'tcp'}, {u'to_port': 443, u'from_port': 443, u'cidr_ip': u'0.0.0.0/0', u'proto': u'tcp'}, {u'to_port': 22, u'from_port': 22, u'cidr_ip': u'0.0.0.0/0', u'proto': u'tcp'}], u'name': u'SG_webapps', u'description': u'for WebApplication Servers'})

TASK [aws_network : create keypair] ****************************************************************************************
ok: [localhost]

TASK [aws_network : create keypair file] ***********************************************************************************
skipping: [localhost]

TASK [aws_network : keypair write] *****************************************************************************************
skipping: [localhost]

PLAY [all] *****************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************
ok: [web01]

TASK [aws_ec2 : get subnet id] *********************************************************************************************
ok: [web01]

TASK [aws_ec2 : create ec2 instance] ***************************************************************************************
ok: [web01]

TASK [aws_ec2 : write host list] *******************************************************************************************
changed: [web01] => (item=web01)

PLAY RECAP *****************************************************************************************************************
localhost                  : ok=8    changed=0    unreachable=0    failed=0
web01                      : ok=4    changed=1    unreachable=0    failed=0
```

hostsはこうなりました。

```ini:/etc/hosts
127.0.0.1       localhost localhost.localdomain localhost4 localhost4.localdomain4
::1             localhost localhost.localdomain localhost6 localhost6.localdomain6
xxx.xxx.xxx.xxx web01 web01.testsite
```

sshできることを確認。

```shell-session:command
$ ssh -i ~/.ssh/testsite_key.pem web01
The authenticity of host 'web01 (xxx.xxx.xxx.xxx)' can't be established.
ECDSA key fingerprint is bf:78:c8:fb:bb:a9:f2:33:ee:f4:8a:3a:a2:2b:07:3a.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'web01,xxx.xxx.xxx.xxx' (ECDSA) to the list of known hosts.

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2017.03-release-notes/
2 package(s) needed for security, out of 6 available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-172-22-1-216 ~]$
```

# 次回
次回は作成したEC2上のAmazon Linuxに共通の設定を適用していきます。

- [aws上に作成したansibleサーバーでテストサイトを構築する(4) サーバー共通の設定](https://qiita.com/yoshino_dd/items/3fa56fc09e9162d6ad4a)

---