我說,軟體開發這門領域的技術真的發展得十分迅速,還記得讀碩士時,前端的三大框架才剛出現。接著在第一家公司好不容易了 Flask 怎麼使用,如何建立一個 App, 架起 Web Server 跟寫 API,沒想到在面試第二家公司時,Micro Service 正夯,又是新的軟體架構設計模式。
在進目前這家公司後,我又一次聽到新的詞: Serverless。
公司目前的軟體是使用 AWS 服務,大量使用 Lambda Service,也是一種 Serverless 技術,在本篇我不會花太多時間解釋 Serverless。
那...? 為什麼我要提到 Serverless 呢?因為這跟我今天的主題有密切的掛鉤。
Heroku 取消免費方案
我相信廣大的軟體工程師大大們,一定聽過 Heroku,而且甚至常常將自己的 Side-project 部署在上面,因為之前的 Heroku 免費方案就很夠讓我的小專案們可以部署在上面,偶爾秀給面試官看,或者甚至可以達到小規模的使用。
Heroku 先前的免費方案可以讓我免費架設我的 Miri 服務(我自己開發的占卜小程式),沒記錯的話每個月 750 小時免費使用基本上一個月絕對足夠,每天會重置的問題不大,只要把暫存的資料存到外部的資料庫就沒可行,5 分內沒使用會睡著的限制也可以透過 cronjob 網站每 5 分鐘 Call Api 的方式喚醒程式通通都能解決。
最方便的是當時連 PostgreSQL 都是免費的,只要你不超過使用量,在限制範圍內都是免費的。我的 Miri 服務還在草創,很少用戶,我也存放很少資料,所以基本上不會超過限制。
Miri 回應的占卜資料跟解釋其實都是靜態資料存入 SQLite,每次讀資料回送給使用者,所以也不太需要額外的資料庫支援。
在 Heroku 上就架著 Miri,架了 2 年多吧,很穩定,完全沒有付出任何維運的時間,每次用都還活著,
你知道 Heroku 的免費方案要在 11 月取消了嗎?
我不敢想像,這是 2 個月前左右的事,但當時(10 月左右),我同事 Gem 跟我講這件事。
當時我才剛完成我從 4 月開始一直利用休閒時間設計跟開發我自己的個人網站,大概 10 月 10 日左右確認完成,馬上就要跳入另外一個坑,去思考要找哪個新地方安置我的 Miri。
最後決定用 Vercel
Miri 是一個用 Python 寫的 Web Service,我用的是 FaskApi 開 API 接口,讓 Line/Telegram Bot Call,要把它架在 GitHub 上成靜態網頁是完全不行的。
我想了想,除了 Heroku 免費方案,我絕對不會想去嘗試 Google Cloud 跟 AWS,我在 AWS 有蠻慘痛的經驗哈哈 (阿不過現在的公司就是使用 AWS),五年前,一開始程式基礎不高時,就去碰 AWS,利用耶誕夜連假三天熬夜戰鬥,結果什麼都沒搞出來,還好沒被扣到錢。至於,我最近在 AWS 上玩 Neptune 被扣 1400 又是另一則故事了,搞得我這陣子不得安寧,有空我再寫這個故事。
當時我的同事 Gem 有推薦我 Vercel,我看了看Vercel 的官方文件,可以部署 Python 的 Serverless,但當時其實是在考慮另外一個網站 Render.com,因為我其實也有使用當時 Heroku 免費的 PostgreSQL,所以為了方便跟無痛轉換,我當時有在參考 Render 裡的 PostgreSQL 服務。
後來衡量一陣子,Render 的 PostgreSQL 方案貌似一開始就需要錢,沒有免費的選擇,所以後來我決定使用 Vercel,但我的 Miri 需要做一些改變,接下來來嘗試寫一寫這中間做了哪些改變。
由於 Miri 目前的內部功能跟使用到的其他技術並不是太複雜,所以只需要更改一些程式設計,大部分 90%的程式都能無痛搬移跟使用。
我認為要用 Serverless 的方式架設在 Vercel 上,目前我覺得的一些點我先列出來。
- 不能使用暫存空間: 不能使用像是 Python 的 Dict 或是 Queue 去 RunTime 存取一些暫時存放的資料,因為 Serverless 基本上就是 Run 一次從開始處理到結束後,就是結束,不會像一般架設 Web Server,會繼續將資料存在暫存。
- 不清楚能不能在程式架設的地方新增/寫檔案: 因為 Serverless 就是運行一次,然後就進入睡眠模式,所以我也不太清楚如果在程式的資料夾層新增檔案會不會消失。(至少我知道是不能寫.log 檔)
如果要將 Web Server 模式轉成 Serverless 的話,給幾個建議。
- 如果有需要資料讀寫,請連接外部的資料庫
- 需要用到暫存方便高速讀取,可以考慮 Redis 這種 No-SQL 資料庫
- 需要檔案讀/寫,也要連接外部的服務
- 目前如果
單純只要
讀資料,我有用 SQLite 存資料,不過就是靜態資料。 - 不太清楚 Serverless 怎麼使用資料庫 Connection Pool,原則上你可以
Call好多次API
等同於多執行緒
(?) - Log 的部分不太清楚,但我當初部署時把所有的 log 都先註解掉不觸發。
接下來來說說為了搬家到 Vercel,我做了哪些改變。
這張圖是還在 Heroku 時的程式架構圖。
Miri 是用 Python 寫的,API 部分是用 FastApi,然後run server
把app run起
,將帳號的資訊讀寫存放到 Heroku 免費方案的 PostgreSQL。
我做了些改變如下:
- 將 FastAPI 改為 Serverless
- 取消使用 PostgreSQL,改用 Firebase
我當時的想法就是做最小幅度的改動,然後迅速換平台上線,因為大概只有 1 個月左右的時間,如果要大改架構或者重新思考其他服務跟資料庫設計,也許下一版本開工時再來思考。
改為 Serverless
- 將 FaskAPI 作更改
- 新增 vercel.json 設定檔
- 新增 runtime.txt 設定檔
剛剛確認了我的程式碼,發現好像也不用改什麼 XD,我曬一下我的 main.py (主程式)
from typing import Optional
import uvicorn
from fastapi import FastAPI
# from source.log import set_log, Logger
from source.api import line, telegram
# TODO Comment this, can not use on serverless
# set_log(Logger())
app = FastAPI()
app.include_router(line.line_api)
app.include_router(telegram.telegram_api)
@app.get("/")
def read_root():
return {"Hello": "World"}
我把 api 們都寫在別的檔案,然後透過 FastAPI 的 include_router 功能將所有 api routes 加進來。
接著需要新增一些設定檔,跟 main.py (root) 同層加一個 vercel.json 設定檔
vercel.json
{
"builds": [{ "src": "main.py", "use": "@vercel/python" }],
"routes": [{ "src": "/(.*)", "dest": "main.py" }]
}
大致上就是告訴 Vercel API 入口在 main.py,然後使用 python 這個語言,第二行跟 api route 有關, "/(.*)"
沒領悟錯的話,就是指 api 路徑在 /
之後都是,有些設計會擺 /api
,這些設定因每個人的程式設計不同需要調整。
再來需要再新增一個檔案 runtime.txt,這個檔案是告訴 Vercel 要使用 Python 的哪個版本。
python-3.8.11
我使用 python 3.8 版本,根據Vercel 的官方文件,目前將 3.9 作為 default 的版本。然後!!!3.6 版,已經不能接受了,所以建議將自己的程式做 Python 版本號上的更新。
大概就以上動作,原則上就是前端 Call Serverless 的 Api 然後進入核心的後端函式處理(看是處理資料/運算/讀寫其他服務),處理完就結束進入待機,然後等到下次前端 Call API 時,在甦醒。
將 PostgreSQL 改成 Firebase
這個部分,我就研究的比較久一點點,畢竟也是重來沒使用過 firebase,也是第一次使用 GCP 雲端服務,需要學習怎麼用權限連接,然後 firebase 的架構跟讀寫指令。
大約學個 2-3 天,就將原本 PostgreSQL 的 Schema 用 Firebase 自己隨便設計很爛的架構,成功搬運過去,關於設計 Firebase 的資料庫架構嘛....,這件事等下個版本時一遍優化吧 XD。
所以整個改為 Serverless 後的架構圖長這個樣子:
不用 Run Server,基本上 Serverless,就是想成是一個大函式,當使用者 Call API 時,這個函式就開始執行然後處理,等到處理完將結果回傳給使用者之後,就進入休眠待機模式,原本的那些變數或者暫存都會清空。
所以如果要達到多執行緒或者一次處理很多個 Request,就是 Call 這個 Serverless Function 很多次,帶入不同的資料。AWS 上的 Lambda Serverless Function 就是這樣運行,當然也許也有其他方式,這只是我目前淺淺的認知。
部署上 Vercel
這部分,沒什麼太困難的。
我是用 GitHub 註冊(沒記錯的話),然後在 Overview 這個 Tab 頁面中,點選 Add New...
-> Project
,可以從你的 Github Repo 中 Import,預設都是 Deploy master 分支的樣子,你可以選擇其他分支做部署。
然後設定完成後,不一回兒就可以看是部署成功還是失敗。原則上 Vercel 會依據你有新的 Git Push 而觸發部署流程。
那大概就是這樣,我在部署的階段沒有卡太久,頂多就是找不到 /log 資料夾,因為 Serverless 不給創建新資料夾 XD,也不能寫 log 進去,所以來來回回後,我把 log 都註解掉,等到我有空來研究怎麼存 log 時再說。
最後,Vercel 完全是前端的大好選擇,我目前 Nuxt.js 跟 Vue.js 專案都無痛部署,完全不用搞任何有的沒的設定,按鈕點一點就部署成功 XD。
給予大家 Vercel 這個選項,趕緊把因為 Heroku 免費方案的取消而不見的專案們都換個地方上線吧~