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


前回の続きです。
HHVMコンテナをFactCGIモードで動かしてみます。
構築はTerraFormを利用し、コンテナ実行環境はAmazon ECSを利用します。

構築イメージ

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

必要なもの

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

利用したDockerイメージ

事前準備(抜粋)

事前準備の一部を記載します。

  • セキュリティグループ

    以下3つのセキュリティグループ(※アウトバウンドはすべて許可)を用意します。

  1. Public ELB用(security_group1)

    インバウンド:HTTP、HTTPSのみすべて許可

  2. 内部 ELB用(security_group2)

    インバウンド:VPC内(private network)のみ9000(FactCGI)ポート許可

  3. EC2 container instance用(security_group3)

    インバウンド:VPC内(private network)のみ全ポート許可、接続元端末より(マイIP)全ポート許可(※各種確認用)

  • apacheコンテナイメージ

    dockerへのenv(environment)の引き回しがうまく動作しなかったため、apacheコンテナのELB(internal)接続はベタ書きしています。。。
    以下を動作させるにはhttps://github.com/Thirosue/docker-apache2をクローンして、以下を修正し、DocekrHubへプッシュしてください。(参考手順

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

構築

TerraFormで構築します。
EC2コンテナインスタンスは、Auto Scalingで構成します。
作成したELBをECS Service(Task Difinision)に紐付けます。
サンプルソース

  • plan
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
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

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

$ ssh -i "[EC2キーペア名]" [email protected]
$ 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
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
terraform apply ./terraform.tfstate

最後に

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