GCP: AppEngine(GAE)的使用

一、基本概念

在全託管式的無服務器平臺上構建可擴縮性極強的應用, 將應用從零無縫擴容到全球級規模,而不用費心管理底層基礎架構。得益於零服務器管理和零配置部署,開發者可以專注於構建出色的應用,省去管理開銷。App Engine 支持多種主流開發語言以及各種開發者工具,可幫助開發者提高工作效率和靈活性。

在其他雲上與之對標的產品有:

  • Azure上的App Service,猛戳這裏
  • AWS 上的AWS Elastic Beanstalk
  • 阿里雲上的Web應用託管服務(Web+)

它有兩種環境,通常我們使用第一種

二、 快速上手(python3)

(一)、創建應用及app.yaml文件

先上目錄,創建得是一個基於python3.8的一個Flask應用,它足夠簡單,以便我們只關注感興趣的部分。

main.py 文件:是應用程序的入口,代碼如下,用戶訪問此網站時,會顯示方法show_hello返回的內容

import os
from flask import Flask
from dotenv import load_dotenv, find_dotenv

app = Flask(__name__)


@app.route('/')
def show_hello():
    myname = os.getenv('myname')
    return myname


if __name__ == '__main__':
    load_dotenv()
    app.run(port=1234)

requirements.txt文件:是依賴包

python-dotenv
flask

.env文件:是配置環境變量的,也就是說當用戶訪問此應用的時候,在本地運行會直接會顯示congcong這個名字。它是敏感文件。

myname=congcong

以上是Flask Web應用的基本的幾個文件,下面我們來看一下gcloud需要的文件

app.yaml文件:最重要就是它了,它定義了當前應用在GCP上的運行時是python3.7,服務的名字是default,設置了一個環境變量myname,在我們在GCP上部署好此應用後,那網頁上就會顯示myname的值。

注意:App Engine上第一個服務的服務名必須是default,第二個至第N個的務的名字隨便你自己定義,不然它會報錯”ERROR: (gcloud.app.deploy) INVALID_ARGUMENT: The first service (module) you upload to a new application must be the 'default' service (module). “,它會將我們的代碼上傳到GCP的某個地方。

runtime: python37
service: default

env_variables:
  myname: "Hello, this is from environment"

.gcloudignore文件:由於python3中的虛擬環境venv文件裏面的內容較多,如果將它部署上去顯然有些費時費力,我們需要把一些不需要的文件都排除掉,使用.gcloudignore

.gcloudignore
.git
.gitignore

# Python pycache:
__pycache__/
# Ignored by the build system
/setup.cfg

venv
.env

(二)、部署應用到GCP

下載並安裝Google SDK, 右擊以管理員身份運行,並配置好Service Account, 如果不會配置,猛戳如何創建並使用service account直接查看裏面的第二節,之後你就可以運行如下命令進行部署了,關鍵的命令只有一行

gcloud app deploy app.yaml

只需要上面的一行命令就可以把本地的代碼部署到雲端,這麼智能? 因爲用到了DevOps,實際上就是上面提到的Google的一個pipeline 服務。下面是完整的命令行及提示:

# 運行以下命令,安裝包含 Python 3.7 版 App Engine 擴展程序的 gcloud 組件:
gcloud components install app-engine-python

# 定位到你項目的根目錄,即包含app.yaml的文件夾下
cd C:\CongStudy\python-flask-nginx-demo

# 運行此命令部署你的應用程序,後面的app.yaml可以省略
c:\CongStudy\python-flask-nginx-demo>gcloud app deploy app.yaml
You are creating an app for project [qwiklabs-gcp-01-dbddd1fe79f0].
WARNING: Creating an App Engine application for a project is irreversible and the region
cannot be changed. More information about regions is at
<https://cloud.google.com/appengine/docs/locations>.

Please choose the region where you want your App Engine application
located:

 [1] asia-east2    (supports standard and flexible)
 [2] asia-northeast1 (supports standard and flexible)
 [3] asia-northeast2 (supports standard and flexible)
 [4] asia-northeast3 (supports standard and flexible)
 [5] asia-south1   (supports standard and flexible)
 ...
 [18] cancel
Please enter your numeric choice:  1

Creating App Engine application in project [qwiklabs-gcp-01-dbddd1fe79f0] and region [asia-east2]..
..done.
Services to deploy:

descriptor:      [c:\CongStudy\python-flask-nginx-demo\app.yaml]
source:          [c:\CongStudy\python-flask-nginx-demo]
target project:  [qwiklabs-gcp-01-dbddd1fe79f0]
target service:  [default]
target version:  [20200314t165844]
target url:      [https://qwiklabs-gcp-01-dbddd1fe79f0.appspot.com]


Do you want to continue (Y/n)?  y

Beginning deployment of service [default]...
╔════════════════════════════════════════════════════════════╗
╠═ Uploading 2 files to Google Cloud Storage                ═╣
╚════════════════════════════════════════════════════════════╝
File upload done.
Updating service [default]...done.
Setting traffic split for service [default]...done.
Deployed service [default] to [https://qwiklabs-gcp-01-dbddd1fe79f0.appspot.com]

You can stream logs from the command line by running:
  $ gcloud app logs tail -s default

To view your application in the web browser run:
  $ gcloud app browse

# 運行此命令,它會在瀏覽器中打開此網站
c:\CongStudy\python-flask-nginx-demo>gcloud app browse
Opening [https://qwiklabs-gcp-01-dbddd1fe79f0.appspot.com] in a new tab in your default browser.

(三)、效果

這是網頁

這是在GCP控制檯看到的截圖

到這裏,你已經基本掌握了app engine的用法了。但仍然還有許多的問題需要解決:

  1. 它的實例默認是隨着訪問量自動擴縮的,是如何自動擴縮的呢,我可以控制嗎?
  2. 它自動擴縮時,肯定是以某一代碼源或者artifact爲基礎,那我上傳的代碼放在哪裏呢?
  3. 如何綁定自定義域名及安全證書,當實例數自動擴展時,它是如何將證書自動安裝到新的實例上?
  4. 如果我有多個服務,卻只有一個域名,如何通過一個域名訪問到多個服務,即反向代理是咋整的?
  5. 多個服務中,服務與服務之間如何調用,它們在同一個局域網內嗎? 互相訪問時可以走內網嗎,走外網就慢了。
  6. 上圖中有一個version 1,可以看出每個服務都有版本控制的,不同版本之間我能對流量進行控制嗎?
  7. 灰度部署/藍綠部署/AB測試這些都支持麼?
  8. 對這些服務如何追蹤和監控,有日誌麼,在哪裏看到我的服務的使用情況。

 

三、 設計應用

爲了回答上面所有的問題,只有一個服務顯然不夠。

以下示例展示了當您在本地開發應用時,一個含有三項服務的應用可能具有的結構。可選的 dispatch.yaml 已添加到該應用的根目錄中。在根目錄下還有三個目錄,分別對應於應用的每項服務。service1 的子目錄包含該服務的源文件和配置文件。同樣,service2 和 service3 均位於獨立的目錄中,這些目錄分別包含對應服務的文件,而 service3 包含 YAML 配置文件的兩個版本:

上面的service<num>.yaml,如service1.yaml,和我們之間定義的app.yaml是同類型文件,它定義了運行時的環境等,專門用於部署的。但上圖中多出來了一個dispatch.yaml,從字面上就可以看出它是用於路由分發的,即所謂的反向代理。這裏我們先列出所有的配置文件出來,共有4個:

(一)、app.yaml

這個我們已經見過了,不僅定義了運行時的環境,而且可以處理擴縮、緩存等,這裏我們列出一個稍複雜的例子。

runtime: python37 # 定義運行時

service: service_name # 定義你的服務名,注意第一個服務名只能叫default

instance_class: F2 # 定義實例了類型,類型有F1、F2、F4、F4_1G

env_variables: #定義環境變量
  BUCKET_NAME: "example-gcs-bucket"

handlers: # 配置單個服務內的路由
# Matches requests to /images/... to files in static/images/...
- url: /images
  static_dir: static/images
  http_headers:   # 還可以設置http頭哦
    X-Foo-Header: foo
    X-Bar-Header: bar value
    Access-Control-Allow-Origin: http://mygame.appspot.com  # 來個CORS 跨域支持

- url: /.* # 重定向
  secure: always
  redirect_http_response_code: 301
  script: auto

error_handlers: # 對錯誤請求的處理
  - file: default_error.html

  - error_code: over_quota
    file: over_quota.html

# 自動擴縮的配置在這裏
automatic_scaling:
  target_cpu_utilization: 0.65           # CPU使用率達到 65% 後會啓動新實例
  min_instances: 5                       # 最小實例數是5            
  max_instances: 100                     # 最大實例數是100
  min_pending_latency: 30ms              # 最小冷卻時間,即允許請求在待處理隊列中等待的最短時間。當處理請求時達到某一閾值後,不會立即擴縮,會等待30ms,例如:當服務過載時,一個請求在待處理隊列中等待,appengine不會立即新增實例,至少等待30ms纔會擴展實例數, 30ms之前肯定不會擴展實例數
  max_pending_latency: 100ms             # 最大冷卻時間,App Engine 允許某請求在待處理隊列中等待的最長時間。App Engine 可以在“min-pending-latency”與“max-pending-latency”中指定的時間之間隨時創建實例。也就是說,App Engine 不會在“min-pending-latency”中指定的時間之前創建實例來處理待處理請求,但 App Engine 會在達到“max-pending-latency”之後創建實例。
  max_concurrent_requests: 50            # 自動擴縮實例可接受的併發請求數
  max_idle_instances                     # App Engine 應爲此版本保留的最大空閒實例數

# 手動擴縮
manual_scaling:
  instances: 5                           # 在開始時分配給服務的實例數
  

(二)、dispatch.yaml 

定義路由規則的配置文件,顧名思義,它就是通過url中的path來查找相應的服務的。例如下面有三個服務分別是service1/service2/service3,你只有一個域名是www.example.com,當你訪問www.example.com/BBB的時候,實例調用得服務是service2

dispatch:
  - url: 'www.example.com/default/*'
    service: default
  - url: 'www.example.com/AAA*'
    service: service1
  - url: 'www.example.com/BBB*'
    service: service2
  - url: 'www.example.com/CCC*'
    service: service3

 如何部署此文件,只需要一行命令,如下:

gcloud app deploy dispatch.yaml

來一個實際的例子,如下圖:

 看到這裏相信你應該明白的三點:

  • 爲什麼第一個服務名必須是default, 因爲當你不定義dispatch.yaml時,即沒有路由規則時,它會調用服務default
  • 拿我第二節創建的demo爲例,可以看出我沒有創建dispatch.yaml文件,所以上圖紅色塊中是爲空的。
  • 只有先部署了你的所有服務之後,才能部署dispatch.yaml文件。即運行了gcloud app deploy app.yaml 後,才能運行gcloud app deploy dispatch.yaml 

(三)、cron.yaml 

這個配置文件是可選的,它是搞定期計劃任務的。漲下面這樣,就不多解釋了

cron:
- description: "daily summary job"
  url: /tasks/summary
  schedule: every 24 hours
- description: "monday morning mailout"
  url: /mail/weekly
  schedule: every monday 09:00
  timezone: Australia/NSW
- description: "new daily summary job"
  url: /tasks/summary
  schedule: every 24 hours
  target: beta

 如何部署此文件,只需要一行命令,如下:

gcloud app deploy cron.yaml

 (四)、index.yaml 

這個配置文件更可選了,不想研究了,是配置 Datastore 索引,有了它,執行查詢時,Datastore 可快速返回結果。下面是官網解釋。

您可以將標準環境中運行的應用的數據存儲在 Cloud Datastore 中。Cloud Datastore 使用索引來處理應用執行的每個查詢。實體發生變化時這些索引也會得到更新,因此在應用執行查詢時,Datastore 可快速返回結果。

四、服務之間通信

重要!單獨列爲一節,要與您的 App Engine 服務進行通信,最簡單的方法是發送定向 HTTP 請求,在網址中包含資源的名稱或 ID。例如,除相應的 GCP 項目 ID 之外,您還可以包含要定位的服務或版本的 ID:

http://[VERSION_ID].[SERVICE_ID].[MY_PROJECT_ID].appspot.com
https://[VERSION_ID]-dot-[SERVICE_ID]-dot-[MY_PROJECT_ID].appspot.com

 App Engine 服務還可以使用 Cloud Pub/Sub 通信,以便在進程(包括 App Engine)之間提供可靠的異步多對多消息傳遞。

 

五、AppEngine存儲數據和文件

App Engine只允許你存入臨時文件,只能存入 /tmp 目錄。 我在實際項目中用到了,所以列出來了,雖然不是重點。

AppEngine裏的實例原則上是不允許你存任何東西的,因爲自動擴縮的時候,實例隨時會銷燬,實例銷燬後,你存的東西全部會刪除。但有時候程序需要存一些臨時文件,舉個例子:用戶點擊下載時生成一個Excel文件、壓縮一下再傳到Google Storage裏供用戶下載,那個Excel文件就需要臨時存一下。

六、使用自定義域名和安全證書

直接在控制檯裏面配置就可以了,注意這裏是cname,將AppEngine自動生成的域名和你定義的域名在域名解析的地方綁定起來就好了。AppEngine自動生成的域名與它背後的服務器之間是天生自帶負載勻衡(7層/4層)的,不需要我們作額外的控制。

七、流量管理

一些相關的概念

  1. 藍綠部署(Blue/Green Deployment):  它是最常見的一種0 downtime部署的方式,就是準備兩個版本,舊版本A在生環境中正常運行,要發部署新版本B後,直接將流量切換到新版本B,如果測試發現新版本B有問題,可以將流量快速切回到舊版本A。
  2. 紅黑部署(Red-Black Deployment):與藍綠部署很相似,藍綠表示生產環境有兩個版本都可用,綠表示生產環境正在用,藍表也可用,但是個備胎;紅黑部署中,紅是正在用,黑是不可用的。但在雲環境中,這兩個概念似乎弱化了,感覺就是同一個意思。
  3. 灰度發佈/金絲雀發佈:就是流量拆分,例如90%的用戶維持使用老版本,10%的用戶嚐鮮新版本。
  4. 滾動發佈(rolling update):一般是取出一個或者多個服務器停止服務,執行更新,並重新將其投入使用。週而復始,直到集羣中所有的實例都更新成新版本,例如每次只取出集羣的20%進行升級,Kubernetes中的滾動升級就是屬於這種。
  5. A/B 測試(A/B Testing): A版本是線上穩定版本,B版本是新版本,如果一下子切到B環境,可能用戶會難以適應,所以先部署一個B環境,分一部分流量出來,收集用戶反饋後逐步改進B版本,直到用戶可以完全接受用B版本替換A版本的程度。

總結:你品,你細品,它們之間都是有區別的。 另外,灰度發佈經常與A/B 測試一起使用,用於測試選擇多種方案。AB test就是一種灰度發佈方式,讓一部分用戶繼續用A,一部分用戶開始用B,如果用戶對B沒有什麼反對意見,那麼逐步擴大範圍,把所有用戶都遷移到B上面來。

當一個服務有多個版本時,有兩種辦法來控制多個版本之間的流量

(一)、部署一個新的版本後,將流量100%從舊版本切換到新版本(Swap方式)

適合小項目,扎心了,沒權限,但它就在這裏,如下圖。

但有個小問題,它是屬於藍綠部署,還是紅黑部署呢?  因爲對於流量爲0版本我也不知道它有沒有死,並且它不止兩個版本,如果有7個版本,你準備怎麼稱呼?

(二)、部署一個新的版本後,對流量進行拆分

 例如,你擔心這個新版本可能會有問題,只將一部分流量,比如5%分給新的版本,剩餘95%繼續給舊版本。點擊下圖的按鈕就可以拆分流量,前提是要有至少2個版本。

那它可以有以下幾種形式進行拆:

  1. IP 地址拆分: 當該應用收到請求時,它會將 IP 地址哈希處理爲介於 0-999 之間的值,並使用該數字來路由請求。IP 地址拆分具有很大的侷限性,比如IP 地址可能會不斷變化,用戶一會用新版本一會用舊版本,體驗不好。

  2. Cookie 拆分:應用會在HTTP頭部查找名爲 GOOGAPPUID 的 Cookie,該 Cookie 中包含一個 0 至 999 之間的值,如果存在此 Cookie,則使用該值路由請求,如果沒有此 Cookie,則會隨機路由請求。

總結:

  1. 流量拆分最容易遇到緩存問題,比如A和B兩個版本,新版本B對CSS文件作改動了,而用戶本地緩存了此文件,A 和 B 兩個版本之間拆分流量後,很可能導致舊版本使用新的CSS文件,最好是在HTTP頭部加上Cache-Control和Expires來解決。
  2. 服務與服務之間的調用最好改用 Cookie 拆分。
  3. 這裏能按人拆分?例如:只有張三登錄才能使用最新版本。  不支持,只有以上兩種方式!!!

八、日誌與監控

GCP有專門的服務來解決這類問題。

  • 查日誌請訪問Logging
  • 查流量、與監控請訪問Trace, "projects/[PROJECT_ID]/traces/[TRACE_ID]"

九、總結

還記得上面第2節中的8個問題麼? 還有一個問題沒有回答。

1. ”它自動擴縮時,肯定是以某一代碼源或者artifact爲基礎,那我上傳的代碼放在哪裏呢“ ?

答:它會將你上傳的代碼或artifact放在Google Storage中,當你發佈你的應用時,它會自動在Google Storage創建一個Buket來存放你的代碼。

2. 我們創建的Python Web應用,它得Web服務器是用得什麼,爲什麼只需要上傳代碼後Web應用自動能運行?

答:Python Web應用最常使用得Web服務器是uswgi,當你部署代碼時,實際上你的代碼會被打包成一個自定義的Docker Image裏,當你選擇運行時時,這個對應的Base Image也就選定好了,Base Image是Google預先定義的,裏面早已有了Python需要的Web服務器及相應的組件,默認使用gunicorn 用作 Web 服務器,參考這裏

3. 上面的8個問題歸納一下

答:用一話來說,就是你的服務治理是如何實現的,我們可以將這些問題總結爲4點:連接、保護、控制、觀測。這些問題由GCP平臺都幫你實現了,我們只需要使用就可以了,如果你想繼續深入的研究,可以學習微服務,本主將會後緒一一更新。解決問題的能力來源於你深入的研究與實踐,加油!

4. 注意事項

答:AppEngine應用應該是“無狀態”的,且實例上不存儲任何內容。

 

參考文獻

https://cloud.google.com/appengine/docs/standard/python3

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章