關於我想看漫畫卻不想看廣告這檔事 (16) — 使用 Terraform + lambda + ecr 佈署漫畫爬蟲

Seaweed
9 min readOct 30, 2021

--

如果你問我人什麼時候會感覺到真正的絕望,那我會回答:

原本跑得好好的程式突然不會動的時候。

本篇程式碼

服務佈署架構

首先就是來看看要部署的服務架構,這裡只針對 AWS ECR 與 Lambda 做說明,雖然也有用 RDS 做資料庫,不過這邊就先省略。

上圖分佈署(Deploy)與執行(Execute)兩種狀況。先看看佈屬:可以很明顯的發現,本藻直接在 Lambda 用的 image 裡面塞了一個瀏覽器(Chrome)以及 ssh tunnel。 雖然佈署起來很麻煩,但這種高度客製化執行環境的 Lambda 目前只能使用這種方法來實現。

另外執行這邊也就沒什麼特別的了。透過 Cloudwatch 的規則來定時啟動 Lambda,接著依序啟動 ssh tunnel 作為 proxy 以及 Chrome 來執行我們的爬蟲。

整體架構看起來很簡單,當然,只有看起來。

佈署程式碼解說

雖然這裡甚至已經使用容器來保證我們的執行環境的一致性,但 Lambda 的執行環境依然與本機有非常大的不同。從這裡開始會出現很多僅針對 AWS Lambda 環境的容器建置方法,還請各位注意(另外本藻也不保證永遠不變)。不過基本流程跟邏輯應該不會變,也許不用過度擔心。

另外由於 AWS Lambda 本身的隔離性(沒辦法連線進去看執行環境),所以為了避免執行失敗時卻無法取得任何輸出,在這裏需要特別注意 log 的輸出,實務上就是盡量輸出 log 到 cloud watch。

改的程式碼有點多,我們分檔案來看:

resources_ecr.tf

這大概是最好說明的部份了,就是加上 dockerfile 的建置參數,就是L5~9,對應原檔案的 L52~56:

resources_lambda.tf

接著是 Lambda 本體的定義部份,首先是調整 timeout, memory,接著給 Lambda 追加連線資料庫的環境變數以及追加操作VPC權限。前兩個很好理解,為了啟動瀏覽器以及連線資料庫必要的操作。

比較有疑問的VPC操作權,這裡不只是要給 Lambda 連線並操作RDS 的權限,同時還包含Cloudwatch Log 的寫入權限,特別注意。當然除上面兩者之外 AWSLambdaVPCAccessExecutionRole 還包含其他權限,因此為了之後的開發與維護,這裡建議使用包好的 Policy,而不是自己寫。

AWSLambdaVPCAccessExecutionRole的相關細節可以看以下:

crawler_utils.py

接著我們要修改一下 crawler_utils.py 內的 getImages 這個函式。

要把原本 L81~87 的這段:

改成這樣:

也就是增加爬不到時的錯誤重試,這部份其實是本藻所踩的其中一個大坑。

舊版程式碼放到 Lambda 上執行後機率性會遇到Timed out receiving message from renderer: 這個意義不明的錯誤。當時就以為是 Chrome 壞了甚至重編好幾次 Chrome…。

撞牆幾天之後發現就只是 ssh tunnel 失去反應(但神奇的 ssh process 依然還在),作為 workaround 就是每次失敗就重啟 ssh tunnel。另外要注意 ssh tunnel 不是一下去就重啟完成,所以這邊固定等五秒。

了解緣由後就知道目前寫法十分粗糙,主要有兩點需要改善:

  1. 若爬蟲失敗原因不是 ssh tunnel 則會陷入無限迴圈,需要方法判斷。(不過 Lambda 有執行時間上限,姑且不會造成資源耗盡)
  2. ssh tunnel 重啟不一定花五秒,需要通知或者 timeout 控制機制。

不過本篇重點也不是寫爬蟲(咦),就交給各位來實現上述吧。

dockerfile

接著是我們的重頭戲 — dockerfile,dockerfile 裡面會做幾件事,這裡我們分段說明:

  1. 安裝特製 Chrome binary(headless chrome) 與對應版本 chromedriver

首先,Lambda 裡面不能跑一般版本的 Chrome。本篇結束(✕)

原因是原版 Chrome 包含圖像界面,會需要各種 gui 函式庫(lib, so),這堆相依性基本上不可能補足,即使是使用 headless 模式開啟也會在 linking 或執行階段拋出無法理解的錯誤。

要說解法嘛…那就只能自己一個沒有 gui 的 Chrome 了呢。這說來輕巧,但這絕對是另外一個足以花費一兩個月的坑(筆者是海藻),這時候只能抱大腿了。

於是本藻找到了這個大腿:

我們雖然是 Python 不能直接用他的 JS 套件,但他有提供 Docker image!!!!

所以本藻透過過藻的毅力,找到可以用的 Chrome 版本(68),配上對應的 Chromedriver(2.42) 總算是把 Chrome 開起來了…

另外,可能會有人對兩個 FROM 感到疑惑,這其實就是一個叫 multi-stage build 的東西,跟 COPY 一起使用,效果就是從另一個 image(或 build) 直接把某個檔案添加到當下的 image。少了手動的複製確保一致性,挺好用的,有興趣可以參閱以下說明:

2. 安裝特製 ssh

關於其他的 yum install 這裡就不贅述了,重點是 ssh 的安裝。理由很簡單,就是不知道為什麼 Lambda 映像沒提供 ssh ,所以就另外搞 binary 吧,然後因為編譯很麻煩,所以同樣是抱大腿。

只是這次是從一個 Lambda layer 拿,所以作法不太一樣。至於 Lambda layer 實際上是什麼東西嘛…直接參考官網吧:

就是原始的 Lambda 佈署中類似 docker layer 的概念,也沒啥特別好說的。我們這邊就拿專案中的 ssh binary,過個水就行。

3. 建立一鍵 ssh tunnel script

前五個 ARG 就是用來承接剛剛 docker build 時指定的 --build-arg PROXY_XXX 的變數。要注意沒寫的話會出錯。

另外,想要一鍵建立 ssh tunnel,當然就得要免密碼登入,所以我們從外部帶私鑰進來。比較特別的是 ssh 用的密鑰一般來說都要求 600 權限,這邊 644 權限居然也通(而且實際上必須給 others read 的權限),估計跟實際執行 Lambda 的 user 不是 root 有關吧。

4. 其他

就是…最後剩下的部份,姑且還是說明一下,不要忘記把專案的程式碼拉進來,並且安裝 python 相依性。

Hint: 佈署前記得確保 $PATH 能找到 mysql-client

就一個小提醒,因為這次佈署我們把 EC2 刪掉了,所以初始化 RDS 就不可避免地被轉移到了執行 Terraform 的本機上,所以佈署前請確保本機有能用的 mysql-client

結語

恭喜各位看完這篇長文,至此我們終於把 Chrome 跟 ssh tunnel 塞到 Lambda 裡並成功啟動了。關於 AWS / Terraform 的佈署議題我們這次大概就走到這裡,真的非常感謝各位願意看海藻的胡言亂語這麼久。

不過本系列還遠遠沒有結束,下篇開始本藻將開始引入 github actions 這個 CI 工具,說明基本用法、加入整合/單元測試,最後會將 Terraform 佈署也塞到 github actions 裡來完成完整的 CI/CD 流程。如果有興趣還請持續關注這個最大才能就是行光合作用的海藻吧👍。

下篇預告

github actions: CI 的一小步,是自動佈署的一大步

下篇傳送門↓

--

--

Seaweed

最大的才能是行光合作用,朋友是矽藻的海藻。