📘

Amazon CloudFront×Terraform

2022/12/07に公開約9,500字

Amazon CloudFrontとは

Amazon CloudFrontとは、CDNserviceのことでコンテンツ自体をキャッシュすることで多くのユーザに対して同一コンテンツを効率的に配信できる仕組みのことです。

CDN(コンテンツ配信ネットワーク)はサービス提供下のサーバ付加軽減を目的としており、ユーザに対する応答速度向上を提供します。

全てのアクセスをサービス事業者に集中させるとサーバダウンの可能性を多少なりとも含みますが、
ユーザに対して同一コンテンツを配信する場面(静的コンテンツ(CSS/JS/写真や動画)等)では、その負荷を別サーバ(CDN)に委託することで余裕ができ、動的コンテンツの配信精度を一定に保てるようになるため、結果的にサーバ負荷軽減を実現できるという構図です。

どのようにコンテンツを振り分けるのか

結論から言うとサーバ側で自動で静的コンテンツをキャッシュし、

  1. 最初に静的コンテンツを配信
  2. 次に動的コンテンツを配信

という流れです。

全てのコンテンツを保持し動的コンテンツを配信する原点をオリジンサーバと呼びます。
コンテンツのソース自体をオリジンと呼び、その振り分けルールをビヘイビアと呼びます。
これらの機能を使うことにより一つのドメインで、

  1. 静的コンテンツをS3から
  2. 動的コンテンツをEC2から配信する

のように振り分けることができます。

Amazon CloudFrontを構築

長文になりますがご容赦くださいませ。

完成イメージ

構成要素

  • パラメータストア
    機密情報・環境変数を管理できるキーバリューストレージのこと。
    EC2は起動時にパラメータストア値を環境変数に取り込む。
  • ELB
    ELB(Elastic Load Balancer)とは、システムに入ってくるリクエストを複数のEC2インスタンスへ負荷分散する仕組みのことです。各AZごとに分割することでEC2のグルーピングが可能です。例えば、AZ内の負荷分散とAZを飛び越えた負荷分散の2パターンで設定可能です。今回使うELBは、ALB(Application Load Balancer)というHTTP/HTTPS(L7層)の情報を用いて負荷分散を行う形式となります。
  • リスナー
    ロードバランサ自体がどのようなポートを受け付けるかというものを定義します。セキュリティグループの位置づけと少し似ています。
  • ターゲットグループ
    ELBの負荷分散先が”どこにあるのか”を定義したものです。
  • Route53
    DNS(Domain Name Server)サービスのことで、ドメイン名(www.ex.com)をIPアドレス(192.168.0.2)に変換し、ドメイン名を用いることでAWSサービスに接続できるようになります。AWSの場合、IPアドレスではなくエイリアス(testName-elb)を指定できます。ドメインの取得にはお名前.comを利用します。
  • ACM
    ACM(AWS Certificate Manager)とは、プライバシーが保護された通信を保証する証明書を管理するサービスのことです。
  • S3
    S3(Amazon Simple Storage Service)とは、オブジェクトストレージサービスの1種です。データ補完および取り出し、RDSインスタンス等のバックアップ、静的コンテンツの配信等に利用されます。
    バケットというプレフィックス・親パス名を設定した後はリソースをアップロードすれば大体の作業はOKです。その際はアクセス権限の設定や公開範囲の設定等も大事になり、ブロックパブリックアクセスという項目等で設定が可能です。

構築手順

STEP1: ALBで負荷分散ネットワークを設定

  1. ALBを作成
  2. ターゲットグループを作成
  3. ターゲットグループをアタッチ
  4. リスナーを作成

1. ALBを作成

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb

resource "aws_lb" "alb" {
  name               = "${var.project}-${var.environment}-app-alb"
  internal           = false //内部
  load_balancer_type = "application"
  security_groups = [aws_security_group.web_sg.id] //ファイアウォール
  subnets = [ //またがるパブリックサブネット
    aws_subnet.public_subnet_1a.id,
    aws_subnet.public_subnet_1c.id
  ]
}

2. ターゲットグループを作成

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group

resource "aws_lb_target_group" "alb_target_group" {
  name     = "${var.project}-${var.environment}-app-tg"
  port     = 3000 //セキュリティグループのI/Oを参照
  protocol = "HTTP"
  vpc_id   = aws_vpc.vpc.id

  tags = {
    Name    = "${var.project}-${var.environment}-app-tg"
    Project = var.project
    Env     = var.environment
  }
}

3. ターゲットグループをアタッチ

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group_attachment

resource "aws_lb_target_group_attachment" "instance" {
  target_group_arn = aws_lb_target_group.alb_target_group.arn
  target_id        = aws_instance.app_server.id //ターゲットグループに入れたいEC2インスタンス
}

4. リスナーを作成

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener

resource "aws_lb_listener" "alb_listener_http" {
  load_balancer_arn = aws_lb.alb.arn //ARN(Amazon Resource Name)
  port              = "80"
  protocol          = "HTTP"

  default_action { //初期設定でどのEC2に負荷分散するか
    target_group_arn = aws_lb_target_group.alb_target_group.arn
    type             = "forward"
  }
}

ロードバランサーに紐づいていることが確認できました。

STEP2: Route53で独自ドメインを設定

  1. ホストゾーンを作成
  2. Aレコード設定
  3. ドメイン取得元設定

※事前にお名前.comで独自ドメインを取得します。この独自ドメイン名「citypost.site」をIPアドレスに変換する処理をRoute53が受け持つことでインターネットからユーザがアクセスできるようになります。

2.1. ホストゾーンを作成

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_zone

環境変数にお名前ドットコムで取得した独自ドメインを設定します。

terraform.tfvars
project     = "zenn"
environment = "stg"
domain      = "citypost.site"

Route53ゾーンを作成します。

resource "aws_route53_zone" "route53_zone" {
  name          = var.domain //お名前ドットコムで取得した独自ドメイン
  force_destroy = false //ゾーン削除時のゾーン内レコード削除

  tags = {
    Name    = "${var.project}-${var.environment}-domain"
    Project = var.project
    Env     = var.environment
  }
}

確認できました。

2.2. Aレコード設定

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record#alias-record

resource "aws_route53_record" "route53_record" {
  zone_id = aws_route53_zone.route53_zone.id
  name    = "dev-elb.${var.domain}"
  type    = "A"

  alias {
    name                   = aws_lb.alb.dns_name //DNSドメイン名
    zone_id                = aws_lb.alb.zone_id //ホストゾーンID
    evaluate_target_health = true //ヘルスチェック
  }
}

2.3. ドメイン取得元設定(お名前.com側の設定)

NSレコード(値/トラフィックのルーティング先)をお名前ドットコムに反映します、末尾のピリオドは不要です。この反映は2~3日かかります(; ・`д・´)



STEP3: ACMを発行

ACM(AWS Certificate Manager)の導入手順。

1 AWS アカウントで使用する TLS/SSL 証明書をリクエストまたはインポートします。
2 ドメインネームシステム (DNS) または E メール検証を使用して、リクエストした証明書のドメイ
ンの所有権を検証し、証明書の発行を完了します。
3
新しく発行またはインポートした証明書を、Elastic Load Balancing (ELB)、Amazon CloudFront
などのさまざまな AWS のサービスで使用します。

3.1. 証明書をリクエスト

お名前ドットコムで取得した独自ドメインの証明書発行をリクエストします。

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate

resource "aws_acm_certificate" "tokyo_cert" {
  domain_name       = "*.${var.domain}"
  validation_method = "DNS"

  tags = {
    Name = "${var.project}-${var.environment}-wildcard-sslcert"
  }

  lifecycle {
    create_before_destroy = true //削除前生成
  }

  depends_on = [
    aws_route53_zone.route53_zone //お名前ドットコムで取得した独自ドメイン
  ]
}

3.2. DNSの検証設定(ACM)

リクエストしたドメインの所有権を検証します。

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation

https://dev.classmethod.jp/articles/acm-cert-by-terraform/

3.3. DNSの検証設定(お名前.com)

ACM証明書のCNAME値をお名前ドットコム側で設定し、リクエストされたドメインの所有権限が正しいかどうかを検証します。

お名前ドットコムから~

証明書のステータスが検証保留中→発行済みに変化することが確認できました。

3.4. ELBに証明書を設定する

http→ELB→httpsの構成を作りたい。

resource "aws_lb_listener" "alb_listener_http" {
  load_balancer_arn = aws_lb.alb.arn //ARN(Amazon Resource Name)
  port              = 80
  protocol          = "HTTP"

  default_action { //初期設定でどのEC2に負荷分散するか
    target_group_arn = aws_lb_target_group.alb_target_group.arn
    type             = "forward"
  }
}

resource "aws_lb_listener" "alb_listener_https" {
  load_balancer_arn = aws_lb.alb.arn
  port              = "443"
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-2016-08" //ここでSSL証明書をアタッチできる
  certificate_arn   = aws_acm_certificate.tokyo_cert.arn

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.alb_target_group.arn
  }
}

リスナー(=セキュリティグループの立ち位置)が追加されたことを確認できました。

STEP4: CloudFrontを作成

  1. CloudFrontのデフォルトビヘイビアをELBに設定
  2. CloudFrontのビヘイビアをS3に設定

4.1. CLoudFrontのデフォルトビヘイビアをELBに設定

全体イメージ

具体的なイメージ

オリジン(独自ドメイン)にELBを設定後、ビヘイビア(リソース配信元をどこにするか)のデフォルトにELBを設定します。

usersからCloudFrontにhttpsでアクセスする際、「パスパターン = * 」で動的リソースをELBに振り分けます。この際、キャッシュなし。

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_distribution

4.2. CloudFrontのビヘイビアをS3に設定

4.1と同様に、オリジン(独自ドメイン)にS3を設定後、ビヘイビア(リソース配信元をどこにするか)をS3に設定します。デフォルトはELBのままです。

ここでは、USersがCloudFrontにhttpsリクエストしてきた際、静的ファイルをS3にキャッシュしたうえで配信します。

ここで2種類のS3資材を準備します。

  1. 静的コンテンツ配信用S3バケット作成と設定(パブリックバケット)
  2. 開発リソース配信用S3バケット作成と設定(プライベートバケット)

一般に、S3バケットには下記設定が必要になります。

  • パブリックアクセスブロック設定
    バケットが公開されているかどうか
  • バケットポリシー
    読み取り、書き込み、削除、追加のどれを許可するのか

上記設定項目を理解したうえで、S3バケットリソースを書いていきます。
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket

AWSコンソールのオリジンとビヘイビアにてELBとS3の設定状態を確認します。
コンテンツ配信かデフォルトから変化しているかどうかは、ブラウザのNetworkタブからcssファイルの内容を見る、もしくはキャッシュ状態等から確認できます。

おまけ

terraform apply は main.tf と同じディレクトリではないと、実行できない。

https://qiita.com/wonderglot/items/9954b35bddc5a9163f5e

CloudFrontのディストリビューションは1オリジンで一つ

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/HowToDeleteDistribution.html

まとめ

疲れたっす。

次回はEC2オートスケーリングあたりかな。

GitHubで編集を提案

Discussion

ログインするとコメントできます