當前位置:
首頁 > 科技 > 如何在 15 分鐘內構建一個無伺服器服務?

如何在 15 分鐘內構建一個無伺服器服務?

「無伺服器」(Serverless)這個詞已經流行了有一段時間了。

亞馬遜在2015年發布了AWS Lambda服務之後,出現了許多工具,利用這些工具只需幾個命令就可以建個無伺服器服務。與傳統的服務相比,無伺服器服務具有容易開發、容易部署、容易維護的特點。它們的性價比還非常高,特別是對於那些沒有太大流量的簡單服務。

什麼是無伺服器?

顧名思義,「無伺服器」就是說運行服務的時候不需要伺服器。準確地說,服務依然是跑在伺服器上的,但是作為開發者,你不需要考慮伺服器的問題。

以AWS Lambda為例,你可以寫一個「函數」部署到Lambda上,這個函數可以處理HTTP請求。AWS會跑一個伺服器,負責運行所有函數,每當你的HTTP endpoint被訪問時,Lambda就會調用你的函數來處理請求。但伺服器的事情完全不需要你操心,你要做的只有寫個函數,然後扔到Lambda上。

這種服務最大的好處就是非常便宜。還以AWS Lambda為例,Lambda每個月提供免費的100萬次訪問,和40萬GB秒的計算力(1GB秒的意思是你的運算可以佔用1GB的內存1秒鐘)——對於絕大部分的小型服務來說這就足夠了。相比之下,EC2的nano實例的費用是每小時$0.0058,摺合每天$0.14,還是Lambda要便宜得多。

用AWS部署一個無服務架構的個人網站

在這篇文章里我想介紹下怎樣利用AWS部署一個無服務架構的個人網站。這個個人網站將具備以下特點:

包含前端和後端;

基本上以靜態文件為主,或者主要的計算都在前端(比如React應用);

與後台通過API通信,但數量非常少;

後台不需要太大內存或CPU(比如一個簡單的網頁計數器,每次請求只需要訪問一次資料庫)。

服務將部署到以下域名上(這裡用的都是假想的域名):

API服務:https://myservice-api.example.com

前端:https://myfrontend.example.com

這裡用了HTTPS,因為各大瀏覽器早已開始將HTTP協議標記為不安全協議了。為了保證安全,HTTPS是必要的,後面會介紹如何設置證書等。

整個網站將使用以下的AWS服務:

Lambda + API Gateway + S3,用於跑API伺服器;

DynamoDB,數據存儲;

S3,靜態網站;

Cloudfront,分散式CDN,用作靜態網站和API的前端;

AWS Certificate Manager(ACM),為https網站生成證書。

至於API伺服器的開發部署,我們採用Python + Flask的組合開發服務,然後用Zappa(https://github.com/Miserlou/Zappa)作為無伺服器部署工具。

設置AWS環境

首先需要設置AWS環境,以便從代碼和zappa中訪問AWS。需要兩個步驟:

創建AWS用戶,用於程序訪問;

設置本地環境,使代碼使用AWS用戶。

創建AWS用戶

登錄到AWS中,選擇「IAM」服務來管理用戶。

創建一個名為「myservice-admin」的用戶(或者任何你喜歡的用戶名),勾選「Programmatic access」選項。

在下一步中,點擊「Attach existing policies directly」按鈕,然後將「AdministratorAccess」添加到該用戶。

從安全的角度來說這種做法並不好。不過出於演示的目的,本文不再詳述怎樣找出部署無服務架構所需的許可權了。

點擊「Next」按鈕,最後點擊「Create User」按鈕,myservice-admin用戶就建好了。注意在創建成功的那個畫面上會顯示Access Key ID和Secret access key兩個值。務必要將這兩個複製保存下來,稍後要用它們來設置本地環境。

這個畫面是唯一能看到Secret access key的地方!如果你忘了複製就關閉了頁面,那就只能去用戶的詳細畫面去生成新的access key和secret了。

設置本地AWS環境

為了在本地使用AWS,我們需要創建本地環境。

首先安裝awscli工具,用它來幫我們配置環境:

安裝結束後,就可以使用aws configure命令進行設置:

這裡需要輸入上一步保存下來的Access Key ID和Secret Access Key值。至於區域,我用的是us-east-1。其他區域應該也可以,但如果你要像我一樣使用CloudFront的話,其他區域可能會有一些麻煩。

在DynamoDB中創建表

我們的後台API要實現一個計數器。為了保存計數器的數值,我們需要使用DynamoDB。DynamoDB是AWS提供的一個鍵值資料庫。首先我們需要在DynamoDB中建一個表,並設置好我們需要的計數器初始值。

在AWS控制台中選擇DynamoDB服務,然後點擊「Create Table」按鈕。在「Create DynamoDB table」畫面,在Table name中填寫myservice-dev,Primary key欄位填寫id,然後點擊Create Table按鈕。

幾秒鐘之後表就建好了。選擇剛剛建好的表,然後在右側選擇Items選項卡,單擊Create item按鈕創建一個項目,項目內容為id="counter"及counter_value=0。

創建值時需要點擊左側的加號按鈕才能添加counter_value屬性,而且別忘了把counter_value屬性的類型設置為Number。

創建API服務

接下來我們要建立API服務。這個API將提供一個計數器API,每次調用都會將計數器的值加一。計數器值保存在DynamoDB中。API的endpoint如下:

POST /counter/increase:增加計數器的值,並返回計數器值;

GET /counter:返回計數器值。

用Python和Flask編寫API服務

首先我們要創建Python虛擬環境,並安裝必要的包:

Flask是Web框架,boto3是訪問DynamoDB必須的包。simplejson可以解決一些JSON轉換時遇到的問題。接下來創建myservice.py,內容如下:

再創建一個run.py,以便在本地測試該服務:

運行服務:

這樣就可以在命令行中測試這個服務了(再開一個終端輸入下面的命令):

我們可以看到計數器的值增加了,說明這個服務可以用了!

將服務部署到Lambda上

要部署API到Lambda上,可以使用Zappa包。Zappa包使得部署微服務變得極其容易。首先安裝Zappa:

然後執行Zappa init命令初始化Zappa環境。它會問你幾個問題,但基本上可以使用默認值來回答:

初始化完成後,在目錄下會生成一個zappa_settings.json文件。然後就可以部署服務了:

現在我們的服務就部署成功了。可以用下面的Curl命令測試,也可以打開瀏覽器測試GET的API:

綁定自定義域名

不過上面的API服務還有一個小問題。自動生成的API endpoint是2ks1n5nrxh.execute-api.us-east-1.amazonaws.com,很難記也不好用。不過我們可以很容易地給它綁定一個自定義域名。

我們的自定義域名是https://myservice-api.example.com。為了使用HTTPS,我們需要現申請一個證書。AWS的Certificate Manager服務提供免費的證書。生成證書之後就可以在AWS的API Gateway里自定義域名了。

申請證書

從AWS控制台切換到ACM服務(服務名稱叫Certificate Manager,但敲ACM就能搜索到)。點擊Request a certificate按鈕,然後選擇Request a public certificate選項。選擇公開的證書就是免費的。

下一步,我們需要向AWS證明我們擁有這個域名。我這個域名是從Google Domains申請的,所以我在這裡選擇DNS validation。點擊Review按鈕然後點擊Confirm and Request。

現在證書請求已經生成了,AWS會顯示一個驗證畫面,上面寫明了怎樣驗證該域名:

根據說明,我們需要在域名下添加一條CNAME記錄。由於我的域名是從Google Domains申請的,我就打開Google Domains,找到域名example.com,然後添加上面指定的CNAME:

這裡在Name欄中只添加了_2adee19a0967c7dd5014b81110387d11字元串,去掉了後面的.example.com部分,否則.example.com就重複了。

接下來要等待大約10分鐘,AWS Certificate Manager就會去驗證域名了。驗證成功後,Status欄會顯示「Issued」。

現在證書已經申請好了,我們可以繼續去給API綁定域名。

為API服務綁定自定義域名

切換到API Gateway服務。從左側的APIs一欄可以看到,Zappa已經幫我們建好了myservice-dev服務。

從左側點擊「Custom Domain Names」,然後點擊右側的Create Custom Domain Name按鈕,填寫必要的欄位。

這裡我希望API使用CloudFront服務,這樣能在全世界都達到最理想的訪問速度,因此我選擇了Edge Optimized。如果不使用CloudFront,你可以選擇Regional。

點擊Save按鈕後,這個自定義域名綁定就建好了。實際上要等待大約40分鐘左右域名綁定才能正常使用,不過我們可以利用這段時間去配置DNS。

回到Google Domains添加這條CNAME:

該步驟完成後,等待大約40分鐘,等API Gateway中的「Initializing...」字樣消失後,自定義域名就可以使用了。

前端的靜態網站

接下來我們要給這個API服務創建一個前端。作為例子,這裡只創建一個非常簡單的頁面,它能調用/counter/increase。

前端編程

先建一個目錄myfrontend:

然後建一個簡單的HTML文件index.html:

將前端發布到S3

我們可以把前端部署到S3上。首先需要建一個桶,桶的名字就是域名。

接下來要把我們的網站放到這個桶中。打開該桶,選擇Properties選項卡,然後選擇Static Web Hosting。在彈出的對話框中選擇Use this bucket to host a website,在Index document欄位中輸入index.html。點擊Save關閉對話框。

上面顯示了「Endpoint」鏈接,我們稍後會用這個URL測試靜態網站。

最後一件事就是讓這個桶允許公開訪問。我們需要添加一個桶策略來實現這一點。打開這個桶,選擇Permissions選項卡,然後點擊Bucket Policy按鈕。

保存之後,我們應該可以在Bucket Policy按鈕上以及Permissions選項卡上看到橙色的「public」字樣,表明我們的桶是可以被公開訪問的。

這樣桶就建好了,但裡面還是空的,現在需要把網站的內容上傳到這個桶中。首先進入剛才建好的myfrontend目錄中,然後輸入下面的命令:

上面的命令會把當前目錄下(注意命令中的那個點 . )的所有文件都上傳到S3中。

現在就完成了!在瀏覽器中打開下面的地址就可以看到網站內容了(地址就是前面創建桶時顯示的Endpoint的URL):

嗯?貌似不太對。計數器沒有顯示任何值呢?

而且似乎有JavaScript錯誤。打開瀏覽器的控制台就能看到以下錯誤:

Failed to load https://myservice-api.example.com/counter: No "Access-Control-Allow-Origin" header is present on the requested resource. Origin "http://myfrontend.example.com.s3-website-us-east-1.amazonaws.com" is therefore not allowed access. If an opaque response serves your needs, set the request"s mode to "no-cors" to fetch the resource with CORS disabled.

顯然,我們需要設置CORS header(https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)才能讓這個腳本工作,因為後台API被放到了另一個域名上(myservice-api.example.com和myfront.example.com不是同一個域名)。

不過由於我們還要給前端綁定自定義域名,綁定後URL會發生變化,所以這裡先放一放,等一會兒綁定好域名之後再來考慮CORS的問題。

給靜態網站設置CloudFront和自定義域名

最後一步就是給前端設置CloudFront並綁定自定義域名。前面我們已經申請了*.example.com的證書,所以這一步就很容易了。

從AWS控制台中切換到CloudFront服務。點擊Create Distribution按鈕,然後點擊Web里的Start按鈕。

在「Create Distribution」畫面上,我們需要填寫以下信息:

創建好distribution後,就可以在distribution列表中看到CloudFront的域名了。

上面的狀態還是「In Progress」,我們可以利用這段時間去設置DNS。跟前面類似,去Google Domains里添加一個CNAME:

解決CORS問題

現在唯一的問題就是CORS了。CORS是由於前端和後台的域名不一致導致的,為了讓前端能訪問後台API,我們需要給後台添加CORS支持。

回到API的代碼目錄(myservice),激活Python環境。然後安裝flask_cors包:

然後編輯myservice.py,添加以下幾行(3和6):

最後發布到AWS Lambda:

試著刷新下瀏覽器。現在就能看到計數器顯示了正確的值。點擊「Increase Counter」按鈕也能增加計數器的值了。

總結

這篇文章介紹了創建一個簡單的無伺服器服務所需的多種AWS服務。如果你對AWS不熟悉,你可能會覺得我們用到了太多的服務,但其實絕大部分AWS服務都是一次性的,一旦設置好之後就不用再管了。以後的開發中用得上的只有zappa update和aws s3 sync兩條命令而已。

而且至少,這種方法要比自己設置一台VPS、安裝Web伺服器再寫個Jenkins腳本做持續部署要方便多了。

作為總結,下面是這篇文章的一些重點:

Lambda可以運行簡單的服務,服務可以通過API Gateway暴露成HTTP服務;

如果要用Python寫無伺服器服務,那麼Zappa是個非常方便的工具;

S3桶可以用作靜態網站使用;

要想使用HTTPS,可以通過AWS ACM申請證書;

API Gateway和CloudFront都支持自定義域名。

希望這篇文章對你有所幫助!


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 CSDN 的精彩文章:

程序員如何做到每天 17:00 準時下班?
掙扎 7 年,蘋果 Siri 還是被「拋棄」了

TAG:CSDN |