今天的主題是幫我們的 Laravel web service 加上 CDN(Content Delivery Network 或 Content Distribution Network),讓使用者可以更快取得取得服務。AWS 的 CDN 服務是 Cloudfront,是個 global 層級的服務。
之前用 ALB DNS name 連 Laravel 的時候是用 HTTP 連線,但在這時代沒有 HTTPS 大概是很難過下去(?)的,我們要在 ALB 前面加上 CDN,並且由 CDN 提供 HTTPS 的存取。這麼做可以讓從 CDN 往 cloud 內部的 network traffic 都不需要再做加解密的動作,因為已經在 cloud 內部是可以不用在傳輸時加密,稍微可以提昇 performance。
使用 Cloudfront 會需要一個 domain name,我們會以 Route 53 管理的 domain name 來實作。那~就開始吧~(本日程式碼)
要有 HTTPS 要有個有效的 certificate,可以到 AWS Certificate Manager(ACM)產生,給 Cloudfront 用的 certificate 必須建立在 us-east-1
region。
點右上角 request 按鈕:
進到 request certificate 頁面,直接 Next:
接著輸入準備要給 Laravel web application 使用的 domain name,並且用 DNS validation:
request 後會在 list 看到 pending 的 certificate:
我們使用 DNS 驗證,它利用在 DNS 加上指定的 record name 跟 value 來確認 domain 確實是申請人所有。如果是用 Route 53,可以直接進到 certificate 的頁面,在 Domains 點 Create records in Route 53:
Route 53 的 record:
一旦驗證機制查到確實有相符的 DNS record,就能確定這個 domain 確實是申請人擁有。等個幾分鐘應該會 issue 完成:
如果不是用 Route 53,就要依據 certificate 中 Domains 的資訊到 DNS 設定 record 完成驗證。
要用 cloudfront 來 distribute 我們的資料時,就是要建立一個 distribution,它會設定資料來源(origin)、安全性(使用 https)、cache 等等。
首先開個存 cdn log 用的 s3 bucket:
resource "aws_s3_bucket" "cf_log" {
bucket = "my-app-cdn-log"
}
resource "aws_s3_bucket_ownership_controls" "cf_log" {
bucket = aws_s3_bucket.cf_log.id
rule {
object_ownership = "BucketOwnerPreferred"
}
}
接著加入 cloudfront distribution 的 resource:
resource "aws_cloudfront_distribution" "cdn" {
enabled = true
comment = "Laravel"
is_ipv6_enabled = true
aliases = [var.domain]
viewer_certificate {
acm_certificate_arn = var.domain_cert_arn
cloudfront_default_certificate = false
minimum_protocol_version = "TLSv1.2_2021"
ssl_support_method = "sni-only"
}
origin {
origin_id = aws_lb.alb.dns_name
domain_name = aws_lb.alb.dns_name
connection_attempts = 3
connection_timeout = 10
custom_origin_config {
http_port = 80
https_port = 443
origin_keepalive_timeout = 5
origin_protocol_policy = "http-only"
origin_read_timeout = 60
origin_ssl_protocols = [
"TLSv1",
"TLSv1.1",
"TLSv1.2"
]
}
}
default_cache_behavior {
allowed_methods = ["GET", "HEAD", "DELETE", "POST", "PUT", "PATCH", "OPTIONS"]
cached_methods = ["GET", "HEAD"]
target_origin_id = aws_lb.alb.dns_name
viewer_protocol_policy = "redirect-to-https"
compress = true
cache_policy_id = "4135ea2d-6df8-44a3-9df3-4b5a84be39ad" # CachingDisabled
origin_request_policy_id = "216adef6-5c7f-47e4-b989-5492eafa07d3" # AllViewer
}
# 不做限制
restrictions {
geo_restriction {
restriction_type = "none"
}
}
logging_config {
bucket = aws_s3_bucket.cf_log.bucket_domain_name
include_cookies = false
prefix = "cdn"
}
}
其中變數 domain
是要給這個 distribution 用的 domain name,也就是剛剛 certificate 的 domain。distribution 會有個自己的 domain name,但長得很亂碼(下面會看到),應該是沒人記得起來,所以通常會用個好記的 domain 指過去。
變數 domain_cert_arn
則是剛剛產生的 certificate 的 ARN,viewer_certificate
內指定一些跟安全性有關的設定,細節可以參考 文件。
origin
block 是設定要透過 cdn distribute 的資料來源(content origin),我們的資料來源是 ALB,所以在 domain_name
指定 ALB 的 domain。並且設定是以 HTTP 取得 origin 的內容,因為我們的 ALB 是開啟 HTTP,不是用 HTTPS~
接下來設定 behavior——連到 distribution 的某個 path 時要給什麼資料。一定要設定一個 default behavior,也就是這邊的 default_cache_behavior
block。behavior 會看 request 的 path,看符合哪個 behavior 的 path pattern,就把 request 送向 behavior 指定的 origin,並且依據 cache、viewer protocol 等 policy 將 response 回給 client。如果 request 的 path 不符合所有 pattern,就會用 default behavior。
上面 distribution 的 default behavior 會長這樣:
我們允許所有 HTTP method 並且對 GET 跟 HEAD 做 cache,這個 behavior 的 origin 是 ALB,client 如果用 HTTP 連線會被轉去 HTTPS。
在 default_cache_behavior
的設定可以看到有兩個寫死的 policy id,它們是 AWS 管理的 policy,但寫死 id 不好、沒有註解就看不懂是什麼,我們把它改成比較好的寫法:用 data source 取得 AWS 管理的 policy。
data "aws_cloudfront_cache_policy" "caching_disabled" {
name = "Managed-CachingDisabled"
}
data "aws_cloudfront_origin_request_policy" "all_viewer" {
name = "Managed-AllViewer"
}
resource "aws_cloudfront_distribution" "cdn" {
default_cache_behavior {
...(ignored)
cache_policy_id = data.aws_cloudfront_cache_policy.caching_disabled.id
origin_request_policy_id = data.aws_cloudfront_origin_request_policy.all_viewer.id
}
}
在 Day 20 我們有用過 data source 處理 task definition 的版本問題,現在則是用來取得 AWS 管理的 cloudfront 相關 policy 的 resource,避免使用看起來像 magic number 的 hard code id。
logging_config
設定前面的 s3 bucket 作為存 log 的地方,prefix 是 log 所在的 folder,像這邊寫 cdn
就表示 log 檔案們會存在 bucket 的 cdn/
底下。
設定好 cloudfront distribution,可以用 distribution domain name 連到 Laravel:
最後到 Route 53 新增 record 把 domain 指到 distribution domain name:
在 Route 53 裡可以直接用 A record 的 alias 指到 distribution:
如果是用其他服務管理 DNS,就要加上 CNAME record。
最後確認 domain 可以連上、而且是 https: