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

1. 總覽

本系列程式碼研究室 (自助教學和實作教學課程) 旨在引導 Google App Engine (標準環境) 開發人員透過一系列遷移作業,協助他們翻新自家應用程式。最重要的步驟就是捨棄原始執行階段套裝組合服務,因為新一代執行階段更有彈性,為使用者提供更多種服務選項。轉換至新一代的執行階段可讓您更輕鬆地整合 Google Cloud 產品、使用更多元的支援服務,以及支援目前的語言版本。

這個選用教學課程可讓開發人員瞭解如何從 Cloud NDB 遷移至 Cloud Datastore 做為用戶端程式庫,以便與 Datastore 服務通訊。偏好 NDB 的開發人員可以繼續使用,因為這個與 Python 3 相容,所以這就是選擇遷移的必要性。只有當使用者想與已使用 Cloud Datastore 的其他應用程式建立一致的程式碼集和共用程式庫時,才需要進行這項遷移作業。詳細資訊請參閱「背景」專區。

你將瞭解如何

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

軟硬體需求

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

問卷調查

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

只能閱讀 閱讀並完成練習

2. 背景

雖然 Cloud NDB 是對長期 App Engine 開發人員而言的絕佳 Datastore 解決方案,而且可協助轉移至 Python 3,但不是 App Engine 開發人員存取 Datastore 的唯一方式。當 App Engine 的 Datastore 在 2013 年成為獨立產品時,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 NDB 替換為 Cloud Datastore 用戶端程式庫
  3. 更新應用程式

3. 設定/事前作業

在開始教學課程的主要部分之前,我們先設定專案、取得程式碼,然後部署基準應用程式,以便瞭解要開始使用有效的程式碼。

1. 設定專案

如果您已完成單元 2 程式碼研究室,建議您重複使用相同的專案和程式碼。或者,您可以建立新的專案,或是重複使用其他現有專案。請確認專案具備有效的帳單帳戶,且 App Engine (應用程式) 已啟用。

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

其中一項必要條件是,擁有能正常運作的模組 2 範例應用程式。完成該教學課程後,請採用您的解決方案。你可以立即完成這項程序 (如上方連結)。如果想略過這個步驟,請複製模組 2 存放區 (連結如下)。

無論您使用自有還是我們的模組,我們都會開始使用單元 2 程式碼。本單元單元 3 會逐步引導您完成每個步驟,完成後,應該會與 FINISH 點的程式碼類似。本教學課程有 Python 2 和 3 版本,請在下方擷取正確的程式碼存放區。

Python 2

Python 2 單元 2 START 的目錄 (您或我們的檔案) 應如下所示:

$ 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 啟動檔案 (您自己或我們的) 的目錄應如下所示:

$ 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 NDB 替換為 Cloud Datastore 用戶端程式庫

唯一的設定變更是 requirements.txt 檔案中的次要套件替換。

1. 更新「requirements.txt

完成單元 2 後,您的 requirements.txt 檔案會如下所示:

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

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

  • 之後 (Python 2):
Flask==1.1.2
google-cloud-datastore==1.15.3
  • 之後 (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 模組,以手動建立。按照慣例,標準程式庫匯入作業會高於第三方套件匯入作業。完成變更後,畫面會如下所示:

  • 更新後:
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 程式庫更具彈性可讓您在不「預先宣告」的情況下建立實體建構應用程式更新之後,這部分 main.py 應如下所示:

  • 更新後:
app = Flask(__name__)
ds_client = datastore.Client()

3. 資料儲存庫存取權

遷移至 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 程式碼有何不同:

  • 更新後:
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 重新部署應用程式,並確認應用程式可以正常運作。您的程式碼現在應與單元 3 存放區資料夾中的內容相符:

如果您進入本系列課程,但未執行先前的任何程式碼研究室,應用程式本身並不會改變。它會記錄主網頁 (/) 的所有造訪,而且在您造訪此網站足夠的時間後看起來像下面這樣:

Visitme 應用程式

恭喜您完成單元 3 程式碼研究室。您已經知道可以同時使用 Cloud NDB「和」Cloud Datastore 用戶端程式庫存取 Datastore。遷移至後者,您就可以享有以下好處:共用程式庫、通用程式碼和重複使用程式碼,以維持一致性,並降低維護成本。

選用:清除

在準備進入下一個遷移程式碼研究室之前,您該如何清除所用資源,以免產生費用?身為現有開發人員,您可能已經能快速瞭解 App Engine 的定價資訊

選用:停用應用程式

如果尚未準備好進行下一個教學課程,請停用應用程式,以免產生費用。當您準備好前往下一個程式碼研究室時,即可重新啟用。停用的應用程式不會獲得任何流量,不過如果用量超過免費配額,我們就會向您收取 Datastore 用量的費用,因此刪除費用就會低於配額上限。

另一方面,如果不想繼續進行遷移作業,想要完全刪除所有項目,請關閉專案

後續步驟

您可以在此探索下列下一個遷移單元:

  • 單元 3 額外說明:請繼續閱讀獎勵一節,瞭解如何移植到 Python 3 和新一代的 App Engine 執行階段。
  • 單元 7:App Engine 推送工作佇列 (如果您使用 [push] 工作佇列,則為必要項目)
    • 將 App Engine taskqueue 推送工作新增至模組 1 應用程式
    • 協助使用者做好準備,以便遷移至 Cloud Tasks
  • 模組 4:透過 Docker 遷移至 Cloud Run
    • 將應用程式容器化,讓應用程式透過 Docker 在 Cloud Run 中執行
    • 允許您繼續使用 Python 2
  • 單元 5:遷移至 Cloud Run 與 Cloud Buildpacks
    • 將應用程式容器化,讓應用程式透過 Cloud Buildpacks 在 Cloud Run 中執行
    • 不必瞭解 Docker、容器或 Dockerfile 的任何相關資訊
    • 您需要將應用程式遷移至 Python 3
  • 模組 6:遷移至 Cloud Firestore
    • 遷移至 Cloud Firestore 即可使用 Firebase 功能
    • Cloud Firestore 支援 Python 2,但本程式碼研究室僅適用於 Python 3。

7. BONUS:遷移至 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

更新後:

在 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。您也必須將啟動開發伺服器的程式碼新增到應用程式中,才能透過 main.py 的底部加入以下小部分,以透過通訊埠 8080 啟動 0.0.0.0 介面:

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

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

刪除「appengine_config.py」和「lib

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

無論您是自行複製的第三方程式庫/套件,還是使用內建於 App Engine 伺服器 (內建) 中的程式庫/套件,appengine_config.py 設定檔都可用於辨識這類資訊。移至 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. 確保所有應用程式依附元件完全相容
  2. 確認您的應用程式至少在 2.6 版上執行 (建議使用 2.7)
  3. 確保應用程式通過整個測試套件 (且涵蓋率至少有 80%)
  4. 使用相容性程式庫,例如 six、Future 和/或翻新
  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

程式碼

程式碼

Module 3

程式碼

程式碼

App Engine 資源

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