S3 + Cloudfront 실습

CDN과 연동하여 버킷 구성하기

AWS Cloudfront + S3

보통 정적 컨텐츠를 저장할 목적으로 S3를 많이 활용합니다. 이렇게 저장한 파일은 S3 자체에서 제공할수도 있지만, 비용 절감과 속도 향상을 위해서 CDN(Contents Delivery Network)을 사용합니다. AWS에서는 Cloudfront라는 대표적인 서비스를 제공하는데, 본 실습에서는 이 Cloudfront와 S3를 연동하여 한 번에 생성해보도록 하겠습니다.

Domain이 있는 경우

본 실습을 위해서는 Custom Domain과 ACM을 미리 생성해주시기 바랍니다. Domain 등록과 ACM은 콘솔에서 수동으로 생성하시고, 생성된 리소스를 기반으로 글로벌 변수에 값을 넣어주시면 됩니다.

page글로벌 변수 세팅

terraform/s3/dayone-prod/prod_apnortheast2/contents-dayone.tf
# S3 Bucket for storing contents
resource "aws_s3_bucket" "contents_dayone" {
  bucket = "${var.account_namespace}-contents-${var.shard_id}"

  versioning {
    enabled = true
  }

  # Store logs of access to this bucket will be store in other bucket
  logging {
    target_bucket = aws_s3_bucket.apps_logs_bucket.id
    target_prefix = "${var.account_namespace}-contents-dayonee${var.region_namespace}/"
  }

  # S3 Bucket Policy for allowing access to s3 bucket 
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowAccessIdentity",
      "Action": "s3:GetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Resource": "arn:aws:s3:::${var.account_namespace}-contents-${var.shard_id}/*"
    },
    {
      "Sid": "AllowListBucketFromCFwes",
      "Action": "s3:ListBucket",
      "Effect": "Allow",
      "Principal": {
        "AWS": "${aws_cloudfront_origin_access_identity.dayone_cdn_distribution_origin_access_identity.iam_arn}"
      },
      "Resource": "arn:aws:s3:::${var.account_namespace}-contents-${var.shard_id}"
    }
  ]
}
EOF

  lifecycle_rule {
    enabled = true
    transition {
      days          = 30
      storage_class = "STANDARD_IA"
    }
  }

}

# Cloudfront Origin Access Identity 
resource "aws_cloudfront_origin_access_identity" "dayone_cdn_distribution_origin_access_identity" {
  comment = "dayone origin access identity in ${var.region_namespace}"
}

# Cloudfront Distribution
resource "aws_cloudfront_distribution" "dayone_cdn_distribution" {

  origin {
    domain_name = aws_s3_bucket.contents_dayone.bucket_regional_domain_name
    origin_id   = "dayone_origin"

    s3_origin_config {
      # Set origin id created above
      origin_access_identity = aws_cloudfront_origin_access_identity.dayone_cdn_distribution_origin_access_identity.cloudfront_access_identity_path
    }

  }

  enabled             = true
  is_ipv6_enabled     = true

  comment             = "Cloudfront configuration for cdn-contents.dayonedevops.com"
  default_root_object = "index.html"
    
  # Alias of cloudfront distribution
  aliases = [var.public_dayone_cdn_domain_name]
  
  # Default Cache behavior 
  default_cache_behavior {
    allowed_methods  = ["GET", "HEAD"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "dayone_origin"
    compress         = false

    forwarded_values {
      query_string = true
      query_string_cache_keys = ["d"]

      cookies {
        forward = "all"
      }
    }

    # List of Lambda Edge Association
    #lambda_function_association {
    #  event_type   = "viewer-request"
    #  lambda_arn   = "<< Lambda Edge ARN"
    #  include_body = true
    #}

    #lambda_function_association {
    #  event_type   = "origin-response"
    #  lambda_arn   = "<< Lambda Edge ARN"
    #  include_body = false
    #}

    #lambda_function_association {
    #  event_type = "viewer-response"
    #  lambda_arn   = "<< Lambda Edge ARN"
    #  include_body = false
    #}

    viewer_protocol_policy = "redirect-to-https"

    # cache TTL Setting
    min_ttl                = 0
    default_ttl            = 1800
    max_ttl                = 1800

  }
  
  # List of Custom Cache behavior
  # This behavior will be applied before default
  ordered_cache_behavior {

    path_pattern           = "*.gif"

    allowed_methods  = ["GET", "HEAD"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "dayone_origin"
    compress         = false

    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 3600

    forwarded_values {
      query_string = true
      query_string_cache_keys = ["d"]

      cookies {
        forward = "all"
      }
    }
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }
 
  # Certification Settings 
  viewer_certificate {
    acm_certificate_arn      = var.r53_variables.prod.star_dayonedevops_com_acm_arn_apnortheast2
    minimum_protocol_version = "TLSv1.1_2016"
    ssl_support_method       = "sni-only"
  }
 
  # Cloudfront Logging Settings

  logging_config {
    include_cookies = false
    # Set bucket to applitcaion logs you created before
    bucket          = "dayone-prod-apps-logs-dayonepapne2.s3.amazonaws.com"
    prefix          = "cdn-contents.dayone.io_access_log/"
  }

  # You can set custom error response 
  custom_error_response {
    error_caching_min_ttl = 5
    error_code            = 404
    response_code         = 404
    response_page_path    = "/404.html"
  }

  custom_error_response {
    error_caching_min_ttl = 5
    error_code            = 500
    response_code         = 500
    response_page_path    = "/500.html"
  }

  custom_error_response {
    error_caching_min_ttl = 5
    error_code            = 502
    response_code         = 502
    response_page_path    = "/500.html"
  }
  
  # Tags of cloudfront
  tags = {
    Name = "cdn-contents.dayonedevops.com"
  }
}

# Route 53 Record for cloudfront
resource "aws_route53_record" "dayone_cdn" {
  zone_id         = var.r53_variables.prod.dayonedevops_com_zone_id
  name            = var.public_dayone_cdn_domain_name
  type            = "A"

  alias {
    name                   = aws_cloudfront_distribution.dayone_cdn_distribution.domain_name
    zone_id                = "Z2FDTNDATAQYW2"
    evaluate_target_health = false
  }
}

contents-dayone.tf

Domain이 없는 경우

  • 도메인이 없는 경우에는 Cloudfront에서 제공해주는 Default Domain을 사용하실 수 있습니다.

terraform/s3/dayone-prod/prod_apnortheast2/contents-dayone-without-domain.tf
# S3 Bucket for storing contents
resource "aws_s3_bucket" "contents_dayone" {
  bucket = "${var.account_namespace}-contents-${var.shard_id}"

  versioning {
    enabled = true
  }

  # Store logs of access to this bucket will be store in other bucket
  logging {
    target_bucket = aws_s3_bucket.apps_logs_bucket.id
    target_prefix = "${var.account_namespace}-contents-dayonee${var.region_namespace}/"
  }

  # S3 Bucket Policy for allowing access to s3 bucket 
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowAccessIdentity",
      "Action": "s3:GetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Resource": "arn:aws:s3:::${var.account_namespace}-contents-${var.shard_id}/*"
    },
    {
      "Sid": "AllowListBucketFromCFwes",
      "Action": "s3:ListBucket",
      "Effect": "Allow",
      "Principal": {
        "AWS": "${aws_cloudfront_origin_access_identity.dayone_cdn_distribution_origin_access_identity.iam_arn}"
      },
      "Resource": "arn:aws:s3:::${var.account_namespace}-contents-${var.shard_id}"
    }
  ]
}
EOF

  lifecycle_rule {
    enabled = true
    transition {
      days          = 30
      storage_class = "STANDARD_IA"
    }
  }

}

# Cloudfront Origin Access Identity 
resource "aws_cloudfront_origin_access_identity" "dayone_cdn_distribution_origin_access_identity" {
  comment = "dayone origin access identity in ${var.region_namespace}"
}

# Cloudfront Distribution
resource "aws_cloudfront_distribution" "dayone_cdn_distribution" {

  origin {
    domain_name = aws_s3_bucket.contents_dayone.bucket_regional_domain_name
    origin_id   = "dayone_origin"

    s3_origin_config {
      # Set origin id created above
      origin_access_identity = aws_cloudfront_origin_access_identity.dayone_cdn_distribution_origin_access_identity.cloudfront_access_identity_path
    }

  }

  enabled             = true
  is_ipv6_enabled     = true

  comment             = "Cloudfront configuration for cdn-contents"
  default_root_object = "index.html"
  
  # Default Cache behavior 
  default_cache_behavior {
    allowed_methods  = ["GET", "HEAD"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "dayone_origin"
    compress         = false

    forwarded_values {
      query_string = true
      query_string_cache_keys = ["d"]

      cookies {
        forward = "all"
      }
    }

    viewer_protocol_policy = "redirect-to-https"

    # cache TTL Setting
    min_ttl                = 0
    default_ttl            = 1800
    max_ttl                = 1800

  }
  
  # List of Custom Cache behavior
  # This behavior will be applied before default
  ordered_cache_behavior {

    path_pattern           = "*.gif"

    allowed_methods  = ["GET", "HEAD"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "dayone_origin"
    compress         = false

    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 3600

    forwarded_values {
      query_string = true
      query_string_cache_keys = ["d"]

      cookies {
        forward = "all"
      }
    }
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }
 
  # Certification Settings 
  viewer_certificate {
    cloudfront_default_certificate = true
  }
 
  # Cloudfront Logging Settings

  logging_config {
    include_cookies = false
    # Set bucket to applitcaion logs you created before
    bucket          = "dayone-prod-apps-logs-dayonepapne2.s3.amazonaws.com"
    prefix          = "cdn-contents.dayone.io_access_log/"
  }

  # You can set custom error response 
  custom_error_response {
    error_caching_min_ttl = 5
    error_code            = 404
    response_code         = 404
    response_page_path    = "/404.html"
  }

  custom_error_response {
    error_caching_min_ttl = 5
    error_code            = 500
    response_code         = 500
    response_page_path    = "/500.html"
  }

  custom_error_response {
    error_caching_min_ttl = 5
    error_code            = 502
    response_code         = 502
    response_page_path    = "/500.html"
  }
  
  # Tags of cloudfront
  tags = {
    Name = "cdn-contents for dayone CDN"
  }
}

Last updated