OpenFaaS - 以自己的方式運行容器化函數

轉自:https://zhuanlan.zhihu.com/p/446565729
長期以來,無服務器(serverless) 對我來說無非就是 AWS Lambda 的代名詞。Lambda 提供了一種方便的途徑,可以將任意代碼附加到平臺事件(云實例的狀態變更、DynamoDB 記錄的更新或新的 SNS 消息)中。但是,我時不時會想到某個邏輯,但其又沒大到足以有自己的服務,同時有不適合任何現有服務的范圍。因此,我經常將其放入函數中,以便日后使用 CLI 命令或者 HTTP 調用來調用它。

幾年前,我來開了 AWS,自那以后,我一直懷念部署無服務器功能的便利性。因此,當我得知 OpenFaaS 項目時驚喜萬分。它將在 Kubernetes 集群上部署函數變得簡單,甚至僅需要 Containerd 就可以部署到虛擬機上。

有興趣?那么繼續!

無服務器與 FaaS

無服務器 已成為一個流行詞,目前其實際含義扔不夠清晰。

許多現代平臺被視為 無服務器 平臺。在 AWS Fargate 或 GCP Cloud Run 上部署容器化服務?無服務器!在 Heroku 上運行應用程序?也可能是無服務器的。

同時,我更喜歡將 FaaS 視為一種具體的設計模式。按照 FaaS 范式,可以部署代碼片段(響應某些外部時間執行的函數)。這些函數與事件驅動程序中的回調類似,但是是運行在其他人的的服務器上。由于操作的是函數而不是服務器,顧名思義 FaaS 是無服務器的。

image.png

source

OpenFaaS 項目旨在將 Kubernetes 集群或者獨立的虛擬機等低級基礎設施轉化為管理無服務器函數的高級平臺。

站在開發人員的角度,這樣一個平臺看起來是真的無服務器的 -- 你只需要知道特定的 CLI/UI/API 來處理 函數 抽象。但站在運維的角度,需要了解 OpenFaaS 如何使用 服務器 來運行這些函數。

就我而言,我經常既是開發又是運維,下面我將嘗試從二者展開說明。然而,我認為在評估 UX 時,我們應該明確區分它們。

開發人員眼中的 OpenFaaS

OpenFaaS 創建于 2016 年,現在網上也有大量的教程。這里不會重復介紹,但可以通過以下鏈接了解:

相反,我將描述我所理解的 OpenFaaS。我希望有助于一些需要評估該技術是否解決其問題的人,以及那些希望更有效地使用該技術的人。

函數運行時

在進入正式編碼之前,有必要了解下其未來的執行環境(又名運行時)?;蛘?,簡單說:

  • 如何啟動函數
  • 如何組織 I/O 操作
  • 如何重置 / 終止函數
  • 如何隔離函數和調用

OpenFaaS 自帶多個運行時模式,這些模式針對不同的場景定制。因此,不同的場景下上述問題的答案會略有不同。

OpenFaaS 函數在容器中運行,并且每個容器必須遵守簡單的約定 :它作為監聽在預設端口(默認為 8080)上的 HTTP 服務器,臨時存儲并且是無狀態的。

然而,OpenFaaS 通過 函數 watchdog(譯者注:watchdog 不做翻譯)模式避免了用戶編寫此類服務器。函數 watchdog 是一種輕量級 HTTP 服務器,可以感知如何執行實際函數業務邏輯。因此,安裝在容器中的所有內容加上作為入口點的 watchdog,就構成了函數的運行時環境。

經典 watchdog

最簡單的開始,或者又是被稱為經典 watchdog

image.png

這種模式下,watchdog 啟動了監聽在 8080 端口的輕量級 HTTP 服務器,每個進來的請求都會:

  • 讀取請求頭和請求體
  • fork 或者 exec 包含實際函數的可執行文件
  • 將請求頭和請求體寫入到函數進程的 stdin
  • 等待函數進程的退出(或者超市)
  • 讀取函數進程的 stdoutstderr
  • 在 HTTP 響應中將去讀的字節發送回調用方

上述邏輯類似于傳統的 通用網關接口(CGI)。一方面,每次函數調用都啟動單獨的進程看起來不夠高效,而另一方面,它確實超級方便,因為 任何使用 stdio 流進行 I/O 處理的程序(包括最喜歡的 CLI 工具)都可以部署為 OpenFaaS 函數。

提起隔離,我們有必要區分下函數調用

  • OpenFaaS 中的不同函數始終分布在不同的容器中
  • 一個函數可以有一個或多個容器 —— 取決于縮放選項
  • 同一函數的獨立調用可能會最終進入同一個容器
  • 同一函數的獨立調用將始終使用不同的進程進行

反向代理 watchdog

注意:使用 OpenFaaS 官方術語,本節討論在 HTTP 模式下運行的 of-watchdog。但我個人認為稱之為反向代理 watchdog 更加形象。

如果 經典 運行時類似于 CGI,那么這個運行時模式類似于后來的 FastCGI。運行時希望在 watchdog 后面有一個長期運行的 HTTP 服務器,而不是每次函數調用時創建新的進程。這本質上是 將 watchdog 組件變成反向代理

image.png

當容器啟動時,反向代理 watchdog 也會創建一個監聽在 8080 端口的輕量級 HTTP 服務器。然而,與 經典 watchdog 不同的是反向代理watchdog 只創建一次函數的進程,并將其當成(長期運行的)上游服務器。然后,函數調用轉變成到該上游的 HTTP 請求。

然而,反向代理模式并不為了取代經典模式。經典模式的強項在于其函數的編寫非常簡單。這也是沒有 HTTP 服務器的代碼的唯一選擇。比如使用 Cobol、bash 或者 PowerShell 腳本等等編寫的函數。

何時該使用反向代理運行時模式:

  • 函數需要在兩次調用之間保持狀態:
    • 緩存
    • 持久連接(例如,保持從函數到數據庫的連接打開)
    • 有狀態函數
  • 每個函數啟動一個進程可能開銷很大,為每個調用帶來了延遲
  • 你想運行一個(微)服務作為函數

根據 OpenFaaS 的創建者 Alex Ellis 的解釋,FaaS,特別是 OpenFaaS,可以被視為在不依賴服務器抽象的情況下 部署微服務的簡化方式。即 FaaS 是無服務器架構的規范示例。

因此,使用反向代理的方式,函數可以被看作是部署微服務的固執的方式。方便、快速、簡單。但使用有狀態函數時,要留意由于多個調用可能在同一個進程中結束而導致的警告:

  • 在一個進程中結束的并發調用可能會觸發代碼中的競爭條件(例如,一個帶有全局變量的 Go 函數,而全局變量沒有鎖的保護)。
  • 在一個進程中結束的后續調用可能會導致交叉調用數據泄露(當然,就像傳統微服務一樣)。
  • 由于該進程在兩次調用之間被復用,因此代碼中的任何內存泄漏都不會被緩解。

其他運行時模式

經典運行時模式在將函數結果發送回調用方之前緩沖了函數的整個響應。但如果響應的大小超出了容器的內存怎么辦?OpenFaaS 提供了另一種運行時模式,該模式仍然為每個調用創建進程,但添加了響應流。

另一個又去的場景是從函數中提供靜態文件服務。OpenFaaS 也有解決方案。

這可能是所有的內置運行時模式。但如果仍未滿足需求,OpenFaaS 是一個開源項目!看下現有的watchdog(1 & 2),簡潔明了。因此,可以隨時提交 PR 或者 issue,讓整個社區因你的貢獻收益。

編寫函數

此時,我們已經了解函數如何在配備了函數 watchdog 的容器中運行。那么最小的函數是什么樣子的?

下面的示例將簡單的 shell 腳本封裝到 OpenFaaS 函數中:

########################################################
# WARNING: Not for Production - No Security Hardening! #
########################################################

# This FROM is just to get the watchdog executable.
FROM ghcr.io/openfaas/classic-watchdog:0.2.0 as watchdog

# FROM this line the actual runtime definion starts.
FROM alpine:latest

# Mandatory step - put the watchdog.
COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog

# Optionally - install extra packages, libs, tools, etc.

# Function's payload - script echoing its STDIN, a bit transformed.
RUN echo '#!/bin/sh' > /echo.sh
RUN echo 'cat | rev | tr "[:lower:]" "[:upper:]"' >> /echo.sh
RUN chmod +x /echo.sh

# Point the watchdog to the actual thingy to run.
ENV fprocess="/echo.sh"

# Start the watchdog server.
CMD ["fwatchdog"]

當構建、部署和調用時,上面的函數作為 回顯服務器,倒轉并大寫其輸入。

稍微高級點的例子:一個 Node.js Hello World 腳本作為函數:

########################################################
# WARNING: Not for Production - No Security Hardening! #
########################################################

FROM ghcr.io/openfaas/classic-watchdog:0.2.0 as watchdog

FROM node:17-alpine

COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog

RUN echo 'console.log("Hello World!")' > index.js

ENV fprocess="node index.js"

CMD ["fwatchdog"]

因此,要編寫一個簡單的函數,只需要在 Dockerfile 中加入:

  • 實際腳本(或可執行文件)
  • 它的所有依賴項——軟件包、操作系統庫等
  • 首選的 watchdog

然后將 watchdog 指向該腳本(或可執行文件),并將 watchdog 作為入口。有點酷,因為:

  • 可以完全控制函數未來的運行時
  • 可以部署任何可以在容器中作為函數運行的東西

但上述方法有個明顯的缺點 -- 一個生產就緒的 Dockerfile 可能有上百行。如果我只想運行一個簡單的 Node.js/Python 腳本或者一個小的 Go 程序作為函數,要怎么處理 Dockerfile?就不能有一個占位符來粘貼代碼片段?

功能模板

OpenFaaS 的美妙之處在于,我們可以兩者兼而有之 —— 使用 Dockerfile 進行低級修補或目標語言編寫高級腳本!得益于豐富的功能模板庫

$ faas-cli template store list
NAME                     SOURCE             DESCRIPTION
csharp                   openfaas           Classic C# template
dockerfile               openfaas           Classic Dockerfile template
go                       openfaas           Classic Golang template
java8                    openfaas           Java 8 template
...
node14                   openfaas           HTTP-based Node 14 template
node12                   openfaas           HTTP-based Node 12 template
node                     openfaas           Classic NodeJS 8 template
php7                     openfaas           Classic PHP 7 template
python                   openfaas           Classic Python 2.7 template
python3                  openfaas           Classic Python 3.6 template
...
python3-flask            openfaas           Python 3.7 Flask template
python3-http             openfaas           Python 3.7 with Flask and HTTP
...
golang-http              openfaas           Golang HTTP template
...

上述功能模板由 OpenFaaS 作者和社區精心只做。典型的模板附帶一個復雜的 Dockerfile,指向虛擬處理程序函數。當引導新函數時,通過 faas-cli new 命令來使用這些模板。例如:

$ faas-cli new --lang python my-fn
Folder: my-fn created.
Function created in folder: my-fn
Stack file written: my-fn.yml

$ cat my-fn/handler.py
def handle(req):
    """ PUT YOUR BUSINESS LOGIC HERE """
    return req

因此,對于模板,編寫函數的工作可以歸結為簡單地將業務邏輯放入響應的處理程序文件中。

使用模板時,了解使用那種 watchdog模式 很重要:

  • 使用經典的類似 CGI 的 watchdog,處理程序通常被編寫為接受和返回純字符串的函數(例如:python3php7
  • HTTP 模式下,使用 of-watchdog時,處理程序看起來更像 HTTP 處理程序接受請求并返回響應結構(例如:python3-http,node17)。

函數商店

你的最佳函數是什么?對,你不需要寫。OpenFaaS 接受這種想法,并帶來了函數商店(經過社區測試并根據過往經驗選擇的 OpenFaaS 函數精選索引)。

該商店包含一些有趣的函數,可以一鍵部署到現有的 OpenFaaS 中:

$ faas-cli store list

FUNCTION                        DESCRIPTION
NodeInfo                        Get info about the machine that you'r...
alpine                          An Alpine Linux shell, set the "fproc...
env                             Print the environment variables prese...
sleep                           Simulate a 2s duration or pass an X-S...
shasum                          Generate a shasum for the given input
Figlet                          Generate ASCII logos with the figlet CLI
curl                            Use curl for network diagnostics, pas...
SentimentAnalysis               Python function provides a rating on ...
hey                             HTTP load generator, ApacheBench (ab)...
nslookup                        Query the nameserver for the IP addre...
SSL/TLS cert info               Returns SSL/TLS certificate informati...
Colorization                    Turn black and white photos to color ...
Inception                       This is a forked version of the work ...
Have I Been Pwned               The Have I Been Pwned function lets y...
Face Detection with Pigo        Detect faces in images using the Pigo...
Tesseract OCR                   This function brings OCR - Optical Ch...
Dockerhub Stats                 Golang function gives the count of re...
QR Code Generator - Go          QR Code generator using Go
Nmap Security Scanner           Tool for network discovery and securi...
ASCII Cows                      Generate a random ASCII cow
YouTube Video Downloader        Download YouTube videos as a function
OpenFaaS Text-to-Speech         Generate an MP3 of text using Google'...
Docker Image Manifest Query     Query an image on the Docker Hub for ...
face-detect with OpenCV         Detect faces in images. Send a URL as...
Face blur by Endre Simo         Blur out faces detected in JPEGs. Inv...
Left-Pad                        left-pad on OpenFaaS
normalisecolor                  Automatically fix white-balance in ph...
mememachine                     Turn any image into a meme.
Business Strategy Generator     Generates a Business Strategy (using ...
Image EXIF Reader               Reads EXIF information from an URL or...
Open NSFW Model                 Score images for NSFW (nudity) content.
Identicon Generator             Create an identicon from a provided s...

這些函數實際上是存儲在 Docker Hub 或者 Quay 等公共庫的容器鏡像,可以自由復用。

場景示例:

  • 使用 env 函數調試函數接收的HTTP標頭
  • 使用 curl 函數從 OpenFaaS 部署內部測試連接
  • 從運行多個副本的函數中使用 hey 來增加負載

函數的構建和部署

由于函數是在容器中運行的,因此需要有人為這些容器構建鏡像。無論你喜不喜歡,這都是開發人員的事情。OpenFaaS 提供了方便的 faas-cli build 命令,但沒有服務器端構建。因此,要么需要(在安裝 Docker 的機器上)手動運行 faas-cli build,要么使用 CI/CD 完成。

接下來,構建好的鏡像需要通過 faas-cli push 到倉庫。顯然,這種倉庫也應該可以從 OpenFaaS 服務器端訪問。否則,使 用faas-cli deploy 部署函數時會失敗。

開發人員的工作流程如下:

image.png

調用函數

函數部署后,可以通過向 $API_HOST:$API_PORT/function/<fn-name> 端點發送 GET、POST、PUT 或者 DELET HTTP 請求來調用它。常見的調用方式有:

  • 各種鉤子(webhook)
  • faas-cli invoke
  • event connectors!

前兩個選項相當簡單。使用函數作為作為 webhook 處理器(GitHub、IFTTT 等)很方便,每個函數開發人員都已經安裝了 faas-cli,因此可以成為日常腳本編寫的組成部分。

那什么是事件連接器?

在本文開頭是我對 AWS Lambda 與 AWS 平臺事件緊密集成的溫暖回憶。請記住,可以在響應新 SQS/SNS 消息、新的 Kinesis 記錄、EC2 實例生命周期等事件時調用 Lambda。OpenFaaS 函數是否存在類似的東西呢?

顯然,OpenFaaS 無法開箱即用地與任何生態系統集成。然而,它提供了一種名為事件連接器模式的通用解決方案。

image.png

官方支持的連接器:

OpenFaaS 還提供了很小的連接器-sdk庫來簡化連接器的開發。

運維眼中的 OpenFaaS

開發眼中的 OpenFaaS 是個黑盒,提供簡單的 API 來部署和調用函數。然而,作為運維可能會從了解 一點 OpenFaaS 內部原理中受益。

OpenFaaS 通用架構

OpenFaaS 有一個簡單但強大的架構,允許使用不同的基礎設施作為后端。如果已經有了 Kubernetes 集群,可以通過在上面部署 OpenFaaS 輕松將其變成 FaaS 解決方案。但是如果舊的虛擬(或物理)機,仍然可以在上面安裝 OpenFaaS,并獲得差不多功能的更小的 FaaS 解決方案。

image.png

上面的架構中唯一面向用戶的組件是 API 網關。OpenFaaS 的 API 網關:

  • 暴露 API 來管理和調用函數
  • 提供內置的 UI 來管理函數
  • 處理函數的自動縮放
  • 預計后面會有兼容的 OpenFaaS 提供商

因此,當開發人員運行 faas-cli deployfaas-cli list 或使用 curl $API_URL/function/foobar 調用函數等內容時,請求將發送到上述的 API 網關。

上圖中的另一個重要組成部分是 faas-provider。它不是一個具體的組件,而更像是接口。任何實現(非常簡潔)的提供商 API 的軟件都可以成為提供商。OpenFaaS 提供商:

  • 管理功能(部署、列表、縮放、刪除)
  • 調用函數
  • 暴露一些系統信息

兩個最注明的提供商是 faas-netes(Kubernetes 上的 OpenFaaS)和 faasd(Containerd 上的 OpenFaaS)。下面,將介紹他們的實現。

Kubernetes 上的 OpenFaaS(faas-nets)

當部署在 Kubernetes 上時,OpenFaaS 利用了該平臺開箱即用的強大原語。

image.png

關鍵要點:

  • API 網關成為標準(部署+服務)對。因此,可以隨心所欲地擴展它。也可以隨心所欲地把它暴露出來
  • 每個函數也成為(部署+服務)對??赡懿粫苯犹幚砗瘮?,但對于 faas-netes,縮放變得就像調整相應的副本數一樣簡單
  • 高可用性和開箱即用的水平縮放 - 同一功能的 pod 可以(而且應該)跨多個集群節點運行。
  • Kubernetes 作為一個數據庫工作;例如,當運行 faas-cli list 等命令來獲取當前部署的函數列表時,faas-netes 只會將其轉換為相應的 Kubernetes API 查詢

Containerd 上的 OpenFaaS(faasd)

對于沒有使用 Kubernetes 集群的人來說,OpenFaaS 提供了名為 faasd 的替代輕量級提供商。它可以安裝在(虛擬或物理)服務器上,,并利用 containerd 來管理容器。正如我之前寫的那樣,容器是一個在 Docker 和 Kubernetes 下使用的較低級別的容器管理器。結合 CNI 插件,它成為編寫容器調度器的構建組件,OpenFaaS 的 faasd 是個很好的講究案例:

image.png

關鍵要點:

  • 被設計為在 IoT 設備上或 VM 中運行
  • 使用 containerd 的原生 pause (通過cgroup freezers)和超快速函數冷啟動快速擴展到零
  • 它可以在每臺服務器上運行比 faas-netes 多十倍的函數,并且可以有效地使用更便宜的硬件,包括樹莓派
  • containerd 和 faasd 作為 systemd 服務進行管理,因此會自動處理日志、重啟等
  • 沒有 Kubernetes DNS,但 faasd 確保 DNS 在函數之間共享以簡化互操作
  • containerd 扮演著數據庫的角色(比如 faas-cli list 變成了類似 ctr container list的操作 ),所以如果服務器掛了,所有狀態就會丟失,每個函數都需要重新部署
  • 沒有開箱即用的高可用性或水平擴展(參見 issue/225

總結

對你所使用的軟件有個好的心智模型是是有益的,它可以提高開發效率,防止單例場景的發生,并簡化了故障排查。

以 OpenFaaS 為例,區分開發人員和運維人員對系統的看法可能是個很好的思路。從開發人員角度來看,這是一個簡單而強大的無服務器解決方案,主要關注 FaaS 場景。該解決方案由一個用于管理和調用函數的簡潔 API、一個涵蓋開發人員工作流的命令行工具以及一個函數模板庫組成。無服務器函數以不同的運行時模式(類 CGI、反向代理)在容器中運行,并提供不同的隔離和狀態保障。

運維人員的角度來看,OpenFaaS 是一個具有靈活架構的模塊化系統,可以部署在不同類型的基礎設施之上:從樹莓派到裸機或虛擬機、以及成熟的 Kubernetes、OpenShift 或 Docker Swarm 集群。當然,每種選擇都有其優缺點,需要詳細評估取舍。但即使現有選項都不合適,簡單的 faas-provider 抽象允許開發自己的后端來運行無服務器功能。

上述內容主要集中在 OpenFaaS 基礎知識上。但是 OpenFaaS 也有一些高級功能。通過以下鏈接進一步了解:

資源

作者介紹: Ivan Velichko Software Engineer at heart, SRE at day job, Tech Storyteller at night. 文章統一發布在公眾號[云原生](https://zhida.zhihu.com/search?content_id=187366319&content_type=Article&match_order=1&q=%E4%BA%91%E5%8E%9F%E7%94%9F&zhida_source=entity)指北

http://weixin.qq.com/r/i3QmPtDERo05rZRd9yGZ (二維碼自動識別)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。