模块 2:从 App Engine ndb 迁移到 Cloud NDB

这一系列的 Codelab(自定进度的动手教程)旨在帮助 Google App Engine(标准)开发者通过一系列迁移来指导他们的应用现代化。最重要的步骤是摆脱原始的运行时捆绑服务,因为下一代运行时更加灵活,为用户提供了更多的服务选项。移至新一代运行时,您可以更轻松地与 Google Cloud 产品集成,使用更多支持的服务,并支持当前的语言版本。

本教程介绍如何从 App Engine 的内置 ndb(下一代数据库)客户端库迁移到 Cloud NDB 客户端库

您将了解如何

  • 使用 App Engine ndb 库(如果您不熟悉该库)
  • ndb 迁移到 Cloud NDB
  • 进一步将应用迁移到 Python 3

所需条件

调查问卷

如何使用此 Codelab?

只是阅读 阅读并完成练习

在模块 1 中,我们已将 Web 框架从 App Engine 的内置 webapp2 迁移到 Flask。在此 Codelab 中,我们通过从 App Engine 的 ndb 库切换到 Google Cloud NDB 来逐步弃用 App Engine 的内置服务。

完成此迁移后,您可以:

  1. 迁移到 Python 3 和下一代 App Engine 运行时
  2. 迁移到 Cloud Datastore(适用于非 App Engine 应用的客户端库)
  3. 将 Python 2(或 3)应用容器化迁移到 Cloud Run
  4. 添加 App Engine(推送)任务队列,然后迁移到 Cloud Tasks

不过,我们目前还没有实现这项工作。在考虑以下后续步骤之前,请先完成此 Codelab。本教程的迁移具备以下主要步骤:

  1. 设置/准备工作
  2. 添加 Cloud NDB 库
  3. 更新应用文件

在开始学习本教程的主要部分之前,我们先设置项目,获取代码,然后部署基准应用,以便开始使用代码。

1.设置项目

如果您已完成模块 1 Codelab,我们建议您重复使用同一项目(和代码)。或者,您可以创建一个全新的项目或重复使用另一个现有项目。确保该项目具有有效的结算帐号,并且已启用 App Engine。

2.获取基准示例应用

一个必备条件是要有一个正常工作的模块 1 示例应用。如果您已完成本教程,请使用您的解决方案。您可以立即完成以上链接(上面链接),或跳过它,然后复制模块 1 的代码库(链接如下)。

无论您是使用自己的代码还是我们的代码,我们都能在模块 1 中。本模块 2 Codelab 将会逐步引导您完成每个步骤,完成之后,它应该类似于 FINISH 点的代码(包括从 Python 2 到 3 的可选“bonus”端口):

您的 START 模块 1 代码文件夹 应包含以下内容:

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

如果您已完成模块 1 教程,那么还会有一个 lib 文件夹,其中包含 Flask 及其依赖项。如果您没有 lib 文件夹,请使用 pip install -t lib -r requirements.txt 命令创建它,以便我们在下一步中部署此基准应用。如果您同时安装了 Python 2 和 3,我们建议您使用 pip2 而不是 pip,以避免与 Python 3 混淆。

3.(重新)部署模块 1 应用

现在,您需要执行剩余的准备工作步骤:

  1. 熟悉 gcloud 命令行工具(必要的话)
  2. (重新)将模块 1 代码部署到 App Engine(必要的话)

当您成功执行这些步骤并确认操作有效后,我们将在本教程中进行本教程,从配置文件开始。

许多原始 App Engine 内置服务已构建成自己的产品,Datastore 就是其中之一。目前,非 App Engine 应用可以使用 Cloud Datastore。对于长期 ndb 用户,Google Cloud 团队创建了 Cloud NDB 客户端库来与 Cloud Datastore 通信。它同时适用于 Python 2 和 3。

我们来更新确认文件,以将 App Engine ndb 替换为 Cloud NDB,然后修改我们的应用。

1. 更新 requirements.txt

在模块 1 中,我们的应用的唯一外部依赖项是 Flask。现在,我们来添加 Cloud NDB。您的 requirements.txt 文件在模块 1 末尾类似的内容:

  • 之前:
Flask==1.1.2

从 App Engine ndb 迁移需要 Cloud NDB 库 (google-cloud-ndb),因此将其软件包添加到 requirements.txt

  • 之后:
Flask==1.1.2
google-cloud-ndb==1.7.1

编写此 Codelab 时,最新推荐的版本是 1.7.1,但代码库中的 requirements.txt 可能更新版本。我们建议每个库的最新版本,但如果它们无效,您可以回滚到旧版本。

请删除您的 lib 文件夹(如果您在上面并未创建该文件夹)。现在,根据需要使用 pip install -t lib -r requirements.txt 命令(或 pip)安装更新后的库,并根据需要使用 pip2

2. 更新 app.yaml

添加 Google Cloud 客户端库(例如 google-cloud-ndb)有一些要求,并且围绕 Google Cloud 服务器上可用的第三方软件包“内置”库进行轮替。它们不在 requirements.txt 中,也没有使用 pip install 进行复制。只需满足以下要求:

  1. app.yaml 中指定内置库
  2. 请让对方复制它们可能合作的第三方库(在 lib 中)

以下是模块 1 中的 STARTING app.yaml

  • 之前:
runtime: python27
threadsafe: yes
api_version: 1

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

现在,在新的 libraries 部分中将以下行添加到 app.yaml 以引用一对第三方捆绑软件包:grpciosetuptools

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

为何使用这些内置库? gRPC 是一种开放式 RPC 框架,可供所有 Google Cloud 客户端库(包括 google-cloud-ndb)使用。grpcio 库是 Python gRPC 适配器,因此是必需的。添加 setuptools 的理由即将发布。

  • 之后:

完成上述更改后,更新后的 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

3. 更新 appengine_config.py

pkg_resources 工具是 setuptools 库的一部分,用于让内置第三方库访问捆绑的库。更新 appengine_config.py 以使用 pkg_resources 指向 lib 中的捆绑库。完成此更改后,整个文件应如下所示:

import pkg_resources
from google.appengine.ext import vendor

# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)
# Add libraries to pkg_resources working set to find the distribution.
pkg_resources.working_set.add_entry(PATH)

消除配置文件繁琐过程后,您现在可以从 ndb 迁移到 Cloud NDB。如需完成迁移,请更新导入的库并在 main.py 中添加上下文管理功能。

1.导入数据

main.py 中进行以下导入交换:

  • 之前
from google.appengine.ext import ndb
  • 之后:
from google.cloud import ndb

从 App Engine 库更改为 Google Cloud 库时,从实例到 Google Cloud 库的部分变化有时也是如此。对于已经成为 Google Cloud 完整产品的内置服务,您需要从 google.cloud 而不是 google.appengine 导入特性。

2.Datastore 访问权限

为了能够使用 Cloud NDB 库,您的应用必须使用 Python 上下文管理器。其目的是“托管”资源,使必须先获得这些资源才能使用。上下文管理器基于计算机科学控制技术,该技术称为资源分配是初始化 (OR IRII)。上下文管理器可用于 Python 文件(必须先打开这些文件,然后才能访问)和并发,必须首先获取“固定锁定”,然后才能转到“关键部分”中的代码执行情况。

同样,Cloud NDB 要求您先获取客户端的上下文,然后才能与 Datastore 通信,然后才能执行任何 Datastore 命令。首先,创建客户端 (ndb.Client()),方法是直接在初始化 Flask 后将 ds_client = ndb.Client() 添加到 main.py 中:

app = Flask(__name__)
ds_client = ndb.Client()

Python with 命令仅用于获取对象的上下文。使用 with 语句封装访问 Datastore 的任何代码块。

以下是模块 1 中的相同函数,用于将新实体写入 Datastore,并读取以显示最近添加的实体:

  • 之前:

以下是没有上下文管理的原始代码:

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 (v.to_dict() for v in Visit.query().order(
            -Visit.timestamp).fetch(limit))
  • 之后:

现在添加 with ds_client.context(): 并将 Datastore 访问代码移至 with 块:

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 (v.to_dict() for v in Visit.query().order(
                -Visit.timestamp).fetch(limit))

主驱动程序应用与模块 1 中提供的代码完全相同,因为此处没有 ndb(或 Cloud NDB)代码:

@app.route('/')
def root():
    'main application (GET) handler'
    store_visit(request.remote_addr, request.user_agent)
    visits = fetch_visits(10)
    return render_template('index.html', visits=visits)

最佳做法是确保明确区分应用代码和数据访问。这样,当基础数据存储机制更改时,您的主要应用代码就不会更改,就像我们在迁移过程中所做的那样。

部署应用

使用 gcloud app deploy 重新部署您的应用,并确认应用是否正常运行。您的代码现在应与模块 2 代码库中的代码一致。

如果您跳过本系列,但未执行任何上述 Codelab,则应用本身不会更改;它会记录对主网页 (/) 的所有访问,在您访问过该网站后,将如下所示:

visitme 应用

恭喜您完成本模块 2 的 Codelab。在正好完成后,因为就 Datastore 而言,这是本系列中强烈推荐的迁移中的最后一个。

可选:清理

何不准备好清理,以避免在进入下一个迁移 Codelab 时继续计费?作为现有开发者,您可能已经对 App Engine 的价格信息已经跟上了速度。

可选:停用应用

如果您还没准备好学习下一个教程,请停用应用,以免产生费用。准备好运行下一个 Codelab 后,可以重新启用它。在您的应用被停用的情况下,它不会获取任何流量来产生费用,但是您还需要计费的另一事项是,如果 Datastore 使用量超出免费配额,因此请删除一部分使用量,以使其低于该限制。

另一方面,如果您不打算继续迁移,并想删除所有内容,则可以关停项目

后续步骤

在这里,您可以灵活选择下一个目标。选择以下任一选项:

  • 模块 2 奖励:继续阅读本教程的后续内容,探索如何移植到 Python 3 和下一代 App Engine 运行时。
  • 模块 7:App Engine 推送任务队列(如果您使用 [推送] 任务队列,此为必需参数)
    • 向模块 1 应用添加 App Engine taskqueue 推送任务
    • 准备用户迁移到模块 8 中的 Cloud Tasks
  • 模块 4:使用 Docker 迁移到 Cloud Run
    • 将应用容器化以使用 Docker 在 Cloud Run 上运行
    • 允许您继续使用 Python 2
  • 模块 5:使用 Cloud Buildpack 迁移到 Cloud Run
    • 将应用设置为使用 Cloud Buildpack 在 Cloud Run 上运行
    • 您无需了解有关 Docker、容器或 Dockerfile 的任何信息
    • 您需要将应用迁移到 Python 3
  • 模块 3
    • 对从 Cloud NDB 到 Cloud Datastore 的 Datastore 访问进行现代化改造
    • 这是用于 Python 3 App Engine 应用和非 App Engine 应用的库

要使用最新的 App Engine 运行时和功能,我们建议您迁移到 Python 3。在示例应用中,Datastore 是我们唯一的内置服务,由于我们已从 ndb 迁移到 Cloud NDB,因此现在可以移植到 App Engine 的 Python 3 运行时。

概览

虽然移植到 Python 3 不在 Google Cloud 教程的范围内,但此 Codelab 的这一部分让开发者大致了解 Python 3 App Engine 运行时之间的区别。新一代运行时有一个简化的功能,可简化对第三方软件包的访问;您无需在 app.yaml 中指定内置软件包,也不需要复制或上传非内置库;它们会隐式安装在 requirements.txt 中。

因为我们的示例非常简单,Cloud NDB 兼容 Python 2-3,因此无需将应用代码明确移植到 3.x 中;该应用在 2.x 和 3.x 版本上运行,在这种情况下,唯一需要进行更改的配置如下所示:

  1. 简化了 app.yaml 以引用 Python 3 并移除第三方库。
  2. 删除 appengine_config.pylib 文件夹,因为它们不再需要。

除了 main.py 之外,requirements.txttemplates/index.html 文件保持不变。

简化 app.yaml

之前:

本示例应用的唯一实际更改是显著缩短 app.yaml。在此提醒您,我们在模块 2 结束后,我们 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

之后:

在 Python 3 中,threadsafeapi_versionlibraries 指令都已弃用;假定所有应用的线程安全,因此在 Python 3 中不使用 api_version。App Engine 服务上已不再预先安装内置的第三方软件包,因此也不建议使用 libraries。如需详细了解这些变更,请参阅有关 app.yaml 更改的文档。因此,您应从 app.yaml 中删除所有三个对象并更新到支持的 Python 3 版本(见下文)。

使用 handlers 指令

此外,handlers 指令也用于引导 App Engine 应用引导流量。由于下一代运行时要求网络框架管理应用路由,因此所有“处理程序脚本”都必须更改为“auto”。结合以上更改,您将到达以下 app.yaml

runtime: python38

handlers:
- url: /.*
  script: auto

如需详细了解 script: auto,请参阅相应的文档页面

移除 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 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 installlib 文件夹中,即无 lib 文件夹期限
  3. app.yaml 中无内置第三方库列表
  4. 无需将应用引用至第三方库,因此没有 appengine_config.py 文件

requirements.txt 中列出所有必需的第三方库。

部署应用

重新部署您的应用以确保它能够正常运行。您还可以确认解决方案与模块 2 示例 Python 3 代码的接近程度。如需直观呈现 Python 2 之间的区别,请将代码与 Python 2 版本进行比较。

恭喜学习模块 2 中的奖励步骤!参阅有关如何为 Python 3 运行时准备配置文件的文档。最后,请返回之前的“摘要/清理”步骤,了解后续步骤和清理步骤。

准备应用

当需要迁移应用时,您必须将 main.py 和其他应用文件移植到 3.x,因此,最佳做法是尽量编写 2.x 版应用以“向前兼容”的方式传送。

有很多在线资源可帮助您实现这一目标,但以下是一些重要提示:

  1. 确保所有应用依赖项完全兼容 3.x
  2. 确保您的应用至少在 2.6 上运行(最好是 2.7)
  3. 确保应用通过整个测试套件(至少 80% 的覆盖率)
  4. 使用兼容性库(如 six、Future 和/或现代化改造)
  5. 自行说明关键的不向后兼容 2.x 与 3.x 的不同之处
  6. 任何 I/O 都可能会导致 Unicode 与字节字符串不兼容

设计示例应用时要牢记所有这些,因此为什么该应用可以直接在 2.x 和 3.x 上运行,所以我们可以集中精力向您展示使用下一代平台需要更改的内容。

App Engine 迁移模块 Codelab 问题/反馈

如果您遇到此 Codelab 的任何问题,请先搜索您的问题,然后再提交。用于搜索新问题和创建新问题的链接:

迁移资源

关于模块 1 (START) 和模块 2 (FINISH) 的代码库文件夹的链接,位于下表中。您也可以通过所有 App Engine Codelab 迁移的代码库访问这些代码库,该代码库可以克隆或下载 ZIP 文件。

Codelab

Python 2

Python 3

模块 1

代码

(不适用)

模块 2

代码

代码

App Engine 资源

以下是有关此具体迁移的其他资源: