OpenWhisk 作為一個 Serverless 平台,其架構設計複雜且精妙。理解其核心元件與運作流程對於開發和佈署至關重要。本文不僅剖析了 OpenWhisk 的使用者端組態資訊,也探討了 API 閘道器、控制器、CouchDB、Kafka、呼叫器和 Docker 容器等關鍵元件的功能和相互關係,並以圖表形式清晰地展示了 OpenWhisk 的整體架構。此外,文章也詳細說明瞭如何使用 Docker Skeleton 範本構建自定義的函式執行環境,包含 C 程式範例和 Dockerfile 的解析,以及如何使用 Go 語言編寫函式並利用多階段建置最佳化 Docker 映像的構建過程。
OpenWhisk 技術架構與元件解析
OpenWhisk 是一個強大的無伺服器運算(Serverless)平台,作為 IBM Cloud Functions 的核心基礎,其設計旨在提供一個堅如磐石的 FaaS(Function as a Service)環境。本篇文章將探討 OpenWhisk 的架構組成、關鍵元件及其運作原理。
使用者端組態資訊解析
首先,讓我們分析 OpenWhisk 使用者端的組態資訊:
Client key: 23bc46b1-71f6-4ed5-8c54-816aa4f8c502:123zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
whisk API host: localhost:443
whisk API version: v1
whisk namespace: guest
whisk CLI version: 2017-12-05T00:51:32+00:00
whisk API build: "09/01/2016"
whisk API build number: "latest"
內容解密:
Client key是用於身份驗證的 API 金鑰,由兩部分組成:23bc46b1-71f6-4ed5-8c54-816aa4f8c502和123zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP。任何 OpenWhisk 客戶端都需要此金鑰來進行連線。whisk API host指定了 API 閘道器的位置,位於localhost:443,表示所有請求都會透過此閘道器轉發到後端的控制器。whisk API version表示當前使用的 API 版本為v1。whisk namespace目前設定為guest,代表使用者所處的名稱空間。- 各版本的 CLI 和 API 組建資訊提供了系統的版本控制和相容性參考。
OpenWhisk 元件與架構
OpenWhisk 的設計以 Kafka 為核心,確保每個函式請求都能可靠地傳遞到呼叫層。下面我們將詳細介紹其整體架構和各個元件的功能。
架構概述
下圖展示了 OpenWhisk 的整體架構:
graph LR
A[使用者端] -->|HTTPS| B[API 閘道器]
B -->|HTTP| C[控制器]
C -->|訊息佇列| D[Kafka 叢集]
D -->|呼叫請求| E[呼叫器]
E -->|執行函式| F[Docker 容器]
C -->|儲存資料| G[CouchDB 資料函式庫]
圖表翻譯: 此圖展示了 OpenWhisk 的整體架構,包括使用者端請求如何透過 API 閘道器進入系統,並由控制器協調後將呼叫請求傳遞給 Kafka 叢集,最終由呼叫器執行函式。
元件詳解
API 閘道器
API 閘道器是 OpenWhisk 的邊緣元件,根據 NGINX 和 OpenResty 技術構建。它提供了高效能的請求處理能力,並可選用 Redis 進行快取。API 閘道器主要負責:
- 處理 HTTPS 請求並轉發到後端控制器
- 可選用 Redis 快取提升效能
- 提供安全連線(HTTPS)給使用者端
目前使用的 API 閘道器版本是 adobeapiplatform/apigateway:1.1.0,這是由 Adobe 和 IBM 共同開發的版本。
控制器
控制器是 OpenWhisk 的核心元件之一,主要負責管理函式呼叫的流程。它能夠直接提供 HTTP 服務,但通常由 API 閘道器處理 HTTPS 請求。控制器使用 CouchDB 儲存所有相關組態和函式呼叫資料。
資料函式庫(CouchDB)
OpenWhisk 使用 CouchDB 作為其資料儲存方案。CouchDB 是一個高用性的檔案導向資料函式庫,用於儲存函式呼叫的相關資訊,如啟動記錄等。目前使用的 CouchDB 版本是 couchdb:1.6。
Kafka
Kafka 在 OpenWhisk 中扮演著至關重要的角色。它作為訊息代理,負責接收並可靠地傳遞函式呼叫請求到呼叫器。Kafka 以叢集模式執行,由 ZooKeeper 進行協調。本例中使用的是 wurstmeister/kafka:0.11.0.1 映像。
呼叫器
呼叫器負責從 Kafka 主題中接收呼叫請求,並使用後端執行環境(如 Docker 容器)來執行函式。OpenWhisk 支援原生執行環境和 Docker 執行環境,其中 Docker 執行環境被稱為 blackbox。呼叫器需要存取主機的 Docker Socket,以啟動和管理容器。
動作執行環境
OpenWhisk 提供了多種執行環境,包括 Java、Node.js 和 Python 等原生支援,以及根據 Docker 的 blackbox 執行環境。執行環境負責啟動 Docker 容器來處理函式呼叫請求,並可保持容器執行以提升後續呼叫的效能。
使用 Docker 範本準備函式
本文將討論如何使用 OpenWhisk 提供的 Docker Skeleton 範本來準備函式。
Docker Skeleton 範本的使用
OpenWhisk 提供了一個名為 Docker Skeleton 的範本,用於幫助開發者建立自定義的函式執行環境。開發者可以根據此範本建立自己的 Docker 映像,並註冊到 OpenWhisk 中使用。
# 使用官方 Python 映像作為基礎映像
FROM python:3.9-slim
# 設定工作目錄
WORKDIR /app
# 複製 requirements.txt
COPY requirements.txt .
# 安裝相依套件
RUN pip install --no-cache-dir -r requirements.txt
# 複製應用程式碼
COPY . .
# 設定環境變數
ENV PYTHONUNBUFFERED=1
# 暴露函式執行的埠號
EXPOSE 8080
# 定義容器啟動命令
CMD ["python", "main.py"]
程式碼解密:
- 使用官方的 Python 3.9 精簡映像作為基礎,確保映像大小適中且包含必要的 Python 環境。
- 設定
/app為工作目錄,並複製requirements.txt到容器中。 - 安裝
requirements.txt中指定的 Python 相依套件。 - 複製應用程式碼到容器中。
- 設定
PYTHONUNBUFFERED=1環境變數,以確保 Python 輸出不被緩衝。 - 暴露埠號
8080,供外部存取函式服務。 - 定義容器啟動時執行的命令為
python main.py。
隨著無伺服器運算技術的不斷發展,OpenWhisk 將繼續演進以滿足日益增長的效能和可靠性需求。未來可能會出現更多針對特定場景的最佳化方案,例如:
- 更高效的冷啟動機制
- 更強大的自動擴充套件能力
- 更豐富的執行環境支援
這些改進將進一步鞏固 OpenWhisk 在無伺服器運算領域的地位,使其成為更多企業和開發者的首選平台。
OpenWhisk Docker SDK 使用
安裝 Docker 骨架
要安裝 Docker 骨架,通常我們會執行以下命令:
$ wsk -i sdk install docker
如果本地 OpenWhisk 上沒有該檔案,可以直接從 GitHub 下載:
$ curl -sSL -O https://github.com/apache/incubator-openwhisk-runtime-docker/releases/download/sdk%400.1.0/blackbox-0.1.0.tar.gz
接下來,下載 SDK、解壓縮並進入目錄檢查其內容:
$ tar xf blackbox-0.1.0.tar.gz
$ mv dockerSkeleton docker_c
$ cd docker_c
$ ls
buildAndPush.sh Dockerfile example.c README.md
該骨架包含 Dockerfile、簡單的 C 程式、buildAndPush.sh 指令碼以及 README.md 檔案。
C 程式解析
首先檢視 C 程式內容,瞭解其用途。Docker 骨架 SDK 附帶的 C 程式只包含一個 main 函式和幾條 printf 陳述式:
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("This is a log message from an arbitrary C program!\n");
printf("{ \"msg\": \"Hello from C program!\", \"args\": %s }", (argc == 1) ? "undefined" : argv[1]);
}
最後一條 printf 陳述式揭示了 OpenWhisk action 的工作原理:透過列印 JSON 資料到 STDOUT 來傳回資料。action 接受 JSON 形式的引數,透過 main 函式的 argv 傳遞。action 負責解碼引數並編碼輸出結果。
Dockerfile 解析
Dockerfile 首先宣告 openwhisk/dockerskeleton 為基礎映像,接著定義環境變數 FLASK_PROXY_PORT 為 8080。這裡使用了 Flask,一個 Python 網路框架,作為每個 Docker 函式的包裝器。
接下來的兩行將 C 程式新增到建置容器中,安裝 GCC 編譯器並編譯程式。輸出二進位檔案命名為 exec,必須放在 /action/exec。這是 OpenWhisk actionproxy 所需的可執行檔位置。
# Dockerfile for example whisk docker action
FROM openwhisk/dockerskeleton
ENV FLASK_PROXY_PORT 8080
### Add source file(s)
ADD example.c /action/example.c
RUN apk add --no-cache --virtual .build-deps \
bzip2-dev \
gcc \
libc-dev \
### Compile source file(s)
&& cd /action; gcc -o exec example.c \
&& apk del .build-deps
CMD ["/bin/bash", "-c", "cd actionProxy && python -u actionproxy.py"]
建置 Docker 映像
我們將使用 docker build 命令自行建置映像。請記得使用自己的 <DOCKER ID> 作為儲存函式庫名稱,以便將建置的映像推播到 Docker Hub:
$ docker build -t chanwit/whisk_c .
如果一切正確,請使用 docker push 命令將映像儲存到 Hub。
準備 Go 函式
接下來,我們將使用 Go 程式語言撰寫一個函式,展示如何使用 Go 內建函式庫解碼 JSON 引數。當然,我們將修改 OpenWhisk 的 Docker 骨架,新增 Go 編譯器並使用多階段建置來最佳化建置過程。
首先,重新解壓縮 Docker 骨架,並將目錄重新命名為 docker_go:
$ tar xf blackbox-0.1.0.tar.gz
$ mv dockerSkeleton docker_go
$ cd docker_go
在 docker_go 目錄中,我們將撰寫一個 Go 程式來解碼 action 的 JSON 引數,重新排列它們,將它們編碼回 JSON,並將它們寫回呼叫者:
package main
import (
"encoding/json"
"fmt"
"os"
)
func main() {
rawParams := []byte(os.Args[1])
params := map[string]string{}
// decode JSON to a Go map
err := json.Unmarshal(rawParams, ¶ms)
if err != nil {
fmt.Printf(`{"error":%q}`, err.Error())
os.Exit(0)
}
// re-arrange
keys := []string{}
values := []string{}
for k, v := range params {
keys = append(keys, k)
values = append(values, v)
}
result := map[string]interface{}{
"message": "Hello from Go",
"keys": keys,
"values": values,
}
// encode
rawResult, err := json.Marshal(result)
if err != nil {
fmt.Printf(`{"error":%q}`, err.Error())
os.Exit(0)
}
// write JSON back to the caller
fmt.Print(string(rawResult))
}
我們將此程式儲存為 main.go,然後繼續下一步,撰寫 Dockerfile 以進行多階段建置,編譯 Go 程式並將其封裝為 OpenWhisk action。
以下是新的 Dockerfile 版本。第一個建置階段使用 Go 1.9.2 編譯 Go 程式。請注意,我們將其編譯為靜態連結的二進位檔案,以便在 OpenWhisk 基礎映像中獨立執行。在第二個建置階段,我們將第一階段的二進位檔案 /go/src/app/main 複製為 /action/exec,這是 OpenWhisk actionproxy 需要執行的二進位檔案位置。
# Compile the Go program
FROM golang:1.9.2-alpine3.6
WORKDIR /go/src/app
COPY main.go .
#### 內容解密:
此段Dockerfile使用多階段建置,首先在golang:1.9.2-alpine3.6映像中編譯Go程式。
- `WORKDIR /go/src/app` 設定工作目錄。
- `COPY main.go .` 將main.go檔案複製到容器中。
隨著無伺服器運算的興起,OpenWhisk 等平台正變得越來越重要。未來,我們可以預期看到更多針對 OpenWhisk 的工具和框架出現,以簡化開發流程並提高效能。同時,如何更好地整合現有的開發工具和流程,將是另一個重要的發展方向。
圖表說明
graph LR;
A[開始] --> B{檢查Docker環境};
B -->|是| C[下載OpenWhisk Docker SDK];
B -->|否| D[安裝Docker];
C --> E[解壓縮SDK];
E --> F[修改Dockerfile];
F --> G[建置Docker映像];
G --> H[推播映像到Docker Hub];
圖表翻譯: 此圖表呈現了使用OpenWhisk Docker SDK建立自訂Docker映像的流程。首先檢查Docker環境是否存在,若不存在則安裝Docker。接著下載OpenWhisk Docker SDK並解壓縮。然後根據需求修改Dockerfile,接著建置Docker映像,最後將映像推播到Docker Hub。整個流程清晰地展示了從準備環境到最終佈署的步驟。