作りながら覚えるTerraform入門シリーズの第6回です。
今回はロードバランサを作成して独自ドメインでHTTPS接続できるようにしていきます。
作りながら覚えるTerraform入門シリーズ
- インストールと初期設定
- 基本編
- VPC編
- EC2編
- Route53 + ACM編
- ELB編 => 今回はコチラ
- RDS編
今回の学習ポイントは・・・
Terraform観点だともう基本的なポイントはご紹介したので特にないのですが、強いて挙げれば
- toset関数
- for_each
でしょうか。
あと、これまでに作成してきたリソースが組み合わさって繋がるので最後の接続確認が気持ちいい。笑
ELBの作成
まず、アプリケーションロードバランサ(ALB)を作成します。
elb.tf
を作成して、以下のコードを貼り付けます。
################################
# ELB
################################
# ELB
resource "aws_lb" "alb" {
name = "${var.prefix}-alb"
load_balancer_type = "application"
internal = false
idle_timeout = 60
enable_deletion_protection = false
subnets = [
aws_subnet.public_subnet_1a.id,
aws_subnet.public_subnet_1c.id
]
security_groups = [
aws_security_group.elb_sg.id
]
}
aws_lb
でロードバランサを作成します。
load_balancer_type
は現時点で次の3種類があります。
- application
- network
- gateway
コンソール画面で作成するときに見るアレですね。
ちなみに、aws_elb
を使うとClassic Load Balancerを作成できます。紛らわしいので念のため。
インターネット向けのELBを作成する場合はinternal = false
にします。
subnets
にはパブリックサブネットのIDを、security_groups
には事前に作成してあるELB向けのセキュリティグループのIDを指定します。
ターゲットグループの作成
次に、ターゲットグループを作成します。
elb.tf
に以下のコードを追加します。
# Target group
resource "aws_lb_target_group" "alb_tg" {
name = "${var.prefix}-alb-tg"
target_type = "ip"
vpc_id = aws_vpc.vpc.id
port = 80
protocol = "HTTP"
deregistration_delay = 300
health_check {
protocol = "HTTP"
path = "/"
port = "traffic-port"
healthy_threshold = 5
unhealthy_threshold = 2
timeout = 5
interval = 30
matcher = 200
}
}
resource "aws_lb_target_group_attachment" "alb_tg" {
for_each = toset(["10.0.11.11", "10.0.12.11"])
target_group_arn = aws_lb_target_group.alb_tg.arn
target_id = each.key
}
まず、aws_lb_target_group
でターゲットグループを作成します。
target_type
はip
にしていますが、インスタンスIDを使う場合はinstance
を指定します。(デフォルトがinstance
なので省略できます)
ターゲットグループにインスタンスを登録するにはaws_lb_target_group_attachment
で関連付けます。ここが少しハマったのですが、、
直感的には、target_id
には配列でIPを渡したいのですが、文字列にせよと怒られます。
Error: Incorrect attribute value type
│
│ on elb.tf line 52, in resource "aws_lb_target_group_attachment" "alb_tg":
│ 52: target_id = ["10.0.11.11", "10.0.12.11"]
│
│ Inappropriate value for attribute "target_id": string required.
for_each
を使って配列を回そうとしても、mapかsetでないとダメといわれます。
for_each
には配列を渡すことができないんです。。
Error: Invalid for_each argument
│
│ on elb.tf line 50, in resource "aws_lb_target_group_attachment" "alb_tg":
│ 50: for_each = ["10.0.11.11", "10.0.12.11"]
│
│ The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or set of strings, and you have provided a value of type tuple.
ということで、toset 関数を使って、set型に変換しています。
恥ずかしながら、setという型を初めて知ったのですが、
- 重複した要素がない
- 要素に順番がない
という特徴があるみたいです。toset
で変換すると重複が取り除かれます。
terraform console
> toset(["10.0.11.11", "10.0.12.11", "10.0.12.11"])
toset([
"10.0.11.11",
"10.0.12.11",
])
for_each
で渡した配列もどきのリストはeach.key
で読み込むことができます。
今回は関係ありませんが、もし、mapを渡した場合はeach.key
とeach.value
でキーと値をそれぞれ読み込むことができます。
リスナーの作成
続いて、リスナーを作成します。
elb.tf
に以下のコードを追加します。
# Listener
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.alb.arn
port = "80"
protocol = "HTTP"
default_action {
type = "redirect" # forwad / fixed-response / redirect
redirect {
protocol = "HTTPS"
port = "443"
host = "#{host}"
path = "/#{path}"
query = "#{query}"
status_code = "HTTP_301"
}
}
}
resource "aws_lb_listener" "https" {
load_balancer_arn = aws_lb.alb.arn
port = "443"
protocol = "HTTPS"
certificate_arn = aws_acm_certificate.public.arn
ssl_policy = "ELBSecurityPolicy-2016-08"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.alb_tg.arn
}
}
aws_lb_listener
でHTTPとHTTPSのリスナーを追加しています。
port
とprotocol
の組み合わせで受付ポートとプロトコルを、default_action
でアクションを定義します。
HTTPのリスナーではdefault_action
のタイプをredirect
にして、HTTPSにリダイレクトするようにしています。redirect {}
の中にはリダイレクト時に書き換える内容を指定します。
protocol
がHTTP → HTTPSに、port
が80 → 443に書き換えられますが、残りのホスト名、パス、クエリは変更を加えずデフォルトを利用するという意味になります。status_code = "HTTP_301"
は「完全に移動」を表します。
HTTPSのリスナーではdefault_action
のタイプをforward
にして、先に作成したターゲットグループに転送するようにしています。また、前回のRoute53 + ACM編で作成したパブリック証明書をここで関連付けします。
エイリアスレコードの登録
最後に独自ドメインとELBを紐付けるエイリアスレコードを登録します。
route53.tf
に追加したくなるかもしれませんが、ここは関連するリソースのライフサイクルに合わせてelb.tf
に追加します。
################################
# Route 53 (Alias record)
################################
resource "aws_route53_record" "alb" {
name = "www.${aws_route53_zone.public.name}"
zone_id = aws_route53_zone.public.zone_id
type = "A"
alias {
name = aws_lb.alb.dns_name
zone_id = aws_lb.alb.zone_id
evaluate_target_health = true
}
}
レコードの名前は独自ドメインの頭にwww.
を付けた形にしていますが、ACMをワイルドカード証明書にしてあるのでapp.
とかでも構いませんし、何も付け加えずにaws_route53_zone.public.name
としてネイキッドドメインで接続する形にしてもOKです。ネイキッドドメインを登録できるのは、エイリアスレコードならではの特徴ですね。
ELB編のコードは以上です。
terraform apply
を実行して、もろもろ作成されていることを確認しましょう。
問題なければ独自ドメインでアクセスしてみましょう。
鍵アイコンをクリックして証明書を確認するとAmazon発行のものであることがわかります。
また、ELBには2台のEC2がぶら下がっているので、ブラウザを更新するたびにweb-01
とweb-02
が交互に切り替わるかと思います。(ラウンドロビン)
ブラウザのURLをhttpに変更してもリダイレクトされることが確認できますが、curl
コマンドでも確認することができます。Location
を見るとプロトコルとポートが書き換わっていることがわかります。
curl -I http://www.example.com
HTTP/1.1 301 Moved Permanently
Server: awselb/2.0
Date: Wed, 09 Jun 2021 04:59:29 GMT
Content-Type: text/html
Content-Length: 134
Connection: keep-alive
Location: https://www.example.com:443/
今回は以上です。次回はRDS編ということで、MySQLのRDSをマルチAZ構成で構築してみたいと思います!