使用 CloudFront 搭配 ELB 加速動態內容

筆者最近因為工作上需要利用 AWS CloudFront 加速網站的動態內容,但 Google 了一下發現大多數的 CloudFront 安裝教學都是處理靜態內容(S3 Origin),比較少有動態內容的相關教學,於是整理了一下安裝過程中遇到的各種地雷,希望有幫助到大家。

概述

筆者公司的網站及資料庫皆架設在 AWS 東京,原本考量是台灣連線快速,且數量最多的美國西岸客戶也能享受到低延遲的連線。

不料隨業務增長,歐洲的客戶也逐漸增多。這些客戶的路由大多數經由歐陸上的線路連到德國、英國後,經過大西洋抵達美國東岸,跨過整個美洲大陸,最後從西岸出海經太平洋抵達東京,幾乎繞了地球 3/4 圈,latency 非常高。

起初我將整個網站掛上 Cloudflare 因應。但由於公司並沒有購買 Cloudflare 企業版,導致連線的路由仍時好時壞,尤其是台灣的連線表現很差,明明 Cloudflare 在 TPE, HKG 都有節點,我們透過中華電信的線路卻常常被丟到 LAX 去,繞遠路不說,有時網站載入完成速度竟然高達 6 秒。

故最後,經過評估我們每月的動態流量大小後,我們決定移轉回 CloudFront。

AWS 架構概述

在移除 Cloudflare 的情況下,我們的網站採用 AWS Elastic Load Balancer (ELB) 作為 TLS 終結點,下方則為執行 nginx 的伺服器

aws-ssl-installation

建立 CloudFront Distribution

我的策略是先建立一個讓所有流量都通過 CloudFront 但不要 Cache 的設定,然後再透過白名單的方式列出應該 Cache 的路徑。

首先,選擇 Web 形態的 Distribution,並參考如下設定:

  • Origin Domain Name: 選擇正確的 ELB
  • Origin Path: 空白
  • Origin Protocol Policy: HTTP Only
    • 因為我們希望 TLS 終點在 CloudFront,後面 AWS 內部連線走 HTTP 即可
  • Viewer Protocol Policy: Redirect HTTP to HTTPS
  • Allowed HTTP Methods: GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
    • 這是預設的 Behavior,我們希望所有(包括不能 Cache 的 API 回應)都能正確通過 CloudFront 抵達後面的 Server
  • Cache Based on Selected Request Headers: All
    • 選擇 All 會顯示警告,但原因如上,我們希望所有流量都先通過。
  • Forward Cookies: All
    • 原因如上,如果不 Forward Cookie 會造成一些動態內容無法運作
  • Query String Forwarding and Caching: Forward all, cache based on all
    • 原因如上,如果不 Forward Query String 會造成一些動態內容無法運作 (即網址後面的參數 ?arg= 沒帶過去)
  • Compress Objects Automatically: Yes
  • Alternate Domain Names: 設定你的網站網址
  • SSL Certificate: 可以使用 AWS Certification Manager 建立一個免費的 SSL 憑證,不過記得將憑證建立在 N. Virginia (us-east-1) 才能在 CloudFront 上使用。

測試及除錯

待 CloudFront 部署完成,就可以把你的域名加上一個 CNAME 記錄指向 XXX.cloudfront.net 測試了。

錯誤:Too Many Redirection

這個錯誤的原因是因為設定 Viewer Protocol Policy: Redirect HTTP to HTTPS,而且你的 nginx 也有設定 http to https rewrite 造成的。示意如下

  • HTTPS 以 ===> 表示
  • HTTP 以 —> 表示

[User] ===> [CloudFront] —> [ELB] —> [nginx]

因為 nginx 偵測到 http 流量,所以 rewrite 成 https,然後又產生一個新連線,造成死循環

解決方法

把 nginx 的 HTTPS rewrite 設定關閉即可

錯誤:一直看到 404 頁面或 HTTP server default page

這是因為 Cache Based on Selected Request Headers 設定成了 None,或沒有設定 Whitelist 把 Origin 和 Host 導向到後面。

如果開啟 nginx access log 並選擇列出 virtual host 便可以發現:

10.0.3.165 elb-1234.ap-northeast-1.elb.amazonaws.com - [29/Mar/2018:06:53:11 +0000] "GET /app/js/script.js HTTP/1.1" 404 178 "-" "-"

CloudFront 沒有將 host name 轉發到後面的 ELB,導致你的 nginx 收到錯誤的 host name,回應錯誤的 virtual host。

解決方法

如果是 Default Behavior,將 Cache Based on Selected Request Headers 設為 All,如否則設定 Whitelist 並選擇 Forward Host 和 Origin

螢幕快照 2018-03-29 下午3.09.00

錯誤:無法執行 GET 以外(如登入註冊)等動作

可能是 Allowed HTTP Methods 沒有設定成 GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE,或沒有設定 Forward Cookies: All 造成 Session 或 CSRF Token 等 Cookie 沒有被轉發到後端

解決方法

依照上述建議設定即可

依照 Route 設定 Cache

確認所有 Default Behavior 正確後,我們就可以開始設定 Route Base 的 Cache 來加速指定資源了

螢幕快照 2018-04-09 下午12.00.42建議先從靜態資源開始設定,例如所有 js 檔案,然後記得 Whitelist Header 要加入 Host / Origin 才能避免上述錯誤。

下方的 Object Caching 設定也可以依照自己的需求改變 TTL,Forward Cookie 可以設定為 None,Query String Forwarding and Caching 則看你的 JS 是否有利用 ?v= 方法避免更新後還被 cache 住。

結論

自從改用 Cloudfront 後,公司的網站在 https://tools.pingdom.com 測速上的表現從原本 5 秒多提升到 2 秒多。不過雖然如此,也不能都怪 Cloudflare 太爛,因為畢竟我們只有購買 Cloudflare 每月 20 USD 的方案,而 Cloudfront 則是按照流量計費。

我的建議是依照網站一個月的流量來計算,如果使用 Cloudfront 的價格低於 6000 台幣以上,則可以考慮 Cloudfront,要不然則可以考慮直接向 Cloudflare 購買企業方案,也可以獲得更好的路由和速度。

廣告

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google photo

您的留言將使用 Google 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s