TerraformでALBを構築する

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こんにちは、中山です。

弊社ブログで何度も取り上げているALBですが、v0.7.1でTerraformにも対応しました。早速使ってみたので、レポートします。CloudFormationでのALB利用方法については以下のエントリを参照してください。

構成図

今回作成する構成は以下のとおりです。

aws

パスパターンとルーティング先ターゲットグループは以下のように設定します。

  • 何もマッチしない -> ターゲットグループ1へ
  • /target/* にマッチ -> ターゲットグループ2へ

コード

GitHubにコードを上げておきました。ご自由にお使いください。

ALB関連のリソース

ALBを作成するためのコードとその意味を以下に記載します。なおそれぞれの情報は現時点(2016/08/22)の情報であることに留意してください。

リソース 用途
aws_alb ALBの作成
aws_alb_target_group ターゲットグループの作成
aws_alb_target_group_attachment ターゲットグループとインスタンスまたはコンテナとひも付け
aws_alb_listener リスナーの作成(とALBとターゲットグループのひも付け)
aws_alb_listener_rule リスナーのルール作成

aws_alb

コードは以下のとおりです。

resource "aws_alb" "alb" {
  name                       = "${var.name}"
  security_groups            = ["${aws_security_group.alb.id}"]
  subnets                    = ["${aws_subnet.frontend_subnet.*.id}"]
  internal                   = false
  enable_deletion_protection = false

  access_logs {
    bucket = "${aws_s3_bucket.alb_log.bucket}"
  }
}

設定内容は以下のとおりです。最新の情報はドキュメントを参照するようにしてください。

設定 必須の有無
name ALBの名前。デフォルトでTerraformが自動で生成する。 No
internal true であればInternal ALBになる。 No
security_groups ALBに紐付けるセキュリティグループの配列。 No
access_logs ALBのアクセスログ。指定可能な設定は下記参照。 No
subnets ALBに紐付けるサブネットIDの配列。 Yes
idle_timeout idleタイム。この時間を超過するとコネクションをクローズする。デフォルト60秒。 No
enable_deletion_protection true であればdeletionポリシーを有効する。 No
tags ALBに紐付けるタグ。 No

アクセスログで指定可能な設定は以下のとおりです。

設定 必須の有無
bucket S3のバケット名 Yes
prefix S3のキー名 No

aws_alb_target_group

コードは以下のとおりです。

resource "aws_alb_target_group" "alb" {
  count    = 2
  name     = "${var.name}-tg${count.index+1}"
  port     = 80
  protocol = "HTTP"
  vpc_id   = "${aws_vpc.vpc.id}"

  health_check {
    interval            = 30
    path                = "/index.html"
    port                = 80
    protocol            = "HTTP"
    timeout             = 5
    unhealthy_threshold = 2
    matcher             = 200
  }
}

設定内容は以下のとおりです。最新の情報はドキュメントを参照するようにしてください。

設定 必須の有無
name ターゲットグループの名前。 Yes
port ターゲットグループがトラフィックを待ち受けるポート。ヘルスチェックのポートや、個々のインスタンスをターゲットグループに登録する際のデフォルトポートのように扱われる。 Yes
protocol ターゲットグループがトラフィックを待ち受けるプロトコル。 Yes
vpc_id ターゲットグループが所属しているVPC ID。 Yes
deregistration_delay draining から unused へ推移させるまでの時間。この時間経過後ALBは完全にコネクションをクローズする。デフォルト300秒。 No
stickiness スティッキーセッションの設定。下記参照。 No
health_check ヘルスチェックの設定。下記参照。 No

スティッキーセッションの設定は以下のとおりです。

設定 必須の有無
type スティッキーセッションのタイプ。現在のところ lb_cookie のみサポート。 Yes
cookie_duration cookieが利用されるまでの時間。この時間経過後新しくcookieが生成される。デフォルト1日。 No

ヘルスチェックの設定は以下のとおりです。

設定 必須の有無
interval ヘルスチェックのインターバル時間。デフォルト30秒。 No
path ヘルスチェック先のパス。デフォルト / No
port ヘルスチェックする際のポート。1から65535を指定可能。デフォルトは traffic-port (ターゲットグループに設定したポート番号) 。 No
protocol ヘルスチェックする際のプロトコル。デフォルトHTTP。 No
timeout タイムアウト値。この時間経過後ヘルスチェックに失敗したと判断する。デフォルト5秒。 No
healthy_threshold ヘルスチェックに成功したと判断するまでの回数。デフォルト5。 No
unhealthy_threshold ヘルスチェックに失敗したと判断するまでの回数。デフォルト2。 No
matcher ターゲットから返却した場合、ヘルスチェックに成功したと判断するHTTPレスポンスコード。デフォルト200。カンマ区切り( "200,202" )やレンジ指定( "200-299" )でも可能。 No

aws_alb_target_group_attachment

コードは以下のとおりです。

resource "aws_alb_target_group_attachment" "alb" {
  count            = 2
  target_group_arn = "${element(aws_alb_target_group.alb.*.arn, count.index)}"
  target_id        = "${element(aws_spot_instance_request.web.*.spot_instance_id, count.index)}"
  port             = 80
}

設定内容は以下のとおりです。最新の情報はドキュメントを参照するようにしてください。

設定 必須の有無
target_group_arn ひも付けるターゲットグループのARN。 Yes
target_id ターゲットグループにひも付けるインスタンスまたはコンテナのID。 Yes
port ターゲット(インスタンスまたはコンテナ)が待ち受けるポート番号。 Yes

aws_alb_listener

コードは以下のとおりです。

resource "aws_alb_listener" "alb" {
  load_balancer_arn = "${aws_alb.alb.arn}"
  port              = "443"
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-2015-05"
  certificate_arn   = "${var.alb_config["certificate_arn"]}"

  default_action {
    target_group_arn = "${aws_alb_target_group.alb.0.arn}"
    type             = "forward"
  }
}

設定内容は以下のとおりです。最新の情報はドキュメントを参照するようにしてください。

設定 必須の有無
load_balancer_arn ALBのARN。 Yes
port ALBが待ち受けるポート番号。 Yes
protocol クライアントからALBへのアクセス時に利用されるプロトコル。HTTPかHTTPSのみ指定可能。デフォルトHTTP。 No
ssl_policy クライアンとALB間での通信に利用されるSSLポリシー。現在サポートされているのは ELBSecurityPolicy-2015-05 Yes( protocol がHTTPSの場合)
certificate_arn (AWS上の)SSL証明書のARN。 Yes( protocol がHTTPSの場合 )
default_action リスナーのデフォルトアクションの設定。下記参照。 Yes

アクションで指定可能な設定は以下のとおりです。

設定 必須の有無
target_group_arn ターゲットグループのARN。 このターゲットグループにトラフィックが流れる。 Yes
type トラフィックの流し方。サポートされているのは forward のみ。 Yes

aws_alb_listener_rule

コードは以下のとおりです。

resource "aws_alb_listener_rule" "tg2" {
  listener_arn = "${aws_alb_listener.alb.arn}"
  priority     = 100

  action {
    type             = "forward"
    target_group_arn = "${aws_alb_target_group.alb.1.arn}"
  }

  condition {
    field  = "path-pattern"
    values = ["/target/*"]
  }
}

設定内容は以下のとおりです。最新の情報はドキュメントを参照するようにしてください。

設定 必須の有無
listener_arn ひも付けるリスナーのARN。 Yes
priority ルールのプライオリティ。同じプライオリティで複数のルールを設定することはできない。 Yes
action アクションの設定。下記参照。 Yes
condition コンディションの設定。下記参照。 Yes

アクションの設定は以下のとおりです。

設定 必須の有無
target_group_arn ひも付けるターゲットグループのARN。このターゲットグループへトラフィックを流す。 Yes
type ルーティングの方法。現在サポートされているのは forward のみ。 Yes

コンディションの設定は以下のとおりです。

設定 必須の有無
field コンディションの設定。現在サポートされているのは path-pattern のみ。 Yes
values path-pattern の設定。 Yes

動作確認

いつものようにTerraformを実行後、作成されたALBに curl コマンドでアクセスしてみます。index.htmlにホスト名が表示されるようにしています。

$ curl https://<alb-dns-name> -k
ip-172-16-100-180
$ curl https://<alb-dns-name>/target/index.html -k
ip-172-16-101-87

正常にアクセスされたようです。やりましたね。

続いてS3のアクセスログを見てみます。

$ aws s3 ls s3://alb-demo-alb-log --recursive
2016-08-20 11:07:36         83 AWSLogs/345262022757/ELBAccessLogTestFile
2016-08-20 11:15:12        286 AWSLogs/345262022757/elasticloadbalancing/ap-northeast-1/2016/08/20/************_elasticloadbalancing_ap-northeast-1_app.alb-demo.7e0bdfcfe5063096_20160820T0215Z_52.193.129.155_m7p441u7.log.gz
2016-08-20 11:15:07       1200 AWSLogs/345262022757/elasticloadbalancing/ap-northeast-1/2016/08/20/************_elasticloadbalancing_ap-northeast-1_app.alb-demo.7e0bdfcfe5063096_20160820T0215Z_54.64.132.137_5zfzvcah.log.gz

ログが出力されているようです。中身を見てみます。

https 2016-08-20T02:11:03.935122Z app/alb-demo/7e0bdfcfe5063096 49.239.76.29:28631 172.16.100.180:80 0.001 0.001 0.000 404 404 121 3873 "GET https://<alb-dns-name>:443/target HTTP/1.1" "curl/7.43.0" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 arn:aws:elasticloadbalancing:ap-northeast-1:************:targetgroup/alb-demo-tg1/965b2c1bee015bb4

正常に出力されています。

おまけ

v0.7.1から今回のALBやELBのアクセスログを作成する際に便利なデータソースが追加されました。aws_elb_service_accountです。このデータソースを利用することでバケットのポリシーにELBのアカウントIDを指定可能です。

以下のように使用します。

data "aws_elb_service_account" "alb_log" {}

data "aws_iam_policy_document" "alb_log" {
  statement {
    actions = [
      "s3:PutObject",
    ]

    resources = [
      "arn:aws:s3:::${var.name}-alb-log/*",
    ]

    principals = {
      type = "AWS"

      identifiers = [
        "${data.aws_elb_service_account.alb_log.id}",
      ]
    }
  }
}

resource "aws_s3_bucket" "alb_log" {
  bucket        = "${var.name}-alb-log"
  acl           = "private"
  policy        = "${data.aws_iam_policy_document.alb_log.json}"
  force_destroy = true
}

aws_iam_policy_document で生成されるバケットのポリシーは以下のようになります。今回東京リージョンなので「582318560864」が参照されます。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::582318560864:root"
      },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::alb-demo-alb-log/*"
    }
  ]
}

まとめ

いかがだったでしょうか。

発表されてから比較的サポートされるまでの期間が短かったなという印象です。やはり、ALBは期待度の高い機能なので、Terraformがサポートするのも早くてよかったです。ただ、使ってみて気付いたのですがまだバグがあるようです。リソースの更新を実施すると、更新が完了したと表示されるのに実際は更新されて無かったりします。また、ターゲットグループにインスタンスをひも付けする際、インスタンスのステータスがランニングになっていないと失敗します。この辺りは今後の改善に期待したいと思います。OSSなんだから自分で直せよという話なのですが、Go力が低すぎて、orz。

本エントリがみなさんの参考になれば幸いです。