作者:劉宇(花名:江昱)
作者說:Serverless 架構下的應用開發,與傳統架構的應用開發還是有比較大的區別點的,例如天然分散式架構會讓很多框架喪失一定的"便利性",無狀態的特點又讓很多"傳統架構下看起來再正常不過的操作"變得異常風險。
所以本篇我會介紹一些在 Serverless 架構下,常見的應用開發注意事項,分享一些個人的實戰經驗心得。如果你在 Serverless 開發過程中遇到問題,不妨往下聽聽看吧。
關於應用開發的 7 個心得
如何上傳檔案
在傳統 Web 框架中,上傳檔案是非常簡單和便捷的,例如 Python 的 Flask 框架:
f = request.files['file']
f.save('my_file_path')
但是在 Serverless 架構下,卻不能直接上傳檔案,原因有以下幾點:
- 一些雲平臺的 API 閘道器觸發器會將二進位制檔案轉換成字串;不便直接獲取和儲存;
- 此外,API 閘道器與 FaaS 平臺之間傳遞的資料包有大小限制,很多平臺被限制在 6M;
- FaaS 平臺大都是無狀態的,即使儲存到當前例項中,也會隨著例項釋放而導致檔案丟失;
因此,傳統框架中常用的上傳方案,是不太適合在 Serverless 架構中直接使用的。若是想在 Serverless 架構上傳檔案,可以嘗試以下兩種方法:
- 一種是 BASE64 後上傳,持久化到物件儲存或者是 NAS 中,這種做法可能會觸及到 API 閘道器與 FaaS 平臺之間傳遞的資料包有大小限制,所以一般使用這種上傳方法的通常是上傳頭像等小檔案的業務場景;
- 第二種上傳方法是,通過物件儲存等平臺來上傳,因為客戶端直接通過金鑰等資訊,來將檔案直傳到物件儲存是有一定風險的。所以通常情況是客戶端發起上傳請求,函式計算根據請求內容進行預簽名操作,並將預簽名地址返回給客戶端,客戶端再使用指定的方法進行上傳,上傳完成之後,可以通過物件儲存觸發器等來對上傳結果進行更新,詳情如下圖所示:
檔案讀寫與持久化方法
應用在執行過程中,可能會涉及到檔案的讀寫操作,或者是一些檔案的持久化操作。在傳統的雲主機模式下,通常情況下是可以直接讀寫檔案,或者將檔案持久化某個目錄下,但是在 Serverless 架構下卻並不是這樣的。
由於 FaaS 平臺是無狀態的,並且用過之後會被銷燬,所以檔案如果需要持久化並不能直接持久化在例項中,可以選擇持久化到其他的服務中,例如物件儲存、NAS 等。
同時,在不配置 NAS 的情況下,FaaS 平臺通常情況下之後 /tmp 目錄具有可寫許可權,所以部分臨時檔案可以快取在 /tmp 資料夾下。
慎用部分 Web 框架的特性
函式計算(FC)是請求級別的隔離,所以可以認為這個請求結束了,例項就有可能進入到一個“靜默”的狀態。在函式計算中,API 閘道器觸發器通常是同步呼叫 (以阿里雲函式計算為例,通常只在定時觸發器、OSS 事件觸發器、MNS 主題觸發器和 IoT 觸發器等幾種情況下是非同步觸發) ,這就意味著當 API 閘道器將結果返回給客戶端的時候,整個函式就會進入“靜默”狀態,或者被銷燬,而不是會繼續執行完非同步方法。
所以通常情況下像 Tornado 等框架就很難在 Serverless 架構下發揮其非同步的作用。當然,如果使用者需要非同步能力,可以參考雲廠商所提供非同步方法,以阿里雲函式計算為例,阿里雲函式計算為使用者提供了一種非同步呼叫能力,當函式的非同步呼叫被觸發後,函式計算會將觸發事件放入內部佇列中,並返回請求 ID,而具體的呼叫情況及函式執行狀態將不會返回。如果使用者希望獲得非同步呼叫的結果,可以通過配置非同步呼叫目標來實現,詳情如圖中所示:
Serverless 架構下,應用一旦完成當前請求,就會進入到“靜默”狀態,例項甚至都會被銷燬,所以這就導致一些自帶定時任務的框架沒有辦法正常執行定時任務。因為函式計算通常是由事件觸發,不會自主定時啟動。例如 Egg 專案中設定了一個定時任務,但是在實際的函式計算中如果沒有通過觸發器觸發該函式,那麼該函式是不會被觸發,函式也不會從內部自動啟動來執行定時任務,所以此時可以使用各個雲廠商為其 FaaS 平臺提供的定時觸發器,通過定時觸發器觸發指定方法,來替代定時任務。
要注意應用組成結構
在 Serverless 架構下,靜態資源更應該在物件儲存與 CDN 的加持下對外提供服務;否則所有的資源都在函式中,通過函式計算對外暴露,不僅僅會讓函式的真實業務邏輯併發度降低,也會造成更多的成本支出。尤其是將一些已有的程式遷移到 Serverless 架構上,如 Wordpress 等,更是要注意將靜態資源與業務邏輯進行拆分,不然在高併發的情況下,效能與成本都將會受到嚴格的考驗。
在眾多雲廠商中,函式的收費標準都是依靠執行時間和配置的記憶體,以及產生的流量進行收費的。如果一個函式的記憶體設定不合理,會導致成本翻倍增加。因此我們既要保證記憶體設定合理,更要保證業務邏輯結構的可靠性。
以阿里雲函式計算為例,當一個應用,有兩個對外的介面,其中有一個介面的記憶體消耗在 128MB 以下,另一個介面的記憶體消耗穩定在 3000MB 左右。這兩個介面,平均每天會被觸發 10000 次,並且時間消耗均在 100ms。如果兩個介面寫到一個函式中,那麼這個函式可能需要將記憶體設定在 3072MB,同時在使用者請求記憶體消耗較少的介面時,在冷啟動的情況下可能難以得到較好的效能表現;但是,如果兩個介面分別寫到兩個函式中,那麼兩個函式記憶體分別設定成 128MB 以及 3072MB 即可:
通過上表,我們可以明確看出,當合理的、適當地把業務進行拆分之後,會在一定程度上更節約成本,以上述例子來看,成本節約近 50%!
傳統框架遷移方案與策略
Serverless 的範圍越來越廣,它本質上來講更像是一種設計理念,一種新的程式設計正規化。在這種新的架構下,或者說新的程式設計正規化下,使用全新的思路來做 Serverless 應用是再好不過的了。但是原生的 Serverless 開發框架是非常少的,以 Web 框架為例,目前的主流的 Web 框架“均不支援 Serverless 模式部署”,一方面是要嘗試接觸 Serverless,一方面又沒辦法完全放棄傳統框架,所以如何將傳統框架更簡單、更快速、更科學地部署到 Serverless 架構上是一個值得探討的問題。下面我結合案例分享一下遷移思路:
- 傳統框架遷移案例
請求整合方案實際上就是把真實的 API 閘道器請求,直接透傳給 FaaS 平臺,而不在中途增加任何轉換邏輯。以阿里雲函式計算的 HTTP 函式為例,當開發者想要把傳統框架(例如 Django,Flask,Express,Next.js 等)部署到阿里雲函式計算平臺上,並且體驗 Serverless 帶來的按量付費,彈性伸縮等便捷優勢。
得益於阿里雲函式計算的 HTTP 函式和 HTTP 觸發器,開發者不僅可以快速、簡單地將框架部署到阿里雲函式計算,更可以保持和傳統開發一樣的體驗。以 Python 的 Bottle 框架為例,當我們開發一個 Bottle 專案之後:
# index.pyp
import bottle
@bottle.route('/hello/<name>')
def index(name):
return "Hello world"
if __name__ == '__main__':
bottle.run(host='localhost', port=8080, debug=True)
可以直接在本地進行除錯。當想要把該專案部署到阿里雲函式計算上,只需要增加一個 default_app 的物件即可:
app=bottle.default_app()
整個專案:
# index.py
import bottle
@bottle.route('/hello/<name>')
def index(name):
return "Hello world"
app = bottle.default_app()
if __name__ == '__main__':
bottle.run(host='localhost', port=8080, debug=True)
此時,在阿里雲函式計算平臺,建立函式時,將函式入口設定為:index.app 即可。除了 Bottle 之外,其他的 Web 框架的操作方法是類似的,再以 Flask 為例:
# index.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run(
host="0.0.0.0",
port=int("8001")
)
在配置函式的時候寫上入口函式為:index.app 即可,就可以保證該 Flask 專案執行在函式計算平臺上。
當然,除了使用已有的語言化的 Runtime,還可以考慮使用 Custom Runtime 和 Custom Container 來實現,例如,一個 Web 專案完成之後,可以編寫一個 Bootstrap 檔案(在 Bootstrap 檔案中寫一些啟動命令即可),例如我要啟動一個 Express 的專案,我把我的 Express 專案準備完成之後,可以直接通過 Bootstrap 為:
#!/usr/bin/env bash
export PORT=9000
npm run star
- 通過開發者工具快速遷移/部署
如果通過開發者工具進行傳統框架的支援,可以直接通過 Serverless Devs 的命令,進行案例的專案的建立。目前 Serverless Devs 已經支援以下框架:
詳情可以參考:https://github.com/devsapp/start-web-framework
以 Express 專案為例,可以在命令列工具執行:
s init start-express
即可進行專案的初始化:
_____
| ___|
| |____ ___ __ _ __ ___ ___ ___
| __\ / / '_ | '__/ _ / __/ __|
| |___> <| |_) | | | __/__ __ \
____/_/_\ .__/|_| ___||___/___/
| |
|_|
? please select credential alias default
Welcome to the start-express application
This application requires to open these services:
FC : https://fc.console.aliyun.com/
Express development docs: https://www.expressjs.com.cn/4x/api.html
* 額外說明:s.yaml中宣告瞭actions:
部署前執行:npm install --production
如果遇到npm命令找不到等問題,可以適當進行手動專案構建,並根據需要取消actions內容
* 專案初始化完成,您可以直接進入專案目錄下,並使用 s deploy 進行專案部署
? Thanks for using Serverless-Devs
? You could [cd /Users/jiangyu/Desktop/fc-custom-lua-event/image-prediction-app/start-express] and enjoy your serverless journey!
?️ If you need help for this example, you can use [s -h] after you enter folder.
? Document ❤ Star:https://github.com/Serverless-Devs/Serverless-Devs
完成之後,可以進入專案並部署:
$ s deploy
framework:
region: cn-beijing
service:
name: web-framework
function:
name: express
runtime: custom
handler: index.handler
memorySize: 128
timeout: 60
url:
system_url: https://1583208943291465.cn-beijing.fc.aliyuncs.com/2016-08-15/proxy/web-framework/express/
custom_domain:
-
domain: http://express.web-framework.1583208943291465.cn-beijing.fc.devsapp.net
triggers:
-
type: http
name: httpTrigger
此時可以通過瀏覽器開啟頁面:
此時,可以根據案例提供的 Bootstrap 以及 s.yaml 進行參考,並將自身的專案部署/遷移到阿里雲 Serverless 架構。其中 Bootstrap 為:
#!/bin/bash
node index.js
其中 s.yaml 為:
# ------------------------------------
# 歡迎您使用阿里雲函式計算 FC 元件進行專案開發
# 元件倉庫地址/幫助文件:https://github.com/devsapp/fc
# Yaml參考文件:https://github.com/devsapp/fc/blob/jiangyu-docs/docs/zh/yaml.md
# 關於:
# - Serverless Devs和FC元件的關係、如何宣告/部署多個函式、超過50M的程式碼包如何部署
# - 關於.fcignore使用方法、工具中.s目錄是做什麼、函式進行build操作之後如何處理build的產物
# 等問題,可以參考文件:https://github.com/devsapp/fc/blob/jiangyu-docs/docs/zh/tips.md
# 關於如何做CICD等問題,可以參考:https://github.com/Serverless-Devs/Serverless-Devs/blob/master/docs/zh/cicd.md
# 有問題快來釘釘群問一下吧:33947367
# ------------------------------------
edition: 1.0.0 # 命令列YAML規範版本,遵循語義化版本(Semantic Versioning)規範
name: framework # 專案名稱
access: "default" # 祕鑰別名
services:
framework: # 業務名稱/模組名稱
component: fc # 元件名稱
actions:
pre-deploy: # 在deploy之前執行
- run: npm install --production # 要執行的命令列
path: ./code # 命令列執行的路徑
props: # 元件的屬性值
region: cn-beijing
service:
name: web-framework
description: 'Serverless Devs Web Framework Service'
function:
name: express
description: 'Serverless Devs Web Framework Express Function'
codeUri: './code'
runtime: custom
timeout: 60
caPort: 9000
triggers:
- name: httpTrigger
type: http
config:
authType: anonymous
methods:
- GET
customDomains:
- domainName: auto
protocol: HTTP
routeConfigs:
- path: '/*'
可觀測性
Serverless 應用的可觀測性是被很多使用者所關注的。可觀測性是通過外部表現判斷系統內部狀態的衡量方式,在應用開發中,可觀測性幫助判斷系統內部的健康狀況。在系統出現問題時,幫助定位問題、排查問題、分析問題;在系統平穩執行時,幫助評估風險,預測可能出現的問題。
在 Serverless 應用開發中,如果觀察到函式的併發度持續升高,很可能是業務推廣團隊的努力工作導致業務規模迅速擴張,為了避免達到併發度限制觸發流控,開發者就需要提前提升併發度,以阿里雲函式計算為例,阿里雲函式計算就在可觀測性層面提供了多種緯度,包括 Logging、Metrics 以及 Tracing 等內容。
如圖,在控制檯監控中心,可以檢視到整體的 Metrics,服務級 Metrics 以及每個函式的 Metrics。除此之外,還可以看到當前函式的請求記錄:
根據不同的請求記錄,我們可以檢視到函式的詳細資訊:
除了在控制檯的監控中心處可以檢視到函式的日誌等資訊,在函式詳情頁面,也可以看到函式的詳細日誌資訊:
以及 Tracing 相關資訊:
當然,通過 Serverless Devs 開發者工具,以及函式計算元件也可以進行觀測相關操作,下面我們來一起看一下是怎麼進行的。
- 通過工具進行 Metrics 檢視
詳情參考:
https://github.com/devsapp/fc/blob/main/docs/zh/command/metrics.md
- 有資源描述檔案(Yaml)時,可以直接執行 s metrics 檢視函式的指標資訊;
- 純命令列形式(在沒有資源描述 Yaml 檔案時),需要指定服務所在地區以及服務名稱,函式名等,例如 sclifcmetrics--regionch-hangzhou--service-namemyService--function-namemyFunction;
上述命令的執行結果示例如下:
[2021-06-07T12:20:06.661] [INFO ] [FC-METRICS] - 請用瀏覽器訪問Uri地址進行檢視: http://localhost:3000
此時,通過瀏覽器開啟地址,可以看到函式指標資訊:
P.S.需要開啟請求級別指標,才能檢視函式指標資訊,否則圖表不展示資料。
關於如何開通請求級別指標:
1.https://fcnext.console.aliyun.com/
2.服務及函式中-找到自己region-對應的服務名稱-在操作欄點選配置開啟請求級別指標
- 通過工具進行 Logs 檢視
詳情參考:
https://github.com/devsapp/fc/blob/main/docs/zh/command/logs.md
- 有資源描述檔案(Yaml)時,可以直接執行s logs 進行線上函式的日誌查詢;
- 純命令列形式(在沒有資源描述 Yaml 檔案時),需要指定服務所在地區以及服務名稱,函式名等,例如s cli fc logs --region cn-hangzhou --service-name fc-deploy-service --function-name http-trigger-py36
上述命令的執行結果示例:
FunctionCompute python3 runtime inited.
FC Invoke Start RequestId: 84d6ae81-02ff-4011-b3ca-45e65b210cc3
FC Invoke End RequestId: 84d6ae81-02ff-4011-b3ca-45e65b210cc3
FC Invoke Start RequestId: de4812be-9137-4a33-9869-370cb61ac427
FC Invoke End RequestId: de4812be-9137-4a33-9869-370cb61ac427
如果需要以 tail 模式進行日誌的查詢,可以增加 --tail 引數,例如 s logs --tail;
查詢指定時間段的日誌,可以通過增加 --start-time 和 --end-time 引數實現(例如 s logs -s 2021-11-04T15:40:00 -e 2021-11-04T15:45:00)
如何對應用進行除錯
在應用開發過程中,或者應用開發完成,但是所執行結果不符合預期時,通常要進行一定的除錯工作。但是在 Serverless 架構下,除錯往往會受到極大的環境因素限制,有可能出現的情況是,所開發的應用在本地是可以比較健康的、符合預期的執行,但是在 FaaS 平臺上,則會出現一些不可預測的問題;或者是在一些特殊的環境下,本地沒有辦法模擬線上環境,難以進行專案的開發和除錯。
Serverless 應用除錯被視為 Serverless 落地最大的痛點與挑戰,但是各個雲廠商並沒有因此放棄在除錯方向的不斷深入探索。以阿里雲函式計算為例目前就提供線上除錯、本地除錯等多種除錯方案。一些具體的操作可參考 Serverless 公眾號文章:<硬核除錯實操 | 手把手帶你實現 Serverless 斷點除錯>,這裡就不再贅述啦。
結語
以上就是我在 Serverless 應用開發中的一些經驗分享,Forrester 曾提出:Serverless 架構的興起,讓 FaaS (Function As A Service) 成為繼 IaaS、PaaS、SaaS 之後一種新的雲端計算能力提供方式。未來也將會有大量主流企業的核心應用,從原來的主機架構遷移到 Serverless 架構。
Serverless 架構正當時,其已然開啟從概念到實踐的大規模落地之路,正如 Gartner 報告中的預測:到 2025 年,全球一半的企業將採用 FaaS 部署;
Serverless 不僅可以做到讓開發者無需再管理伺服器,無需再對資源做預估並考慮擴充套件;發揮降本提效,按量付費的優勢,它能讓你進入只專心在業務邏輯的狀態,從而全面提升工作中的研發效能。
好啦,今天是世界讀書日,感謝收聽(閱讀),希望我的分享能夠對你有所幫助。