使用 CloudFront + S3 打造 Video Streaming Service

Neil Wei
BlendVision
Published in
10 min readFeb 13, 2018

大約是2017年的十月,我被指派完成一項新的任務,該任務是去研究如何使用 AWS CloudFront 來取代現有 EC2-based 的影音串流服務,在導入 CloudFront 之前,我們的架構長得像這個樣子:

Original system design

圖中 Traffic Server 扮演一個把影片從 S3 交付到使用者端的一個重要角色,負擔了影片本身的流量傳輸身份驗證,即為這次要被 CloudFront 取代的對象,從圖中可以看到這樣的設計存在著一些缺點:

第一,我們為了要做到 High Availability,勢必要開兩台以上的機器,而這些機器為了要能應付有可能產生瞬間或持續高流量的狀況,例如 Live streamming event 或是大量的使用者同時觀看,經過估算後,我們開了兩台 c4.2xlarge 的機器 24/7 待命,並且設定好 auto scaling policy,根據流量來增減機器。

然而事實上平時根本用不到一成的運算能力,這對成本控制是件相當不划算的事情,白白浪費了以秒計費的 EC2,而且 auto scaling 的速度往往跟不上第一波 peak ,若是能採用 serverless 的架構,可以得到以下好處:

  1. 可以控制成本,用多少算多少。
  2. 不用擔心 server 撐不撐得住的問題,理論上再多的使用者都沒問題
  3. 可以減少維護的人力,小型團隊人力都要用在刀口上啊!

「這將會是件一舉三得的投資」

第二,關於交付影片到使用者端這件事上,由於影片本身具有檔案小/數量多/不常變動的特性,每次將影片從 S3 拉出來再打給客戶端其實是又慢又貴的事情,所以這套系統也自己實做了 cache 的功能,降低對 S3 的傳輸量。另外,在沒有 DRM 的狀況下,該如何確保 Content 的安全?我們也自己打造了一些流程來做驗證與保護,這些都是開發成本提升的原因。

第三,在目前的架構下,我們如果想分析觀看影片的使用者行為是做不到的,例如,想要找出哪些影片最熱門/從哪個地區來的使用者最多/播放裝置為何?

綜合以上,可以歸納出 3 點挑戰:

  1. 為了預留流量及採用 EC2 的架構,造成機器成本過高
  2. 為了保護資料安全與傳輸效率,造成開發/維運成本過高
  3. 為了分析使用者行為,需要再投入人力去開發對應的界面

聽起來 CloudFront 是個非常適合的解決方案,為什麼當初沒有實作?

這和當初的開發團隊知識背景有關,這是個全新的產品,從專案開始到上線,只有短短幾個月的時間,此時的團隊成員有強力的前端與後端,唯獨缺少較有 AWS 經驗的工程師,所以一切先以上線為第一優先,先求有,再求好。

在產品上線之後,我們馬上著手 review 架構上可做優化的地方。

此時我被交付的任務即為「使用 CloudFront + S3」取代現有的串流架構,在功能不變的情況下,必需滿足以下四點需求:

  1. Cost optimization,請把那群吃錢的 c4.2xlarge 的機器們都用 serverless 的架構取代掉吧!
  2. Security,仍然沒有 DRM,但要保護好 Content。
  3. 要有 Dashboard 可以做基本的分析和報表
  4. 上線的時候不能有down time。

OK,在瞭解了需求後,接下來就開始打造我們的 CloudFront 環境吧!

一開始我天真的以為,不就是在 S3 前面接一個 CDN 而已嘛,應該一下子就做好了吧?

事實是這樣沒錯,但裡面的眉眉角角還真不少,當然,也有可能是我自己第一次玩 CloudFront 的關係,這邊列出一些重點避免日後忘記。

AWS 官方文件是你的好朋友,即使他們很難懂,這兩個網址可以解決大半的問題,花點時間好好看他們吧。

在做了簡單的 PoC 確定可行後,就開始著手實做啦!下圖是使用 CloudFront 之後,系統的長相

  • 我們使用 S3 當做 Origin,存放原始 video file。
  • S3 必需是 private,任何人不得使用 S3 URL 存取檔案。
  • CloudFront 使用 Signed cookie,確保 client player 可以播放一整群的影片而非一個片段。
  • 產生 Signed Cookie 的邏輯做在 application 端,通過驗證的使用者,會回傳 signed cookie 給前端,並且這個 cookie 是有期限的。
Overview

S3 部份

因為前端會用到 CORS,所以 S3 bucket 必需加上這段 policy,確保可以跨網域存取

CORS policy

CloudFront 部份

  1. 先取得 CloudFront Key Pair,會得到一個 Access Key 與一個 Private Key File,該 file 必需放在產生 signed cookie 的機器上。
  2. 建立 Origin Access Identity,簡單來說就是授權 CloudFront 可以存取你的 S3 的身份,該 Identity 會在第三步用到。
  3. 以下幾個重要設定,讓你的 S3 Bucket 本身是受保護的,並且只能讓 CloudFront 存取。
Enable Restrict Bucket Access for CloudFront
Allow CloudFront Origin Access Identity to access S3
Enable Signed Cookies and trust the singer to self
Add whitelist headers

Whitelist Headers 這步很重要,這是讓 Client 端可以把需要的 header 打給 CloudFront 的設定。

Application 部份

在 application layer 中,負責了 business logic,決定該使用者是否可以獲得 signed cookie,因此在這台機器上,需要放置先前取得的 CloudFront Private key,官方也很貼心地準備了 sample code

Route 53 部份 (optional)

這部份就比較單純,設定 Route 53 的 alias 讓它指向 CloudFront 的 FQDN。

Route 53 Alias

大功告成! 來測試吧!

首先,模擬認証通過,產生 Signed Cookie。

Signed Cookie

把這段 cookie 利用 Postman 打到設好 Alias 的 Rout53 domain name,確認有得到預期的回覆。

最後,end to end 測試一下,用前端的 player 來播放吧!把 cookie 的資訊帶入後,成功播放影片!

最後,一些經驗分享

請培養好你的耐心

PoC 階段,修修改改很正常的,但是 CloudFront 會需要把設定 propagate 到全球的 edge location,所以只要動到 CloudFront 的任何設定,請先等上半小時吧。

因此在調整設定之前,請先多檢查一下,不然一天的時間只夠測個幾次而已!

新產生的 S3 Bucket 無法馬上接上 CloudFront

在這件事上,我花費了快一整天的時間。因為在 PoC 階段,所有的東西都是新建的,包含 S3 Bucket 也是。但是大家知道 S3 的 URI 其實有分 global 及 regional 的嗎?S3 的其中一項特性是 Read after Write,也就是上傳後,global endpoint 可以馬上存取,但是 S3 的 Bucket 是 regional 的啊,這是怎麼做到的?

redirection,在你的檔案還沒有被 propagate 到 global endpoint 之前,嘗試去使用 global endpoint 存取時,S3 會幫你重新導向至 regional endpoint,好讓你可以馬上存取你的檔案,這樣會發生什麼事呢?

這會造成 CloudFront 無法存取 S3 的檔案!因為 CloudFront 會先設定好你的 Origin 為 global endpoint,但他卻幫你做了重新導向(至regional),所以就失敗啦!若是新建的 bucket,請先等個一小時,確定 global endpoint 已經不會 redirect 後再接上 CloudFront。

記得清快取

咦…就這樣?沒錯,清快取是一個小到另人常常會忘記,但是可能會讓你花上好一陣子去 debug 的原因之一。

例如前面的例子, global s3 endpoint 的更新明明已經生效了,或是你的檔案已經變更成正確的,但卻沒有反應出來,這都是 response 已經被 CloudFront cache 的原因,就算是 404 Not Found,也是會被 cache。

所以,當你發現一切都設定正確,但結果卻不如預期,先想看看是不是 Cache 的原因。

第一次速度有點慢

在 performance 上做了不少測試,結果如下:

我嘗試下載 100 個檔案,每個檔案約為 5MB。(模擬播放影片的 use case)

  1. 直接從 s3 下載影片,平均下載速度約為 12 MB/s。(baseline)
  2. CloudFront cache no hit時,平均下載速度約為 1 MB/s (很慢..)
  3. CloudFront cache hit, 平均下載速度變成 30 MB/s (快多了!)

這個結果有和 AWS 原廠工程師電話討論過,也分析過我們的 Log,其背後的原因是 CloudFront 對於頻寬的分配,和檔案的大小會有關係,而未被 cache 過的檔案,也多了繞了一層回 S3,所以也還算合理。

最後 AWS support 給我們的建議是,如果真擔心 user experience 的話,對於新上架的影片,我們可以自己先打一遍,讓他先被 cache 住,之後 user 就可以直接以 cache hit 的速度拿影片了。(不會出現第一個犧牲者)

後記

在 CloudFront 上線後,順利地完成了 zero downtime 的切換,並且成功淘汏了原本 EC2-based 的架構,每個月也省下了約 22% 的成本開銷,是件令人開心的事情!

--

--