'最佳實踐:讓 Serverless 架構拯救大數據'

"

如果你聽說過 Serverless 的話,你可能會把它當做一種雲架構模式,可以將一個應用程序所需要的、長期維護的基礎設施數量降到比較低的水平。在某些場景下,這種方式可以節省很多成本。而且也確是是這樣的。但是在這篇文章裡面,我會在一個新的場景下,介紹相關的應用程序:高度並行的函數計算程序和生產環境下的機器學習系統。

如果你只是一個數據科學家的話,你可能不太瞭解什麼是 serverless。我們將會以 serverless 最常用的應用場景—— Web 業務內容作為開篇。不過值得注意的是,如果你受限於計算資源的話,serverless 是一個輕量級的替代方案,可以幫助你搭建類似於 Spark 集群一類的內容。在沒有運維團隊的幫助下,就可以很輕鬆的將你的簡單示例發佈到生產環境上面。

Serverless 的爭議

大多數的數據科學家對運維都不是很感興趣,但這也是一個好的場景,能顯示出 serverless 的與眾不同,請耐心的聽我說。

人們普遍認為計算機的這種理念並不是很好。

雲環境裡最棒的一件事情就是,你不需要關心硬件相關的問題。你只需要付費給微軟、谷歌、亞馬遜一類的雲平臺,讓他們幫助你來處理。他們在這方面做的真的很好,以至於讓你忘記還有硬件這件事。

但向雲端遷移會帶來成本的變化。最基本一項就是金錢。主要是因為,儘管我們利用了雲環境的伸縮與拓展的能力,但我們還是不可避免地為一些未被充分利用的雲資源,進行長期的付費。

雖然雲成功地將硬件抽象化,我們還是需要安裝、配置、安全認證,和維護操作系統或數據庫等等。對於我們中的許多人來說,這是一種恥辱,因為這並不是我們想花費時間去做的事。對於大多數的僱主來說,他們並不能從中獲得收益,因為他們主要還是通過部署業務邏輯來盈利。

"

如果你聽說過 Serverless 的話,你可能會把它當做一種雲架構模式,可以將一個應用程序所需要的、長期維護的基礎設施數量降到比較低的水平。在某些場景下,這種方式可以節省很多成本。而且也確是是這樣的。但是在這篇文章裡面,我會在一個新的場景下,介紹相關的應用程序:高度並行的函數計算程序和生產環境下的機器學習系統。

如果你只是一個數據科學家的話,你可能不太瞭解什麼是 serverless。我們將會以 serverless 最常用的應用場景—— Web 業務內容作為開篇。不過值得注意的是,如果你受限於計算資源的話,serverless 是一個輕量級的替代方案,可以幫助你搭建類似於 Spark 集群一類的內容。在沒有運維團隊的幫助下,就可以很輕鬆的將你的簡單示例發佈到生產環境上面。

Serverless 的爭議

大多數的數據科學家對運維都不是很感興趣,但這也是一個好的場景,能顯示出 serverless 的與眾不同,請耐心的聽我說。

人們普遍認為計算機的這種理念並不是很好。

雲環境裡最棒的一件事情就是,你不需要關心硬件相關的問題。你只需要付費給微軟、谷歌、亞馬遜一類的雲平臺,讓他們幫助你來處理。他們在這方面做的真的很好,以至於讓你忘記還有硬件這件事。

但向雲端遷移會帶來成本的變化。最基本一項就是金錢。主要是因為,儘管我們利用了雲環境的伸縮與拓展的能力,但我們還是不可避免地為一些未被充分利用的雲資源,進行長期的付費。

雖然雲成功地將硬件抽象化,我們還是需要安裝、配置、安全認證,和維護操作系統或數據庫等等。對於我們中的許多人來說,這是一種恥辱,因為這並不是我們想花費時間去做的事。對於大多數的僱主來說,他們並不能從中獲得收益,因為他們主要還是通過部署業務邏輯來盈利。

最佳實踐:讓 Serverless 架構拯救大數據


那麼,怎麼樣既能擺脫物理設施的束縛,又能忽視下一層——例如操作系統或者容器相關的內容?如果能做到這一點的話,我們就可以全身心地專注於業務邏輯。

這就是 serverless 的目標。

什麼是 serverless

我們來考慮部署一個簡單的 Serverless 程序,實現兩數相加的邏輯。首先,請求會訪問到某一個網關服務中。這個例子中的請求的內容為 2+2。實際上,網關會傳輸數據到某一臺機器,並把該應用部署到那臺機器上。新機器做出響應後返回結果,然後停止退出。

另一個請求(37+5)過來的時候,也會發生同樣的事情:網關會創建一個全新的機器(並不會複用使用上一個實例的機器),該機器會在運算後就停止退出。至關重要的是,兩數相加的應用程序,並沒有實際的基礎設備。

"

如果你聽說過 Serverless 的話,你可能會把它當做一種雲架構模式,可以將一個應用程序所需要的、長期維護的基礎設施數量降到比較低的水平。在某些場景下,這種方式可以節省很多成本。而且也確是是這樣的。但是在這篇文章裡面,我會在一個新的場景下,介紹相關的應用程序:高度並行的函數計算程序和生產環境下的機器學習系統。

如果你只是一個數據科學家的話,你可能不太瞭解什麼是 serverless。我們將會以 serverless 最常用的應用場景—— Web 業務內容作為開篇。不過值得注意的是,如果你受限於計算資源的話,serverless 是一個輕量級的替代方案,可以幫助你搭建類似於 Spark 集群一類的內容。在沒有運維團隊的幫助下,就可以很輕鬆的將你的簡單示例發佈到生產環境上面。

Serverless 的爭議

大多數的數據科學家對運維都不是很感興趣,但這也是一個好的場景,能顯示出 serverless 的與眾不同,請耐心的聽我說。

人們普遍認為計算機的這種理念並不是很好。

雲環境裡最棒的一件事情就是,你不需要關心硬件相關的問題。你只需要付費給微軟、谷歌、亞馬遜一類的雲平臺,讓他們幫助你來處理。他們在這方面做的真的很好,以至於讓你忘記還有硬件這件事。

但向雲端遷移會帶來成本的變化。最基本一項就是金錢。主要是因為,儘管我們利用了雲環境的伸縮與拓展的能力,但我們還是不可避免地為一些未被充分利用的雲資源,進行長期的付費。

雖然雲成功地將硬件抽象化,我們還是需要安裝、配置、安全認證,和維護操作系統或數據庫等等。對於我們中的許多人來說,這是一種恥辱,因為這並不是我們想花費時間去做的事。對於大多數的僱主來說,他們並不能從中獲得收益,因為他們主要還是通過部署業務邏輯來盈利。

最佳實踐:讓 Serverless 架構拯救大數據


那麼,怎麼樣既能擺脫物理設施的束縛,又能忽視下一層——例如操作系統或者容器相關的內容?如果能做到這一點的話,我們就可以全身心地專注於業務邏輯。

這就是 serverless 的目標。

什麼是 serverless

我們來考慮部署一個簡單的 Serverless 程序,實現兩數相加的邏輯。首先,請求會訪問到某一個網關服務中。這個例子中的請求的內容為 2+2。實際上,網關會傳輸數據到某一臺機器,並把該應用部署到那臺機器上。新機器做出響應後返回結果,然後停止退出。

另一個請求(37+5)過來的時候,也會發生同樣的事情:網關會創建一個全新的機器(並不會複用使用上一個實例的機器),該機器會在運算後就停止退出。至關重要的是,兩數相加的應用程序,並沒有實際的基礎設備。

最佳實踐:讓 Serverless 架構拯救大數據


通常來說,你可以理解為基礎設施的數量與利用率成線性關係,而且截距為 0(這有助你讓你理解為什麼 Serverless 的這個很便宜)

這種描述有點與眾不同。的確也省略了一些工程上的細節內容,不過作為 Serverless 的新用戶而言,很大程度上可以忽視掉這些內容。

順便提一下,“Serverless”這個單詞並不是一個很好地名稱,理論上你還是有服務器的。不過實際上,你的服務器數量是無窮多個(從某種意義上來說,有點像非參數統計,這裡非參數其實是指你有非常多的參數,並不是傳統意義上的“非”。不過實際上而言,你並沒有)。

用 AWS Lambda 和 Zappa 進行 serverless 部署

總的來說,為了使 Serveless 的利益最大化(例如,讓別人來幫你進行運維的工作),你需要為使用 Serverless 平臺支付給雲平臺一定的費用。你可以在 AWS Lambda,Google Cloud Fuctions 和 Azure Functions 裡進行選擇。這些服務會創建、銷燬並以其他方式來管理大型的短生命週期機器池(像以上我們用於計算 2+2 和 35+7 那樣)。

為了演示在 AWS Lambda 上的部署,讓我們用一個簡單的 Python Flask 應用程序來顯示當前時間:

# app.py
import datetime
from flask import Flask
app = Flask(__name__)

@app.route("/time")
def time():
return str(datetime.datetime.now())

if __name__ == "__main__":
app.run()

如果你之前沒有了解過 Flask, 這裡有模板可供你參考,但重點是 time 函數(返回時間)以及它前面的裝飾器。如果有人訪問 /time 路徑,它會告訴 Flask 返回函數的結果。

你可以通過運行 python app.py 在本地“部署”這個應用:

$ python app.py
* Serving Flask app "app" (lazy loading)
* Environment: production
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

當你訪問 127.0.0.1:5000/time 的時候,你將看到當前時間:

 $ curl 127.0.0.1:5000/time
2019-05-09 21:09:46.450550

如果筆記本電腦被合理地設置了防火牆,偶爾會進入睡眠狀態,那麼在本地長時間部署這個應用就不是很合適。現在我們把它部署到 AWS Lambda 上面。

這會涉及到大量的手動的工作,不過好消息是可以通過使用命令行工具來幫助我們完成任務。我推薦Zappa . 之所以喜歡它,是因為它是用 Python 寫的,至少在原則上,它不會關注你往哪個 serverless 平臺上發佈。至於其它的工具,我瞭解的,是名字很容易混淆的 Serverless 和 AWS 的 Chalice .

以下是如何使用 Zappa 發佈上面的 time 應用。首先你要創建並激活一個新的虛擬環境,然後在裡面安裝相關的依賴庫。像上面的例子,就需要安裝 Flask 和 Zappa。 在相同的路徑下,複製上面的 app.py 文件,然後運行 zappa init:

python -m venv venv
source venv/bin/activate
pip install flask zappa
zappa init

zappa init 會快速引導你處理一些相關的問題。這個簡單的小例子中,你接受默認的設置就可以了。以下是生成的配置文件:

$ cat zappa_settings.json
{
"dev": {
"app_function": "app.app",
"aws_region": "us-east-1",
"profile_name": "default",
"project_name": "basic",
"runtime": "python3.6",
"s3_bucket": "zappa-ycr5dtiyk"
}
}

然後執行 zappa deploy, 這時 Zappa 的作用就顯現出來了:

$ zappa deploy
Calling deploy for stage dev..
Downloading and installing dependencies..
- sqlite==python36: Using precompiled lambda package
Packaging project as zip.
Uploading basic-dev-1534393616.zip (6.2MiB)..
100%|██████████████████████████████| 6.54M/6.54M [00:05<00:00, 902KB/s]
Scheduling..
Scheduled basic-dev-zappa-keep-warm-handler.keep_warm_callback with expression rate(4 minutes)!
Uploading basic-dev-template-1534393629.json (1.6KiB)..
100%|█████████████████████████████| 1.59K/1.59K [00:00<00:00, 3.29KB/s]
Waiting for stack basic-dev to create (this can take a bit)..
100%|███████████████████████████████████| 4/4 [00:09<00:00, 4.89s/res]
Deploying API Gateway..
Deployment complete!: https://ucyxlhvz7f.execute-api.us-east-1.amazonaws.com/dev

他幫助我們處理了很多複雜的內容。最值得注意的是,它創建了一個包含應用程序和其依賴項(默認情況下是根據虛擬環境的安裝內容來進行的)Zip 文件,並將其複製到 AWS 上面。在輸出內容的最後,你會發現一個 URL,你可以用它來替換之前使用的 localhost。

該 URL 不會長期有效(因為我運行了 zappa undeploy), 但請相信我,此刻的結果是這樣的:

$ curl https://ucyxlhvz7f.execute-api.us-east-1.amazonaws.com/dev/time
2019-05-10 04:30:59.270893

大體上,對 URL 進行請求和響應的全過程如下:

  • 一個計算機憑空出現了。
  • 跟 Zappa 一起被上傳的應用程序和依賴項會被部署到這臺新的計算機上。
  • 新計算機接受請求。
  • 計算並返回響應。
  • 該計算機被毀掉。

每次訪問這個 URL 的時候都會發生同樣的事情。(實際上,AWS 會對實例進行緩存,來保證請求可以正常響應。但從概念上說,每個請求都會創建新的實例,並在執行結束後銷燬。)

關鍵的一點是,這個相當複雜的過程意味著,你的應用程序正處於生產環境中,是可以公開訪問的,而且在不使用她的時候,你就不用支付任何費用。

我已經把這篇文章中所有的代碼都放在 Github 倉庫裡了。其中我也演示瞭如何升級已發佈的應用程序,傳遞時區作為 URL 的參數。Zappa 讓升級變得很容易(通過執行 zappa update 命令即可),並提供了很方便的 zappa tail 命令,在你的筆記本終端,從已發佈的應用程序組件中整合日誌和錯誤信息。如果你部署出現了問題或者正在運行的應用拋出異常(例如,用戶提供了無效參數),調試會更容易些。一般情況下,你甚至可以使用 serverless(尤其是帶有 AWS Lambda 的 Zappa)配置 cron 任務,使其在 serverless 執行器上運行。

Serverless 的優勢與限制

serverless 的普遍優點在於,它能讓你更專注於業務邏輯,並且在部署時有無限的可擴展性。另一方面,一些人認為 serverless 與微服務架構(例如,複雜性方面)以及雲部署(例如,供應商對資源鎖定的風險)有著相同的缺點。個人工程師購買的範圍各異,當然這跟我們這篇文章中的數據科學部分(即將要討論的)並沒有太大的關係,所以不想在這做過多解釋。

對於數據科學用例來說,serverless 最明顯的優勢就是成本:因為我們只在代碼運行的時候付費,沒有未使用資源浪費的問題,支付的費用與使用量成正比,往往很便宜。比如說,Postlight 的 Readability API 每個月在 EC2 上的費用是 10000 美元,轉移到 AWS Lambda 後,現在每月費用僅為 370 美元。

"

如果你聽說過 Serverless 的話,你可能會把它當做一種雲架構模式,可以將一個應用程序所需要的、長期維護的基礎設施數量降到比較低的水平。在某些場景下,這種方式可以節省很多成本。而且也確是是這樣的。但是在這篇文章裡面,我會在一個新的場景下,介紹相關的應用程序:高度並行的函數計算程序和生產環境下的機器學習系統。

如果你只是一個數據科學家的話,你可能不太瞭解什麼是 serverless。我們將會以 serverless 最常用的應用場景—— Web 業務內容作為開篇。不過值得注意的是,如果你受限於計算資源的話,serverless 是一個輕量級的替代方案,可以幫助你搭建類似於 Spark 集群一類的內容。在沒有運維團隊的幫助下,就可以很輕鬆的將你的簡單示例發佈到生產環境上面。

Serverless 的爭議

大多數的數據科學家對運維都不是很感興趣,但這也是一個好的場景,能顯示出 serverless 的與眾不同,請耐心的聽我說。

人們普遍認為計算機的這種理念並不是很好。

雲環境裡最棒的一件事情就是,你不需要關心硬件相關的問題。你只需要付費給微軟、谷歌、亞馬遜一類的雲平臺,讓他們幫助你來處理。他們在這方面做的真的很好,以至於讓你忘記還有硬件這件事。

但向雲端遷移會帶來成本的變化。最基本一項就是金錢。主要是因為,儘管我們利用了雲環境的伸縮與拓展的能力,但我們還是不可避免地為一些未被充分利用的雲資源,進行長期的付費。

雖然雲成功地將硬件抽象化,我們還是需要安裝、配置、安全認證,和維護操作系統或數據庫等等。對於我們中的許多人來說,這是一種恥辱,因為這並不是我們想花費時間去做的事。對於大多數的僱主來說,他們並不能從中獲得收益,因為他們主要還是通過部署業務邏輯來盈利。

最佳實踐:讓 Serverless 架構拯救大數據


那麼,怎麼樣既能擺脫物理設施的束縛,又能忽視下一層——例如操作系統或者容器相關的內容?如果能做到這一點的話,我們就可以全身心地專注於業務邏輯。

這就是 serverless 的目標。

什麼是 serverless

我們來考慮部署一個簡單的 Serverless 程序,實現兩數相加的邏輯。首先,請求會訪問到某一個網關服務中。這個例子中的請求的內容為 2+2。實際上,網關會傳輸數據到某一臺機器,並把該應用部署到那臺機器上。新機器做出響應後返回結果,然後停止退出。

另一個請求(37+5)過來的時候,也會發生同樣的事情:網關會創建一個全新的機器(並不會複用使用上一個實例的機器),該機器會在運算後就停止退出。至關重要的是,兩數相加的應用程序,並沒有實際的基礎設備。

最佳實踐:讓 Serverless 架構拯救大數據


通常來說,你可以理解為基礎設施的數量與利用率成線性關係,而且截距為 0(這有助你讓你理解為什麼 Serverless 的這個很便宜)

這種描述有點與眾不同。的確也省略了一些工程上的細節內容,不過作為 Serverless 的新用戶而言,很大程度上可以忽視掉這些內容。

順便提一下,“Serverless”這個單詞並不是一個很好地名稱,理論上你還是有服務器的。不過實際上,你的服務器數量是無窮多個(從某種意義上來說,有點像非參數統計,這裡非參數其實是指你有非常多的參數,並不是傳統意義上的“非”。不過實際上而言,你並沒有)。

用 AWS Lambda 和 Zappa 進行 serverless 部署

總的來說,為了使 Serveless 的利益最大化(例如,讓別人來幫你進行運維的工作),你需要為使用 Serverless 平臺支付給雲平臺一定的費用。你可以在 AWS Lambda,Google Cloud Fuctions 和 Azure Functions 裡進行選擇。這些服務會創建、銷燬並以其他方式來管理大型的短生命週期機器池(像以上我們用於計算 2+2 和 35+7 那樣)。

為了演示在 AWS Lambda 上的部署,讓我們用一個簡單的 Python Flask 應用程序來顯示當前時間:

# app.py
import datetime
from flask import Flask
app = Flask(__name__)

@app.route("/time")
def time():
return str(datetime.datetime.now())

if __name__ == "__main__":
app.run()

如果你之前沒有了解過 Flask, 這裡有模板可供你參考,但重點是 time 函數(返回時間)以及它前面的裝飾器。如果有人訪問 /time 路徑,它會告訴 Flask 返回函數的結果。

你可以通過運行 python app.py 在本地“部署”這個應用:

$ python app.py
* Serving Flask app "app" (lazy loading)
* Environment: production
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

當你訪問 127.0.0.1:5000/time 的時候,你將看到當前時間:

 $ curl 127.0.0.1:5000/time
2019-05-09 21:09:46.450550

如果筆記本電腦被合理地設置了防火牆,偶爾會進入睡眠狀態,那麼在本地長時間部署這個應用就不是很合適。現在我們把它部署到 AWS Lambda 上面。

這會涉及到大量的手動的工作,不過好消息是可以通過使用命令行工具來幫助我們完成任務。我推薦Zappa . 之所以喜歡它,是因為它是用 Python 寫的,至少在原則上,它不會關注你往哪個 serverless 平臺上發佈。至於其它的工具,我瞭解的,是名字很容易混淆的 Serverless 和 AWS 的 Chalice .

以下是如何使用 Zappa 發佈上面的 time 應用。首先你要創建並激活一個新的虛擬環境,然後在裡面安裝相關的依賴庫。像上面的例子,就需要安裝 Flask 和 Zappa。 在相同的路徑下,複製上面的 app.py 文件,然後運行 zappa init:

python -m venv venv
source venv/bin/activate
pip install flask zappa
zappa init

zappa init 會快速引導你處理一些相關的問題。這個簡單的小例子中,你接受默認的設置就可以了。以下是生成的配置文件:

$ cat zappa_settings.json
{
"dev": {
"app_function": "app.app",
"aws_region": "us-east-1",
"profile_name": "default",
"project_name": "basic",
"runtime": "python3.6",
"s3_bucket": "zappa-ycr5dtiyk"
}
}

然後執行 zappa deploy, 這時 Zappa 的作用就顯現出來了:

$ zappa deploy
Calling deploy for stage dev..
Downloading and installing dependencies..
- sqlite==python36: Using precompiled lambda package
Packaging project as zip.
Uploading basic-dev-1534393616.zip (6.2MiB)..
100%|██████████████████████████████| 6.54M/6.54M [00:05<00:00, 902KB/s]
Scheduling..
Scheduled basic-dev-zappa-keep-warm-handler.keep_warm_callback with expression rate(4 minutes)!
Uploading basic-dev-template-1534393629.json (1.6KiB)..
100%|█████████████████████████████| 1.59K/1.59K [00:00<00:00, 3.29KB/s]
Waiting for stack basic-dev to create (this can take a bit)..
100%|███████████████████████████████████| 4/4 [00:09<00:00, 4.89s/res]
Deploying API Gateway..
Deployment complete!: https://ucyxlhvz7f.execute-api.us-east-1.amazonaws.com/dev

他幫助我們處理了很多複雜的內容。最值得注意的是,它創建了一個包含應用程序和其依賴項(默認情況下是根據虛擬環境的安裝內容來進行的)Zip 文件,並將其複製到 AWS 上面。在輸出內容的最後,你會發現一個 URL,你可以用它來替換之前使用的 localhost。

該 URL 不會長期有效(因為我運行了 zappa undeploy), 但請相信我,此刻的結果是這樣的:

$ curl https://ucyxlhvz7f.execute-api.us-east-1.amazonaws.com/dev/time
2019-05-10 04:30:59.270893

大體上,對 URL 進行請求和響應的全過程如下:

  • 一個計算機憑空出現了。
  • 跟 Zappa 一起被上傳的應用程序和依賴項會被部署到這臺新的計算機上。
  • 新計算機接受請求。
  • 計算並返回響應。
  • 該計算機被毀掉。

每次訪問這個 URL 的時候都會發生同樣的事情。(實際上,AWS 會對實例進行緩存,來保證請求可以正常響應。但從概念上說,每個請求都會創建新的實例,並在執行結束後銷燬。)

關鍵的一點是,這個相當複雜的過程意味著,你的應用程序正處於生產環境中,是可以公開訪問的,而且在不使用她的時候,你就不用支付任何費用。

我已經把這篇文章中所有的代碼都放在 Github 倉庫裡了。其中我也演示瞭如何升級已發佈的應用程序,傳遞時區作為 URL 的參數。Zappa 讓升級變得很容易(通過執行 zappa update 命令即可),並提供了很方便的 zappa tail 命令,在你的筆記本終端,從已發佈的應用程序組件中整合日誌和錯誤信息。如果你部署出現了問題或者正在運行的應用拋出異常(例如,用戶提供了無效參數),調試會更容易些。一般情況下,你甚至可以使用 serverless(尤其是帶有 AWS Lambda 的 Zappa)配置 cron 任務,使其在 serverless 執行器上運行。

Serverless 的優勢與限制

serverless 的普遍優點在於,它能讓你更專注於業務邏輯,並且在部署時有無限的可擴展性。另一方面,一些人認為 serverless 與微服務架構(例如,複雜性方面)以及雲部署(例如,供應商對資源鎖定的風險)有著相同的缺點。個人工程師購買的範圍各異,當然這跟我們這篇文章中的數據科學部分(即將要討論的)並沒有太大的關係,所以不想在這做過多解釋。

對於數據科學用例來說,serverless 最明顯的優勢就是成本:因為我們只在代碼運行的時候付費,沒有未使用資源浪費的問題,支付的費用與使用量成正比,往往很便宜。比如說,Postlight 的 Readability API 每個月在 EC2 上的費用是 10000 美元,轉移到 AWS Lambda 後,現在每月費用僅為 370 美元。

最佳實踐:讓 Serverless 架構拯救大數據


但事實上,說 serverless 貴也是無可厚非的。考慮一下上面的圖(很簡略)。serverless 的成本與利用率呈線性關係且截距為 0。通常情況下,這要比小型部署的其他方案便宜。而且對於計算使用量很大的情況,它可能更便宜。但如果每秒要處理數千個請求,那麼所需要的成本可能會在交叉點之後,這樣的話,非 serverless 所需的成本反而會更低一些。

可能 serverless 和傳統部署模式之間最根本的區別在於機器的性質,他們會隨時消失或者出現 (與其說是缺點,不如說是 serverless 一系列固有的限制)。到目前為止,我對他們也是一直很模糊。某種程度上,對他們工作方式的理解還是很有限的。但我要把我知道的都告訴你。

這些機器都是動力不足的。對於 AWS Lambda , 寫入時的 RAM 不足 3GB, 本地存儲也相對較小 (75GB)。他們是靠借來的時間過活。函數必須在 15 分鐘內執行完畢。那些條件很隨意又多變(就在前不久,Lambda 的限制變為 1.5GB、500MB 和 300 秒!)

但不可避免的限制就是,這些段生命週期較為短暫的機器是無狀態的,這是 serverless 的固定的前提條件,並且不會改變。之前的實例都是沒有歷史記錄的。他們甚至不能直接與其他運行在 serverless 上的實例進行通信。因此,除去發 Twitter 或寫入數據庫這類的消息之外,serverless 的實例只支持能有輸入和返回值的純函數。沒有全局狀態。

說實話,這時候你可能會想:哇,serverless 聽起來好可怕。繼續跟我往下看。

AWS Lambda 和 pywren 的參數映射函數

因此,會有這樣的機器:

  • 沒有內在狀態。
  • 也不是很快。

但實際上,

  • 這樣的機器有很多。
  • 並且只需在使用時付費。

這大大提升了數據科學家對此感興趣的可能性!本文其餘部分會介紹這部分內容,以及名為 Phwren 的概念驗證工具,最早是在“佔領雲:99% 的分佈式計算”中被提及。儘管你之前可能沒讀過 CS 論文(或者讀過一點兒),不過我強烈推薦這篇文章(或者早報的評論)。

下面是一張數據科學的圖片,預示著我們即將進入本文的另一個新部分。

"

如果你聽說過 Serverless 的話,你可能會把它當做一種雲架構模式,可以將一個應用程序所需要的、長期維護的基礎設施數量降到比較低的水平。在某些場景下,這種方式可以節省很多成本。而且也確是是這樣的。但是在這篇文章裡面,我會在一個新的場景下,介紹相關的應用程序:高度並行的函數計算程序和生產環境下的機器學習系統。

如果你只是一個數據科學家的話,你可能不太瞭解什麼是 serverless。我們將會以 serverless 最常用的應用場景—— Web 業務內容作為開篇。不過值得注意的是,如果你受限於計算資源的話,serverless 是一個輕量級的替代方案,可以幫助你搭建類似於 Spark 集群一類的內容。在沒有運維團隊的幫助下,就可以很輕鬆的將你的簡單示例發佈到生產環境上面。

Serverless 的爭議

大多數的數據科學家對運維都不是很感興趣,但這也是一個好的場景,能顯示出 serverless 的與眾不同,請耐心的聽我說。

人們普遍認為計算機的這種理念並不是很好。

雲環境裡最棒的一件事情就是,你不需要關心硬件相關的問題。你只需要付費給微軟、谷歌、亞馬遜一類的雲平臺,讓他們幫助你來處理。他們在這方面做的真的很好,以至於讓你忘記還有硬件這件事。

但向雲端遷移會帶來成本的變化。最基本一項就是金錢。主要是因為,儘管我們利用了雲環境的伸縮與拓展的能力,但我們還是不可避免地為一些未被充分利用的雲資源,進行長期的付費。

雖然雲成功地將硬件抽象化,我們還是需要安裝、配置、安全認證,和維護操作系統或數據庫等等。對於我們中的許多人來說,這是一種恥辱,因為這並不是我們想花費時間去做的事。對於大多數的僱主來說,他們並不能從中獲得收益,因為他們主要還是通過部署業務邏輯來盈利。

最佳實踐:讓 Serverless 架構拯救大數據


那麼,怎麼樣既能擺脫物理設施的束縛,又能忽視下一層——例如操作系統或者容器相關的內容?如果能做到這一點的話,我們就可以全身心地專注於業務邏輯。

這就是 serverless 的目標。

什麼是 serverless

我們來考慮部署一個簡單的 Serverless 程序,實現兩數相加的邏輯。首先,請求會訪問到某一個網關服務中。這個例子中的請求的內容為 2+2。實際上,網關會傳輸數據到某一臺機器,並把該應用部署到那臺機器上。新機器做出響應後返回結果,然後停止退出。

另一個請求(37+5)過來的時候,也會發生同樣的事情:網關會創建一個全新的機器(並不會複用使用上一個實例的機器),該機器會在運算後就停止退出。至關重要的是,兩數相加的應用程序,並沒有實際的基礎設備。

最佳實踐:讓 Serverless 架構拯救大數據


通常來說,你可以理解為基礎設施的數量與利用率成線性關係,而且截距為 0(這有助你讓你理解為什麼 Serverless 的這個很便宜)

這種描述有點與眾不同。的確也省略了一些工程上的細節內容,不過作為 Serverless 的新用戶而言,很大程度上可以忽視掉這些內容。

順便提一下,“Serverless”這個單詞並不是一個很好地名稱,理論上你還是有服務器的。不過實際上,你的服務器數量是無窮多個(從某種意義上來說,有點像非參數統計,這裡非參數其實是指你有非常多的參數,並不是傳統意義上的“非”。不過實際上而言,你並沒有)。

用 AWS Lambda 和 Zappa 進行 serverless 部署

總的來說,為了使 Serveless 的利益最大化(例如,讓別人來幫你進行運維的工作),你需要為使用 Serverless 平臺支付給雲平臺一定的費用。你可以在 AWS Lambda,Google Cloud Fuctions 和 Azure Functions 裡進行選擇。這些服務會創建、銷燬並以其他方式來管理大型的短生命週期機器池(像以上我們用於計算 2+2 和 35+7 那樣)。

為了演示在 AWS Lambda 上的部署,讓我們用一個簡單的 Python Flask 應用程序來顯示當前時間:

# app.py
import datetime
from flask import Flask
app = Flask(__name__)

@app.route("/time")
def time():
return str(datetime.datetime.now())

if __name__ == "__main__":
app.run()

如果你之前沒有了解過 Flask, 這裡有模板可供你參考,但重點是 time 函數(返回時間)以及它前面的裝飾器。如果有人訪問 /time 路徑,它會告訴 Flask 返回函數的結果。

你可以通過運行 python app.py 在本地“部署”這個應用:

$ python app.py
* Serving Flask app "app" (lazy loading)
* Environment: production
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

當你訪問 127.0.0.1:5000/time 的時候,你將看到當前時間:

 $ curl 127.0.0.1:5000/time
2019-05-09 21:09:46.450550

如果筆記本電腦被合理地設置了防火牆,偶爾會進入睡眠狀態,那麼在本地長時間部署這個應用就不是很合適。現在我們把它部署到 AWS Lambda 上面。

這會涉及到大量的手動的工作,不過好消息是可以通過使用命令行工具來幫助我們完成任務。我推薦Zappa . 之所以喜歡它,是因為它是用 Python 寫的,至少在原則上,它不會關注你往哪個 serverless 平臺上發佈。至於其它的工具,我瞭解的,是名字很容易混淆的 Serverless 和 AWS 的 Chalice .

以下是如何使用 Zappa 發佈上面的 time 應用。首先你要創建並激活一個新的虛擬環境,然後在裡面安裝相關的依賴庫。像上面的例子,就需要安裝 Flask 和 Zappa。 在相同的路徑下,複製上面的 app.py 文件,然後運行 zappa init:

python -m venv venv
source venv/bin/activate
pip install flask zappa
zappa init

zappa init 會快速引導你處理一些相關的問題。這個簡單的小例子中,你接受默認的設置就可以了。以下是生成的配置文件:

$ cat zappa_settings.json
{
"dev": {
"app_function": "app.app",
"aws_region": "us-east-1",
"profile_name": "default",
"project_name": "basic",
"runtime": "python3.6",
"s3_bucket": "zappa-ycr5dtiyk"
}
}

然後執行 zappa deploy, 這時 Zappa 的作用就顯現出來了:

$ zappa deploy
Calling deploy for stage dev..
Downloading and installing dependencies..
- sqlite==python36: Using precompiled lambda package
Packaging project as zip.
Uploading basic-dev-1534393616.zip (6.2MiB)..
100%|██████████████████████████████| 6.54M/6.54M [00:05<00:00, 902KB/s]
Scheduling..
Scheduled basic-dev-zappa-keep-warm-handler.keep_warm_callback with expression rate(4 minutes)!
Uploading basic-dev-template-1534393629.json (1.6KiB)..
100%|█████████████████████████████| 1.59K/1.59K [00:00<00:00, 3.29KB/s]
Waiting for stack basic-dev to create (this can take a bit)..
100%|███████████████████████████████████| 4/4 [00:09<00:00, 4.89s/res]
Deploying API Gateway..
Deployment complete!: https://ucyxlhvz7f.execute-api.us-east-1.amazonaws.com/dev

他幫助我們處理了很多複雜的內容。最值得注意的是,它創建了一個包含應用程序和其依賴項(默認情況下是根據虛擬環境的安裝內容來進行的)Zip 文件,並將其複製到 AWS 上面。在輸出內容的最後,你會發現一個 URL,你可以用它來替換之前使用的 localhost。

該 URL 不會長期有效(因為我運行了 zappa undeploy), 但請相信我,此刻的結果是這樣的:

$ curl https://ucyxlhvz7f.execute-api.us-east-1.amazonaws.com/dev/time
2019-05-10 04:30:59.270893

大體上,對 URL 進行請求和響應的全過程如下:

  • 一個計算機憑空出現了。
  • 跟 Zappa 一起被上傳的應用程序和依賴項會被部署到這臺新的計算機上。
  • 新計算機接受請求。
  • 計算並返回響應。
  • 該計算機被毀掉。

每次訪問這個 URL 的時候都會發生同樣的事情。(實際上,AWS 會對實例進行緩存,來保證請求可以正常響應。但從概念上說,每個請求都會創建新的實例,並在執行結束後銷燬。)

關鍵的一點是,這個相當複雜的過程意味著,你的應用程序正處於生產環境中,是可以公開訪問的,而且在不使用她的時候,你就不用支付任何費用。

我已經把這篇文章中所有的代碼都放在 Github 倉庫裡了。其中我也演示瞭如何升級已發佈的應用程序,傳遞時區作為 URL 的參數。Zappa 讓升級變得很容易(通過執行 zappa update 命令即可),並提供了很方便的 zappa tail 命令,在你的筆記本終端,從已發佈的應用程序組件中整合日誌和錯誤信息。如果你部署出現了問題或者正在運行的應用拋出異常(例如,用戶提供了無效參數),調試會更容易些。一般情況下,你甚至可以使用 serverless(尤其是帶有 AWS Lambda 的 Zappa)配置 cron 任務,使其在 serverless 執行器上運行。

Serverless 的優勢與限制

serverless 的普遍優點在於,它能讓你更專注於業務邏輯,並且在部署時有無限的可擴展性。另一方面,一些人認為 serverless 與微服務架構(例如,複雜性方面)以及雲部署(例如,供應商對資源鎖定的風險)有著相同的缺點。個人工程師購買的範圍各異,當然這跟我們這篇文章中的數據科學部分(即將要討論的)並沒有太大的關係,所以不想在這做過多解釋。

對於數據科學用例來說,serverless 最明顯的優勢就是成本:因為我們只在代碼運行的時候付費,沒有未使用資源浪費的問題,支付的費用與使用量成正比,往往很便宜。比如說,Postlight 的 Readability API 每個月在 EC2 上的費用是 10000 美元,轉移到 AWS Lambda 後,現在每月費用僅為 370 美元。

最佳實踐:讓 Serverless 架構拯救大數據


但事實上,說 serverless 貴也是無可厚非的。考慮一下上面的圖(很簡略)。serverless 的成本與利用率呈線性關係且截距為 0。通常情況下,這要比小型部署的其他方案便宜。而且對於計算使用量很大的情況,它可能更便宜。但如果每秒要處理數千個請求,那麼所需要的成本可能會在交叉點之後,這樣的話,非 serverless 所需的成本反而會更低一些。

可能 serverless 和傳統部署模式之間最根本的區別在於機器的性質,他們會隨時消失或者出現 (與其說是缺點,不如說是 serverless 一系列固有的限制)。到目前為止,我對他們也是一直很模糊。某種程度上,對他們工作方式的理解還是很有限的。但我要把我知道的都告訴你。

這些機器都是動力不足的。對於 AWS Lambda , 寫入時的 RAM 不足 3GB, 本地存儲也相對較小 (75GB)。他們是靠借來的時間過活。函數必須在 15 分鐘內執行完畢。那些條件很隨意又多變(就在前不久,Lambda 的限制變為 1.5GB、500MB 和 300 秒!)

但不可避免的限制就是,這些段生命週期較為短暫的機器是無狀態的,這是 serverless 的固定的前提條件,並且不會改變。之前的實例都是沒有歷史記錄的。他們甚至不能直接與其他運行在 serverless 上的實例進行通信。因此,除去發 Twitter 或寫入數據庫這類的消息之外,serverless 的實例只支持能有輸入和返回值的純函數。沒有全局狀態。

說實話,這時候你可能會想:哇,serverless 聽起來好可怕。繼續跟我往下看。

AWS Lambda 和 pywren 的參數映射函數

因此,會有這樣的機器:

  • 沒有內在狀態。
  • 也不是很快。

但實際上,

  • 這樣的機器有很多。
  • 並且只需在使用時付費。

這大大提升了數據科學家對此感興趣的可能性!本文其餘部分會介紹這部分內容,以及名為 Phwren 的概念驗證工具,最早是在“佔領雲:99% 的分佈式計算”中被提及。儘管你之前可能沒讀過 CS 論文(或者讀過一點兒),不過我強烈推薦這篇文章(或者早報的評論)。

下面是一張數據科學的圖片,預示著我們即將進入本文的另一個新部分。

最佳實踐:讓 Serverless 架構拯救大數據


Pywren 是一個以 Lambda 作為計算後端,跨參數訪問函數的工具。在 Python2 中,大概是這樣:

>>> def square(x):
... return x * x

>>> args = [1, 2, 3, 4, 5, 36]

>>> map(square, args)
[1, 4, 9, 16, 25, 36]

map 函數調用了另一個函數(該例子中為 square 函數)以及函數所需要的參數,並返回這些程序的結果列表。在 Python3 中,由於 map 的計算速度比較慢(也就是,返回一個生成器,你可以立即使用,也可以稍後再用),這最終變得更加冗長。如果你沒遇到這個問題,那就不用擔心(其實我們還是需要擔心的是,2019 年底,Python2 將不再被支持使用)。

如果你是 JS 或 Haskell 以及其他語言的程序員的話,可能對 map 不陌生。但如果你是一個 Python 程序員,可能對 map 函數一無所知。在 Python 的世界裡面,更貼近的是 list 的結構內容,可以幫助你理解,表面看上去不一樣,但基本的思想都是相同的:

>>> [square(x) for x in args]
[1, 4, 9, 16, 25, 36]

Pywren 為 Python 的內置 map 函數提供了一個(幾乎)直接的替代,對每個應用函數的參數都使用了一個獨立的 AWS Lambda 實例,來替代本地機器。其語法如下:

>>> import pywren

>>> pwex = pywren.default_executor()

>>> futures = pwex.map(square, args)

>>> futures
[<pywren.future.ResponseFuture at 0x11422ffd0>,
<pywren.future.ResponseFuture at 0x11422f588>,
<pywren.future.ResponseFuture at 0x11422f358>,
<pywren.future.ResponseFuture at 0x11422f748>,
<pywren.future.ResponseFuture at 0x10bbfff98>,
<pywren.future.ResponseFuture at 0x11422f470>]

>>> futures[0].result()
0

>>> [f.result() for f in futures]
[1, 4, 9, 16, 25, 36]

其實,它也並不完全是 map 的直接替代品,因為它返回的是 futures 對象列表,而不是結果。但或多或少,你也可以從中得到一些信息。

"

如果你聽說過 Serverless 的話,你可能會把它當做一種雲架構模式,可以將一個應用程序所需要的、長期維護的基礎設施數量降到比較低的水平。在某些場景下,這種方式可以節省很多成本。而且也確是是這樣的。但是在這篇文章裡面,我會在一個新的場景下,介紹相關的應用程序:高度並行的函數計算程序和生產環境下的機器學習系統。

如果你只是一個數據科學家的話,你可能不太瞭解什麼是 serverless。我們將會以 serverless 最常用的應用場景—— Web 業務內容作為開篇。不過值得注意的是,如果你受限於計算資源的話,serverless 是一個輕量級的替代方案,可以幫助你搭建類似於 Spark 集群一類的內容。在沒有運維團隊的幫助下,就可以很輕鬆的將你的簡單示例發佈到生產環境上面。

Serverless 的爭議

大多數的數據科學家對運維都不是很感興趣,但這也是一個好的場景,能顯示出 serverless 的與眾不同,請耐心的聽我說。

人們普遍認為計算機的這種理念並不是很好。

雲環境裡最棒的一件事情就是,你不需要關心硬件相關的問題。你只需要付費給微軟、谷歌、亞馬遜一類的雲平臺,讓他們幫助你來處理。他們在這方面做的真的很好,以至於讓你忘記還有硬件這件事。

但向雲端遷移會帶來成本的變化。最基本一項就是金錢。主要是因為,儘管我們利用了雲環境的伸縮與拓展的能力,但我們還是不可避免地為一些未被充分利用的雲資源,進行長期的付費。

雖然雲成功地將硬件抽象化,我們還是需要安裝、配置、安全認證,和維護操作系統或數據庫等等。對於我們中的許多人來說,這是一種恥辱,因為這並不是我們想花費時間去做的事。對於大多數的僱主來說,他們並不能從中獲得收益,因為他們主要還是通過部署業務邏輯來盈利。

最佳實踐:讓 Serverless 架構拯救大數據


那麼,怎麼樣既能擺脫物理設施的束縛,又能忽視下一層——例如操作系統或者容器相關的內容?如果能做到這一點的話,我們就可以全身心地專注於業務邏輯。

這就是 serverless 的目標。

什麼是 serverless

我們來考慮部署一個簡單的 Serverless 程序,實現兩數相加的邏輯。首先,請求會訪問到某一個網關服務中。這個例子中的請求的內容為 2+2。實際上,網關會傳輸數據到某一臺機器,並把該應用部署到那臺機器上。新機器做出響應後返回結果,然後停止退出。

另一個請求(37+5)過來的時候,也會發生同樣的事情:網關會創建一個全新的機器(並不會複用使用上一個實例的機器),該機器會在運算後就停止退出。至關重要的是,兩數相加的應用程序,並沒有實際的基礎設備。

最佳實踐:讓 Serverless 架構拯救大數據


通常來說,你可以理解為基礎設施的數量與利用率成線性關係,而且截距為 0(這有助你讓你理解為什麼 Serverless 的這個很便宜)

這種描述有點與眾不同。的確也省略了一些工程上的細節內容,不過作為 Serverless 的新用戶而言,很大程度上可以忽視掉這些內容。

順便提一下,“Serverless”這個單詞並不是一個很好地名稱,理論上你還是有服務器的。不過實際上,你的服務器數量是無窮多個(從某種意義上來說,有點像非參數統計,這裡非參數其實是指你有非常多的參數,並不是傳統意義上的“非”。不過實際上而言,你並沒有)。

用 AWS Lambda 和 Zappa 進行 serverless 部署

總的來說,為了使 Serveless 的利益最大化(例如,讓別人來幫你進行運維的工作),你需要為使用 Serverless 平臺支付給雲平臺一定的費用。你可以在 AWS Lambda,Google Cloud Fuctions 和 Azure Functions 裡進行選擇。這些服務會創建、銷燬並以其他方式來管理大型的短生命週期機器池(像以上我們用於計算 2+2 和 35+7 那樣)。

為了演示在 AWS Lambda 上的部署,讓我們用一個簡單的 Python Flask 應用程序來顯示當前時間:

# app.py
import datetime
from flask import Flask
app = Flask(__name__)

@app.route("/time")
def time():
return str(datetime.datetime.now())

if __name__ == "__main__":
app.run()

如果你之前沒有了解過 Flask, 這裡有模板可供你參考,但重點是 time 函數(返回時間)以及它前面的裝飾器。如果有人訪問 /time 路徑,它會告訴 Flask 返回函數的結果。

你可以通過運行 python app.py 在本地“部署”這個應用:

$ python app.py
* Serving Flask app "app" (lazy loading)
* Environment: production
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

當你訪問 127.0.0.1:5000/time 的時候,你將看到當前時間:

 $ curl 127.0.0.1:5000/time
2019-05-09 21:09:46.450550

如果筆記本電腦被合理地設置了防火牆,偶爾會進入睡眠狀態,那麼在本地長時間部署這個應用就不是很合適。現在我們把它部署到 AWS Lambda 上面。

這會涉及到大量的手動的工作,不過好消息是可以通過使用命令行工具來幫助我們完成任務。我推薦Zappa . 之所以喜歡它,是因為它是用 Python 寫的,至少在原則上,它不會關注你往哪個 serverless 平臺上發佈。至於其它的工具,我瞭解的,是名字很容易混淆的 Serverless 和 AWS 的 Chalice .

以下是如何使用 Zappa 發佈上面的 time 應用。首先你要創建並激活一個新的虛擬環境,然後在裡面安裝相關的依賴庫。像上面的例子,就需要安裝 Flask 和 Zappa。 在相同的路徑下,複製上面的 app.py 文件,然後運行 zappa init:

python -m venv venv
source venv/bin/activate
pip install flask zappa
zappa init

zappa init 會快速引導你處理一些相關的問題。這個簡單的小例子中,你接受默認的設置就可以了。以下是生成的配置文件:

$ cat zappa_settings.json
{
"dev": {
"app_function": "app.app",
"aws_region": "us-east-1",
"profile_name": "default",
"project_name": "basic",
"runtime": "python3.6",
"s3_bucket": "zappa-ycr5dtiyk"
}
}

然後執行 zappa deploy, 這時 Zappa 的作用就顯現出來了:

$ zappa deploy
Calling deploy for stage dev..
Downloading and installing dependencies..
- sqlite==python36: Using precompiled lambda package
Packaging project as zip.
Uploading basic-dev-1534393616.zip (6.2MiB)..
100%|██████████████████████████████| 6.54M/6.54M [00:05<00:00, 902KB/s]
Scheduling..
Scheduled basic-dev-zappa-keep-warm-handler.keep_warm_callback with expression rate(4 minutes)!
Uploading basic-dev-template-1534393629.json (1.6KiB)..
100%|█████████████████████████████| 1.59K/1.59K [00:00<00:00, 3.29KB/s]
Waiting for stack basic-dev to create (this can take a bit)..
100%|███████████████████████████████████| 4/4 [00:09<00:00, 4.89s/res]
Deploying API Gateway..
Deployment complete!: https://ucyxlhvz7f.execute-api.us-east-1.amazonaws.com/dev

他幫助我們處理了很多複雜的內容。最值得注意的是,它創建了一個包含應用程序和其依賴項(默認情況下是根據虛擬環境的安裝內容來進行的)Zip 文件,並將其複製到 AWS 上面。在輸出內容的最後,你會發現一個 URL,你可以用它來替換之前使用的 localhost。

該 URL 不會長期有效(因為我運行了 zappa undeploy), 但請相信我,此刻的結果是這樣的:

$ curl https://ucyxlhvz7f.execute-api.us-east-1.amazonaws.com/dev/time
2019-05-10 04:30:59.270893

大體上,對 URL 進行請求和響應的全過程如下:

  • 一個計算機憑空出現了。
  • 跟 Zappa 一起被上傳的應用程序和依賴項會被部署到這臺新的計算機上。
  • 新計算機接受請求。
  • 計算並返回響應。
  • 該計算機被毀掉。

每次訪問這個 URL 的時候都會發生同樣的事情。(實際上,AWS 會對實例進行緩存,來保證請求可以正常響應。但從概念上說,每個請求都會創建新的實例,並在執行結束後銷燬。)

關鍵的一點是,這個相當複雜的過程意味著,你的應用程序正處於生產環境中,是可以公開訪問的,而且在不使用她的時候,你就不用支付任何費用。

我已經把這篇文章中所有的代碼都放在 Github 倉庫裡了。其中我也演示瞭如何升級已發佈的應用程序,傳遞時區作為 URL 的參數。Zappa 讓升級變得很容易(通過執行 zappa update 命令即可),並提供了很方便的 zappa tail 命令,在你的筆記本終端,從已發佈的應用程序組件中整合日誌和錯誤信息。如果你部署出現了問題或者正在運行的應用拋出異常(例如,用戶提供了無效參數),調試會更容易些。一般情況下,你甚至可以使用 serverless(尤其是帶有 AWS Lambda 的 Zappa)配置 cron 任務,使其在 serverless 執行器上運行。

Serverless 的優勢與限制

serverless 的普遍優點在於,它能讓你更專注於業務邏輯,並且在部署時有無限的可擴展性。另一方面,一些人認為 serverless 與微服務架構(例如,複雜性方面)以及雲部署(例如,供應商對資源鎖定的風險)有著相同的缺點。個人工程師購買的範圍各異,當然這跟我們這篇文章中的數據科學部分(即將要討論的)並沒有太大的關係,所以不想在這做過多解釋。

對於數據科學用例來說,serverless 最明顯的優勢就是成本:因為我們只在代碼運行的時候付費,沒有未使用資源浪費的問題,支付的費用與使用量成正比,往往很便宜。比如說,Postlight 的 Readability API 每個月在 EC2 上的費用是 10000 美元,轉移到 AWS Lambda 後,現在每月費用僅為 370 美元。

最佳實踐:讓 Serverless 架構拯救大數據


但事實上,說 serverless 貴也是無可厚非的。考慮一下上面的圖(很簡略)。serverless 的成本與利用率呈線性關係且截距為 0。通常情況下,這要比小型部署的其他方案便宜。而且對於計算使用量很大的情況,它可能更便宜。但如果每秒要處理數千個請求,那麼所需要的成本可能會在交叉點之後,這樣的話,非 serverless 所需的成本反而會更低一些。

可能 serverless 和傳統部署模式之間最根本的區別在於機器的性質,他們會隨時消失或者出現 (與其說是缺點,不如說是 serverless 一系列固有的限制)。到目前為止,我對他們也是一直很模糊。某種程度上,對他們工作方式的理解還是很有限的。但我要把我知道的都告訴你。

這些機器都是動力不足的。對於 AWS Lambda , 寫入時的 RAM 不足 3GB, 本地存儲也相對較小 (75GB)。他們是靠借來的時間過活。函數必須在 15 分鐘內執行完畢。那些條件很隨意又多變(就在前不久,Lambda 的限制變為 1.5GB、500MB 和 300 秒!)

但不可避免的限制就是,這些段生命週期較為短暫的機器是無狀態的,這是 serverless 的固定的前提條件,並且不會改變。之前的實例都是沒有歷史記錄的。他們甚至不能直接與其他運行在 serverless 上的實例進行通信。因此,除去發 Twitter 或寫入數據庫這類的消息之外,serverless 的實例只支持能有輸入和返回值的純函數。沒有全局狀態。

說實話,這時候你可能會想:哇,serverless 聽起來好可怕。繼續跟我往下看。

AWS Lambda 和 pywren 的參數映射函數

因此,會有這樣的機器:

  • 沒有內在狀態。
  • 也不是很快。

但實際上,

  • 這樣的機器有很多。
  • 並且只需在使用時付費。

這大大提升了數據科學家對此感興趣的可能性!本文其餘部分會介紹這部分內容,以及名為 Phwren 的概念驗證工具,最早是在“佔領雲:99% 的分佈式計算”中被提及。儘管你之前可能沒讀過 CS 論文(或者讀過一點兒),不過我強烈推薦這篇文章(或者早報的評論)。

下面是一張數據科學的圖片,預示著我們即將進入本文的另一個新部分。

最佳實踐:讓 Serverless 架構拯救大數據


Pywren 是一個以 Lambda 作為計算後端,跨參數訪問函數的工具。在 Python2 中,大概是這樣:

>>> def square(x):
... return x * x

>>> args = [1, 2, 3, 4, 5, 36]

>>> map(square, args)
[1, 4, 9, 16, 25, 36]

map 函數調用了另一個函數(該例子中為 square 函數)以及函數所需要的參數,並返回這些程序的結果列表。在 Python3 中,由於 map 的計算速度比較慢(也就是,返回一個生成器,你可以立即使用,也可以稍後再用),這最終變得更加冗長。如果你沒遇到這個問題,那就不用擔心(其實我們還是需要擔心的是,2019 年底,Python2 將不再被支持使用)。

如果你是 JS 或 Haskell 以及其他語言的程序員的話,可能對 map 不陌生。但如果你是一個 Python 程序員,可能對 map 函數一無所知。在 Python 的世界裡面,更貼近的是 list 的結構內容,可以幫助你理解,表面看上去不一樣,但基本的思想都是相同的:

>>> [square(x) for x in args]
[1, 4, 9, 16, 25, 36]

Pywren 為 Python 的內置 map 函數提供了一個(幾乎)直接的替代,對每個應用函數的參數都使用了一個獨立的 AWS Lambda 實例,來替代本地機器。其語法如下:

>>> import pywren

>>> pwex = pywren.default_executor()

>>> futures = pwex.map(square, args)

>>> futures
[<pywren.future.ResponseFuture at 0x11422ffd0>,
<pywren.future.ResponseFuture at 0x11422f588>,
<pywren.future.ResponseFuture at 0x11422f358>,
<pywren.future.ResponseFuture at 0x11422f748>,
<pywren.future.ResponseFuture at 0x10bbfff98>,
<pywren.future.ResponseFuture at 0x11422f470>]

>>> futures[0].result()
0

>>> [f.result() for f in futures]
[1, 4, 9, 16, 25, 36]

其實,它也並不完全是 map 的直接替代品,因為它返回的是 futures 對象列表,而不是結果。但或多或少,你也可以從中得到一些信息。

最佳實踐:讓 Serverless 架構拯救大數據


當我們使用 Pywren 的時候都發生了什麼?以下是我的理解:

  1. Pywren 將函數進行序列化並放到 S3 上,並引入 Lambda(上圖中的“host submit”)。
  2. Lambdas 開始工作(上圖中的“task start”)。
  3. 他們需要做一些設置,包括從 S3 上提取和反序列化 job, 以及安裝 Anaconda Python 運行時機制(上圖中的“setup done”)。
  4. 計算結果並寫入 S3(“task done”), 等待我們調用 result 方法。
  5. 當我們調用這個方法時,會把下載的結果返回給客戶端(“result returned”)。

注意,順便說一下,X 軸上的刻度。是的,我們並行做了很多事,但相對任務來說,開銷(約 20 秒!)是巨大的。我們回過頭再來討論這個問題。

高度並行計算和胡蘿蔔問題

由於高度並行計算問題,square 函數只能作為 Pywren 的候選方案。這是為什麼呢?

想象一下有一片胡蘿蔔地。總共 8 行,每行 10 個。你有一臺收割機,每秒可以摘 1 根胡蘿蔔。那麼,收割所有的胡蘿蔔需要用 80 秒。

如果有 2 臺收割機會怎麼樣呢?他們可以獨立工作,用一半的時間就可以收割這些胡蘿蔔。

那麼,要是有 8 臺收割機,10 秒就能收割整片地。

因為拖拉機可以獨立工作,速度與工人數量成正比。這個過程與共同編寫一本書還是有差異的,因為兩個作者需要經常交流,告訴對方各自到目前為止都完成了什麼。但拖拉機是完全獨立的。沒有員工之間的交流。這就是並行。

在 Python 中,通常你會把它實現成列表的形式,就跟我們描述的典型問題一樣。for 循環雖有副作用,但並不會引發高度並行問題。但如果把循環轉換成我們理解的那樣,那麼也會有高度並行問題。

高度並行的 Pywren 示例:

現在,將前 6 位數進行平方計算是一件很酷的事,真實的工作原理又是怎樣的呢?讓我們看以下幾個例子:

1. 網站檢索

我想從某個會議網站上搜集成千上萬的機器學習論文的摘要。因為我是一次性把它們都抓取出來,這樣在我自己的電腦上會花費很長的時間。本來可以通過並行 CPU 上的任務來加快這個進程,但仍會受到網絡連接速度帶來的限制。

通過使用 pywren,將此場景轉換為 lambda 的話,就可以並行工作 //RH: 我能夠並行工作嗎?同時,也得益於 AWS 更快的網絡連接!代碼大致如下:

>>> def scrape_batch(batch):
... for paper in batch:
... paper["abstract"] = scrape_abstract_for_one_paper(paper)
... return batch

>>> batches = [[paper1, paper2, ...], [paper101, paper201, ...], ...]

>>> pywren.get_all_results(pwex.map(scrape_batch, batches))

這種情況下,將所有的論文列表都收集到一個論文上,並沒有通過方法進行映射。相反地,我可以通過函數進行映射,該函數將一批文件剪貼到一批文件的列表上。我這樣做是因為,處理一篇論文的速度相對比較快。那麼如果每次處理一篇論文,這就意味著,主要的開銷轉嫁到設置 Lambda 的實例數量上面去了。不斷在增加論文的數量,直到 20 秒左右的執行時間即可,最大限度地提高了速度。

2. 布萊克 - 斯科爾斯方程

布萊克 - 斯科爾斯方程是描述股票期權演化的偏微分方程。布拉德福德·林奇解決數百萬種構型的問題。每個解都是一個相當複雜的數值計算,需要幾十秒。如果沒有並行化,這項任務將在一臺機器上執行 3 天的時間。在使用 pywren 的 AWS lambda 上,只需要 16 分鐘。

3. 視頻編碼

視頻編碼算法的某些部分是高度並行的方式,因為它們一次只能執行一幀。“編碼,快和慢:使用數千個小線程的低延遲來進行視頻處理”一文中,描述了類似於 Pywren 的方法在這個問題上的應用。

比較有趣的是,因為在視頻編碼中有一個非常重要的速度閾值:你能以每秒 24(或 60)幀的速度編碼嗎?除此之外,你還處於實時視頻編碼領域,這將打開新型的用例。這一點也很有趣,因為與 Pywren 不同,這種方法並不會對單個批處理執行單個 lambda 提交;作者為正在進行的視頻提供了一個長期存在的管道。Serverless 數據工程!

4. 超參數優化

一般來說,機器學習算法很難並行化。例如,在開始討論分佈的系統問題之前,分佈式梯度下降就是一個著名的算法問題。

但是,ML 工作流中的一部分,非常適合 Pywren,即超參數優化。

"

如果你聽說過 Serverless 的話,你可能會把它當做一種雲架構模式,可以將一個應用程序所需要的、長期維護的基礎設施數量降到比較低的水平。在某些場景下,這種方式可以節省很多成本。而且也確是是這樣的。但是在這篇文章裡面,我會在一個新的場景下,介紹相關的應用程序:高度並行的函數計算程序和生產環境下的機器學習系統。

如果你只是一個數據科學家的話,你可能不太瞭解什麼是 serverless。我們將會以 serverless 最常用的應用場景—— Web 業務內容作為開篇。不過值得注意的是,如果你受限於計算資源的話,serverless 是一個輕量級的替代方案,可以幫助你搭建類似於 Spark 集群一類的內容。在沒有運維團隊的幫助下,就可以很輕鬆的將你的簡單示例發佈到生產環境上面。

Serverless 的爭議

大多數的數據科學家對運維都不是很感興趣,但這也是一個好的場景,能顯示出 serverless 的與眾不同,請耐心的聽我說。

人們普遍認為計算機的這種理念並不是很好。

雲環境裡最棒的一件事情就是,你不需要關心硬件相關的問題。你只需要付費給微軟、谷歌、亞馬遜一類的雲平臺,讓他們幫助你來處理。他們在這方面做的真的很好,以至於讓你忘記還有硬件這件事。

但向雲端遷移會帶來成本的變化。最基本一項就是金錢。主要是因為,儘管我們利用了雲環境的伸縮與拓展的能力,但我們還是不可避免地為一些未被充分利用的雲資源,進行長期的付費。

雖然雲成功地將硬件抽象化,我們還是需要安裝、配置、安全認證,和維護操作系統或數據庫等等。對於我們中的許多人來說,這是一種恥辱,因為這並不是我們想花費時間去做的事。對於大多數的僱主來說,他們並不能從中獲得收益,因為他們主要還是通過部署業務邏輯來盈利。

最佳實踐:讓 Serverless 架構拯救大數據


那麼,怎麼樣既能擺脫物理設施的束縛,又能忽視下一層——例如操作系統或者容器相關的內容?如果能做到這一點的話,我們就可以全身心地專注於業務邏輯。

這就是 serverless 的目標。

什麼是 serverless

我們來考慮部署一個簡單的 Serverless 程序,實現兩數相加的邏輯。首先,請求會訪問到某一個網關服務中。這個例子中的請求的內容為 2+2。實際上,網關會傳輸數據到某一臺機器,並把該應用部署到那臺機器上。新機器做出響應後返回結果,然後停止退出。

另一個請求(37+5)過來的時候,也會發生同樣的事情:網關會創建一個全新的機器(並不會複用使用上一個實例的機器),該機器會在運算後就停止退出。至關重要的是,兩數相加的應用程序,並沒有實際的基礎設備。

最佳實踐:讓 Serverless 架構拯救大數據


通常來說,你可以理解為基礎設施的數量與利用率成線性關係,而且截距為 0(這有助你讓你理解為什麼 Serverless 的這個很便宜)

這種描述有點與眾不同。的確也省略了一些工程上的細節內容,不過作為 Serverless 的新用戶而言,很大程度上可以忽視掉這些內容。

順便提一下,“Serverless”這個單詞並不是一個很好地名稱,理論上你還是有服務器的。不過實際上,你的服務器數量是無窮多個(從某種意義上來說,有點像非參數統計,這裡非參數其實是指你有非常多的參數,並不是傳統意義上的“非”。不過實際上而言,你並沒有)。

用 AWS Lambda 和 Zappa 進行 serverless 部署

總的來說,為了使 Serveless 的利益最大化(例如,讓別人來幫你進行運維的工作),你需要為使用 Serverless 平臺支付給雲平臺一定的費用。你可以在 AWS Lambda,Google Cloud Fuctions 和 Azure Functions 裡進行選擇。這些服務會創建、銷燬並以其他方式來管理大型的短生命週期機器池(像以上我們用於計算 2+2 和 35+7 那樣)。

為了演示在 AWS Lambda 上的部署,讓我們用一個簡單的 Python Flask 應用程序來顯示當前時間:

# app.py
import datetime
from flask import Flask
app = Flask(__name__)

@app.route("/time")
def time():
return str(datetime.datetime.now())

if __name__ == "__main__":
app.run()

如果你之前沒有了解過 Flask, 這裡有模板可供你參考,但重點是 time 函數(返回時間)以及它前面的裝飾器。如果有人訪問 /time 路徑,它會告訴 Flask 返回函數的結果。

你可以通過運行 python app.py 在本地“部署”這個應用:

$ python app.py
* Serving Flask app "app" (lazy loading)
* Environment: production
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

當你訪問 127.0.0.1:5000/time 的時候,你將看到當前時間:

 $ curl 127.0.0.1:5000/time
2019-05-09 21:09:46.450550

如果筆記本電腦被合理地設置了防火牆,偶爾會進入睡眠狀態,那麼在本地長時間部署這個應用就不是很合適。現在我們把它部署到 AWS Lambda 上面。

這會涉及到大量的手動的工作,不過好消息是可以通過使用命令行工具來幫助我們完成任務。我推薦Zappa . 之所以喜歡它,是因為它是用 Python 寫的,至少在原則上,它不會關注你往哪個 serverless 平臺上發佈。至於其它的工具,我瞭解的,是名字很容易混淆的 Serverless 和 AWS 的 Chalice .

以下是如何使用 Zappa 發佈上面的 time 應用。首先你要創建並激活一個新的虛擬環境,然後在裡面安裝相關的依賴庫。像上面的例子,就需要安裝 Flask 和 Zappa。 在相同的路徑下,複製上面的 app.py 文件,然後運行 zappa init:

python -m venv venv
source venv/bin/activate
pip install flask zappa
zappa init

zappa init 會快速引導你處理一些相關的問題。這個簡單的小例子中,你接受默認的設置就可以了。以下是生成的配置文件:

$ cat zappa_settings.json
{
"dev": {
"app_function": "app.app",
"aws_region": "us-east-1",
"profile_name": "default",
"project_name": "basic",
"runtime": "python3.6",
"s3_bucket": "zappa-ycr5dtiyk"
}
}

然後執行 zappa deploy, 這時 Zappa 的作用就顯現出來了:

$ zappa deploy
Calling deploy for stage dev..
Downloading and installing dependencies..
- sqlite==python36: Using precompiled lambda package
Packaging project as zip.
Uploading basic-dev-1534393616.zip (6.2MiB)..
100%|██████████████████████████████| 6.54M/6.54M [00:05<00:00, 902KB/s]
Scheduling..
Scheduled basic-dev-zappa-keep-warm-handler.keep_warm_callback with expression rate(4 minutes)!
Uploading basic-dev-template-1534393629.json (1.6KiB)..
100%|█████████████████████████████| 1.59K/1.59K [00:00<00:00, 3.29KB/s]
Waiting for stack basic-dev to create (this can take a bit)..
100%|███████████████████████████████████| 4/4 [00:09<00:00, 4.89s/res]
Deploying API Gateway..
Deployment complete!: https://ucyxlhvz7f.execute-api.us-east-1.amazonaws.com/dev

他幫助我們處理了很多複雜的內容。最值得注意的是,它創建了一個包含應用程序和其依賴項(默認情況下是根據虛擬環境的安裝內容來進行的)Zip 文件,並將其複製到 AWS 上面。在輸出內容的最後,你會發現一個 URL,你可以用它來替換之前使用的 localhost。

該 URL 不會長期有效(因為我運行了 zappa undeploy), 但請相信我,此刻的結果是這樣的:

$ curl https://ucyxlhvz7f.execute-api.us-east-1.amazonaws.com/dev/time
2019-05-10 04:30:59.270893

大體上,對 URL 進行請求和響應的全過程如下:

  • 一個計算機憑空出現了。
  • 跟 Zappa 一起被上傳的應用程序和依賴項會被部署到這臺新的計算機上。
  • 新計算機接受請求。
  • 計算並返回響應。
  • 該計算機被毀掉。

每次訪問這個 URL 的時候都會發生同樣的事情。(實際上,AWS 會對實例進行緩存,來保證請求可以正常響應。但從概念上說,每個請求都會創建新的實例,並在執行結束後銷燬。)

關鍵的一點是,這個相當複雜的過程意味著,你的應用程序正處於生產環境中,是可以公開訪問的,而且在不使用她的時候,你就不用支付任何費用。

我已經把這篇文章中所有的代碼都放在 Github 倉庫裡了。其中我也演示瞭如何升級已發佈的應用程序,傳遞時區作為 URL 的參數。Zappa 讓升級變得很容易(通過執行 zappa update 命令即可),並提供了很方便的 zappa tail 命令,在你的筆記本終端,從已發佈的應用程序組件中整合日誌和錯誤信息。如果你部署出現了問題或者正在運行的應用拋出異常(例如,用戶提供了無效參數),調試會更容易些。一般情況下,你甚至可以使用 serverless(尤其是帶有 AWS Lambda 的 Zappa)配置 cron 任務,使其在 serverless 執行器上運行。

Serverless 的優勢與限制

serverless 的普遍優點在於,它能讓你更專注於業務邏輯,並且在部署時有無限的可擴展性。另一方面,一些人認為 serverless 與微服務架構(例如,複雜性方面)以及雲部署(例如,供應商對資源鎖定的風險)有著相同的缺點。個人工程師購買的範圍各異,當然這跟我們這篇文章中的數據科學部分(即將要討論的)並沒有太大的關係,所以不想在這做過多解釋。

對於數據科學用例來說,serverless 最明顯的優勢就是成本:因為我們只在代碼運行的時候付費,沒有未使用資源浪費的問題,支付的費用與使用量成正比,往往很便宜。比如說,Postlight 的 Readability API 每個月在 EC2 上的費用是 10000 美元,轉移到 AWS Lambda 後,現在每月費用僅為 370 美元。

最佳實踐:讓 Serverless 架構拯救大數據


但事實上,說 serverless 貴也是無可厚非的。考慮一下上面的圖(很簡略)。serverless 的成本與利用率呈線性關係且截距為 0。通常情況下,這要比小型部署的其他方案便宜。而且對於計算使用量很大的情況,它可能更便宜。但如果每秒要處理數千個請求,那麼所需要的成本可能會在交叉點之後,這樣的話,非 serverless 所需的成本反而會更低一些。

可能 serverless 和傳統部署模式之間最根本的區別在於機器的性質,他們會隨時消失或者出現 (與其說是缺點,不如說是 serverless 一系列固有的限制)。到目前為止,我對他們也是一直很模糊。某種程度上,對他們工作方式的理解還是很有限的。但我要把我知道的都告訴你。

這些機器都是動力不足的。對於 AWS Lambda , 寫入時的 RAM 不足 3GB, 本地存儲也相對較小 (75GB)。他們是靠借來的時間過活。函數必須在 15 分鐘內執行完畢。那些條件很隨意又多變(就在前不久,Lambda 的限制變為 1.5GB、500MB 和 300 秒!)

但不可避免的限制就是,這些段生命週期較為短暫的機器是無狀態的,這是 serverless 的固定的前提條件,並且不會改變。之前的實例都是沒有歷史記錄的。他們甚至不能直接與其他運行在 serverless 上的實例進行通信。因此,除去發 Twitter 或寫入數據庫這類的消息之外,serverless 的實例只支持能有輸入和返回值的純函數。沒有全局狀態。

說實話,這時候你可能會想:哇,serverless 聽起來好可怕。繼續跟我往下看。

AWS Lambda 和 pywren 的參數映射函數

因此,會有這樣的機器:

  • 沒有內在狀態。
  • 也不是很快。

但實際上,

  • 這樣的機器有很多。
  • 並且只需在使用時付費。

這大大提升了數據科學家對此感興趣的可能性!本文其餘部分會介紹這部分內容,以及名為 Phwren 的概念驗證工具,最早是在“佔領雲:99% 的分佈式計算”中被提及。儘管你之前可能沒讀過 CS 論文(或者讀過一點兒),不過我強烈推薦這篇文章(或者早報的評論)。

下面是一張數據科學的圖片,預示著我們即將進入本文的另一個新部分。

最佳實踐:讓 Serverless 架構拯救大數據


Pywren 是一個以 Lambda 作為計算後端,跨參數訪問函數的工具。在 Python2 中,大概是這樣:

>>> def square(x):
... return x * x

>>> args = [1, 2, 3, 4, 5, 36]

>>> map(square, args)
[1, 4, 9, 16, 25, 36]

map 函數調用了另一個函數(該例子中為 square 函數)以及函數所需要的參數,並返回這些程序的結果列表。在 Python3 中,由於 map 的計算速度比較慢(也就是,返回一個生成器,你可以立即使用,也可以稍後再用),這最終變得更加冗長。如果你沒遇到這個問題,那就不用擔心(其實我們還是需要擔心的是,2019 年底,Python2 將不再被支持使用)。

如果你是 JS 或 Haskell 以及其他語言的程序員的話,可能對 map 不陌生。但如果你是一個 Python 程序員,可能對 map 函數一無所知。在 Python 的世界裡面,更貼近的是 list 的結構內容,可以幫助你理解,表面看上去不一樣,但基本的思想都是相同的:

>>> [square(x) for x in args]
[1, 4, 9, 16, 25, 36]

Pywren 為 Python 的內置 map 函數提供了一個(幾乎)直接的替代,對每個應用函數的參數都使用了一個獨立的 AWS Lambda 實例,來替代本地機器。其語法如下:

>>> import pywren

>>> pwex = pywren.default_executor()

>>> futures = pwex.map(square, args)

>>> futures
[<pywren.future.ResponseFuture at 0x11422ffd0>,
<pywren.future.ResponseFuture at 0x11422f588>,
<pywren.future.ResponseFuture at 0x11422f358>,
<pywren.future.ResponseFuture at 0x11422f748>,
<pywren.future.ResponseFuture at 0x10bbfff98>,
<pywren.future.ResponseFuture at 0x11422f470>]

>>> futures[0].result()
0

>>> [f.result() for f in futures]
[1, 4, 9, 16, 25, 36]

其實,它也並不完全是 map 的直接替代品,因為它返回的是 futures 對象列表,而不是結果。但或多或少,你也可以從中得到一些信息。

最佳實踐:讓 Serverless 架構拯救大數據


當我們使用 Pywren 的時候都發生了什麼?以下是我的理解:

  1. Pywren 將函數進行序列化並放到 S3 上,並引入 Lambda(上圖中的“host submit”)。
  2. Lambdas 開始工作(上圖中的“task start”)。
  3. 他們需要做一些設置,包括從 S3 上提取和反序列化 job, 以及安裝 Anaconda Python 運行時機制(上圖中的“setup done”)。
  4. 計算結果並寫入 S3(“task done”), 等待我們調用 result 方法。
  5. 當我們調用這個方法時,會把下載的結果返回給客戶端(“result returned”)。

注意,順便說一下,X 軸上的刻度。是的,我們並行做了很多事,但相對任務來說,開銷(約 20 秒!)是巨大的。我們回過頭再來討論這個問題。

高度並行計算和胡蘿蔔問題

由於高度並行計算問題,square 函數只能作為 Pywren 的候選方案。這是為什麼呢?

想象一下有一片胡蘿蔔地。總共 8 行,每行 10 個。你有一臺收割機,每秒可以摘 1 根胡蘿蔔。那麼,收割所有的胡蘿蔔需要用 80 秒。

如果有 2 臺收割機會怎麼樣呢?他們可以獨立工作,用一半的時間就可以收割這些胡蘿蔔。

那麼,要是有 8 臺收割機,10 秒就能收割整片地。

因為拖拉機可以獨立工作,速度與工人數量成正比。這個過程與共同編寫一本書還是有差異的,因為兩個作者需要經常交流,告訴對方各自到目前為止都完成了什麼。但拖拉機是完全獨立的。沒有員工之間的交流。這就是並行。

在 Python 中,通常你會把它實現成列表的形式,就跟我們描述的典型問題一樣。for 循環雖有副作用,但並不會引發高度並行問題。但如果把循環轉換成我們理解的那樣,那麼也會有高度並行問題。

高度並行的 Pywren 示例:

現在,將前 6 位數進行平方計算是一件很酷的事,真實的工作原理又是怎樣的呢?讓我們看以下幾個例子:

1. 網站檢索

我想從某個會議網站上搜集成千上萬的機器學習論文的摘要。因為我是一次性把它們都抓取出來,這樣在我自己的電腦上會花費很長的時間。本來可以通過並行 CPU 上的任務來加快這個進程,但仍會受到網絡連接速度帶來的限制。

通過使用 pywren,將此場景轉換為 lambda 的話,就可以並行工作 //RH: 我能夠並行工作嗎?同時,也得益於 AWS 更快的網絡連接!代碼大致如下:

>>> def scrape_batch(batch):
... for paper in batch:
... paper["abstract"] = scrape_abstract_for_one_paper(paper)
... return batch

>>> batches = [[paper1, paper2, ...], [paper101, paper201, ...], ...]

>>> pywren.get_all_results(pwex.map(scrape_batch, batches))

這種情況下,將所有的論文列表都收集到一個論文上,並沒有通過方法進行映射。相反地,我可以通過函數進行映射,該函數將一批文件剪貼到一批文件的列表上。我這樣做是因為,處理一篇論文的速度相對比較快。那麼如果每次處理一篇論文,這就意味著,主要的開銷轉嫁到設置 Lambda 的實例數量上面去了。不斷在增加論文的數量,直到 20 秒左右的執行時間即可,最大限度地提高了速度。

2. 布萊克 - 斯科爾斯方程

布萊克 - 斯科爾斯方程是描述股票期權演化的偏微分方程。布拉德福德·林奇解決數百萬種構型的問題。每個解都是一個相當複雜的數值計算,需要幾十秒。如果沒有並行化,這項任務將在一臺機器上執行 3 天的時間。在使用 pywren 的 AWS lambda 上,只需要 16 分鐘。

3. 視頻編碼

視頻編碼算法的某些部分是高度並行的方式,因為它們一次只能執行一幀。“編碼,快和慢:使用數千個小線程的低延遲來進行視頻處理”一文中,描述了類似於 Pywren 的方法在這個問題上的應用。

比較有趣的是,因為在視頻編碼中有一個非常重要的速度閾值:你能以每秒 24(或 60)幀的速度編碼嗎?除此之外,你還處於實時視頻編碼領域,這將打開新型的用例。這一點也很有趣,因為與 Pywren 不同,這種方法並不會對單個批處理執行單個 lambda 提交;作者為正在進行的視頻提供了一個長期存在的管道。Serverless 數據工程!

4. 超參數優化

一般來說,機器學習算法很難並行化。例如,在開始討論分佈的系統問題之前,分佈式梯度下降就是一個著名的算法問題。

但是,ML 工作流中的一部分,非常適合 Pywren,即超參數優化。

最佳實踐:讓 Serverless 架構拯救大數據

假設我們有一些訓練數據,有兩個類(黃色和紫色)和兩個特性。我們可以使用 scikit-learn 在本地訓練此數據模型。這是用來說明這個想法的幼稚的假設。不要糾結在這個驗證的結果,想象一下這個場景背後的問題!

>>> from sklearn.neighbors import KNeighborsClassifier

>>> classifier = KNeighborsClassifier(n_neighbors=5)

>>> classifier.fit(Xtrain, ytrain)

>>> classifier.score(Xtest, ytest)
0.838

這裡,n_neighbors 是一個“超參數”——ML 算法需要一個神奇的數字,你必須根據當前的問題逐層分析。在這種情況下,我們需要選擇有多少的附近訓練樣本,來決定下一步的權重問題。

通常的方法是用一個簡單的搜索方式:嘗試所有可能的方法,並選擇一個給出最精確模型。這是典型的高度並行的例子,所以我們可以使用 pywren!我們需要一個 n_neighbors 的值列表供我們測試,以及一個能返回經過訓練的模型並映射到列表上的函數:

>>> all_hyperparams
[{'n_neighbors': 1},
{'n_neighbors': 2},
{'n_neighbors': 3},
{'n_neighbors': 4},
{'n_neighbors': 5},
{'n_neighbors': 6},
{'n_neighbors': 7},
{'n_neighbors': 8}]

>>> def train_model(hyperparams):
... classifier = KNeighborsClassifier(**hyperparams)
... classifier.fit(Xtrain, ytrain)
... return classifier

現在,我們可以將這個內容發送到 AWS lambda:

>>> futures = pwex.map(train_model, all_hyperparams)

>>> classifiers = pywren.get_all_results(futures)

>>> for hyperparams, classifier in zip(all_hyperparams, classifiers):
... print(hyperparams, classifier.score(Xtest, ytest))

{'n_neighbors': 1} 0.758
{'n_neighbors': 2} 0.782
{'n_neighbors': 3} 0.814
{'n_neighbors': 4} 0.82
{'n_neighbors': 5} 0.838
{'n_neighbors': 6} 0.838
{'n_neighbors': 7} 0.842
{'n_neighbors': 8} 0.828

值得注意的是,pywren 不僅發送例如數字這種簡單的結果。它還可以返回具有屬性和方法的 Python 對象(在本例中,是經過訓練的 SciKit 學習分類器)。這使我們能夠使用本地測試集來計算每個模型的精度,並從筆記本電腦中,設置並選擇最好的一個。

serverless 的機器學習活動

假設我們序列化最佳模型(顯然 n_neighbors=7),並將其上傳到到公共的 S3 中。這樣我們就可以做最後的操作了!

還記得 Zappa 嗎,我在本文開頭演示的 CLI,可以幫助我們部署 flask 的應用。如果使用 zappa 來部署這個 Flask 應用程序,那麼你在生產環境中,就可以進行機器學習了!

url = "https://s3.amazonaws.com/modelservingdemo/classifier.pkl"
r = requests.get(url)
classifier = pickle.loads(r.content)

@app.route("/predict")
def predict():
X = [[float(request.args['feature_1']),
float(request.args['feature_2'])]]
label = classifier.predict(X)
if label == 0:
return "purple blob"
else:
return "yellow blob"

如果你不相信我的話,請訪問已經部署的應用程序,更改 URL 參數嘗試一下。

有一個完整的創業公司的具體實踐,該方法通過 serverless 的基礎設施,將機器學習引入到生產環境。在這裡比上面的十幾行 python 代碼更加健壯和複雜。

更多的相關限制

文章開始的時候,我們談及對 serverless(複雜性、鎖定、成本)的工程性評論及其侷限性(無狀態、有限的 RAM 和生存週期)。

在結束之前,我想在數據科學的討論內容中,添加一些內容。

首先,讓我們解決一些對 lambda 實例規範的基本限制。如果單個函數應用程序花費的時間超過 900 秒,那麼除非您將其分解為較小的原子任務,否則 AWS lambda 將無法發揮其作用。這可能很棘手。Lambda 實例沒有 GPU,這限制了他們深度學習的效果。

其次,讓我們更深入地來討論一下算法。實際上 Pywren 只適合解決高度並行的問題。雖然 MapReduce 的工作是高度並行的,但他們大多數都會有重組的過程,這個過程裡面,結果會被聚合在一起並分發出去,方便進一步處理。 Pywren 團隊和其他人士已演示了將中間結果寫入 S3 的解決方案。但此時,pywren 不再是 python map 函數的內置替代品。你需要更仔細地考慮將要並行化的算法結構。

幸運的是,在線性代數方面,Pywren 團隊通過發佈 numpywren ,為我們解決了一些問題。它聽起來就像:從用戶角度來看,一個與 numpy 非常相似的線性代數庫。後端使用 AWS Lambda 來進行計算,但通過處理業務流程和所需的通信,將最快的算法應用於 serverless 的線性代數上,這遠遠超越了 Pywren。

結論

所以,你已經瞭解了 serverless 在網站部署和運行 cron 作業上的優點。最後你看到了,它在某一種計算上是很有優勢的,也就是高度並行化的東西。你甚至可以結合這些思想,想象一個工作流:使用 pywren 在 AWS lambda 上運行超參數優化,然後使用 zappa 將最佳模型部署到 AWS lambda 上。serverless 在這些方面是很有優勢的,因為它相對容易設置,並且使用起來很便宜。

它之所以便宜(不考慮本文中的高級原則),是因為這些應用程序有著共同點:它們都是突發式數據。有的時候他們需要大量資源,在這種情況下,lambda 可以通過擴展來容納這些資源。但有時候,它們幾乎或者完全不需要任何資源。你的 web 產品是新的,你的 cron 作業沒有啟動,或者你正在做交互式的工作,或者正在盯著筆記本,試圖找出這結果意味著什麼。這時如果你用了 serverless,你就不必再為這段時間花錢。

所以:serverless 的優勢不在於你用它的時候它是多麼強大(儘管擴張能力很強),而是在你不用它的時候。

AWS 不是為你使用的東西付費,而是為你忘記關掉的東西付費。-DeadProgrammer(@DeadProgrammer) 2019 年 4 月 24 日

還記得 Pywren 論文被稱為“ 99% 的分佈式計算”嗎?1% 的數據科學在從事數據科學的組織中運作,其規模總是需要一些計算資源的,全球各地都在使用,所以幾部不存在不使用的情況,又或者是他們的使用量非常大,相比較而言,購買集群機器更加合適(譯者注:前提提到的曲線交點,超過交點,使用 Serverless 反倒費錢)。假如數據科學家有數據工程學、工具和維護團隊,來處理這些長期使用的資源。本文中描述的 serverless,並不是為上述的這種數據科學家所準備的。

但是,如果你是獨立運營的,並且沒有資金和工程學支持的團隊,或對重量級解決方案沒有什麼興趣,那麼就瞭解一下 Zappa、Pywren 和 serverless 生態系統吧。

如果你還想知道更多的面試信息、技術知識,歡迎關注Java高級架構師阿谷,後續會繼續為大家分享更多的程序員進階指南!

"

相關推薦

推薦中...