Blog Cover Image

Inspire you to have New thinking, Walk out your unique Road.

有的時候,你無意間遇到的一些故事,會激發你的靈感,改變你的想法,接下來你會用與之前全然不同的觀念去創造屬於你獨特的故事。

Sign @MinaYu.

從Heroku到Vercel | 將我的Python Web server 改成 Serverless Function (含其它Host選項)

Posted on

我說,軟體開發這門領域的技術真的發展得十分迅速,還記得讀碩士時,前端的三大框架才剛出現。接著在第一家公司好不容易了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 serverapp 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免費方案的取消而不見的專案們都換個地方上線吧~