Site icon Fox Nest

將服務遷移到Google Cloud Functions

最近開始把腦筋動到每個月要吃掉我20鎂的linode,看看能不能用Google Compute Platform來取代掉單純的VM。GCP本身有提供Free Tier, 所以只要小心地控制用量,搬遷應該是可以省下一筆,而且也可以玩玩GCP的一些新功能。

需求分析

我這次先搬遷之前寫的一個爬蟲服務,這個爬蟲服務應該是我現有服務裡面最簡單的一個,首先先分析一下他有什麼特性:

舊有的方案以及缺點

之前的方案是一個純application,走infinite for loop,在裡面放ticker來計時10分鐘一次,然後把他包成docker image用daemon mode跑起來。

這個跑法算是滿傳統的,在非雲端時代也是一個很標準的做法。只是這樣跑了幾個月,發現了有以下這些問題:

這些問題其實都能以傳統手段解決,比方說最簡單的方式就是把它改成cron job,從OS層面來排程而非code層面,docker.io也不是很常掛點,真的掛掉的話改用其他免費的image registry也是可以的。

選擇Serverless

Google提供了三種不同的方法來作Serverless:

這個爬蟲本身吃的資源不多,也不需要http跟外界溝通,用pubsub也挺方便的,所以就選擇了用Cloud Functions來當作deploy的方法。重點是,Cloud Functions免費額度應該算是最彈性的,應該怎麼打都不太會超過免費額度上限,所以就用吧!

實作

結構

預期中的結構會是像這樣

而爬蟲的config由於有credential,所以由Secret Manager來存,然後用mount的方式掛進Cloud Functions。

Scheduler

首先我們需要一個定時器。幸運的是,GCP本身就有提供Cloud Scheduler來提供定時的功能。

Frequency基本上就是Cron的format,比方說例子中的*/15 * * * *就是每15分鐘一次。Target Type選擇Pub/Sub,下面的message道是隨意,給個空Body {}就好。不過,這邊有個限制,就是message / message attributes至少要有一個,就隨便放一個就好。Pub/Sub topic則是直接選擇Create a topic就好。

Cloud Source Repositories

Functions有很多種deploy方式,不過最方便的方式應該是用git。但是,使用git的話,必須要用GCP自己的Cloud Source Repositories(CSR)託管。幸運的是,CSR是可以作為GitHub的mirror存在的,也就是說,當我們把code push到GitHub後,很快該commit就會被mirror到CSR。

首先,先進入CSR,按下右上角的Add Repository,然後選擇Connect External Repository

接下來就是行禮如儀,選擇Project,選擇GitHub當作Git Provider,然後經過一連串的Authorization後,把這個repo給Mirror進來就可以了。

Secret Manager

由於我們的config包含了一些credential,所以比較適合存放在Secret Manager內。這東西使用起來非常直觀,就建立一個key,把config塞進去就好

他可以upload檔案,所以甚至可以把gcp credential(JSON)塞進去。

Cloud Functions

他基本原理還滿簡單的,不過有些眉角:

  1. 他的package name要是FQDN。比方說AppleTwNCCSpy不行,github.com/Rayer/AppleTwNCCSpy 才行。
  2. 目前來講放置EntryPoint的.go 必須要在最上層。如果是放子目錄的話,無論該子目錄有沒有獨立的go.mod, 都會在fetch dependency的階段有錯誤。這問題很隱晦,如果不想浪費時間的話,請務必把它放在最上層。
  3. 我會建議先推一個絕對可以過的版本再慢慢改。Functions Deploy非常麻煩,而且所有輸入的數據都要成功deploy才會儲存——亦即,如果你deploy failed,所有東西都要重打!超煩的 orz

首先,建立一個任何名字的.go檔案,裡面放一個這種簽名的function : func Func(ctx context.Context, m PubSubMessage) error ,記得函數名稱開頭要大寫export,不然會讀取不到。事實上,他一開始建立的範例也會提供這個簽名給你參考。我個人是建議開一個新的branch把它給push上去,接下來就可以開始設定了。

然後進入Cloud Functions,選Create Function。

Basics沒啥好注意的,就Trigger Type選擇剛建立好的Pub/Sub以及Topic選擇剛建立好的Topic即可。

Runtime倒是隨意,要是工作loading很低的話建議把Memory選低一點。像是我這種Crawler的話,下面的Autoscaling的min設定0,max設定1是最好的。注意,如果min不是設定0的話,會一直有一個instance長在上面,很貴!有這種需求的話App Engine或者VM會是更好的選擇。

Secrets就是重頭戲了,我們會把Config寫成yaml放在Secret裡面。舉例來說,我們Secret是剛剛設定的DemoConfig Reference Method選擇Mounted as volumn(建議不要用env expose),Mount Path/secrets, 最後的path選擇/secrets/DemoConfig.yml 這樣設定的話,我們在function就可以這樣得到這個值:

	confByte, err := ioutil.ReadFile("/secrets/DemoConfig.yml")
	if err != nil {
		return err
	}

接下來看你要用json parse, yaml parse還是raw text就都可以了。

最後,最麻煩的地方來了

Runtime當然選Go 1.16,Source Code我是先建議先選擇Inline Editor然後存一次,再回來改,不然deploy的話你剛打的東西都要重打一次!存好回來以後,Source Code選擇Cloud Source Repository,右邊的Repository的名字…請多開一個窗去CSR,把repository name拷貝回來貼上,這邊沒有自動完成 …

還記得我建議嗎?把function寫在根目錄,多開一個branch,所以這邊就行裡如宜的選Branch,填入你剛寫好的branch name,然後Directory with source code就維持/,最後Entry Point填入function name,按下deploy! 順利的話應該就會建立出來了。

Re-Deploy

目前來講command line還缺了一些東西,所以強烈建議先用UI Deploy一次後,再用command line redeploy。以我自己的例子來講 ,如果要deploy from git:

gcloud functions deploy apple-ncc-crawler-dev --region=us-central1 --entry-point CrawlAndAnalyze --memory 128MB --runtime go116 --source "https://source.developers.google.com/projects/iris-303620/repos/github_rayer_appletwnccspy/moveable-aliases/master/paths//"

如果要從local deploy,拿掉–source標籤即可。其實deploy from local應該比較方便,只是command line缺不少設定,比方說目前secret->mount沒辦法從command line去設定(但是很妙的是,command line會告訴你相關訊息)。

Command deploy有些要注意的地方,最坑的就是--source 這個parameter。他看起來很像是一個https開頭的網址,事實上不是。比方說,他是https://source.developers.google.com開頭,不是CSR的網址https://source.cloud.google.com 。所以最保險的方法就是拷貝這條一個個自己改:

https://source.developers.google.com/projects/<ProjectID>/repos/<Repository名字>/moveable-aliases/<Branch>/paths//

這樣應該就可以大功告成了。建議把deploy指令寫在project裡面一個.sh裡面方便以後使用。

Exit mobile version