这一系列的 Codelab(自定进度的动手教程)旨在帮助 Google App Engine(标准)开发者通过一系列迁移来指导他们的应用现代化。最重要的步骤是摆脱原始的运行时捆绑服务,因为下一代运行时更加灵活,为用户提供了更多的服务选项。移至新一代运行时,您可以更轻松地与 Google Cloud 产品集成,使用更多支持的服务,并支持当前的语言版本。
本教程介绍如何从 App Engine 的内置 ndb
(下一代数据库)客户端库迁移到 Cloud NDB 客户端库。
您将了解如何
- 使用 App Engine
ndb
库(如果您不熟悉该库) - 从
ndb
迁移到 Cloud NDB - 进一步将应用迁移到 Python 3
所需条件
- 符合以下条件的 Google Cloud Platform 项目:
- 基本 Python 技能
- 常用 Linux 命令的实践知识
- 具备开发和部署 App Engine 应用的基础知识
- 有效的模块 1 App Engine 应用
调查问卷
如何使用此 Codelab?
在模块 1 中,我们已将 Web 框架从 App Engine 的内置 webapp2
迁移到 Flask。在此 Codelab 中,我们通过从 App Engine 的 ndb
库切换到 Google Cloud NDB 来逐步弃用 App Engine 的内置服务。
完成此迁移后,您可以:
- 迁移到 Python 3 和下一代 App Engine 运行时
- 迁移到 Cloud Datastore(适用于非 App Engine 应用的客户端库)
- 将 Python 2(或 3)应用容器化并迁移到 Cloud Run
- 添加 App Engine(推送)任务队列,然后迁移到 Cloud Tasks
不过,我们目前还没有实现这项工作。在考虑以下后续步骤之前,请先完成此 Codelab。本教程的迁移具备以下主要步骤:
- 设置/准备工作
- 添加 Cloud NDB 库
- 更新应用文件
在开始学习本教程的主要部分之前,我们先设置项目,获取代码,然后部署基准应用,以便开始使用代码。
1.设置项目
如果您已完成模块 1 Codelab,我们建议您重复使用同一项目(和代码)。或者,您可以创建一个全新的项目或重复使用另一个现有项目。确保该项目具有有效的结算帐号,并且已启用 App Engine。
2.获取基准示例应用
一个必备条件是要有一个正常工作的模块 1 示例应用。如果您已完成本教程,请使用您的解决方案。您可以立即完成以上链接(上面链接),或跳过它,然后复制模块 1 的代码库(链接如下)。
无论您是使用自己的代码还是我们的代码,我们都能在模块 1 中。本模块 2 Codelab 将会逐步引导您完成每个步骤,完成之后,它应该类似于 FINISH 点的代码(包括从 Python 2 到 3 的可选“bonus”端口):
- START:模块 1 代码
- FINISH:模块 2 Python 2 代码(BONUS:Python 3 代码)
- 整个代码库(用于克隆或下载 ZIP)
您的 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 应用
现在,您需要执行剩余的准备工作步骤:
- 熟悉
gcloud
命令行工具(必要的话) - (重新)将模块 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
进行复制。只需满足以下要求:
- 在
app.yaml
中指定内置库 - 请让对方复制它们可能合作的第三方库(在
lib
中)
以下是模块 1 中的 STARTING app.yaml
:
- 之前:
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
现在,在新的 libraries
部分中将以下行添加到 app.yaml
以引用一对第三方捆绑软件包:grpcio
和 setuptools
:
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,则应用本身不会更改;它会记录对主网页 (/
) 的所有访问,在您访问过该网站后,将如下所示:
恭喜您完成本模块 2 的 Codelab。在正好完成后,因为就 Datastore 而言,这是本系列中强烈推荐的迁移中的最后一个。
可选:清理
何不准备好清理,以避免在进入下一个迁移 Codelab 时继续计费?作为现有开发者,您可能已经对 App Engine 的价格信息已经跟上了速度。
可选:停用应用
如果您还没准备好学习下一个教程,请停用应用,以免产生费用。准备好运行下一个 Codelab 后,可以重新启用它。在您的应用被停用的情况下,它不会获取任何流量来产生费用,但是您还需要计费的另一事项是,如果 Datastore 使用量超出免费配额,因此请删除一部分使用量,以使其低于该限制。
另一方面,如果您不打算继续迁移,并想删除所有内容,则可以关停项目。
后续步骤
在这里,您可以灵活选择下一个目标。选择以下任一选项:
- 模块 2 奖励:继续阅读本教程的后续内容,探索如何移植到 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 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 版本上运行,在这种情况下,唯一需要进行更改的配置如下所示:
- 简化了
app.yaml
以引用 Python 3 并移除第三方库。 - 删除
appengine_config.py
和lib
文件夹,因为它们不再需要。
除了 main.py
之外,requirements.txt
和 templates/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 中,threadsafe
、api_version
和 libraries
指令都已弃用;假定所有应用的线程安全,因此在 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.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
中列出所有必需的第三方库。
部署应用
重新部署您的应用以确保它能够正常运行。您还可以确认解决方案与模块 2 示例 Python 3 代码的接近程度。如需直观呈现 Python 2 之间的区别,请将代码与 Python 2 版本进行比较。
恭喜学习模块 2 中的奖励步骤!参阅有关如何为 Python 3 运行时准备配置文件的文档。最后,请返回之前的“摘要/清理”步骤,了解后续步骤和清理步骤。
准备应用
当需要迁移应用时,您必须将 main.py
和其他应用文件移植到 3.x,因此,最佳做法是尽量编写 2.x 版应用以“向前兼容”的方式传送。
有很多在线资源可帮助您实现这一目标,但以下是一些重要提示:
- 确保所有应用依赖项完全兼容 3.x
- 确保您的应用至少在 2.6 上运行(最好是 2.7)
- 确保应用通过整个测试套件(至少 80% 的覆盖率)
- 使用兼容性库(如
six
、Future 和/或现代化改造) - 自行说明关键的不向后兼容 2.x 与 3.x 的不同之处
- 任何 I/O 都可能会导致 Unicode 与字节字符串不兼容
设计示例应用时要牢记所有这些,因此为什么该应用可以直接在 2.x 和 3.x 上运行,所以我们可以集中精力向您展示使用下一代平台需要更改的内容。
App Engine 迁移模块 Codelab 问题/反馈
如果您遇到此 Codelab 的任何问题,请先搜索您的问题,然后再提交。用于搜索新问题和创建新问题的链接:
迁移资源
关于模块 1 (START) 和模块 2 (FINISH) 的代码库文件夹的链接,位于下表中。您也可以通过所有 App Engine Codelab 迁移的代码库访问这些代码库,该代码库可以克隆或下载 ZIP 文件。
Codelab | Python 2 | Python 3 |
(不适用) | ||
模块 2 |
App Engine 资源
以下是有关此具体迁移的其他资源:
- Python NDB 参考
- (旧)从 Python 2.5 和
webapp
迁移到 2.7 和webapp2
- 迁移到 Python 3 和 GAE 下一代运行时
- 常规