夏日清風 - 基於Docker Swarm的極簡Serverless實踐
在今年4月份的DockerCon壓軸的 Moby's Cool Hack Session上,Alex Ellis給大家展現了一個名為Function as a Service (FaaS)項目。FaaS基於Docker Swarm集群上實現了一個極簡的Serverless框架,支持將任意Unix進程作為函數實現來對外提供服務。
FaaS 架構
在 FaaS 原型係統中
- 任何進程都可以轉化成為一個函數,並利用Docker鏡像進行打包和交付
- 利用 Docker Swarm 集群的資源調度和routing mesh的負載均衡能力簡潔地實現了函數的調度能力。其中每個函數對應一個Docker集群中的服務
- 基於 Prometheus 實現函數調用監控和自動伸縮
其設計架構非常簡單,其中
- API Gateway 負責接受服務調用,路由請求到後端函數實現,並采集服務調用的指標發送給 Prometheus。 Prometheus 則會根據一段時間內服務調用的次數,回調API Gateway 來動態伸縮服務容器實例數量。
- Function Watchdog 將HTTP請求轉發為進程調用,並將請求數據通過 STDIN 傳遞給進程,而將進程的 STDOUT 作為 HTTP 響應的結果返回給調用者。將函數進程和Function Watchdog打包成一個容器鏡像進行部署。其調用流程如下:
FaaS 在本地部署非常簡單
首先你需要準備一個本地的Docker Swarm集群,如果沒有,可以安裝最新Docker Engine並執行下麵命令:
docker swarm init
執行如下命令來部署FaaS
git clone https://github.com/alexellis/faas
cd faas
./deploy_stack.sh
在部署完成之後,我們可以通過如下命令檢查FaaS的狀態
$ docker stack services func
ID NAME MODE REPLICAS IMAGE
1a8b2tb19ulk func_gateway replicated 1/1 functions/gateway:0.5.6
4jdexem6kppg func_webhookstash replicated 1/1 functions/webhookstash:latest
9ju4er5jur9l func_wordcount replicated 1/1 functions/alpine:health
e190suippx7i func_markdown replicated 1/1 alexellis2/faas-markdownrender:latest
l70j4c7kf99t func_alertmanager replicated 1/1 functions/alertmanager:latest
mgujgoa2u8f3 func_decodebase64 replicated 1/1 functions/alpine:health
o44asbnhqbda func_hubstats replicated 1/1 alexellis2/faas-dockerhubstats:latest
q8rx49ow3may func_echoit replicated 1/1 functions/alpine:health
t1ao5psnsj0s func_base64 replicated 1/1 functions/alpine:health
vj5z7rpdlo48 func_prometheus replicated 1/1 functions/prometheus:latest
xmwzd4z7l4dv func_nodeinfo replicated 1/1 functions/nodeinfo:latest
隨後通過瀏覽器來訪問 https://127.0.0.1:8080/ui FaaS
整個流程非常簡單,就像夏日的清風,讓人感到自然愉悅。
在阿裏雲上測試FaaS
由於FaaS是基於Docker Swarm mode集群進行部署的,你首先需要在阿裏雲容器服務創建一個Swarm mode集群
然後利用如下模板來部署應用
version: "3"
services:
# Core API services are pinned, HA is provided for functions.
gateway:
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
ports:
- 8080:8080
labels:
aliyun.routing.port_8080: faas
image: functions/gateway:0.5.6
networks:
- functions
environment:
dnsrr: "true" # Temporarily use dnsrr in place of VIP while issue persists on PWD
deploy:
placement:
constraints: [node.role == manager]
prometheus:
image: functions/prometheus:latest # autobuild from Dockerfile in repo.
command: "-config.file=/etc/prometheus/prometheus.yml -storage.local.path=/prometheus -storage.local.memory-chunks=10000 --alertmanager.url=https://alertmanager:9093"
ports:
- 9090:9090
depends_on:
- gateway
- alertmanager
labels:
aliyun.routing.port_9090: prometheus
environment:
no_proxy: "gateway"
networks:
- functions
deploy:
placement:
constraints: [node.role == manager]
alertmanager:
image: functions/alertmanager:latest # autobuild from Dockerfile in repo.
environment:
no_proxy: "gateway"
command:
- '-config.file=/alertmanager.yml'
networks:
- functions
ports:
- 9093:9093
deploy:
placement:
constraints: [node.role == manager]
# Sample functions go here.
# Service label of "function" allows functions to show up in UI on https://gateway:8080/
webhookstash:
image: functions/webhookstash:latest
labels:
function: "true"
depends_on:
- gateway
networks:
- functions
environment:
no_proxy: "gateway"
https_proxy: $https_proxy
# Pass a username as an argument to find how many images user has pushed to Docker Hub.
hubstats:
image: alexellis2/faas-dockerhubstats:latest
labels:
function: "true"
depends_on:
- gateway
networks:
- functions
environment:
no_proxy: "gateway"
https_proxy: $https_proxy
# Node.js gives OS info about the node (Host)
nodeinfo:
image: functions/nodeinfo:latest
labels:
function: "true"
depends_on:
- gateway
networks:
- functions
environment:
no_proxy: "gateway"
https_proxy: $https_proxy
# Uses `cat` to echo back response, fastest function to execute.
echoit:
image: functions/alpine:health
labels:
function: "true"
depends_on:
- gateway
networks:
- functions
environment:
fprocess: "cat"
no_proxy: "gateway"
https_proxy: $https_proxy
# Counts words in request with `wc` utility
wordcount:
image: functions/alpine:health
labels:
function: "true"
com.faas.max_replicas: "10"
depends_on:
- gateway
networks:
- functions
environment:
fprocess: "wc"
no_proxy: "gateway"
https_proxy: $https_proxy
# Calculates base64 representation of request body.
base64:
image: functions/alpine:health
labels:
function: "true"
depends_on:
- gateway
networks:
- functions
environment:
fprocess: "base64"
no_proxy: "gateway"
https_proxy: $https_proxy
# Decodes base64 representation of request body.
decodebase64:
image: functions/alpine:health
labels:
function: "true"
depends_on:
- gateway
networks:
- functions
environment:
fprocess: "base64 -d"
no_proxy: "gateway"
https_proxy: $https_proxy
# Converts body in (markdown format) -> (html)
markdown:
image: alexellis2/faas-markdownrender:latest
labels:
function: "true"
depends_on:
- gateway
networks:
- functions
environment:
no_proxy: "gateway"
https_proxy: $https_proxy
networks:
functions:
driver: overlay
和本地部署相比隻是增加了兩個 label,定義了API Gatway和Prometheus的路由
- "aliyun.routing.port_8080: faas" : API Gatway的虛擬域名
- "aliyun.routing.port_9090: prometheus" : prometheus服務的虛擬域名
然後基於上麵模板創建應用,
注:這裏的應用名為 “faas_default”,部署完成之後所有函數服務和訪問的名空間都基於這個名稱。
部署完成之後,我們可以看見相應的服務列表
選擇路由列表標簽,我們可以看到之前定義的路由地址已經出現在列表中
可以點擊連接上麵連接訪問FaaS的API Gateway和Prometheus服務界麵
下麵我們來進行一個測試來驗證服務的伸縮性,首先,我們參照文檔將Prometheus的URL修改為
https://<prometheus-endpoint>/graph?g0.range_input=15m&g0.expr=gateway_service_count&g0.tab=0&g1.range_input=15m&g1.expr=rate(gateway_function_invocation_total%5B20s%5D)&g1.tab=0&g2.range_input=15m&g2.expr=gateway_functions_seconds_sum+%2F+gateway_functions_seconds_count&g2.tab=0
注:其中URL的prometheus-endpoint需要替換為上文中端點地址
然後在本地運行如下命令
while [ true ] ; do curl -X POST https://<faas-endpoint>/function/faas-default_echoit -d 'Hello, Function as a Service'; done
注:命令中路徑需要替換faas-endpoint,如果服務名稱與faas-default_echoit不同,也請自行調整。
在Prometheus界麵可以看到服務調用量的變化
在容器服務的應用服務列表界麵,可以看到,faas-default_echoit的容器實例從1個擴容到20個。
當結束測試之後,服務實例也會縮容到一個
如果對創建自己的函數感興趣,可以參考 Alex 的博客,本文不再贅述
https://blog.alexellis.io/build-and-deploy-with-faas/
總結
FaaS基於Docker Swarm集群技術,實現了一個極簡的Serverless框架,支持將任意Unix進程作為函數來對外提供服務。FaaS目前還是一個原型係統,如果大家需要完備的Function as a Service能力,體驗無服務器運維,還是建議采用阿裏雲 FunctionCompute服務。
FaaS框架展示了一些有趣的可能性
- 將現有程序封裝成為函數,可以作為服務方便地集成到應用業務邏輯中。將函數計算和現有微服務架構應用有機結合在一起。
- 基於Docker和Swarm集群技術,部署運維非常簡單。可以在任何地方部署,甚至是ARM設備上。下圖來自RICHARD GEE,演示了在樹莓派集群上運行FaaS。
最後更新:2017-07-23 20:39:40