TerraformとAWSに同時入門する

GMOペパボ、ムームードメインのエンジニア@litencattです。

昨日は@dp42による、["Hello, World."の次としてのチャットボット](https://dp42.net/posts/2017/12/03/pepabo-advent-calendar/)でした。

今日は、入社以来専らWeb開発がメインでやってきたけど、最近興味のあるAWSやTerraformによるインフラ構築について読んだ本をベースにやってみたことについて書いていきます。

## やること
「[Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版](https://www.amazon.co.jp/Amazon-Web-Services-%E5%9F%BA%E7%A4%8E%E3%81%8B%E3%82%89%E3%81%AE%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF-%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E6%A7%8B%E7%AF%89/dp/4822237443/ref=sr_1_1?ie=UTF8&qid=1512274397&sr=8-1&keywords=amazon+web+service)」のインフラ環境や手順を参考に、AWS環境構築をTerraformを用いて行います。

なお今回はEC2インスタンスに対するApacheなどの各種インストールは直接インスタンス内に入ってコマンド実行していきます。

### ゴール
AWS上に以下の環境構築を行ないます

- EC2インスタンス x 2台
    - Webサーバ
        - インターネットゲートウェイを持つ
        - Apache(httpd)上でWordPressが動作している
    - DBサーバ
        - NATゲートウェイを通してインターネットに接続する
        - MySQLが起動している

この2つのインスタンスは同一VPC上に存在し、

具体的には以下のAWSリソースを扱います。

- EC2
- VPC
- サブネット
- ルートテーブル
- インターネットゲートウェイ
- セキュリティグループ
- Elastic IP
- NATゲートウェイ

### 今回参考にした本について
この記事内ではVPCやサブネットなど各種用語の説明まではおこなっていないのですが、
そのあたりについては今回参考とした本においてとても詳しい解説があり、またAWSで構築していく環境についての図がとてもわかりやすいと思います。
なので、このあたりについて初めて触る人にとってはとてもおすすめな本だと思いますので、ぜひ本を手元に用意してこの記事の内容も試してみていただけると良いかなと思っております:smile:

## [Terraform](https://www.terraform.io/)とは
- AWSなど様々なサービスProviderに対して、サーバの構築・変更・管理を行うためのツール
- [HashiCorp](https://www.hashicorp.com/)のプロダクト
- Enterprise版もあるみたい

ペパボではプライベートクラウドとしてOpenStackを自社運用しています。
TerraformはOpenStackにも対応しており、最近は各サービスのインフラ管理がTerraformで行われるようになってきています。

今回はこのTerraformをAWSに対して使っていきます。
https://www.terraform.io/docs/providers/aws/index.html

### 今回のコードのレポジトリ
https://github.com/litencatt/terraform-aws-templates

## 準備

### Terraformのインストール(Mac)

```
$ brew install terraform
```

### 使用バージョン
2017/12/3の執筆時点での最新リリースバージョンをつかいます

```
$ terraform version
Terraform v0.11.1
+ provider.aws v1.5.0
```
https://github.com/hashicorp/terraform/blob/master/CHANGELOG.md#0111-november-30-2017

### 主にVim向け
HCL扱う場合は入れとくと便利そうです
https://github.com/hashivim/vim-hashicorp-tools

### AWSの準備

- AWSアカウント作成
    - https://portal.aws.amazon.com/billing/signup#/start
- AWSアクセスキー作成
    - ユーザーを作成してアクセスキーとシークレットアクセスキーを取得します
    - また、TerraformよりEC2インスタンス作成するには`AmazonEC2FullAccess`のポリシーをグループに対してアタッチしておく必要があります
    - 参考: https://qiita.com/miwato/items/291c7a8c557908de5833

### `terraform.tfvars`の設定
今回はAWSのアクセスキーなどの秘匿情報を`terraform.tfvars`に持つようにしています。
ここに作成したアクセスキーとシークレットアクセスキーを設定してください。
リージョンなども変更したい場合は必要に応じて変更してください。

```terraform.tfvars
access_key = "AWS_ACCESS_KEY"
secret_key = "AWS_SECRET_KEY"
region     = "ap-northeast-1"
key_name   = "KEY_PAIR_NAME"
```

:warning:実際のアクセスキーなどが書かれた`terraform.tfvars`はレポジトリには登録しないよう注意ください

#### EC2インスタンスへのログイン時に必要な鍵ファイルについて
今回はAWSのダッシュボード上の`キーペア`で鍵を作成し、それをEC2インスタンス作成時に使用するように指定しています。そのため、`key_name`には作成したキーペア名を設定してください。
また、EC2インスタンスへのログイン時に必要なため、作ったキーペアファイルは同じディレクトリ内に置いておくと良さそうです。


## VPC作成
まずはVPCを作成します
https://github.com/litencatt/terraform-aws-templates/pull/1

```main.tf
variable "access_key" {}
variable "secret_key" {}
variable "region" {}

provider "aws" {
  access_key = "${var.access_key}"
  secret_key = "${var.secret_key}"
  region     = "${var.region}"
}

resource "aws_vpc" "vpc-1" {
  cidr_block = "10.0.0.0/16"
  tags {
    Name = "vpc-1"
  }
}
```
`main.tf`ファイル作成後`$ terraform init`を実行し、AWSのpluginを取得します。

その後、`$ terraform apply`を実行して成功するとVPCが作成され、AWSのVPCダッシュボードのVPCページでも確認することが出来ます。
![image](https://user-images.githubusercontent.com/17349045/33484412-71ae05b2-d6e5-11e7-876c-1064c598f0ea.png)

ちなみにv0.11.0より`$ terraform apply`した場合は下記のように`yes`を入力しないとapplyが実行されないように変更されています。

```
$ terraform apply

(省略)

Plan: 15 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value:
```

### `init`せずに`plan`などを実行した場合のエラー
`$ terraform init`を先に実行してください

```
$ terraform plan
Plugin reinitialization required. Please run "terraform init".
Reason: Could not satisfy plugin requirements.
...(略
```

## 以降の作業
すべて記事内に書くと結構なボリュームになりそうなので、以降の作業内容については各PRを参照ください。
PRを作成して手順や差分をわかりやすくし、必要に応じて説明を入れています:nerd_face:
また、各PRでは確認のために毎回`$ terraform apply`を実行させています。


- [パブリックサブネットの作成](https://github.com/litencatt/terraform-aws-templates/pull/2)
- [インターネットゲートウェイの作成](https://github.com/litencatt/terraform-aws-templates/pull/3)
- [パブリックルートテーブルの作成](https://github.com/litencatt/terraform-aws-templates/pull/4)
- [tfファイルの分割](https://github.com/litencatt/terraform-aws-templates/pull/5)
- [EC2インスタンス(Webサーバ)の作成](https://github.com/litencatt/terraform-aws-templates/pull/6)
- [WebサーバへのElastic IPの適用](https://github.com/litencatt/terraform-aws-templates/pull/7)
- [Webサーバのセキュリティグループへアウトバウンド設定追加](https://github.com/litencatt/terraform-aws-templates/pull/8)
- [WebサーバへのApacheインストールなど](https://github.com/litencatt/terraform-aws-templates/pull/9)
- [プライベートサブネットの作成〜DBサーバ作成](https://github.com/litencatt/terraform-aws-templates/pull/10)
- [NATゲートウェイの作成〜各種インストール](https://github.com/litencatt/terraform-aws-templates/pull/11)

### WordPressページの表示確認
[NATゲートウェイの作成〜各種インストール](https://github.com/litencatt/terraform-aws-templates/pull/11)のPR内の作業までをすべて完了後、
WebサーバのURLに対してブラウザよりアクセスするとWordPressのスタートページの表示を確認することが出来ます。
![image](https://user-images.githubusercontent.com/17349045/33521459-a5ebdd5a-d815-11e7-8009-309545947cdc.png)

:warning:この記事で作成したインスタンスは既にdestroy済みなのでPR上のURLにはアクセスできませんのでご注意ください

## TerraformでAWSの環境構築してみて
- 例えばEC2を作成した場合、未設定項目はデフォルト設定が適用される
    - ネットワークインターフェースは`eth0`が追加される
    - ストレージも`gp2, 8GiB`のタイプが適用される
- [ICMPエコー許可設定](https://github.com/litencatt/terraform-aws-templates/pull/10#issuecomment-348666872)
    - サーバに対してpingを有効にするためにセキュリティグループにICMPの許可設定を追加する場合に、通常は`from_port`、`to_port`にはポート番号を設定しますがICMPを追加する場合はICMP番号という番号で指定しなければならないようです

    ```diff:vpc.tf
    resource "aws_security_group" "web-sg" {

    ...

    +  ingress {
    +    from_port   = 8
    +    to_port     = 0
    +    protocol    = "icmp"
    +    cidr_blocks = ["0.0.0.0/0"]
    +  }
    ```
- Elastic IPはデフォルトの上限が`5`
    - [Amazon Elastic Compute Cloud (Amazon EC2) 制限](http://docs.aws.amazon.com/ja_jp/general/latest/gr/aws_service_limits.html#limits_ec2)
    - それを超えた場合、apply時にエラーが出ます(エラーなどの詳細は下記リンク参照ください)
        - [NATゲートウェイの作成](https://github.com/litencatt/terraform-aws-templates/pull/11#issuecomment-348733093)
- 請求料金などについて
    - Elastic IPやNATゲートウェイは使用していない時は開放する
    - ダッシュボード上のリソースの使用数表示はリージョン毎らしいので注意する
        - [AWSで無料枠を利用し「Elastic IP」も全て解放済みのはずなのに料金が発生した件の原因と対応](http://gattsu09.hatenadiary.com/entry/2017/04/28/204755)

### 今後のやっていき
- lifecycleを使ってリソースの再作成が必要以上に行われないようにする
- 最新のAMIが常に指定されるようなami設定にする
- [Basic Two-Tier AWS Architecture](https://github.com/terraform-providers/terraform-provider-aws/tree/master/examples/two-tier)なファイル運用にする
- moduleを使ってみる
    - [AWS VPC Terraform module](https://github.com/terraform-aws-modules/terraform-aws-vpc)

## 最後に
今回、WordPress環境をAWS上にTerraformを主に使って構築してみましたが、
WordPress環境だけでなく、Node.jsやRailsなどの環境がなんと約10秒で出来上がってしまうという[**ロリポップ！マネージドクラウド**](https://mc.lolipop.jp/)のβ版が現在無料公開中ですので、そちらも是非宜しくお願いします:smile:
https://mc.lolipop.jp/

## 参考にさせて頂いたサイト
- [AWSでTerraformに入門](https://dev.classmethod.jp/cloud/terraform-getting-started-with-aws/)
- [Terraformで複数台のEC2インスタンスを構築する場合のTIPS](https://dev.classmethod.jp/cloud/tips-for-creating-multiple-aws_instance-resources/)

など多数