1. 概览
这一系列的 Codelab(自定进度的动手教程)旨在帮助 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 应用的基础知识
- 有效的模块 2 App Engine 2.x 或 3.x 应用。
调查问卷
如何使用此 Codelab?
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 的代码
- 提高代码库的一致性,并提升代码的可重用性
- 支持使用通用/共享库,有助于降低总体维护成本
此迁移具有以下主要步骤:
- 设置/准备工作
- 将 Cloud NDB 替换为 Cloud Datastore 客户端库
- 更新应用
3. 设置/准备工作
在开始学习本教程的主要部分之前,让我们设置项目、获取代码,然后部署基准应用,以便我们知道我们从工作代码开始。
1.设置项目
如果您已完成模块 2 Codelab,我们建议您重复使用同一项目(和代码)。或者,您可以创建一个全新的项目或重复使用另一个现有项目。确保该项目具有有效的结算账号,并且已启用 App Engine(应用)。
2. 获取基准示例应用
前提条件之一是拥有有效的工作模块 2 示例应用。如果您已完成该教程,请使用您的解决方案。您可以立即完成此步骤(上面的链接),也可以选择跳过此步骤,然后复制模块 2 代码库(下面的链接)。
无论您是使用自己的代码还是我们的代码,我们都能在模块 2 中。本模块 3 Codelab 将会逐步引导您完成每个步骤,完成之后,它应该类似于 FINISH 点的代码。本教程有 Python 2 和 3 版本,因此请抓取下面的正确代码库。
Python 2
Python 2 模块 2 启动文件(您或我们的文件)的目录应如下所示:
$ 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,lib 和 appengine_config.py 均不适用。
3. (重新)部署模块 2 应用
您现在需要执行的剩余预处理步骤:
- 熟悉
gcloud命令行工具(必要的话) - (重新)将模块 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
更新 requirements.txt,将 Cloud NDB 库 (google-cloud-ndb) 替换为最新版本的 Cloud Datastore 库 (google-cloud-datastore),同时保留 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 文件可能会反映较新的版本。我们建议使用每个库的最新版本,但如果这些版本无法正常运行,您可以回滚到旧版本。上述版本号是此 Codelab 上次更新时的最新版本号。
2. 其他配置文件
其他配置文件(即 app.yaml 和 appengine_config.py)应与上一个迁移步骤中的保持不变:
app.yaml应(仍然)引用第三方捆绑软件包grpcio和setuptools。appengine_config.py应(仍然)将pkg_resources和google.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 库更加“灵活”,允许您创建实体,而无需像 NDB 那样“预先声明”其结构。更新后,main.py 的这一部分应如下所示:
- 之后:
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 代码有何不同:
- 之后:
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 代码库文件夹中的内容相匹配:
如果您跳过本系列,但未执行任何上述 Codelab,则应用本身不会更改;它会记录对主网页 (/) 的所有访问,在您访问过该网站后,将如下所示:

恭喜您完成本模块 3 的 Codelab。现在,您已经知道可以使用 Cloud NDB 和 Cloud Datastore 客户端库来访问 Datastore。迁移到后者后,您现在可以获得共享库、通用代码和代码重用的好处,从而提高一致性并降低维护成本。
可选:清理
何不准备好清理,以避免在进入下一个迁移 Codelab 时继续计费?作为现有开发者,您可能已经了解 App Engine 的价格信息。
可选:停用应用
如果您尚未准备好学习下一个教程,请停用应用,以免产生费用。准备好继续学习下一个 Codelab 后,您可以重新启用该功能。应用被停用后,不会产生任何流量产生费用,但如果超出免费配额,您可能还需要为 Datastore 使用量付费,因此请删除足够的数据,使使用量低于该上限。
另一方面,如果您不打算继续进行迁移,并希望完全删除所有内容,可以关闭项目。
后续步骤
您可以从此处开始探索以下迁移模块:
- 模块 3 奖励:继续阅读奖励部分,了解如何移植到 Python 3 和下一代 App Engine 运行时。
- 模块 7:App Engine 推送任务队列(如果您使用 [推送] 任务队列,此为必需参数)
- 向模块 1 应用添加了 App Engine
taskqueue推送任务 - 为用户在第 8 模块中迁移到 Cloud Tasks 做准备
- 向模块 1 应用添加了 App Engine
- 模块 4:使用 Docker 迁移到 Cloud Run
- 使用 Docker 将应用容器化,以便在 Cloud Run 上运行
- 让您继续使用 Python 2
- 模块 5:使用 Cloud Buildpack 迁移到 Cloud Run
- 使用 Cloud Buildpacks 将应用容器化,以便在 Cloud Run 上运行
- 无需了解 Docker、容器或
Dockerfiles - 要求您已将应用迁移到 Python 3
- 模块 6:迁移到 Cloud Firestore
- 迁移到 Cloud Firestore 以访问 Firebase 功能
- 虽然 Cloud Firestore 支持 Python 2,但此 Codelab 仅提供 Python 3 版本。
7. 奖励:迁移到 Python 3
为了使用最新的 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 Datastore 与 Python 2-3 兼容,因此无需将任何应用代码显式移植到 3.x:该应用可在 2.x 和 3.x 上未经修改地运行,这意味着在这种情况下,唯一需要更改的是配置:
- 简化了
app.yaml以引用 Python 3,并移除了对捆绑的第三方库的引用。 - 删除
appengine_config.py和lib文件夹,因为它们不再必要。
main.py 和 templates/index.html 应用文件保持不变。
更新requirements.txt
支持 Python 2 的 Cloud Datastore 的最终版本为 1.15.3。将 requirements.txt 更新为 Python 3 的最新版本(现在可能已更新)。编写本教程时,最新版本为 2.1.0,因此请修改该行,使其如下所示(或修改为最新版本):
google-cloud-datastore==2.1.0
简化 app.yaml
之前:
此示例应用的唯一实际更改是大幅缩短 app.yaml。提醒一下,以下是我们在第 3 模块结束时 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 中,threadsafe、api_version 和 libraries 指令均已弃用;所有应用都被假定为线程安全,并且 api_version 不在 Python 3 中使用。App Engine 服务上不再预安装内置的第三方软件包,因此 libraries 也已被弃用。如需详细了解这些变更,请参阅有关 app.yaml 变更的文档。因此,您应从 app.yaml 中删除这三个版本,并更新为受支持的 Python 3 版本(见下文)。
可选:使用 handlers 指令
此外,用于将流量定向到 App Engine 应用的 handlers 指令也已弃用。由于下一代运行时要求 Web 框架管理应用路由,因此所有“处理程序脚本”都必须更改为“auto”。结合上述更改,您将得到以下 app.yaml:
runtime: python38
handlers:
- url: /.*
script: auto
如需详细了解 script: auto,请参阅 app.yaml 参考页面。
移除 handlers 指令
由于 handlers 已被弃用,您也可以移除整个部分,只保留单行 app.yaml:
runtime: python38
默认情况下,这将启动 Gunicorn WSGI Web 服务器,该服务器适用于所有应用。如果您熟悉 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 中列出的软件包。
appengine_config.py 配置文件用于识别第三方库/软件包,无论您是自行复制这些库/软件包,还是使用 App Engine 服务器上已提供的库/软件包(内置)。迁移到 Python 3 时,主要变更总结如下:
- 不捆绑复制的第三方库(在
requirements.txt中列出) - 没有
pip install到lib文件夹中,即无lib文件夹期限 - 未在
app.yaml中列出内置的第三方库 - 无需引用应用到第三方库,因此没有
appengine_config.py文件
只需在 requirements.txt 中列出所有必需的第三方库即可。
部署应用
重新部署应用,确保其正常运行。您还可以确认您的解决方案与模块 3 中的 Python 3 示例代码的接近程度。如需直观呈现与 Python 2 的差异,请将代码与其 Python 2 版本进行比较。
恭喜您完成了模块 3 中的奖励步骤!请参阅有关为 Python 3 运行时准备配置文件的文档。最后,查看上文中的摘要,了解后续步骤和清理操作。
准备您的申请
当需要迁移应用时,您必须将 main.py 和其他应用文件移植到 3.x,因此,最佳做法是尽量编写 2.x 版应用以“向前兼容”的方式传送。
网上有很多资源可以帮助您实现这一目标,但以下是一些关键提示:
- 确保所有应用依赖项都与 3.x 完全兼容
- 确保您的应用在至少 2.6(最好是 2.7)版本的 Python 上运行
- 确保应用通过整个测试套件(且覆盖率至少达到 80%)
- 使用兼容性库,例如
six、Future 和/或 Modernize - 了解 2.x 与 3.x 之间不向后兼容的主要区别
- 任何 I/O 都可能会导致 Unicode 与字节字符串不兼容
设计示例应用时要牢记所有这些,因此为什么该应用可以直接在 2.x 和 3.x 上运行,所以我们可以集中精力向您展示使用下一代平台需要更改的内容。
8. 其他资源
App Engine 迁移模块 Codelab 问题/反馈
如果您发现本 Codelab 存在任何问题,请先搜索您的问题,然后再提交。用于搜索和创建新问题的链接:
迁移时可参考的资源
下表中提供了指向模块 2(开始)和模块 3(完成)的代码库文件夹的链接。您还可以从所有 App Engine 迁移的代码库中访问这些示例,您可以克隆该代码库或下载 ZIP 文件。
Codelab | Python 2 | Python 3 |
模块 3 |
App Engine 资源
以下是有关此特定迁移的其他资源:
- Python Cloud NDB 和 Cloud Datastore 参考文档
- 迁移到 Python 3 和 GAE 新一代运行时
- 常规