關於我想看漫畫卻不想看廣告這檔事 (13) — Terraform 佈署漫畫爬蟲 & 閱讀器

Seaweed
10 min readSep 4, 2021

--

照慣例,正篇開始前…

來源: https://youtu.be/JIp9Tmwju2s?t=322

不知道黑色口罩會不會漲價呢w

本篇程式碼

服務整體架構

開始之前我們先來看要佈署的架構:

本篇算是第一次使用 Terraform 佈署漫畫爬蟲以及漫畫閱讀器,所以我們單純使用一台EC2作為我們的執行環境。

爬蟲是透過 crontab 來重複定時啟動,爬取指定數量的新集數存入資料庫後關閉。閱讀器則是一個透過 python3 實現的單純 http 服務(Flask)。

程式碼架構

由於這次新增了不少程式碼,這裡本藻重新介紹一次專案的結構:

├── db_build_sample.sql          # 建立comics/episodes/images用的.sql
├── env # 佈署用程式碼的資料夾(內容下段介紹)
│ ├── outputs.tf
│ ├── providers.tf
│ ├── resources_ec2.tf
│ ├── terraform.tfvars
│ ├── terraform.tfvars.sample
│ └── variables.tf
├── [proxy].pem # 用於登入外部機器,建立 ssh tunnel
├── README.md
├── requirements.txt # 專案 python 的相依性
├── [ec2].pem # 用來登入建立好的 EC2 用的 pem, 就是於
│ # AWS 網頁建立 keypair 後下載的東西
└── src # 內含漫畫爬蟲與閱讀器程式碼
├── api.py # 閱讀器後端
├── crawler_main.py # 爬蟲主程式
├── crawler_utils.py # 爬蟲需要的一些 lib
├── index.html # 閱讀器前端
└── storage.py # 同時給爬蟲與閱讀器使用的資料庫存取 lib

可以看出,比起 section_11,這次新增了用於佈署的 env 以及其他相關的設定檔(一個 .sql兩個 .pem)。

佈署程式解說

接著我們來介紹 env 底下的各個 .tf 檔:

variables.tf

基本上都跟之前介紹的差不多,只是針對 db_sql_path 要稍微補充一下:

用來初始化的.sql可以不只包含 comics/episode/images 的 table 建立,同樣可以在這裡寫 INSERT 語法把要下載的漫畫一併放進去,總之就是個正常的 .sql 檔,關於其的詳細用法可以看以下:

providers.tf

之前說過,就是指定提供雲端服務的實體,這裡同樣使用 aws

outputs.tf

與之前相同,就是輸出最後建立的 ec2 的對外域名。

resources_ec2.tf — (ec2之外的部份)

ec2 之外的 resources 定義有點長,所以這邊切成定義資料(data, locals)與 VPC 與網路環境(vpc, routing, security group)定義的兩部份:

  • Data/Locals

正如上一篇所介紹,data 在這裡是抓取可用的 availability zoneami

locals 則是定義區域變數,src 放的是 env 的上層資料夾的絕對路徑,而 ssh_cmd 則是為求後續方便,把之後會用到的建立 ssh tunnel 指令變成一個字串變數。

眼尖的讀者可能會發現這邊使用 <<-EOT 來輸入多行變數,比起 <<EOT 多了個 - ,兩者的差距是每行前面的空白會不會被省略,這邊也就是為了排版漂亮吧,關於兩者的 terraform 支援可以看這邊:

  • Networking resources

每次這裡都會變得很長,本藻其實也放棄了。vpc igw subnet rtb rtb-subnet 的部份基本已經變成套組了,這邊的解釋就先跳過吧。這邊最重要的是記得設定 aws_security_group 的 ingress/egress 規則,給連入 port 22(ssh), 3306(Mysql), 5000(web service) 解除防火牆限制,同時因為要跑爬蟲,解除對連出的所有限制(port=0 就是全部的 port, 0.0.0.0/0 就是 ipv4 的全網)。

resources_ec2.tf — (ec2的部份)

然後我們的 EC2 定義真的很長,這裡分為除 remote-exec 的其他設定remote-exec 的兩部份:

  • 除 remote-exec 的其他設定(EC2 設定)

可以看到,這邊就是先設定 EC2 必要參數 ami instance_type subnet_id vpc_security_group_ids key_name。然後使用 connection 指定接下來的檔案複製與初始化要使用的連線,顯然本藻用的是 ssh,所以記得把 private_key_path 檔案中的內容用 file 丟進去。

另外需要特別介紹的是 provisioner "file" 以及 provisioner "remote-exec" ,前者用於上傳本機檔案到遠端的 ec2,後者則是用在於遠端 ec2 執行佈署指令(shell script)。由於這邊的 remote-exec 內容有點複雜,本藻放在下個部份。另外,關於 ec2 的其他設定看官方教學,而如果對 provisioner 想了解更多的則可以看這個官方教學

  • remote-exec(初始化 scripts) 很長

這裡就到我們的重頭戲了,一個 EC2 起來的時候裡面實際上就跟一個全新的系統一樣,所以在啟動我們的爬蟲與網頁之前得先處理一些相依性的各種雜事,因此這邊把 remote-exec 內部的程式碼分成「相依性安裝」、「網頁初始化與啟動」與「爬蟲初始化與註冊定時啟動」三個部分。

特別注意:爬蟲因為其本身的特性,我們並不需要它常駐啟動,所以這邊我們採用定時週期啟動的方式(如果有一些 web hook 或 rss 可用的話也可考慮事件驅動)。

相依性安裝」:

可以發現,這邊除了熟悉的 apt-get 還用到了一個 while [ ! -f …/boot-finished ]; do 的迴圈。首先, /var/lib/cloud/instance/boot-finished 這檔案存在表示 EC2 順利開機完成,反之,就是還沒開好

也許你會想説「都能 ssh 進去了,機器應該都開好了吧?但很抱歉答案當然是 No。本藻也不知道為什麼機器都還沒開好 AWS 就讓你連進去,但本藻知道,如果沒有等到那個神奇檔案出現(就是開機開好)

網路會連不出去

所以之後配置請不要忘記。

「網頁初始化與啟動」:

這裡主要包含兩個動作:

資料庫設定變更與初始化

  1. /etc/mysql/mysql.conf.d/mysqld.cnf 中要 bind 的 ip 從只針對本機的 127.0.0.1 改為可讓外網存取的 0.0.0.0
  2. 透過剛剛傳進來的 .sql 建立必要 table 與插入資料
  3. 建立可從外部連線的 db 使用者

啟動漫畫閱讀器

  1. 移動到剛剛 provisioner "file" 複製的專案資料夾安裝 requirements.txt
  2. 設定資料庫連線參數(用 export)
  3. 啟動服務(用 nohup 掛背景運行,不然 terminal 關掉後服務就會死去)

整體來說並沒有太難理解。

「爬蟲初始化與註冊定時啟動」:

總之這裡主要包含三個部分:「生成一鍵啟動 ssh tunnel 的 script」、「安裝可用 chrome 與 chromedriver」與最後的「將爬蟲啟動 script 插入 crontab」,以下個別拆解。

生成一鍵啟動 ssh tunnel 的 script

  1. 把剛剛用 provisioner "file" 上傳進來的 proxy.pem 權限設為 600(否則 ssh 連線時會告訴你太開放(too-open) 然後失敗)
  2. 生成 launch_proxy.sh (locals那邊定義的 ssh_cmd 就在這裡使用)
  3. launch_proxy.sh加上執行權限

安裝可用 chrome 與 chromedriver

  1. 從 google-chrome 官方選擇固定版本的 chrome 下載(使用 chrome 非常重要,不是 chromium,本藻在寫這篇的時候被 chromium 莫名其妙的問題雷到體無完膚)
  2. 安裝載下來的 .deb (一定要用 apt install, 用 dpkg -i 會遇到相依性無法安裝的問題,然後死去)
  3. 依據 chrome 的版本下載對應的 chromedriver
  4. 把 chromedriver 複製到 /home/ubuntu/.local/bin (就是把程式移到 $PATH 包含的路徑)

最後關於將爬蟲啟動 script 插入 crontab的部分,其實也就是 echo XXXX | crontab - 完全取代 crontab 內容的一點變化,只是後面的 echo 被換成一個 () 包起來的一坨程式碼的輸出。() 內部的詳細如下:

  1. 使用 crontab -l 2>/dev/null 輸出既有的 crontab 內容(並避免錯誤的輸出)
  2. 使用 echo 添加新一行 task(依照 crontab 的格式,同時 launch_proxy.sh與資料庫連線的必要參數設定(export)必須在爬蟲啟動前)

至此希望還算好理解。雖然很長,但總之這樣就寫完必要的檔案了。

執行 & 測試

最後,透過 terraform init terraform plan -out dev.tfplan terraform plan dev.tfplan 部署看到以下就算成功了:

網頁

打開瀏覽器連線剛剛 terraform 輸出的域名後面接上 :5000

爬蟲

把 crontab 裡面的指令複製出來直接執行。

結語

恭喜你又看完了一篇海藻的自虐。這次算是第一次深入使用 ec2,可以發現隨著越深入使用 AWS,自虐的地方越來越多了。不過請不要擔心,一時用 AWS 一時虐,一直用 AWS 一直虐。我們會繼續虐下去,在下篇我們將要另外引入一台 RDS 當資料庫,敬請期待吧。

下篇預告

資料庫很重要,所以很貴 — AWS RDS

下篇傳送門↓

--

--

Seaweed

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