昨日Reversalをデプロイした際にCDN経由でコンテンツ配信されるように色々追記したのでそれについての記事です。

主にTerraformで構築しているサーバについて変更を加えました。Rails側の変更は asset_sync Gemを使うようにして量は少ないです。

assetsを配置するためのバケットとそれをバケットにCloudFrontからアクセスを許可するポリシーを設定します。

s3.tf に追加

resource "aws_s3_bucket" "cdn" {
  bucket = "reversal-cdn"
  acl    = "public-read"
  policy = "${data.template_file.cdn_policy.rendered}"
}

data "template_file" "cdn_policy" {
  template = "${file("cdn_policy.json.tpl")}"

  vars {
    bucket_name            = "reversal-cdn"
    origin_access_identity = "${aws_cloudfront_origin_access_identity.origin_access_identity.id}"
  }
}

バケットを作りそれに対してバケットポリシーをテンプレートから適用しています。テンプレートは以下です。 cdn_policy.json.tpl

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadForGetBucketObjets",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${origin_access_identity}"
      },
      "Action": ["s3:GetObject"],
      "Resource": ["arn:aws:s3:::${bucket_name}/*"]
    }
  ]
}

ClordFrontからのGetObjectを許可するポリシーになっています。次にCloudFrontにDistributionを作成します。 cdn.tf

resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {
  comment = "reversal_cloudfront_origin_access_identity"
}

resource "aws_cloudfront_distribution" "cdn" {
  enabled = true
  comment = "reversal_cdn"
  price_class         = "PriceClass_200"
  aliases = ["ftg-reversal.net", "cdn.ftg-reversal.net"]

  origin {
    origin_id   = "reversal-cdn"
    domain_name = "reversal-cdn.s3.amazonaws.com"

    s3_origin_config {
      origin_access_identity = "${aws_cloudfront_origin_access_identity.origin_access_identity.cloudfront_access_identity_path}"
    }
  }

  default_cache_behavior {
    target_origin_id = "${aws_s3_bucket.cdn.id}"

    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE", "JP"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

ポイントは origin_iddomain_name に作成したS3バケットの値を入れるのとCNAMEを設定するために aliases を設定しておくことです。 他は基本的にテンプレを埋める感じです。

最後に配信されるドメインにRoute53からCNAMEレコードを設定します。 route53.tf

resource "aws_route53_record" "cdn-ftg-reversal-net" {
  zone_id = "${aws_route53_zone.ftg-reversal-net-public.id}"
  name    = "cdn.ftg-reversal.net"
  type    = "CNAME"
  records = ["${aws_cloudfront_distribution.cdn.domain_name}"]
  ttl     = "300"
}

これで設定したバケットのファイルににRoute53からアクセスできることを確認したらRails側の設定です。

assets_sync の設定をしたら config/environments/production.rb から config.action_controller.asset_host = '//cdn.ftg-reversal.net'asset_precompile でS3に配置したファイルにCloudFront経由でアクセスしてくれます。

本当は assets_sync についてもCapistranoのタスクをいじって asset_precompile をデプロイサーバ上で行ってS3上にアップロードするように書いた方が良いのですがWebサーバがまだ1台なのでとりあえずこれでよしとします。

もう少しハマると思ってたら案外すんなりいきました。