iT邦幫忙

2021 iThome 鐵人賽

DAY 20
2

https://ithelp.ithome.com.tw/upload/images/20211005/20113277srBHTjApHM.png

CDN 這個名詞在前面的篇章應該出現過蠻多次的,一直感到困惑的朋友們不用擔心,今天終於要來好好介紹它了!

什麼是 CDN ?

CDN 的全名為 Content Delivery Network 內容傳遞網路

要知道距離不僅僅是愛情的毒藥(誤),也是影響 response time 的重大因素。假設你身在台灣,跟一個架設在台灣的 server 取資料,花費的時間只要 500 ms,但如果去跟一個架設在美國的 server 取相同的資料,這時候的 response time 可能就增長為 3000 ms。

CDN 就是透過在各個地理位置建立 edge server 來避免取資源時都要跟距離遙遠的 server 溝通,造成效能的低落。當使用者對被 CDN 加速過的域名發出 request 時,CDN 會自動將 request 導到地理位置離使用者較近或是流量較不吃緊的 edge server,儘管第一次取資源時因為 CDN 還沒有快取的資料,所以仍然需要跟 original server 要資料,不過之後的 request 就可以透過地理位置離使用者較近的 CDN cache 取得,加快 client 端資源載入的速度。除了 cahce 機制以外,CDN 某方面也算是增強了服務的可用性、負載功能、安全性(降低 DDOS 對網站的影響)。

是的,跟前兩天的主題一樣,CDN 也提供了 Cache 機制。

這邊出一個問題讓各位思考一下:

到目前為止我們介紹了幾種 Cache:

- `Memory Cache`
- `Disk Cache`
- `Service Worker Cache`
- `CDN Cache`

那你知道各種 Cache 的優先層級嗎?
意思是指在需要資源時會先去哪種 Cache 看看有沒有你要的資料,沒有的話再到另一種 Cache 找。大家可以思考一下,我會在本篇的最後告訴各位解答。

為什麼需要使用 CDN ?

我們先從一個網站的運營者的角度出發,你會期望你的網站可以有以下特性:

  • 網站載入的速度夠快
  • 可以 handle 大流量
  • 不管在世界的哪個角落都可以順利且快速的訪問網站(不考慮網速差異)

使用 CDN 最重要的原因就是 - 加快網站的訪問速度

當然使用 CDN 還有一些額外的好處:

跨地域的全網覆蓋與有效節省頻寬

先前也提到 CDN 可以覆蓋遍及全球的網路,所有用戶都不再向同一個伺服器讀取資料,充分利用了網路頻寬資源與平衡了網站的流量,也大量降低了原本主機的頻寬及負載。

節省成本

基本上現在要使用 CDN 都會透過第三方的 Provider,我們不必自己購買與維護 server。它們也幫忙處理了不同節點 server 之間的狀態與資料同步,大大節省了人力與成本。

更專注於業務邏輯開發

CDN Procider 通常除了 CDN 以外,還會提供許多多元的服務。例如 AWS 的 CDN 服務 CloudFront 就可以與 AWS 其他不同的服務串接,AWS 也會盡量確保提供高可用性、高可靠性的服務給使用者,讓我們可以更專注於業務邏輯的開發,而不用時時刻刻手動監控系統的運行狀況。

異地備援 & 提高可用性

當某個 server 故障時,系統會自動切換到另一個臨近且健康的 node 來處理請求,提高系統的可用性,盡量讓系統做到永遠不中斷服務。

提高安全性

CDN 的 Load Balancing 與分散式儲存技術可以加強網站的可靠性,也讓網站變的更能應對惡意攻擊,例如上個段落提到的可以降低 DDOS 攻擊對網站的影響。

CDN 基礎架構

最簡單的 CDN 架構可以由多個 DNS server、Load Balancer 與 Cache server 組成。

我們來模擬使用者造訪一個放在 CDN 上的網站,假設叫 https://i-use-cdn.com 好了,背後的流程如下:

  1. 當使用者在瀏覽器輸入 https://i-use-cdn.com ,會經過 local 端的 DNS server 解析,這個 local 的 DNS server 會將域名的解析權交給 CNAME 指向的 CDN 專用 DNS server。(CNAME 記錄用於將一個域名映射到另一個域名,域名解析伺服器遇到 CNAME 記錄會以映射到的目標重新開始查詢。不熟悉的讀者可以參考這裡)

  2. CDN 的 DNS server 將 CDN 的 Global Load Balancer IP address 返回給使用者

  3. 用户向 CDN 的 Global Load Balancer 發出請求。

  4. CDN 的 Global Load Balancer 根據用戶的 IP address,以及用戶請求內容的 URL,選擇一台用戶所屬區域的「區域 Load Balancer」,並告訴用戶向這台機器發出請求。

  5. 區域 Load Balancer 會為用戶選擇一台 cache server 來提供服務,但負載平衡器是怎麼決定要把請求導到哪一個 cache server 呢?首先它會根據用戶的 IP address,判斷哪一台 server 離用戶比較近,再來根據使用者的請求內容,看看哪台 server 有用戶想要的資源。它還會根據當前不同 server 的負載狀況,看看哪台 server 還有餘力可以處理用戶的請求。經過這些判斷之後,區域的 Load Balancer 會向 Global Load Balancer 返回一個 cache server 的 IP address。

  6. Global Load Balancer 把 cache server 的 IP address 給用戶。

  7. 之後用戶向 cache server 發起請求,如果快取中有用戶想要的資料,就會回傳給他,如果快取中沒有用戶要的資料,則這台 cache server 就要往上一個層級的快取看有沒有自己要的資料,最遠則會回到 origin server 取得資料,再更新到這台 cache server 的快取中,以備未來用戶又再次請求同樣資料時可以返回快取的結果。

一些常見疑問

我發現一些觀念用問答的形式來講解似乎蠻適合的,這些觀念剛好也是第一次接觸 CDN 的人心中可能會有的疑問,那就來個自問自答吧!

CDN 到底是對什麼加速?

CDN 是對網站的「域名 Domain」加速!如果同一個網站有多個域名,卻只針對其中一個域名做 CDN 加速,那麼就只有訪問該域名時會通過 CDN 快取加速,其他的域名則無法獲得加速效果。

要啟用 CDN 需要大改網站架構嗎?

基本上,不用修改任何程式碼就可以獲得 CDN 加速,記得嗎?CDN 是針對「域名」加速的。

哪些資源適合放到 CDN ?

CDN 適合放一些長時間不會變動的資源,例如圖片、影音串流或是一些 CSS 與 JS 檔案,甚至是整個靜態網站也可以放。當然會變動的資源也可以放,不過既然改了就要更新快取,以避免使用者仍然拿到舊的資料,所以如果是太常改變或者是動態生成的資源就不太適合放到 CDN 上。

既然也是快取,那 CDN 如何處理內容更新的狀況?

只要是 Cache,就一定會遇到更新快取的問題,如果網站的靜態資源如圖片更改了,CDN 快取卻沒有更新,那使用者看到的就還會是舊的內容。

通常 CDN Provider 都會提供 URL 推送的功能,通知 CDN 節點某些資源更新了,要把對應資源快取的資料清除掉。

通常 URL 推送服務可以透過給予完整資源 URL 針對單一資源如圖片更新快取,不過如果一次改動的太多,也可以使用 folder 的形式,例如指定 kylemoironman.com/articles 代表把 kylemoironman.com 這個域名的 articles folder 內的資源的快取都做更新。

關於修改或刪除快取,蠻推薦看看這篇文章,像淘寶這樣大型的電商一定會有大量的商品圖片需要放到 CDN 上,在遇到例如雙 11 時會因為促銷需要改圖片上的價錢,這時舊的快取勢必要更新,可以參考它們的快取架構與更新快取的機制,會對 CDN 的架構更加了解。

如何區分需要被快取與不需要被快取的資源?

有些動態的內容或資料並不適合被快取,因為經常變動,需要經常更新快取,造成 cache miss 的機率很高,整體來說沒什麼效益。那要如何區分要快取與不用被快取的資料呢?其中一種做法是將網站分成兩個域名,一個透過 CDN 加速,一個不使用 CDN。變動性或即時性比較高的資源就放到沒有使用 CDN 的域名下。

CDN Provider

以上幾個是業界較常使用的 CDN 服務,想看看更多 CDN Provider 可以看這個網站

Demo Time (For 沒有設定過 CDN 的人)

還記得當初剛學習 CDN 時,概念是聽懂了,但完全不知道到底怎麼幫網站加上 CDN 啊!!!有點類似拿到魔杖卻不會用的感覺,真難受QQ

為了讓你們不要跟我經歷一樣難受的心情,今天就來個最簡單的 Demo 吧!

今天會 demo 使用 AWS S3 部署 React 應用程式,並整合 AWS CloudFront 這個 CDN 服務。

雖然內容會有點繁瑣,但我認為可以讓剛認識 CDN 的朋友更快速了解 CDN 的綁定過程,雖然每家 Provider 的流程會有些許不同,但概念都是差不多的。如果會設置的朋友就可以直接跳過 demo 囉!

What is AWS S3 ?

AWS S3,全名為 Amazon Simple Storage Service,是 AWS 提供的一個雲端儲存服務,可以讓我們 host 一些靜態資源,在本篇的範例中,我們會使用 S3 來 host 被 Webpack build 過的 React 檔案,作靜態網頁的託管。

What is AWS CloudFront ?

AWS 提供的 CDN 服務,也就是今天的主角。

而 AWS 不同服務間的整合是非常容易且方便的,這點在等等的 demo 中讀者也定會深刻體會到。

Let's Start !

既然是要部署一個 React 應用,首先就用 create-react-app 來快速兜一個專案出來吧 ! 在 terminal 輸入

npx create-react-app deploy-sample

接著隨便改改內容讓你看得出是自己改過後的網站,接著試著跑

npm run start

確認 react app 可以正常在 local 啟動,並顯示自己改動後的內容。

AWS Console 設定

AWS console 就是 AWS 提供的 web 管理介面,可以設定各種 AWS 服務,首先你得先註冊一個 AWS 的帳號,這邊要特別注意的是,AWS 的計價方式是按照使用量付費的,也有許多種方案,可以參考這裡,通常新的使用者會有一年的免費用量,可以拿來練習,不過關於費用還是請讀者自行研究,萬一突然被收費不要告我喔喔喔喔 ?

關於這次的部署流程,我們會需要設定三個 AWS 的服務:

  • AWS IAM
  • AWS S3
  • AWS CloudFront

AWS IAM

AWS Identity and Access Management,可以對各種服務的存取權限作管理,我們可以透過 IAM 建立多個使用者,並給予不同使用者不同的權限。以這個部署範例來說,我們需要建立一個有權限操作 S3 與 CloudFront 的使用者。

在登入 AWS console 後,我們首先尋找 IAM 服務

進到 IAM Dashboard 後,點選左側的使用者

接著點選新增使用者的按鈕

名字可以隨意取,存取類型選擇程式設計方式存取,點選下一步。

接著點選連接現有政策,在 filter input 輸入 S3 與 CloudFront,會看到很多權限種類,這邊為了方便 demo,一律選擇 full access(實際就看讀者的需求了)

兩個都選取後,點選下一步

當使用者建立成功後,會看到上面的畫面,並出現兩組 KEY,這兩組 KEY 只會出現這麼一次,所以務必要好好保管好,未來如果需要在 CLI 或是 CI/CD workflow 做權限驗證都會需要用到它們!

AWS S3

首先一樣先搜尋 S3,進入 S3 的 Dashboard

點選右上角的建立儲存實體(bucket)

進到創建 bucket 的頁面後,bucket 的名字可以隨意取,然後將「封鎖所有公開存取權」的選項取消勾選。

點擊建立實體後,回到 S3 bucket 列表,會看到剛剛建立的 bucket,此時點擊 bucket 連結進行更詳細的設定,進到單一 bucket 的 Dashboard 後會看到

react-deploy-sample 是我自己取的 bucket name,接下來點選「屬性」這個 tab,往下滑動會看到像下圖一樣的靜態網頁託管這個區塊,預設是不會開啟靜態網託管的,在這邊記得要啟用它,下圖中最下面的「儲存貯體網站端點」這個 URL 我們得先記起來,待會在 CloudFront 的設定會用到。

接下來再切換到「許可」這個 tab,會看到如下圖的區塊

預設會是空的,讀者必須點選編輯然後貼上以下的 JSON 格式:

{
    "Version": "2012-10-17",
    "Id": "Policy1611823861546",
    "Statement": [
        {
            "Sid": "Stmt1611823850414",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject",
                "s3:*"
            ],
            "Resource": [
                "arn:aws:s3:::換成你自己的 Bucket Name/*",
                "arn:aws:s3:::換成你自己的 Bucket Name"
            ]
        }
    ]
}

回到 react 的 project,在 terminal 輸入

npm run build

產生 bundle 後的靜態檔案,再回到 S3 console 上傳上去。

那麼 S3 的設定就完成了,最後剩下 CloudFront 的設定囉!

AWS CloudFront

首先一樣搜尋 CloudFront 服務並進入 Dashboard (很簡單,就不放截圖了),接著點選建立分佈

會進到一個要填寫很多資料的頁面

源網域名稱要填入上面要讀者記住的 S3 的「儲存貯體網站端點」,也就是說 CloudFront 會從我們的 S3 bucket 去抓資料,「檢視器通訊協定政策」則要改成勾選第二個「重新導向 HTTP 到 HTTPS」。

如果你有自己的 domain name 的話,可以在分佈設定這個區塊做設定,不過這篇文章只會使用預設的 CloudFront 網域。

接著點選建立分佈,回到 CloudFront Dashboard 會看到列表中出現自己剛創建的分佈,一樣點擊他做更細部的設定,點選「一般」tab 並選擇編輯會看到

預設根物件填入 index.html,儲存後回到「一般」tab

要特別注意狀態的部分,因為創建 CloudFront 分佈需要一點時間,所以得確認等到狀態變為「已啟用」才代表 CloudFront 成功生效喔!

最後在瀏覽器中訪問上圖 CloudFront Dashboard 顯示的網域名稱,應該就可以看到自己的 react app 了!

Demo URL : https://d1aik19n6llqss.cloudfront.net/

注意看左上角的網域名稱,我們的 react app 已經成功 host 在 AWS S3 上,並且由 CloudFront CDN 作快取伺服器囉!(如果想要用自己的 domain name 要記得去額外設定)

如果不放心的話可以到 DevTool 的 network tab 看看有沒有 cache hit。

最後因為我們的 react 是 SPA,所以只需要 host 一個頁面,但是現在如果輸入一個不存在的 path,例如 /test,會導致 404 not found 的 error,所以我們可以在 S3 的靜態網頁託管區塊設定一下 redirect 規則。

然後輸入

[
    {
        "Condition": {
            "HttpErrorCodeReturnedEquals": "404"
        },
        "Redirect": {
            "HostName": "d1aik19n6llqss.cloudfront.net",
            "ReplaceKeyWith": ""
        }
    }
]

代表如果出現 404 的 Error 的話就 redirect 回首頁。有了這個設定以後如果輸入不存在的路徑都會被自動導回首頁囉!

解答時間:訪問各種 Cache 的先後順序

回到一開始請大家思考的問題

- Memory Cache
- Disk Cache
- Service Worker Cache
- CDN Cache

你知道各種 Cache 的優先層級嗎?

公佈答案!

memory cache -> service worker cache -> disk cache (HTTP Cache | Browser Cache) -> CDN cache

相信如果這三天關於快取的內容都有看完的話,應該不難理解前三個的順序。至於今天介紹的 CDN 快取,很明顯已經離開了瀏覽器的儲存空間並實際發出了網路請求,得去跟鄰近的 edge server 要資料了,所以順位一定是四者的最後一個。

你答對了嗎?

延伸閱讀

最後想厚臉皮的自薦一下自己的兩篇文章讓有興趣的讀者可以延伸閱讀 ?

Coding Your CDN! 充滿驚奇的 Lambda@Edge:CDN 除了快取還能做什麼?最近越來越多 Serverless function 結合 CDN edge server 的服務了,讓我們可以在地理位置鄰近的 edge server 上跑 serverless 的 function,Lambda@Edge 就是其中一個。原來今天介紹的 CDN 還能做更多事嗎!趕快去探索一下吧!

與 Github Actions 的再次相遇!AWS S3 + CloudFront 自動部署 React Web App : 應該說是今天 Demo 的進階版本。按照我們今天 demo 的流程,每次網站更新都要重新打包再手動上傳到 S3 也太麻煩了吧!別怕,CI/CD Pipeline 可以幫我們做到自動化這些步驟!

本日小結

關於 CDN,我只能說它真的是很重要很重要的技術。

今天介紹了 CDN 的概念,還有它的運作流程,也用問答的形式解釋一些我自己之前的疑惑。最後附上了一個簡單的 demo,主要是因為自己以前聽懂 CDN 的概念後仍然不知道到底要怎麼使用它,為了讓初學的人少走我走過的歪路,所幸用 AWS CloudFront 這個 CDN 服務來個簡易的 demo。

最近一些 CDN 服務商都釋出了可以在離使用者較近的 CDN edge server 上跑 serverless function 的服務,例如 AWS 的 Lambda@Edge 與 Cloudflare 的 Cloudflare Worker,建議讀者可以去體驗看看,未來的發展真的讓人十分期待呢!

References & 圖片來源

https://reurl.cc/vqz8Vk
https://reurl.cc/Dg2NRe
https://reurl.cc/kZe4Rd
https://www.cloudflare.com/zh-tw/learning/cdn/what-is-a-cdn/
https://www.zhihu.com/question/36514327
https://web.dev/service-worker-caching-and-http-caching/
https://www.akamai.com/zh/our-thinking/cdn/what-is-a-cdn


上一篇
Day19 X Application Shell Architecture
下一篇
Day21 X Upgrade Your HTTP Connection
系列文
今晚,我想來點 Web 前端效能優化大補帖!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言