本文作者Alex將帶我們瞭解了OpenFaaS的最新改進,也就是對無狀態微服務的完整支持,並給出了一個用Ruby和Sinatra編寫的留言簿示例。
目前,我們已經在OpenFaaS 0.9.0中合併併發布了對無狀態微服務的支持。這意味着工程師現在可以利用OpenFaaS中簡單而強大的開發人員體驗,打造出管理你所有FaaS函數和微服務的單一平臺。整套體驗的內容從CLI到Prometheus度量,再到內置的自動擴展都包括在內。甚至縮放到零都得到了支持。本文中,我將引領大家部署一個用Ruby和Sinatra編寫的留言簿示例,這個留言簿由MySQL支持,使用Kubernetes部署到OpenFaaS上。
爲什麼要這樣做?
現代的雲原生微服務和FaaS函數之間有很多重疊部分,我將在下一部分中具體說明。OpenFaaS一直支持在Windows上運行任何容器或進程,無論是FaaS函數、AWS CLI、ImageMagick甚至是PowerShell都可以。社區在短時間內提出了兩個要求,這兩個要求成爲了我們的故事進一步發展的催化劑。
我們在Wireline.io的一位新用戶提出了一項功能請求,希望增強函數的HTTP路由功能。Wireline希望編寫出無需任何額外更改,就可以同時運行在AWS Lambda和OpenFaaS上的函數。大約在同一時間,GitLab的首席執行官Sid Sijbrandi聯繫了我們,希望瞭解更多關於無服務器的信息,並想知道如何在GitLab上利用它。Sid問我,OpenFaaS能否同時用來管理FaaS函數,和他的團隊更熟悉的一些微服務(例如Sinatra應用)。他對在空閒時縮放到零的能力表現出了特別的興趣。
在開始研究示例,展示如何將Sid的請求付諸實踐之前,我們先來看一些背景知識。
什麼是函數?
在瞭解什麼是“無狀態微服務”之前,我們先來看"FaaS函數"的定義。我在2017年1月發表的博客文章:函數即服務(FaaS)中給出了具體定義。
函數往往會:
- 調用短期函數(Lambda有默認的1秒超時設置)
- 不發佈TCP服務——經常訪問第三方服務或資源
- 往往是臨時的/事件驅動的,例如響應Webhooks
- 應該平穩地應對流量高峯
- 不管是什麼名稱,都會運行在由流動的、非確定性的基礎架構支持的服務器上
- 正常工作時會簡化複雜的事物
- 有幾個相關的主題:基礎架構管理、批處理、訪問控制、定義、擴展和依賴關係
從第一篇文章發表以來,我拓展了自己最初的觀察,將函數的定義重寫爲一系列屬性。
函數是:
- 無狀態的
- 短暫的
- 自動擴展的
- 單一用途的
函數是無狀態的,因爲它們不依賴內部存儲器、狀態機、存儲的文件或掛載的卷。根據外部服務,對函數的每次調用應導致相同的最終結果。函數不必嚴格冪等,但在嚴格冪等時更易管理。
函數是短暫的,因爲在任何時候都可以用相同的副本替換它們,而不會影響行爲。這一屬性意味着我們可以用相同的方式管理所有函數——檢查函數的運行狀況、查看它們的生命週期、日誌記錄,乃至監控工作等都可以用同一種方法來處理。
函數能自動從最小副本數擴展到最大副本數,甚至自動降到零並再次擴展上來,以匹配需求或節省資源。
函數是單一用途的,但必須以常識爲準。我們用不着每次在代碼中編寫 function x() {} 或 def x: 時都創建一個新的FaaS函數。函數的單一用途可能是“一條員工記錄的CRUD”“格式化此IBAN”或“使用機器學習模型識別此圖像的內容”。
只要你編寫了至少一個無服務器函數,那麼你就會意識到入口點往往已經從你那裏抽象出來了。你只需編寫一個處理程序——然後所有依賴項都以一種通用格式表示,例如Node.js的package.json或Python的requirements.txt文件。
下面是一個Node.js函數的示例:
"use strict"
module.exports = (context, callback) => {
callback(undefined, {status: "done"});
}
作爲開發人員,你用不着瞭解用於引導處理程序的機制——這是無聊的重複性細節,你可以讓FaaS框架或工具包來操心這些事情。
我們看到了類似的抽象模式,也看到了諸如Sinatra、Django和Express.js之類的微服務框架中隱藏無聊細節的現象。一種尺寸很少能滿足所有需求,因此每種語言或運行時都有多種選擇。
什麼是無狀態微服務?
無狀態微服務是一種微服務,它可以像FaaS函數一樣進行部署,並可以通過FaaS框架或OpenFaaS這樣的平臺來管理。因此,在OpenFaaS CLI、Gateway API或UI中不需要特殊的路由(route)、標誌(flag)或過濾器(filter)。
就OpenFaaS組件而言,函數可以用任何語言編寫並打包在Docker映像中。它必須通過8080端口上的HTTP提供內容,並將鎖定文件寫入/tmp/.lock。如果你的函數或服務檢測出自身的異常,那麼你可以刪除鎖定文件,OpenFaaS將重新啓動/重新安排你的函數。
OpenFaaS有一個Ruby語言模板,可用於創建Ruby FaaS函數。Ruby無狀態微服務是使用https://rubyonrails.org/](https://rubyonrails.org/">Ruby on Rails(、Sinatra或其他Ruby微服務框架創建的Ruby微服務。其中的主要區別在於,與FaaS函數相比你要做的工作更多了。現在,你必須管理自己的Dockerfile、狀況檢查和路由。
Sinatra是Ruby的DSL或框架,用於快速構建微服務。
下面是官方網站上的hello-world示例:
require 'sinatra'
get '/frank-says' do
'Put this in your pipe & smoke it!'
end
如果我們將該文件另存爲main.rb並運行gem install sinatra,然後運行ruby main.rb,則將在默認端口5678上啓動一個Web服務器,然後可以跳轉至URL:http://127.0.0.1:5678/frank-says上。
OpenFaaS CLI可以模板化、構建和部署這個微服務。然後,OpenFaaS平臺將跟蹤這個微服務的調用指標,並將其自動放大、縮小甚至降爲零並再次放大。
創建Sinatra無狀態微服務
下面我們來使用Sinatra創建一個無狀態微服務。
你首先需要準備一些工具:
- 適用於Mac/Linux/Windows的Docker
- 一個Docker Hub帳戶,或其他Docker倉庫的帳戶
- OpenFaaS和CLI——可選Kubernetes或Swarm
創建一個Hello World服務
首先創建一個新文件夾並生成一個dockerfile函數。其中dockerfile模板會告訴OpenFaaS CLI,在不應用其他任何腳手架或模板的情況下運行Docker構建,你必須提供自己的Dockerfile。
$ mkdir -p sinatra-for-openfaas/ \
&& cd sinatra-for-openfaas/
$ faas-cli new --prefix=alexellis2 --lang dockerfile frank-says
用你的Docker Hub帳戶或另一個Docker倉庫替換alexellis2。一個Docker映像將被推送到這裏,這是build/faas-cli up命令的一部分。
這將創建兩個文件,就像你用faas-cli new上列出的一種語言創建函數時一樣:
./frank-says/Dockerfile
./frank-says.yml
創建一個Gemfile和main.rb文件:
./frank-says/main.rb:
require 'sinatra'
set :port, 8080
set :bind, '0.0.0.0'
open('/tmp/.lock', 'w') { |f|
f.puts "Service started"
}
get '/' do
'Frank has entered the building'
end
get '/logout' do
'Frank has left the building'
end
有關OpenFaaS負載的注意事項:
- 它們必須綁定到TCP端口8080
- 準備接收流量時,它們必須寫一個文件/tmp/.lock。
./frank-says/Gemfile:
source 'https://rubygems.org'
gem "sinatra"
可以在此文件中添加任意gems列表。
現在將./frank-says/Dockerfile替換爲:
FROM ruby:2.4-alpine3.6
WORKDIR /home/app
COPY . .
RUN bundle install
RUN addgroup -S app \
&& adduser app -S -G app
RUN chown app:app -R /home/app
WORKDIR /home/app
HEALTHCHECK --interval=5s CMD [ -e /tmp/.lock ] || exit 1
USER app
CMD ["ruby", "main.rb"]
Dockerfile執行以下操作:
- 添加非root用戶
- 添加Ruby源代碼和Gemfile,然後安裝sinatra gem
- 添加一個間隔爲5秒的健康檢查
- 設置啓動命令
部署示例
現在你已經做好了準備,可以使用OpenFaaS CLI構建和部署示例了。
- 使用你的帳戶詳細信息登錄
$ docker login
- 運行up命令,該命令是build、push和deploy的別名。
$ faas-cli up --yaml frank-says.yml
Deploying: frank-says.
Deployed. 200 OK.
URL: http://127.0.0.1:8080/function/frank-says
使用curl調用你的微服務,或在Web瀏覽器中查看它:
$ curl http://127.0.0.1:8080/function/frank-says/
Frank has entered the building.
你也可以嘗試自定義路徑:
$ curl http://127.0.0.1:8080/function/frank-says/logout
Frank has left the building.
你可以嘗試更新消息或添加其他路由,然後再次運行faas-cli以重新部署微服務。
現在檢查faas-cli list,能看到每次訪問微服務時調用計數會隨之增加。
$ faas-cli list
Function Invocations Replicas
frank-says 5 1
觸發自動擴展
現在,我們可以使用簡單的bash for循環來觸發自動擴展:
$ for i in {1..10000}
do
sleep 0.01 \
&& curl http://127.0.0.1:8080/function/frank-says \
&& echo
done
在另一個窗口中輸入:watch faas-cli list或者定期運行faas-cli list 。自動擴展生效後,你應該能看到Invocations的值增加,並且Replicas值也會增加。
Function Invocations Replicas
frank-says 702 4
當bash for循環完成或使用Ctrl + C取消它時,你會看到副本數減少到1。
你也可以在以下網址使用OpenFaaS UI監視和調用微服務:http://127.0.0.1:8080
部署帶有MySQL的Sinatra留言簿
$ git clone https://github.com/openfaas-incubator/openfaas-sinatra-guestbook \
&& cd openfaas-sinatra-guestbook
在./sql.yml中配置你的MySQL數據庫細節。如果你還沒有MySQL,那麼只需花幾分鐘使用Kubernetes上的helm或docker run,以及官方Docker映像就能部署完畢。
$ cp sql.example.yml sql.yml
最後部署留言簿:
$ faas-cli up
http://127.0.0.1:8080/function/guestbook
使用上面命令提供的URL來訪問微服務。
在UI中登錄留言簿,完成後,你可以隨時發佈到/function/guestbook/reset來重置MySQL表。
留言簿代碼的狀態存儲在MySQL表單中,這意味着它可以隨時重新啓動而不會丟失數據。這是FaaS函數和無狀態微服務的一個關鍵屬性。如果OpenFaaS添加了我們代碼的其他副本——每個副本都將具有相同的環境視圖,因爲它依賴於外部數據庫來獲取其數據。
啓用零縮放
要啓用縮放到零的功能,只需按照文檔說明啓用faas-idler。
然後在你的stack.yml文件中添加一個標籤,以告知OpenFaaS你的函數可以進行零縮放:
labels:
com.openfaas.scale.zero: true
最後,通過faas-cli up重新部署留言簿。一旦檢測到空閒狀態,faas-idler現在會將你的函數縮放到零副本。默認的空閒時間設置爲5分鐘,可以在部署時配置。
回到Sid最初的問題,我們已經部署了一個用Ruby編寫的無狀態微服務,該服務將在空閒時縮放到零,還能及時擴展以服務流量。管理它的方式與我們現有的FaaS函數完全相同,這意味着你可以專注於構建重要的內容,而不必操心Kubernetes或Docker Swarm的內部細節。
總結
現在,我們已經使用MySQL、ebs視圖、Bootstrap和Sinatra部署了一個簡單的hello-world Sinatra服務和一個更完整的留言簿示例。接下來你就可以開始使用OpenFaaS簡化開發人員工作流程了——你可以使用FaaS函數,或者簡化微服務的管理工作。
作者介紹:
Alex Ellis([email protected])是@openfaas的創始人。