單元 3:從 Google Cloud NDB 遷移至 Cloud Datastore

1. 總覽

本系列程式碼研究室 (可自行調整步調的實作教學課程) 旨在協助 Google App Engine (標準環境) 開發人員完成一系列遷移作業,進而將應用程式現代化。最重要的步驟是捨棄原始的執行階段隨附服務,因為新一代執行階段更具彈性,可為使用者提供更多服務選項。改用新一代執行階段後,您就能更輕鬆地與 Google Cloud 產品整合、使用更多支援的服務,以及支援目前的語言版本。

本選用教學課程說明如何從 Cloud NDB 遷移至 Cloud Datastore,做為與 Datastore 服務通訊的用戶端程式庫。偏好使用 NDB 的開發人員可以繼續使用,因為 NDB 與 Python 3 相容,因此這項遷移作業為選用。如果您想與已使用 Cloud Datastore 的其他應用程式建立一致的程式碼集和共用程式庫,才需要進行這項遷移作業。詳情請參閱「背景」一節。

課程內容

  • 使用 Cloud NDB (如果您不熟悉這項服務)
  • 從 Cloud NDB 遷移至 Cloud Datastore
  • 進一步將應用程式遷移至 Python 3

軟硬體需求

  • 具備有效 GCP 帳單帳戶的 Google Cloud Platform 專案
  • Python 基礎技能
  • 具備基本 Linux 指令的實務知識
  • 具備開發及部署 App Engine 應用程式的基本知識
  • 運作中的 Module 2 App Engine 2.x3.x 應用程式。

問卷調查

您會如何使用這個程式碼研究室?

僅閱讀內容 閱讀內容並完成練習

2. 背景

Cloud NDB 是適合長期使用 App Engine 開發人員的絕佳 Datastore 解決方案,有助於轉移至 Python 3,但這並非 App Engine 開發人員存取 Datastore 的唯一方式。2013 年,App Engine 的 Datastore 成為獨立產品,因此 Google 建立了新的Cloud Datastore 用戶端程式庫,讓所有使用者都能使用 Datastore。

Python 3 App Engine 和 App Engine 開發人員應使用 Cloud Datastore (而非 Cloud NDB)。建議 Python 2 App Engine 開發人員從 ndb 遷移至 Cloud NDB,然後移植到 Python 3,但他們也可以選擇進一步遷移至 Cloud Datastore。對於已使用 Cloud Datastore 撰寫程式碼 (例如上述程式碼) 的開發人員,以及想在所有應用程式中建立共用程式庫的開發人員來說,這項決定尤其合理。程式碼重複使用和程式碼一致性都是最佳做法,兩者都有助於降低整體維護成本,摘要如下:

從 Cloud NDB 遷移至 Cloud Datastore

  • 開發人員可以專注於單一程式碼集,存取 Datastore
  • 避免維護部分使用 Cloud NDB 的程式碼,以及其他使用 Cloud Datastore 的程式碼
  • 程式碼集更一致,程式碼重複使用率更高
  • 可使用常見/共用程式庫,有助於降低整體維護成本

這項遷移作業的主要步驟如下:

  1. 設定/準備工作
  2. 以 Cloud Datastore 用戶端程式庫取代 Cloud NDB
  3. 更新應用程式

3. 設定/準備工作

在開始本教學課程的主要部分之前,請先設定專案、取得程式碼,然後部署基準應用程式,確保我們從可運作的程式碼著手。

1. 設定專案

如果您已完成第 2 模組程式碼研究室,建議重複使用該專案 (和程式碼)。或者,您也可以建立全新專案,或重複使用其他現有專案。確認專案已啟用有效的帳單帳戶和 App Engine (應用程式)。

2. 取得基準範例應用程式

其中一項必要條件是擁有可運作的第 2 課範例應用程式。如果您已完成該教學課程,請使用您的解決方案。您可以立即完成 (上方連結),也可以選擇略過,然後複製第 2 堂課的存放區 (下方連結)。

無論您使用自己的程式碼或我們的程式碼,我們都會從第 2 模組的程式碼開始。本第 3 模組程式碼研究室會逐步說明每個步驟,完成後應與「FINISH」點的程式碼類似。本教學課程提供 Python 2 和 3 版本,請從下方取得正確的程式碼存放區。

Python 2

Python 2 模組 2 STARTING 檔案的目錄 (您的或我們的) 應如下所示:

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

如果您已完成第 2 模組教學課程,也會有包含 Flask 及其依附元件的 lib 資料夾。如果沒有 lib 資料夾,請使用 pip install -t lib -r requirements.txt 指令建立,以便在下一個步驟中部署這個基準應用程式。如果您同時安裝了 Python 2 和 3,建議使用 pip2,而非 pip,以免與 Python 3 混淆。

Python 3

Python 3 模組 2 STARTING 檔案的目錄 (您的或我們的) 應如下所示:

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

Python 3 不會使用 libappengine_config.py

3. (重新) 部署第 2 模組應用程式

請立即執行剩餘的準備步驟:

  1. 重新熟悉 gcloud 指令列工具 (如有必要)
  2. (重新)將模組 1 程式碼部署至 App Engine (如有必要)。

成功執行這些步驟並確認運作正常後,我們將繼續本教學課程,從設定檔開始。

4. 以 Cloud Datastore 用戶端程式庫取代 Cloud NDB

您只需要在 requirements.txt 檔案中進行小幅套件交換,即可完成設定變更。

1. 更新「requirements.txt

完成第 2 模組後,您的 requirements.txt 檔案應如下所示:

  • 之前 (Python 2 和 3):
Flask==1.1.2
google-cloud-ndb==1.7.1

更新 requirements.txt,將 Cloud NDB 程式庫 (google-cloud-ndb) 替換為最新版 Cloud Datastore 程式庫 (google-cloud-datastore),並保留 Flask 的項目,請注意,與 Python 2 相容的 Cloud Datastore 最終版本為 1.15.3:

  • AFTER (Python 2):
Flask==1.1.2
google-cloud-datastore==1.15.3
  • AFTER (Python 3):
Flask==1.1.2
google-cloud-datastore==2.1.0

請注意,這個存放區的維護頻率比本教學課程更高,因此 requirements.txt 檔案可能反映較新版本。建議使用各程式庫的最新版本,但如果無法運作,可以回溯至舊版。上述版本號碼是上次更新本程式碼研究室時的最新版本。

2. 其他設定檔

其他設定檔 (app.yamlappengine_config.py) 應與上一個遷移步驟保持不變:

  • app.yaml 應 (仍) 參照第三方組合套件 grpciosetuptools
  • appengine_config.py 應 (仍) 將 pkg_resourcesgoogle.appengine.ext.vendor 指向 lib 中的第三方資源。

現在我們來看看應用程式檔案。

5. 更新應用程式檔案

template/index.html 沒有異動,但 main.py 有幾項更新。

1. 匯入

匯入部分的起始程式碼應如下所示:

  • 更新前:
from flask import Flask, render_template, request
from google.cloud import ndb

google.cloud.ndb 匯入作業替換為 Cloud Datastore 的匯入作業:google.cloud.datastore。由於 Datastore 用戶端程式庫不支援在實體中自動建立時間戳記欄位,因此請一併匯入標準程式庫 datetime 模組,手動建立時間戳記欄位。依慣例,標準程式庫匯入作業會優先於第三方套件匯入作業。完成這些變更後,程式碼應如下所示:

  • AFTER:
from datetime import datetime
from flask import Flask, render_template, request
from google.cloud import datastore

2. 初始化和資料模型

初始化 Flask 後,模組 2 範例應用程式會建立 NDB 資料模型類別及其欄位,如下所示:

  • 更新前:
app = Flask(__name__)
ds_client = ndb.Client()

class Visit(ndb.Model):
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

Cloud Datastore 程式庫沒有這類別,因此請刪除 Visit 類別宣告。您仍需要用戶端與 Datastore 通訊,因此請將 ndb.Client() 變更為 datastore.Client()。Datastore 程式庫更具「彈性」,可讓您建立實體,而不必像 NDB 一樣「預先宣告」實體結構。更新後,main.py 的這部分應如下所示:

  • AFTER:
app = Flask(__name__)
ds_client = datastore.Client()

3. Datastore 存取權

如要遷移至 Cloud Datastore,您必須變更建立、儲存及查詢 Datastore 實體 (使用者層級) 的方式。對您的應用程式而言,這項遷移作業的難度取決於 Datastore 程式碼的複雜程度。在我們的範例應用程式中,我們盡可能簡化更新作業。以下是我們的起始程式碼:

  • 更新前:
def store_visit(remote_addr, user_agent):
    with ds_client.context():
        Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    with ds_client.context():
        return (v.to_dict() for v in Visit.query().order(
                -Visit.timestamp).fetch_page(limit)[0])

使用 Cloud Datastore 建立一般實體,並以「金鑰」識別實體中的分組物件。使用鍵/值組合的 JSON 物件 (Python dict) 建立資料記錄,然後使用預期的 put() 將其寫入 Datastore。Datastore 的查詢方式類似,但更簡單明瞭。您可以在這裡查看等效的 Datastore 程式碼有何不同:

  • AFTER:
def store_visit(remote_addr, user_agent):
    entity = datastore.Entity(key=ds_client.key('Visit'))
    entity.update({
        'timestamp': datetime.now(),
        'visitor': '{}: {}'.format(remote_addr, user_agent),
    })
    ds_client.put(entity)

def fetch_visits(limit):
    query = ds_client.query(kind='Visit')
    query.order = ['-timestamp']
    return query.fetch(limit=limit)

如上所示更新 store_visit()fetch_visits() 的函式主體,並保持簽章與先前版本相同。主要處理常式 root() 完全沒有變更。完成這些變更後,應用程式現在已可使用 Cloud Datastore,並準備好進行測試。

6. 摘要/清除

部署應用程式

使用 gcloud app deploy 重新部署應用程式,並確認應用程式是否正常運作。現在程式碼應與「Module 3」存放區資料夾中的程式碼相符:

如果您直接跳到本系列課程,而未完成任何先前的程式碼研究室,應用程式本身不會變更,但會記錄所有對主要網頁 (/) 的造訪,且在您造訪網站足夠次數後,會顯示如下:

visitme 應用程式

恭喜您完成本程式碼研究室 (第 3 模組)。您現在知道可以使用 Cloud NDB Cloud Datastore 用戶端程式庫存取 Datastore。遷移至後者後,您現在可以享有共用程式庫、通用程式碼和程式碼重複使用的優點,確保一致性並降低維護成本。

選用:清除

在準備進行下一個遷移程式碼研究室之前,請先清除資源,以免產生帳單費用。身為現有開發人員,您可能已熟悉 App Engine 的價格資訊

選用:停用應用程式

如果還沒準備好進行下一個教學課程,請停用應用程式,以免產生費用。準備好進行下一個程式碼研究室時,可以重新啟用這項功能。應用程式停用後,不會產生任何流量,因此不會產生費用。不過,如果資料儲存空間用量超出免費配額,您仍須支付相關費用,因此請刪除足夠的資料,確保用量低於上限。

另一方面,如果您不打算繼續遷移,並想完全刪除所有內容,可以關閉專案

後續步驟

歡迎瀏覽下列遷移作業單元:

  • 單元 3 額外內容:繼續前往額外內容部分,瞭解如何移植到 Python 3 和下一代 App Engine 執行階段。
  • 單元 7:App Engine 發送工作佇列 (如果您使用 [發送] 工作佇列,則為必填)
    • 將 App Engine taskqueue 推送工作新增至模組 1 應用程式
    • 為第 8 單元中遷移至 Cloud Tasks 的作業做好準備
  • 單元 4:使用 Docker 遷移至 Cloud Run
    • 使用 Docker 將應用程式容器化,以便在 Cloud Run 上執行
    • 可讓您繼續使用 Python 2
  • 單元 5:使用 Cloud Buildpacks 遷移至 Cloud Run
    • 使用 Cloud Buildpacks 將應用程式容器化,以便在 Cloud Run 上執行
    • 不需要瞭解 Docker、容器或 Dockerfile
    • 您必須先將應用程式遷移至 Python 3
  • 單元 6:遷移至 Cloud Firestore
    • 遷移至 Cloud Firestore,存取 Firebase 功能
    • 雖然 Cloud Firestore 支援 Python 2,但本程式碼研究室僅提供 Python 3 版本。

7. 加分題:遷移至 Python 3

如要使用最新的 App Engine 執行階段和功能,建議您遷移至 Python 3。在我們的範例應用程式中,Datastore 是我們使用的唯一內建服務,而且由於我們已從 ndb 遷移至 Cloud NDB,因此現在可以移植到 App Engine 的 Python 3 環境。

總覽

雖然遷移至 Python 3 並非 Google Cloud 教學課程的範圍,但本程式碼研究室的這部分可讓開發人員瞭解 Python 3 App Engine 執行階段的差異。新一代執行階段的一項出色功能是簡化第三方套件的存取權:您不需要在 app.yaml 中指定內建套件,也不需要複製或上傳內建程式庫;系統會從 requirements.txt 中列出的項目隱含安裝這些程式庫。

由於我們的範例非常基本,且 Cloud Datastore 與 Python 2-3 相容,因此不必將應用程式程式碼明確移植到 3.x:應用程式可在 2.x 和 3.x 上執行,且不必修改,也就是說,在這個案例中,唯一需要變更的是設定:

  1. 簡化 app.yaml,以參照 Python 3,並移除對隨附第三方程式庫的參照。
  2. 刪除 appengine_config.pylib 資料夾,因為不再需要這些項目。

main.pytemplates/index.html 應用程式檔案維持不變。

更新「requirements.txt

支援 Python 2 的 Cloud Datastore 最終版本為 1.15.3。使用 Python 3 的最新版本更新 requirements.txt (現在可能已有新版)。撰寫本教學課程時,最新版本為 2.1.0,因此請編輯該行,如下所示 (或使用最新版本):

google-cloud-datastore==2.1.0

簡化 app.yaml

更新前:

這個範例應用程式的唯一實際變更,就是大幅縮短 app.yaml。提醒您,以下是我們在app.yaml第 3 個單元結束時的狀態

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

AFTER:

在 Python 3 中,threadsafeapi_versionlibraries 指令已全數淘汰;所有應用程式都會假設為執行緒安全,且 Python 3 不會使用 api_version。App Engine 服務不再預先安裝內建的第三方套件,因此 libraries 也已淘汰。如要進一步瞭解這些異動,請參閱app.yaml變更說明文件。因此,您應從 app.yaml 刪除這三個檔案,並更新至支援的 Python 3 版本 (請參閱下文)。

選用:使用 handlers 指令

此外,引導 App Engine 應用程式流量的 handlers 指令也已淘汰。由於新一代執行階段會要求網頁架構管理應用程式路徑,因此所有「處理常式指令碼」都必須變更為「auto」。結合上述變更後,您會得到以下 app.yaml

runtime: python38

handlers:
- url: /.*
  script: auto

如要進一步瞭解 script: auto,請參閱app.yaml參考資料頁面

正在移除 handlers 指令

由於 handlers 已淘汰,您也可以移除整個區段,只留下單行 app.yaml

runtime: python38

根據預設,這會啟動 Gunicorn WSGI 網頁伺服器,適用於所有應用程式。如果您熟悉 gunicorn,這是以基本 app.yaml 預設啟動時執行的指令:

gunicorn main:app --workers 2 -c /config/gunicorn.py

選用:使用 entrypoint 指令

不過,如果應用程式需要特定啟動指令,則可使用 entrypoint 指令指定,產生如下所示的 app.yaml

runtime: python38
entrypoint: python main.py

這個範例特別要求使用 Flask 開發伺服器,而非 gunicorn。您也必須在應用程式中加入啟動開發伺服器的程式碼,才能透過通訊埠 8080 在 0.0.0.0 介面上啟動,方法是在 main.py 底部加入這個小節:

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080, debug=True)

如要進一步瞭解 entrypoint,請參閱app.yaml參考資料頁面。如需更多範例和最佳做法,請參閱 App Engine 標準環境啟動說明文件App Engine 彈性環境啟動說明文件

刪除 appengine_config.pylib

刪除 appengine_config.py 檔案和 lib 資料夾。遷移至 Python 3 時,App Engine 會取得並安裝 requirements.txt 中列出的套件。

appengine_config.py 設定檔可用於辨識第三方程式庫/套件,無論您是自行複製,還是使用 App Engine 伺服器上已有的程式庫/套件 (內建),都適用。改用 Python 3 時,主要變更摘要如下:

  1. 不會將複製的第三方程式庫 (列於 requirements.txt) 組合在一起
  2. 沒有 pip install 進入 lib 資料夾,也就是沒有 lib 資料夾週期
  3. app.yaml 中未列出內建第三方程式庫
  4. 不必參照應用程式至第三方程式庫,因此沒有 appengine_config.py 檔案

您只需要在 requirements.txt 中列出所有必要的第三方程式庫。

部署應用程式

重新部署應用程式,確保應用程式正常運作。您也可以確認解決方案與單元 3 的 Python 3 程式碼範例有多接近。如要以視覺化方式呈現 Python 2 的差異,請比較程式碼與其 Python 2 版本

恭喜你完成第 3 單元的加分步驟!請參閱這份文件,瞭解如何準備 Python 3 執行階段的設定檔。最後,請參閱上方的摘要,瞭解後續步驟和清除作業。

準備您的應用程式

當您要遷移應用程式時,必須將 main.py 和其他應用程式檔案移植到 3.x,因此最佳做法是盡量讓 2.x 應用程式「向前相容」。

網路上有許多資源可協助您達成目標,但以下是一些重要提示:

  1. 確認所有應用程式依附元件完全相容於 3.x 版
  2. 確認應用程式至少執行 2.6 版 (最好是 2.7 版)
  3. 確保應用程式通過整個測試套件 (且涵蓋率至少達 80%)
  4. 使用相容性程式庫,例如 six、Future 和/或 Modernize
  5. 瞭解 2.x 與 3.x 的主要差異 (不相容於舊版)
  6. 任何 I/O 都可能導致 Unicode 與位元組字串不相容

設計範例應用程式時,我們已將上述事項納入考量,因此應用程式可直接在 2.x 和 3.x 上執行,方便我們專注於向您說明使用新一代平台時需要變更的內容。

8. 其他資源

App Engine 遷移模組程式碼研究室問題/意見回饋

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

遷移資源

下表提供第 2 模組 (START) 和第 3 模組 (FINISH) 的存放區資料夾連結。您也可以從所有 App Engine 遷移作業的存放區存取這些範例,並複製或下載 ZIP 檔案。

Codelab

Python 2

Python 3

Module 2

code

code

Module 3

code

code

App Engine 資源

以下是與這項特定遷移作業相關的其他資源: