從 App Engine Memcache 遷移至 Cloud Memorystore (單元 13)

1. 總覽

無伺服器遷移站系列的程式碼研究室系列 (自助式實作教學課程) 和相關影片,旨在引導 Google Cloud 無伺服器開發人員透過一或多種遷移作業 (主要用於遷移舊版服務) 逐步翻新應用程式。這麼一來,您的應用程式就能更具可攜性,並提供更多選擇和使用彈性,進而整合及使用更多 Cloud 產品,也更容易升級至較新的語言版本。本系列課程一開始將著重在最早的 Cloud 使用者 (主要是 App Engine (標準環境) 開發人員),但涵蓋其他無伺服器平台,包括 Cloud FunctionsCloud Run 或其他無伺服器平台 (如適用)。

本程式碼研究室旨在向 Python 2 App Engine 開發人員說明如何從 App Engine Memcache 遷移至 Cloud Memorystore (適用於 Redis)。另外也提供從 App Engine ndbCloud NDB隱含遷移功能,但主要涵蓋在單元 2 程式碼研究室中。請參閱這裡的更多逐步說明

在接下來的研究室中

  • 透過 Cloud 控制台或 gcloud 工具設定 Cloud Memorystore 執行個體
  • 透過 Cloud 控制台或 gcloud 工具設定 Cloud Serverless VPC Access 連接器
  • 從 App Engine Memcache 遷移至 Cloud Memorystore
  • 在範例應用程式中使用 Cloud Memorystore 快取
  • 從 App Engine ndb 遷移至 Cloud NDB

軟硬體需求

問卷調查

您會如何使用這個教學課程?

只能閱讀 閱讀並完成練習

您對 Python 的使用體驗有何評價?

新手 中級 還算容易

針對使用 Google Cloud 服務的經驗,您會給予什麼評價?

新手 中級 還算容易

2. 背景

本程式碼研究室說明如何將範例應用程式從 App Engine Memcache (和 NDB) 遷移至 Cloud Memorystore (和 Cloud NDB)。這個程序包括替換 App Engine 套裝組合服務的依附元件,提高應用程式的可攜性。您可以選擇繼續使用 App Engine,或考慮改用上述的替代方案。

與本系列中的其他單元相比,這項遷移作業需要投注更多心力。如要取代 App Engine Memcache,建議改用 Cloud Memorystore 這項全代管雲端式快取服務。Memorystore 支援一組常見的開放原始碼快取引擎 RedisMemcached。這個遷移模組會使用 Cloud Memorystore for Redis。詳情請參閱 Memorystore 和 Redis 總覽

由於 Memorystore 需要執行的伺服器,因此也需要 Cloud VPC。具體來說,您必須建立無伺服器虛擬私有雲存取連接器,App Engine 應用程式才能透過私人 IP 位址連線至 Memorystore 執行個體。完成這項練習後,您會更新應用程式,讓應用程式恢復正常運作,Cloud Memorystore 會成為快取服務,並取代 App Engine 的 Memcache 服務。

本教學課程一開始先從Python 2 中的模組 12 範例應用程式開始,接著進行額外的 (選用) 小幅升級至 Python 3。如果您已熟悉如何透過 Python 3 App Engine SDK 存取 Python 3 的 App Engine 配套服務,可改為從模組 12 範例應用程式的 Python 3 版本著手。這麼做需要移除 SDK 的使用行為,因為 Memorystore 不是 App Engine 套裝組合服務。瞭解如何使用 Python 3 App Engine SDK,但不屬於本教學課程的內容。

本教學課程包含以下重要步驟:

  1. 設定/準備
  2. 設定快取服務
  3. 更新設定檔
  4. 更新主要應用程式

3. 設定/準備

準備 Cloud 專案

建議您重複使用先前用於完成單元 12 程式碼研究室的專案。或者,您可以建立新的專案,或是重複使用其他現有專案。本系列文章中的每個程式碼研究室都有「開始」(起始的基準程式碼) 和「FINISH」(已遷移的應用程式)。我們提供 FINISH 代碼,方便您比較自己的解決方案與我們的解決方案,以備不時之需。如果發生問題,您隨時都可以復原至 START。這些查核點旨在確保您能順利瞭解如何執行遷移作業。

無論您使用的任何 Cloud 專案,都請確保該專案具備有效的帳單帳戶。此外,請確認已啟用 App Engine。請詳閱並確認您瞭解進行這些教學課程中的一般費用影響。不過,本程式碼研究室使用的 Cloud 資源「沒有」免費方案,因此與本系列中的其他資源不同。在練習過程中,系統會產生一些費用。我們也會提供更多具體的費用資訊,以及減少用量的建議,包括最後說明如何釋出資源以將帳單費用降到最低。

取得基準範例應用程式

從我們要開始的基準單元 12 程式碼,本程式碼研究室將逐步引導您完成遷移作業。完成後,您將會進入正常運作的模組 13 應用程式,且應用程式與其中一個 FINISH 資料夾中的程式碼十分相似。這些資源如下:

START 資料夾應包含下列檔案:

$ ls
README.md               app.yaml                main.py                 requirements.txt        templates                

如果您是從 Python 2 版本開始,系統也會提供 appengine_config.py 檔案,如果您已完成單元 12 程式碼研究室,也可能含有 lib 資料夾。

(重新) 部署模組 12 應用程式

剩餘的前置作業步驟:

  1. 如有需要,您可以重新熟悉 gcloud 指令列工具
  2. (視需要重新) 將模組 12 程式碼部署至 App Engine

Python 2 使用者應使用下列指令刪除並重新安裝 lib 資料夾:

rm -rf ./lib; pip install -t lib -r requirements.txt                

現在,所有 (Python 2 和 3 使用者) 都必須透過以下指令將程式碼上傳至 App Engine:

gcloud app deploy                

成功部署後,請確認應用程式的外觀和功能就像單元 12 中的應用程式一樣,這是一個可以追蹤造訪的網頁應用程式,然後快取至同一使用者一小時。

dfe56a02ae59ddd8.png

由於系統會快取最近的造訪記錄,因此網頁重新整理能夠快速載入。

4. 設定快取服務

Cloud Memorystore 並非無伺服器。必須提供執行個體;在本例中有 1 個執行 Redis與 Memcache 不同,Memorystore 是獨立的 Cloud 產品,提供免費方案。在繼續操作之前,請務必詳閱 Memorystore for Redis 定價資訊。為了盡可能降低這項練習的費用,我們建議至少可運作的資源量:「基本」服務級別和 1 GB 的容量。

Memorystore 執行個體與 App Engine 應用程式 (執行個體) 位於不同網路,因此必須建立無伺服器虛擬私有雲存取連接器,App Engine 才能存取您的 Memorystore 資源。如要盡可能降低虛擬私有雲費用,可選擇執行個體類型 (f1-micro) 和要要求的執行個體數量下限 (建議至少 2 個最多 3 個)。另請查看 VPC 定價資訊頁面

我們會引導您建立各項必要資源,同時也會重複採用這些有助於降低成本的建議。此外,在 Cloud 控制台建立 Memorystore 和虛擬私有雲資源時,右上角會顯示各項產品的定價計算工具,方便您預估每月費用 (請見下方插圖)。如果變更選項,這些值會自動調整。大致如下:

7eb35ebf7248c010.png

這兩項資源都是必要資源,也可以自行建立。如果是先建立 Memorystore 執行個體,App Engine 應用程式就無法在沒有虛擬私有雲連接器的情況下連線。同樣地,如果是先建立虛擬私有雲連接器,App Engine 應用程式就無法在該虛擬私有雲網路之間進行通訊。本教學課程說明如何先建立 Memorystore 執行個體,接著建立虛擬私有雲連接器。

這兩項資源都連上網路後,請在 app.yaml 中新增相關資訊,以便應用程式存取快取。您也可以參閱官方說明文件中的 Python 2Python 3 指南。您可以參考 Cloud NDB 遷移頁面 ( Python 2Python 3) 的資料快取指南。

建立 Cloud Memorystore 執行個體

由於 Cloud Memorystore 沒有免費方案,建議您分配最少的資源來完成程式碼研究室。您可以使用下列設定,將費用降到最低:

  • 選取最低的服務級別:[基本] (控制台預設值:「標準」,gcloud 預設值:「基本」)。
  • 選取最低的儲存空間容量:1 GB (控制台預設值:16 GB,gcloud 預設:1 GB)。
  • 一般來說,「任何」軟體需要的資源數量最多,但我們不建議您選取最舊的版本。第二個最新的版本是 Redis 版本 5.0 (控制台預設值:6.x)

瞭解這些設定後,下一節會引導您透過 Cloud 控制台建立執行個體。如果您偏好透過指令列進行這項操作,請略過這個步驟。

使用 Cloud 控制台

前往 Cloud 控制台的 Cloud Memorystore 頁面 (系統可能會要求您提供帳單資訊)。如果尚未啟用 Memorystore,系統會提示您啟用:

68318997e3105db6.png

啟用後 (可能也伴隨計費),您就會進入 Memorystore 資訊主頁。您可以在這裡查看專案中建立的所有執行個體。下方的專案沒有任何,因此會顯示「沒有可顯示的資料列」。如要建立 Memorystore 執行個體,請按一下頂端的「建立執行個體」

63547aa575838a36.png

本頁面提供用來建立 Memorystore 執行個體所需設定的表單:

b77d927287fdf4c7.png

如要降低本教學課程和範例應用程式的費用,請遵循先前提及的建議。選好之後,按一下「建立」。建立程序需要幾分鐘的時間才能完成。作業完成後,請複製執行個體的 IP 位址通訊埠編號,並將其新增至 app.yaml

透過指令列

雖然從 Cloud 控制台建立 Memorystore 執行個體可獲得豐富的視覺化資訊,但有些偏好使用指令列。請務必先安裝初始化 gcloud,再繼續操作。

和 Cloud 控制台一樣,您必須啟用 Cloud Memorystore for Redis。發出 gcloud services enable redis.googleapis.com 指令,然後等待作業完成,範例如下:

$ gcloud services enable redis.googleapis.com
Operation "operations/acat.p2-aaa-bbb-ccc-ddd-eee-ffffff" finished successfully.

如果服務已啟用,再次執行指令就不會產生 (負面) 副作用。啟用服務後,就可以建立 Memorystore 執行個體。指令如下所示:

gcloud redis instances create NAME --redis-version VERSION \
    --region REGION --project PROJECT_ID

Memorystore 執行個體選擇名稱。本研究室使用「demo-ms」做為名稱和專案 ID「my-project」。這個範例應用程式的區域為 us-central1 (與 us-central 相同),但如有延遲問題,建議使用較靠近您的區域。您必須選取與 App Engine 應用程式相同的區域。您可以選取任何偏好的 Redis 版本,但我們會按照建議步驟使用第 5 版。根據這些設定,以下是你要使用的指令 (以及相關輸出內容):

$ gcloud redis instances create demo-ms --region us-central1 \
    --redis-version redis_5_0 --project my-project

Create request issued for: [demo-ms]
Waiting for operation [projects/my-project/locations/us-central1/operations/operation-xxxx] to complete...done.
Created instance [demo-ms].

與 Cloud 控制台的預設值不同,gcloud 預設為最少的資源。因此,該指令產生的結果不需要服務層級和儲存空間容量。建立 Memorystore 執行個體需要幾分鐘的時間,完成後請記下執行個體的 IP 位址和通訊埠編號,因為這些執行個體很快就會新增至 app.yaml

確認執行個體已建立

使用 Cloud 控制台或指令列

無論您是透過 Cloud 控制台或指令列建立執行個體,都能使用下列指令來確認執行個體可供使用:gcloud redis instances list --region REGION

下列指令會執行檢查 us-central1 區域中的執行個體,以及預期輸出內容 (顯示剛剛建立的執行個體):

$ gcloud redis instances list --region us-central1
INSTANCE_NAME  VERSION    REGION       TIER   SIZE_GB  HOST         PORT  NETWORK  RESERVED_IP     STATUS  CREATE_TIME
demo-ms        REDIS_5_0  us-central1  BASIC  1        10.aa.bb.cc  6379  default  10.aa.bb.dd/29  READY   2022-01-28T09:24:45

當系統要求您提供執行個體資訊或設定應用程式時,請務必使用 HOSTPORT (而非 RESERVED_IP)。Cloud 控制台的 Cloud Memorystore 資訊主頁現在應會顯示該執行個體:

c5a6948ec1c056ed.png

從 Compute Engine 虛擬機器

如果您有 Compute Engine 虛擬機器 (VM),也可以從 VM 傳送 Memorystore 執行個體直接指令,確認執行個體是否正常運作。請注意,無論使用中的資源為何,使用 VM 可能產生相關費用。

建立無伺服器虛擬私有雲存取連接器

與 Cloud Memorystore 一樣,您可以透過 Cloud 控制台或指令列建立無伺服器 Cloud 虛擬私有雲連接器。同樣地,Cloud 虛擬私有雲沒有免費方案,因此建議您分配最少的資源來完成程式碼研究室,藉此盡可能降低成本,而且可以透過以下設定達成:

  • 選取執行個體數量上限:3 (控制台和 gcloud 預設值:10)
  • 選擇費用最低的機器類型:f1-micro (控制台預設值:e2-micro,無 gcloud 預設值)

下一節將引導您使用 Cloud 控制台,在 Cloud 控制台中使用上述的 Cloud 虛擬私有雲設定建立連接器。如果您偏好透過指令列執行這項作業,請跳到下一節。

透過 Cloud 控制台

前往 Cloud Networking 的「Serverless VPC access」(無伺服器虛擬私有雲存取) 頁面頁面 (系統可能提示您提供帳單資訊)。如果您尚未啟用 API,系統會提示您啟用:

e3b9c0651de25e97.png

啟用 API (可能與帳單一併啟用) 後,系統會將您導向資訊主頁,其中列出所有已建立的虛擬私有雲連接器。下方螢幕截圖中使用的專案沒有任何,因此會顯示「No rows to 顯示」(沒有可顯示的資料列)。在控制台中,按一下頂端的「Create Connector」

b74b49b9d73b7dcf.png

使用所需設定填寫表單:

6b26b2aafa719f73.png

為您的應用程式選擇適當的設定。在本教學課程中,以及幾乎不需要用到的範例應用程式,建議您將費用降至最低,因此請遵循前述建議。選好之後,按一下「建立」。要求虛擬私有雲連接器會在幾分鐘內完成。

透過指令列

建立虛擬私有雲連接器之前,請先啟用 Serverless VPC Access API。發出下列指令後,您應該會看見類似的輸出內容:

$ gcloud services enable vpcaccess.googleapis.com
Operation "operations/acf.p2-aaa-bbb-ccc-ddd-eee-ffffff" finished successfully.

啟用 API 後,會建立虛擬私有雲連接器,指令如下所示:

gcloud compute networks vpc-access connectors create CONNECTOR_NAME \
    --range 10.8.0.0/28 --region REGION --project PROJECT_ID

選擇連接器的名稱,以及未使用的 /28 CIDR 區塊起始 IP 位址。本教學課程提出了下列假設:

  • 專案 IDmy-project
  • 虛擬私有雲連接器名稱demo-vpc
  • 執行個體數量下限:2 個 (預設) 和 上限執行個體:3 個
  • 執行個體類型f1-micro
  • Region (區域):us-central1
  • IPv4 CIDR 區塊10.8.0.0/28 (如 Cloud 控制台中的建議選項)

如果您在遵守上述假設的情況下執行以下指令,輸出結果可能會與下方顯示的內容類似:

$ gcloud compute networks vpc-access connectors create demo-vpc \
    --max-instances 3 --range 10.8.0.0/28 --machine-type f1-micro \
    --region us-central1  --project my-project

Create request issued for: [demo-vpc]
Waiting for operation [projects/my-project/locations/us-central1/operations/xxx] to complete...done.
Created connector [demo-vpc].

上述指令省略指定預設值,例如最少 2 個例項和名為 default 的網路。虛擬私有雲連接器建立作業需要幾分鐘才能完成。

確認已建立連接器

程序完成後,請發出以下 gcloud 指令 (假設該區域為 us-central1 地區),確認已建立且可供使用:

$ gcloud compute networks vpc-access connectors list --region us-central1
CONNECTOR_ID  REGION       NETWORK  IP_CIDR_RANGE  SUBNET  SUBNET_PROJECT  MIN_THROUGHPUT  MAX_THROUGHPUT  STATE
demo-vpc      us-central1  default  10.8.0.0/28                            200             300             READY

同樣地,資訊主頁現在應會顯示您剛建立的連接器:

e03db2c8140ed014.png

記下 Cloud 專案 ID、虛擬私有雲連接器名稱與區域。

您已建立必要的其他 Cloud 資源 (無論是透過指令列或控制台),現在要更新應用程式設定,使其支援使用情形。

5. 更新設定檔

首先,對設定檔進行所有必要的更新。協助 Python 2 使用者遷移是本程式碼研究室的主要目標,但內容通常會後續提供有關進一步移植至 Python 3 的資訊 (請見以下各節的說明)。

requirements.txt

在本節中,我們會新增套件來支援 Cloud Memorystore 和 Cloud NDB。如果您的 Cloud Memorystore for Redis,只要使用 Python 適用的標準 Redis 用戶端 (redis) 即可,因為沒有可用的 Cloud Memorystore 用戶端程式庫。將 redisgoogle-cloud-ndb 附加至 requirements.txt,從單元 12 彙整 flask

flask
redis
google-cloud-ndb

這個 requirements.txt 檔案不提供任何版本號碼,表示已選取最新版本。如果發生任何不相容的問題,請在可工作版本中指定要鎖定的版本號碼。

app.yaml

要新增的區段

Python 2 App Engine 執行階段在使用 Cloud API (如 Cloud NDB) 時,需要特定的第三方套件,也就是 grpciosetuptools。Python 2 使用者必須列出這類內建程式庫,以及 app.yaml 中的可用版本。如果您還沒有 libraries 區段,請建立其中兩個程式庫,並新增下列兩個程式庫:

libraries:
- name: grpcio
  version: latest
- name: setuptools
  version: latest

遷移應用程式時,應用程式可能已具備 libraries 區段。如果是,而且缺少 grpciosetuptools,請將兩者新增至現有的 libraries 區段。

接下來,我們的範例應用程式需要 Cloud Memorystore 執行個體和虛擬私有雲連接器資訊,因此無論您使用哪一個 Python 執行階段,系統都會在 app.yaml 中新增以下兩個部分:

env_variables:
    REDIS_HOST: 'YOUR_REDIS_HOST'
    REDIS_PORT: 'YOUR_REDIS_PORT'

vpc_access_connector:
    name: projects/PROJECT_ID/locations/REGION/connectors/CONNECTOR

這已經完成必要更新。更新後的 app.yaml 現在應如下所示:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

libraries:
- name: grpcio
  version: 1.0.0
- name: setuptools
  version: 36.6.0

env_variables:
    REDIS_HOST: 'YOUR_REDIS_HOST'
    REDIS_PORT: 'YOUR_REDIS_PORT'

vpc_access_connector:
    name: projects/PROJECT_ID/locations/REGION/connectors/CONNECTOR

下方為「前後對照」說明您應套用至 app.yaml 的更新內容:

ec2bb027a67debb6.png

*Python 3 的差異

本節是可選的,只有在需要移植至 Python 3 時才會如此。如果要這麼做,需要變更 Python 2 設定。如果您目前未升級,請略過這個部分。

Python 3 執行階段不會使用 threadsafeapi_version,因此請刪除這兩項設定。最新的 App Engine 執行階段不支援內建第三方程式庫複製內建程式庫。第三方套件的唯一規定是在 requirements.txt 中列出。因此,可以刪除 app.yaml 的整個 libraries 區段。

接下來,Python 3 執行階段需要使用自行轉送的網路架構,因此我們向開發人員說明如何在單元 1 中從 webp2 遷移至 Flask。因此,所有指令碼處理常式都必須變更為 auto。這個應用程式不提供任何靜態檔案,因此採用「無點」圖示才能列出處理常式 (因為它們都是 auto),所以您也可以移除整個 handlers 區段。因此,針對 Python 3 調整的新縮寫 app.yaml 應縮減如下:

runtime: python39

env_variables:
    REDIS_HOST: 'YOUR_REDIS_HOST'
    REDIS_PORT: 'YOUR_REDIS_PORT'

vpc_access_connector:
    name: projects/PROJECT_ID/locations/REGION/connectors/CONNECTOR

匯總移植至 Python 3 時,app.yaml 的差異:

  • 刪除「threadsafe」和「api_version」設定
  • 刪除「libraries」區段
  • 刪除 handlers 區段 (如果應用程式提供靜態檔案,請只刪除 script 處理常式)

替換值

新章節中 Memorystore 和虛擬私有雲連接器的值只是預留位置。將這些大寫的值 (YOUR_REDIS_HOST, YOUR_REDIS_PORT, PROJECT_ID, REGION, CONNECTOR_NAME) 替換為先前建立這些資源時儲存的值。關於您的 Memorystore 執行個體,請務必使用 HOST (而非 RESERVED_IP) 和 PORT。以下提供一個快速指令列方法,可用來取得 HOSTPORT (假設執行個體名稱為 demo-msREGIONus-central1):

$ gcloud redis instances describe demo-ms --region us-central1 \
    --format "value(host,port)"
10.251.161.51   6379

如果我們的 Redis 執行個體 IP 位址範例是10.10.10.10使用專案中的通訊埠 6379my-project位於區域 us-central1,虛擬私有雲連接器名稱為 demo-vpc,則以下 app.yaml 部分會如下所示:

env_variables:
    REDIS_HOST: '10.10.10.10'
    REDIS_PORT: '6379'

vpc_access_connector:
    name: projects/my-project/locations/us-central1/connectors/demo-vpc

建立或更新 appengine_config.py

新增對內建第三方程式庫的支援

就像先前使用 app.yaml 的做法一樣,新增 grpciosetuptools 程式庫的使用方式。修改 appengine_config.py 以支援內建的第三方程式庫。如果您不太熟悉,可能是因為在模組 2 中從 App Engine ndb 遷移至 Cloud NDB 時,必須一併返回單元 2。確切的變更是將 lib 資料夾新增至 setuptools.pkg_resources 工作集:

4140b3800694f77e.png

*Python 3 的差異

本節是可選的,只有在需要移植至 Python 3 時才會如此。App Engine 第二代其中一項歡迎變更,是複製 app.yaml 中的 (非內建) 第三方套件並參照內建第三方套件 (有時稱為「供應商」),不再需要刪除整個 appengine_config.py 檔案。

6. 更新應用程式檔案

由於只有一個應用程式檔案 (main.py),因此這個部分的所有變更只會影響該檔案。關於將這個應用程式遷移至 Cloud Memorystore 的變更項目,我們準備了圖例。這項資訊僅供參考,無法仔細分析。所有工作都是在程式碼變更中完成。

5d043768ba7be742.png

我們先從頂端開始逐一說明。

更新匯入作業

單元 12 中 main.py 的匯入部分會使用 Cloud NDB 和 Cloud Tasks;匯入類型如下:

變更前:

from flask import Flask, render_template, request
from google.appengine.api import memcache
from google.appengine.ext import ndb

切換至 Memorystore 需要讀取環境變數。也就是說,我們需要 Python os 模組和 redis (Python Redis 用戶端)。Redis 無法快取 Python 物件,因此請使用 pickle 彙整最近的造訪清單,因此也請匯入。Memcache 的優點是會自動進行物件序列化作業,Memorystore 則較「DIY」。最後,將 google.appengine.ext.ndb 替換為 google.cloud.ndb,從 App Engine ndb 升級至 Cloud NDB。這些變更後,匯入作業現在應如下所示:

變更後:

import os
import pickle
from flask import Flask, render_template, request
from google.cloud import ndb
import redis

更新初始化

模組 12 初始化包含將 Flask 應用程式物件 app 執行個體化,並設定常數,以執行一小時的快取作業:

之前

app = Flask(__name__)
HOUR = 3600

使用 Cloud API 需要用戶端,因此請在 Flask 後立即將 Cloud NDB 用戶端執行個體化。接著,從您在 app.yaml 中設定的環境變數,取得 Memorystore 執行個體的 IP 位址和通訊埠編號。利用這些資訊將 Redis 用戶端執行個體化。完成這些更新後,程式碼看起來會像這樣:

變更後:

app = Flask(__name__)
ds_client = ndb.Client()
HOUR = 3600
REDIS_HOST = os.environ.get('REDIS_HOST', 'localhost')
REDIS_PORT = os.environ.get('REDIS_PORT', '6379')
REDIS = redis.Redis(host=REDIS_HOST, port=REDIS_PORT)

*Python 3 遷移

如果您是從 Python 3 版的 Module 12 應用程式開始,此為選用章節。如果是的話,匯入和初始化作業需要涉及幾項必要變更。

首先,由於 Memcache 是 App Engine 套裝服務,因此在 Python 3 應用程式中使用 Memcache 需要 App Engine SDK,具體來說是包裝 WSGI 應用程式 (以及其他必要設定):

變更前:

from flask import Flask, render_template, request
from google.appengine.api import memcache, wrap_wsgi_app
from google.appengine.ext import ndb

app = Flask(__name__)
app.wsgi_app = wrap_wsgi_app(app.wsgi_app)
HOUR = 3600

由於我們預計遷移至 Cloud Memorystore (非 App Engine 套裝組合服務,例如 Memcache),因此必須移除 SDK 使用方式。這個方法非常簡單,只要刪除整個匯入 memcachewrap_wsgi_app 的那一行即可。一併刪除呼叫 wrap_wsgi_app() 的這行程式碼。這些更新會讓應用程式的這個部分 (實際上是整個應用程式) 與 Python 2 版本相同。

變更後:

import os
import pickle
from flask import Flask, render_template, request
from google.cloud import ndb
import redis

app = Flask(__name__)
ds_client = ndb.Client()
HOUR = 3600
REDIS_HOST = os.environ.get('REDIS_HOST', 'localhost')
REDIS_PORT = os.environ.get('REDIS_PORT', '6379')
REDIS = redis.Redis(host=REDIS_HOST, port=REDIS_PORT)

最後,請移除 app.yaml (刪除這行 app_engine_apis: true) 和 requirements.txt 中的 SDK (刪除這行程式碼:appengine-python-standard)。

遷移至 Cloud Memorystore (和 Cloud NDB)

Cloud NDB 的資料模型與 App Engine ndb 的相容,因此 Visit 物件的定義將維持不變。模擬模組 2 遷移至 Cloud NDB 的作業時,store_visit()fetch_visits() 中的所有 Datastore 呼叫都會擴增並嵌入新的 with 區塊 (因為必須使用 Cloud NDB 結構定義管理員)。變更之前的呼叫如下:

變更前:

def store_visit(remote_addr, user_agent):
    'create new Visit entity in Datastore'
    Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    'get most recent visits'
    return Visit.query().order(-Visit.timestamp).fetch(limit)

同時為兩個函式新增 with ds_client.context() 區塊,並將 Datastore 呼叫放入 (並縮排)。在此情況下,您不需要對呼叫本身進行任何變更:

變更後:

def store_visit(remote_addr, user_agent):
    'create new Visit entity in Datastore'
    with ds_client.context():
        Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    'get most recent visits'
    with ds_client.context():
        return Visit.query().order(-Visit.timestamp).fetch(limit)

接著來看看快取異動以下是模組 12 的 main() 函式:

變更前:

@app.route('/')
def root():
    'main application (GET) handler'
    # check for (hour-)cached visits
    ip_addr, usr_agt = request.remote_addr, request.user_agent
    visitor = '{}: {}'.format(ip_addr, usr_agt)
    visits = memcache.get('visits')

    # register visit & run DB query if cache empty or new visitor
    if not visits or visits[0].visitor != visitor:
        store_visit(ip_addr, usr_agt)
        visits = list(fetch_visits(10))
        memcache.set('visits', visits, HOUR)  # set() not add()

    return render_template('index.html', visits=visits)

Redis 含有「get」,然後「設定」呼叫,就像 Memcache現在只要替換個別的用戶端程式庫,對吧?就差一點,如前所述,我們無法透過 Redis 快取 Python 清單 (因為您需要先將清單序列化,Memcache 就會自動處理),因此在 set() 呼叫中,「pickle」會將造訪轉換為包含 pickle.dumps() 的字串。同樣地,從快取中擷取造訪資料時,您需要在 get() 之後使用 pickle.loads() 取消選擇。以下是實作這些變更後的主要處理常式:

變更後:

@app.route('/')
def root():
    'main application (GET) handler'
    # check for (hour-)cached visits
    ip_addr, usr_agt = request.remote_addr, request.user_agent
    visitor = '{}: {}'.format(ip_addr, usr_agt)
    rsp = REDIS.get('visits')
    visits = pickle.loads(rsp) if rsp else None

    # register visit & run DB query if cache empty or new visitor
    if not visits or visits[0].visitor != visitor:
        store_visit(ip_addr, usr_agt)
        visits = list(fetch_visits(10))
        REDIS.set('visits', pickle.dumps(visits), ex=HOUR)

    return render_template('index.html', visits=visits)

您已完成 main.py 中必要的變更,將範例應用程式使用 Memcache 轉換為 Cloud Memorystore。HTML 範本和移植到 Python 3 呢?

要將 HTML 範本檔案和通訊埠更新為 Python 3 嗎?

驚喜大放送!因為應用程式設計為可在 Python 2 和 3 執行,完全不需要變更程式碼,也不需要相容性程式庫,所以不必執行任何操作。您會看到 main.pymod13a (2.x) 和 mod13b (3.x)「FINISH」中相同資料夾。requirements.txt 也是如此,但除了版本號碼不同 (如有使用) 之外,由於使用者介面維持不變,因此 templates/index.html 也不會更新。

先前在 Python 3 App Engine 執行此應用程式所需的一切作業,均已在之前的設定中完成:不必要的指令已從 app.yaml 中移除,appengine_config.pylib 資料夾也已刪除,因為 Python 3 未使用這些指令。

7. 摘要/清除

本節總結此程式碼研究室的內容,做法是部署應用程式,確認應用程式是否正常運作,以及任何反映的輸出內容。驗證應用程式後,請執行所有清理作業,並考慮後續步驟。

部署及驗證應用程式

最後一個檢查一律是部署範例應用程式。Python 2 開發人員:使用下列指令刪除並重新安裝 lib。(如果您在系統中同時安裝 Python 2 和 3,可能需要明確執行 pip2)。

rm -rf ./lib
pip install -t lib -r requirements.txt

Python 2 和 3 開發人員現在應使用下列指令部署應用程式:

gcloud app deploy

因為您只是在內部重新連結內容,提供完全不同的快取服務,因此應用程式本身應與模組 12 應用程式相同:

模組 7 Visitme 應用程式

這個步驟完成了程式碼研究室。歡迎您將更新後的範例應用程式與模組 13 資料夾進行比較:mod13a (Python 2) 或 mod13b (Python 3)。

清除所用資源

一般

如果您現階段已完成設定,建議您停用 App Engine 應用程式,以免產生帳單費用。不過,如果您想測試或進行其他測試,App Engine 平台提供免費配額,而且只要不超出用量限制,就不需支付任何費用。這適用於運算,但相關 App Engine 服務可能也會產生費用,詳情請參閱定價頁面。如果這項遷移作業涉及其他 Cloud 服務,我們會另外計費。無論採用哪種情況,請參閱「本程式碼研究室的專屬」以下章節。

如要完整揭露,部署至 Google Cloud 無伺服器運算平台 (如 App Engine) 會產生少許建構和儲存空間費用Cloud Build 提供的免費配額與 Cloud Storage 相同。該映像檔的儲存空間會佔用部分配額。不過,您可能居住的區域沒有這類免費方案,因此請留意儲存空間用量,盡可能降低潛在費用。特定 Cloud Storage「資料夾」請務必查看:

  • console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/images
  • console.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com
  • 上方的儲存空間連結取決於您的 PROJECT_ID 和 *LOC*,例如「us」如果應用程式是由美國代管

另一方面,如果您不打算繼續使用這個應用程式或其他相關的遷移程式碼研究室,且想要徹底刪除所有項目,請關閉專案

本程式碼研究室的專屬功能

下列服務專屬於本程式碼研究室。詳情請參閱每項產品的說明文件:

  • Cloud Memorystore 需要執行個體,且沒有免費方案;如要進一步瞭解使用費,請參閱定價頁面
  • Cloud Serverless VPC Access 連接器需要使用執行個體,且沒有免費方案。如要進一步瞭解使用費,請參閱 Cloud VPC 定價頁面的說明
  • Cloud Datastore (Cloud Firestore (Datastore 模式)) 提供免費方案;詳情請參閱定價頁面

這個教學課程牽涉到四種 Cloud 產品的使用方法:

  • App Engine
  • Cloud Datastore
  • Cloud Memorystore
  • Cloud VPC

以下說明如何釋出這些資源,以及避免/降低帳單費用。

關閉 Memorystore 執行個體和虛擬私有雲連接器

這些是不含免費方案的產品,因此系統會立即產生帳單。如果未關閉 Cloud 專案 (請參閱下一節),您必須同時刪除 Memorystore 執行個體和虛擬私有雲連接器,才能停止計費。與建立這些資源時一樣,您也可以透過 Cloud 控制台或指令列釋出資源。

透過 Cloud 控制台

如要刪除 Memorystore 執行個體,請返回 Memorystore 資訊主頁,然後點選執行個體 ID:

2b09baf1aa2e0a25.png

進入該執行個體的詳細資料頁面後,點選「刪除」並確認以下事項:

f9d9eb1c1d4c6107.png

如要刪除虛擬私有雲連接器,請前往資訊主頁,勾選要刪除的連接器旁的核取方塊,然後點選「刪除」並確認以下事項:

ca5fbd9f4c7c9b60.png

透過指令列

以下一組 gcloud 指令會分別刪除 Memorystore 執行個體和虛擬私有雲連接器:

  • gcloud redis instances delete INSTANCE --region REGION
  • gcloud compute networks vpc-access connectors delete CONNECTOR --region REGION

如果您尚未使用 gcloud config set project 設定專案 ID,可能需要提供 --project PROJECT_ID。如果 Memorystore 執行個體分別呼叫了 demo-ms 和虛擬私有雲連接器 demo-vpc,且兩者均位於區域 us-central1,請發出以下兩組指令並確認:

$ gcloud redis instances delete demo-ms --region us-central1
You are about to delete instance [demo-ms] in [us-central1].
Any associated data will be lost.

Do you want to continue (Y/n)?

Delete request issued for: [demo-ms]
Waiting for operation [projects/PROJECT/locations/REGION/operations/operation-aaaaa-bbbbb-ccccc-ddddd] to complete...done.
Deleted instance [demo-ms].
$
$ gcloud compute networks vpc-access connectors delete demo-vpc --region us-central1
You are about to delete connector [demo-vpc] in [us-central1].
Any associated data will be lost.

Do you want to continue (Y/n)?

Delete request issued for: [demo-vpc]
Waiting for operation [projects/PROJECT/locations/REGION/operations/aaaaa-bbbb-cccc-dddd-eeeee] to complete...done.
Deleted connector [demo-vpc].

每個要求都需要幾分鐘的時間才能執行。如果您選擇按照前述方式關閉整個 Cloud 專案,則這些步驟為選用步驟,不過在關閉程序完成前,您仍須支付帳單費用。

後續步驟

除了本教學課程之外,其他著重於淘汰舊版套裝組合服務的其他遷移模組,建議提供以下建議:

  • 模組 2:從 App Engine ndb 遷移至 Cloud NDB
  • 模組 7 至 9:從 App Engine 工作佇列推送工作遷移至 Cloud Tasks
  • 模組 12-13:從 App Engine Memcache 遷移至 Cloud Memorystore
  • 模組 15 至 16:從 App Engine Blob 遷移至 Cloud Storage
  • 模組 18-19:從 App Engine 工作佇列 (提取工作) 遷移至 Cloud Pub/Sub

App Engine 不再是 Google Cloud 中唯一的無伺服器平台。如果您有小型 App Engine 應用程式,或功能有限,但想將其轉換為獨立微服務,或想將單體式應用程式拆解成多個可重複使用的元件,請考慮改用 Cloud Functions。如果容器化已成為應用程式開發工作流程的一部分,特別是在含有 CI/CD (持續整合/持續推送軟體更新或部署) 管道的情況下,請考慮遷移至 Cloud Run。以下單元將介紹這些情境:

  • 從 App Engine 遷移至 Cloud Functions:請參閱單元 11
  • 從 App Engine 遷移至 Cloud Run:請參閱模組 4,使用 Docker 將應用程式容器化;或參閱模組 5,瞭解如何在沒有容器、Docker 或 Dockerfile 的情況下執行應用程式

您可以選擇是否要改用其他無伺服器平台,在進行任何變更前,建議您考量應用程式和用途的最佳選擇。

無論接下來選擇使用哪個遷移模組,所有無伺服器遷移站內容 (程式碼研究室、影片、原始碼 [如有]) 都可以透過其開放原始碼存放區存取。存放區的 README 還針對應考慮遷移的項目和任何相關的「訂單」提供指引接下來介紹遷移模組

8. 其他資源

以下列出開發人員可進一步探索這個或相關的遷移模組和相關產品。包括提供這項內容意見回饋的地方、程式碼連結,以及各種實用的說明文件。

程式碼研究室問題/意見回饋

如果您在本程式碼研究室中發現任何問題,請先搜尋您的問題再提出申請。搜尋及建立新問題的連結:

遷移資源

下表提供模組 12 (START) 和單元 13 (FINISH) 的存放區資料夾連結。您也可以透過所有 App Engine 程式碼研究室遷移作業的存放區存取這些資料,可以複製或下載 ZIP 檔案。

Codelab

Python 2

Python 3

單元 12

程式碼

程式碼

單元 13 (本程式碼研究室)

程式碼

程式碼

線上參考資料

以下為可能與本教學課程相關的線上資源:

App Engine

App Engine NDB 和 Cloud NDB

App Engine Memcache 和 Cloud Memorystore

Cloud VPC

其他 Cloud 資訊

授權

這項內容採用的是創用 CC 姓名標示 2.0 通用授權。