TerraFormでHHVMコンテナをAmazon ECS上で動かす

[前回](http://qiita.com/takeshi_hirosue/items/c4dea2cde71b7921c4a0 "前回")の続きです。
HHVMコンテナをFactCGIモードで動かしてみます。
構築はTerraFormを利用し、コンテナ実行環境はAmazon ECSを利用します。

## 構築イメージ

ELB(Public)より、apacheコンテナに流し、ELB(Internal)を経て、HHVMコンテナ(FactCGI)に接続します。

![ECS.png](https://qiita-image-store.s3.amazonaws.com/0/73176/d1cd4080-d864-355e-7902-a8cff0d1d8bf.png)

## 必要なもの
+ TerraForm
+ Amaozon AccessKey/SecretKey(secret.tfvars)
+ Amazon VPC
+ AWS セキュリティグループ
+ EC2キーペア
+ apacheコンテナイメージ

## 利用したDockerイメージ
+ [apache](https://hub.docker.com/r/mirrored1976/apache2/)
+ [hhvm](https://hub.docker.com/r/mirrored1976/hhvm-sample/)

## 事前準備(抜粋)
事前準備の一部を記載します。

+ セキュリティグループ<p>
以下３つのセキュリティグループ（※アウトバウンドはすべて許可）を用意します。<p>

  1. Public ELB用(security_group1)<p>
  インバウンド：HTTP、HTTPSのみすべて許可
  1. 内部 ELB用(security_group2)<p>
  インバウンド：VPC内（private network）のみ9000(FactCGI)ポート許可
  1. EC2 container instance用(security_group3)<p>
  インバウンド：VPC内（private network）のみ全ポート許可、接続元端末より（マイIP）全ポート許可（※各種確認用）

+ apacheコンテナイメージ<p>
dockerへのenv(environment)の引き回しがうまく動作しなかったため、apacheコンテナのELB（internal）接続はベタ書きしています。。。<br>
以下を動作させるには[https://github.com/Thirosue/docker-apache2](https://github.com/Thirosue/docker-apache2)をクローンして、以下を修正し、DocekrHubへプッシュしてください。（[参考手順](http://qiita.com/takeshi_hirosue/items/9cfd22c2263866cece99)）

```:hhvm_proxy_fcgi.conf
ProxyPassMatch ^/(.+\.(hh|php)(/.*)?)$ fcgi://{内部ELBのAレコードを記載}:9000/app/$1
```

## 構築
TerraFormで構築します。
EC2コンテナインスタンスは、Auto Scalingで構成します。
作成したELBをECS Service（Task Difinision）に紐付けます。
[サンプルソース](https://github.com/Thirosue/terraform-sample/tree/master/aws_ecs "サンプルソース")

+ plan

```bash
terraform plan -var-file=../secret.tfvars \
 -var 'ssh_key_name=[EC2キーペア名]' \
 -var 'security_groups_internal=[security_group3のIDを記載]' \
 -var 'security_groups_dmz=[security_group3のIDを記載]' \
 -var 'hhvm_vip=internal-hhvm-892282372.ap-northeast-1.elb.amazonaws.com'
```

+ apply

```bash
terraform apply -var-file=../secret.tfvars \
 -var 'ssh_key_name=[EC2キーペア名]' \
 -var 'security_groups_internal=[security_group3のIDを記載]' \
 -var 'security_groups_dmz=[security_group3のIDを記載]' \
 -var 'hhvm_vip=internal-hhvm-892282372.ap-northeast-1.elb.amazonaws.com'
```

```:variable.tf
#######################
# Required
#######################

variable "access_key" {}
variable "secret_key" {}

# SSH key
variable "ssh_key_name" {}

# security group internal
variable "security_groups_internal" {}

# security group dmz
variable "security_groups_dmz" {}

# internal elb for hhvm
variable "hhvm_vip" {}

#######################
# Option
#######################

variable "region" {
  default = "ap-northeast-1"
}

variable "aws_amis" {
  default = {
      "ap-northeast-1" = "ami-2b08f44a"
  }
}

# Instance Type
variable "instance_type" {
  default = "t2.micro"
}
```

```:ecs_cluster.tf
provider "aws" {
    access_key = "${var.access_key}"
    secret_key = "${var.secret_key}"
    region = "${var.region}"
}

resource "aws_ecs_cluster" "hhvm" {
  name = "hhvm"
}
```

※以下詳細なソース

```:task_difinision.tf
resource "aws_ecs_task_definition" "hhvm" {
  family = "hhvm"
  container_definitions = "${file("task-definitions/hhvm.json")}"
}

resource "aws_ecs_task_definition" "httpd" {
  family = "httpd"
  container_definitions = "${file("task-definitions/httpd.json")}"
}
```

```:hhvm.json
[
  {
    "name": "hhvm",
    "image": "mirrored1976/hhvm-sample",
    "cpu": 10,
    "memory": 300,
    "essential": true,
    "portMappings": [
      {
        "containerPort": 9000,
        "hostPort": 9000
      }
    ]
  }
]
```

```:httpd.json
[
  {
    "name": "httpd",
    "image": "[用意したapacheコンテナのイメージを指定]",
    "cpu": 10,
    "memory": 300,
    "essential": true,
    "portMappings": [
      {
        "containerPort": 80,
        "hostPort": 80
      }
    ],
    "environment": [
        {
            "name": "HHVM_VIP",
            "value": "internal-hhvm-892282372.ap-northeast-1.elb.amazonaws.com"
        }
    ]
  }
]
```

```:elb.tf
resource "aws_elb" "httpd" {
  name = "httpd"
  availability_zones = ["ap-northeast-1a"]
  security_groups = ["${var.security_groups_dmz}"]

  listener {
    instance_port = 80
    instance_protocol = "http"
    lb_port = 80
    lb_protocol = "http"
  }

  health_check {
    healthy_threshold = 2
    unhealthy_threshold = 10
    timeout = 10
    target = "HTTP:80/"
    interval = 30
  }

  cross_zone_load_balancing = true
  idle_timeout = 400
  connection_draining = true
  connection_draining_timeout = 400
}
```

```:auto_scaling.tf
resource "aws_launch_configuration" "hhvm" {
    name = "hhvm"
    image_id = "${lookup(var.aws_amis, var.region)}"
    key_name = "${var.ssh_key_name}"
    instance_type = "${var.instance_type}"
    iam_instance_profile = "ecsInstanceRole"
    security_groups = [
        "${var.security_groups_internal}"
    ]
    user_data = "${file("user_data/userdata.sh")}"
}

resource "aws_autoscaling_group" "hhvm" {
  availability_zones = ["ap-northeast-1a"]
  name = "hhvm"
  max_size = 2
  min_size = 2
  desired_capacity = 2
  health_check_grace_period = 300
  health_check_type = "ELB"
  force_delete = true
  launch_configuration = "${aws_launch_configuration.hhvm.name}"
}
```

```:ecs_service.tf
resource "aws_ecs_service" "hhvm" {
  name = "hhvm"
  cluster = "${aws_ecs_cluster.hhvm.id}"
  task_definition = "${aws_ecs_task_definition.hhvm.arn}"
  desired_count = 2
  iam_role = "ecsServiceRole"

  load_balancer {
    elb_name = "[用意した内部ELBの名前を記載]"
    container_name = "hhvm"
    container_port = 9000
  }
}

resource "aws_ecs_service" "httpd" {
  name = "httpd"
  cluster = "${aws_ecs_cluster.hhvm.id}"
  task_definition = "${aws_ecs_task_definition.httpd.arn}"
  desired_count = 2
  iam_role = "ecsServiceRole"

  load_balancer {
    elb_name = "${aws_elb.httpd.name}"
    container_name = "httpd"
    container_port = 80
  }
}
```

## 確認

+ HTTP/FactCGI
ブラウザで以下にアクセス。
http://[Public ELBのIP]/phpinfo.php
<br>

+ dockerコンテナ
各EC2 container　Instanceへsshすると、apacheコンテナとhhvmコンテナ及びecs-agentコンテナが動いているのが確認できる。

```bash
$ ssh -i "[EC2キーペア名]" ec2-user@ec2-52-197-239-125.ap-northeast-1.compute.amazonaws.com
$ docker ps
CONTAINER ID        IMAGE                            COMMAND                  CREATED             STATUS              PORTS                    NAMES
ad616c73345f        mirrored1976/apache2             "/usr/sbin/apachectl "   About an hour ago   Up About an hour    0.0.0.0:80->80/tcp       ecs-httpd-11-httpd-88cea3affcb5c0c55e00
1d70ef3a503c        mirrored1976/hhvm-sample         "/start.sh"              About an hour ago   Up About an hour    0.0.0.0:9000->9000/tcp   ecs-hhvm-23-hhvm-e2b583edaa80be909501
6172ac18f2af        amazon/amazon-ecs-agent:latest   "/agent"                 About an hour ago   Up About an hour                             ecs-agent
```

## 後処理
サンプルを破棄します。

+ plan destroy

```bash
terraform plan -destroy -out=./terraform.tfstate -var-file=../secret.tfvars \
 -var 'ssh_key_name=[EC2キーペア名]' \
 -var 'security_groups_internal=[security_group3のIDを記載]' \
 -var 'security_groups_dmz=[security_group3のIDを記載]' \
 -var 'hhvm_vip=internal-hhvm-892282372.ap-northeast-1.elb.amazonaws.com'
```

+ do destroy

```bash
terraform apply ./terraform.tfstate
```

## 最後に
今回の構築はかなり手こずりましたが、お陰さまでAmazon ECSが大分理解できました。
初めは、ECSは難しいと感じましたが、理解できるとシンプルなので、実戦投入できるよう詰めていきます。