这一系列的 Codelab(自定进度的动手教程)旨在帮助 Google App Engine(标准)开发者通过一系列迁移来指导他们的应用现代化。最重要的步骤是摆脱原始的运行时捆绑服务,因为下一代运行时更加灵活,为用户提供了更多的服务选项。移至新一代运行时,您可以更轻松地与 Google Cloud 产品集成,使用更多支持的服务,并支持当前的语言版本。
此初始教程介绍了在 App Engine 应用中实现 Web 框架现代化的迁移步骤:从 webapp2
迁移到 Flask。在您的应用中,您可以使用任何处理路由的 Web 框架,但是在本教程中,我们使用 Flask,因为社区广泛使用了 Flask。
您将了解如何
- 使用第三方库(内置或其他方法)
- 更新配置文件
- 将一个简单的应用从
webapp2
迁移到 Flask
所需条件
- 符合以下条件的 Google Cloud Platform 项目:
- 基本 Python 技能
- 基本 Linux 命令的实践知识
- 具备开发和部署 App Engine 应用的基础知识
调查问卷
如何使用此 Codelab?
当 App Engine 于 2008 年在 Python 2.5 上首次启动时,webapp
webapp 框架便被捆绑了。几年后,在 2013 年 2.7 版运行时环境淘汰了 2.5 版运行时环境时它由接替者 webapp2
替换。
虽然 webapp2
(请参阅文档)仍然存在,并且可以在 App Engine 外部用作 WSGI 兼容的 Web 框架,但它不会自己将用户请求路由到应用中的相应代码。而是依靠 App Engine,配置文件和开发者来执行将网络流量路由到相应“处理程序”的路由。此外,webapp2
的核心优势与 App Engine 的捆绑服务有着千丝万缕的联系,可以有效地将它淘汰,即使它在 Python 3 上也可以运作(另请参阅相关问题)。
此模块为实践者提供了将简单的 webapp2
应用迁移到 Flask 的实践经验,Flask 是 App Engine 支持的框架,并且是 Google Cloud 之外的更多服务,从而使应用程序更加可移植。如果 Flask 不是将您的应用迁移到的目标理想框架,则可以选择另一个,只要它执行自己的路由即可。此 Codelab 向信息技术决策者 (ITDM) 和开发者展示了迁移步骤是什么,因此无论您实际迁移到哪个框架,您都可以熟悉此过程。
以下是此迁移的主要步骤:
- 设置/准备工作
- 添加 Flask 第三方库
- 更新应用文件
- 更新 HTML 模板文件
在开始学习本教程的主要部分之前,让我们设置项目,获取代码,然后(重新)熟悉 gcloud
命令并部署基准应用,以便我们知道我们从工作代码入手。
1.设置项目
作为现有开发者,您的 App Engine 信息中心可能已经显示了您正在运行的服务。就本教程而言,我们建议您创建一个全新的项目或在本教程中重复使用现有的项目。确保该项目具有有效的结算帐号,并且已启用 App Engine(应用)。
2.下载基准示例应用
GAE 迁移代码库包含您需要的所有代码。克隆或下载其 ZIP 文件。对于本教程,您将从模块 0 文件夹 (START) 中的代码开始,并且在完成本教程后,您的代码应与模块 1 文件夹 (FINISH) 相匹配。如果不是,请检查差异,以便您可以进行下一个实验。
模块 0 文件夹应具有如下所示的文件,如 POSIX ls
命令所示:
$ ls
app.yaml index.html main.py
3.(重新)熟悉使用 w/gcloud
命令
如果您的机器上还没有 gcloud
命令,请安装 Google Cloud SDK,并确保 gcloud
是您的执行路径的一部分,并熟悉以下 gcloud
命令:
gcloud components update
— 更新 Google Cloud SDKgcloud auth login
— 登录您的凭据凭据帐号gcloud config list
— 列出 GCP 项目配置设置gcloud config set project PROJECT_ID
— 设置 GCP 项目 IDgcloud app deploy
— 部署 App Engine 应用
如果您最近没有使用 gcloud
进行 App Engine 开发,则应先运行前四个命令 (#1-#4) 以进行设置,然后再执行后续步骤。我们快速了解一下这些命令。
首先,gcloud components update
可确保您使用的是最新的 Cloud SDK 版本。运行此命令应该会输出如下内容:
$ gcloud components update Your current Cloud SDK version is: 317.0.0 You will be upgraded to version: 318.0.0 ┌──────────────────────────────────────────────────┐ │ These components will be updated. │ ├──────────────────────────┬────────────┬──────────┤ │ Name │ Version │ Size │ ├──────────────────────────┼────────────┼──────────┤ │ Cloud SDK Core Libraries │ 2020.11.06 │ 15.5 MiB │ │ gcloud cli dependencies │ 2020.11.06 │ 10.6 MiB │ └──────────────────────────┴────────────┴──────────┘ The following release notes are new in this upgrade. Please read carefully for information about new features, breaking changes, and bugs fixed. The latest full release notes can be viewed at: https://cloud.google.com/sdk/release_notes 318.0.0 (2020-11-10) . . . (release notes) . . . Subscribe to these release notes at https://groups.google.com/forum/#!forum/google-cloud-sdk-announce. Do you want to continue (Y/n)? ╔════════════════════════════════════════════════════════════╗ ╠═ Creating update staging area ═╣ ╠════════════════════════════════════════════════════════════╣ ╠═ Uninstalling: Cloud SDK Core Libraries ═╣ ╠════════════════════════════════════════════════════════════╣ ╠═ Uninstalling: gcloud cli dependencies ═╣ ╠════════════════════════════════════════════════════════════╣ ╠═ Installing: Cloud SDK Core Libraries ═╣ ╠════════════════════════════════════════════════════════════╣ ╠═ Installing: gcloud cli dependencies ═╣ ╠════════════════════════════════════════════════════════════╣ ╠═ Creating backup and activating new installation ═╣ ╚════════════════════════════════════════════════════════════╝ Performing post processing steps...done. Update done! To revert your SDK to the previously installed version, you may run: $ gcloud components update --version 317.0.0
接下来,使用 gcloud auth login
对 gcloud
命令进行身份验证,您可以在日后遇到问题:
$ gcloud auth login Your browser has been opened to visit: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id= . . . You are now logged in as [YOUR_EMAIL]. Your current project is [PROJECT_ID]. You can change this setting by running: $ gcloud config set project PROJECT_ID
使用 gcloud config list
查看您当前的项目设置:
$ gcloud config list [core] account = YOUR_EMAIL disable_usage_reporting = False project = PROJECT_ID Your active configuration is: [default]
以上命令应指导您创建新项目或选择现有项目。如果 gcloud config list
的输出与您要在本教程中使用的所选项目不匹配,请运行 gcloud config set project PROJECT_ID
以设置项目 ID。然后,通过再次运行 gcloud config list
来确认设置的项目 ID 已设置。
$ gcloud config set project PROJECT_ID Updated property [core/project].
如果您改用 Cloud Console,则可以在需要时使用界面创建新项目,也可以使用已有的现有项目。在项目的信息中心,您应该会看到显示有其 ID 的项目信息卡片(以及项目名称和编号):
最后一个命令 (#5) gcloud app deploy
用于将您的应用部署到 App Engine。由于我们刚刚开始运行,所以现在并非必需,但不要部署模块 0 代码以确认模块是否有效。在执行时,选择您希望该应用在哪些地理区域(通常是您所在的国家/地区)运行。不过,一旦设置便无法更改。然后关注其余的部署信息。完成后,系统会通知您的应用将提供的网址。以下是您可能会看到的删节版本:
$ gcloud app deploy Services to deploy: descriptor: [/private/tmp/mod0-baseline/app.yaml] source: [/private/tmp/mod0-baseline] target project: [PROJECT_ID] target service: [default] target version: [20201116t220827] target url: [https://PROJECT_ID.REG_ABBR.r.appspot.com] Do you want to continue (Y/n)? Beginning deployment of service [default]... ╔════════════════════════════════════════════════════════════╗ ╠═ Uploading 1 file to Google Cloud Storage ═╣ ╚════════════════════════════════════════════════════════════╝ File upload done. Updating service [default]...done. Setting traffic split for service [default]...done. Deployed service [default] to [https://PROJECT_ID.REG_ABBR.r.appspot.com] You can stream logs from the command line by running: $ gcloud app logs tail -s default To view your application in the web browser run: $ gcloud app browse
如果您有一段时间没有使用 App Engine 了,您可能会注意到原来的部署 appcfg.py update
命令已被 gcloud app deploy
取代。如需详细了解 gcloud app deploy
,请参阅其文档页面。
另一个最近更改是已部署应用的网址,该网址从 http://PROJECT_ID.appspot.com
调整为 http://PROJECT_ID.REG_ABBR.r.appspot.com
。大多数应用最终都会改用新格式。如需详细了解网址格式,请参阅请求和路由文档。
部署应用后,刷新浏览器(可能几次),以查看最新访问:
如果您的应用是全新的,您将只会看到一次或少数次访问。
Python 2 App Engine 运行时提供了一组“内置”第三方库,您只需在 app.yaml
文件中指定这些库。虽然此迁移不需要使用,但它们将在下一个迁移教程(适用于模块 2)中完成。
必须在名为 requirements.txt
的文件中指定非内置的第三方库,并将其本地安装在与将所有内容上传到 App Engine 的应用代码相同的目录下的 lib
文件夹中。如需了解详情,请参阅捆绑第三方库的文档。
复制的库(例如 Flask)要求您告诉 App Engine 使用 appengine_config.py
配置文件在 lib
文件夹中查找它们。appengine_config.py
配置文件与 requirements.txt
和 lib
位于同一顶级应用文件夹中。在本教程的这一部分中,您将学习以下操作:
- 创建
requirements.txt
(指定已复制的 [非内置] 第三方库) - 创建
appengine_config.py
(识别第三方库) - 安装(第三方)软件包和依赖项
1.创建 requirements.txt
创建一个 requirements.txt
文件以指定您的软件包。在我们的示例中,Flask 是必要的第三方库。在撰写本文时,最新版本为 1.1.2,因此请创建含有此行的 requirements.txt
:
Flask==1.1.2
请参阅 requirements.txt
文档,详细了解可接受的格式。
2.创建 appengine_config.py
下一步是让 App Engine 识别外部第三方库。创建名为 appengine_config.py
且包含以下内容的文件:
from google.appengine.ext import vendor
# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)
此代码的作用与我们之前指定的完全相同,即,将 App Engine 指向复制的库的 lib
文件夹。
3.安装软件包和依赖项
现在,运行 pip install
命令以创建 lib
文件夹并在该文件夹中安装 Flask 及其依赖项:
$ pip install -t lib -r requirements.txt
无论您是使用 pip
还是 pip2
,在软件包安装完成后,您应该拥有如下所示的 lib
文件夹:
$ ls lib bin/ click/ click-7.1.2.dist-info/ flask/ Flask-1.1.2.dist-info/ itsdangerous/ itsdangerous-1.1.0.dist-info/ jinja2/ Jinja2-2.11.2.dist-info/ markupsafe/ MarkupSafe-1.1.1.dist-info/ werkzeug/ Werkzeug-1.0.1.dist-info/
现在,让我们更新应用文件 main.py
。
1.导入数据
导入内容会首先出现在所有 Python 文件中。webapp2
框架导入后跟 ndb
Datastore 库,最后是处理 Django 定制模板的 App Engine 扩展程序。您应该会看到以下内容:
- 之前:
import webapp2
from google.appengine.ext import ndb
from google.appengine.ext.webapp import template
移动到 Flask 时,您可以同时导入 Flask 和模板渲染器。删除 webapp2
相关导入对,然后按以下方式替换它们(保持 ndb
导入不变):
- 之后:
from flask import Flask, render_template, request
from google.appengine.ext import ndb
2.启动
使用 webapp2
的应用需要一个数组(Python 列表),其中列出了任何 Python 文件中的所有路由和处理程序(可能有其他路径):
- 之前:
app = webapp2.WSGIApplication([
('/', MainHandler),
], debug=True)
请注意,app.yaml
执行更高级的路由,可能会调用不同的处理程序。示例应用非常简单,所有路由都可发送到 main.py
处理程序。
Flask 不使用这样的路由表,因此请在 main.py
中删除这些行。Flask 还需要初始化,因此请在 main.py
顶部的导入项下方添加以下行:
- 之后:
app = Flask(__name__)
在 Flask 中,初始化框架,然后使用修饰器来定义路由。此外,路由与函数配对,而不是类和方法。
此 Codelab 中包括 Flask 教程的内容超出范围,因此,请花些时间通读 Flask 教程并查看 Flask 文档,以便更好地了解框架。
3.数据模型
此处没有任何更改。Datastore 将是下一个 Codelab 的重点。
4.处理程序
无论您使用哪个框架(webapp2
或 Flask),应用都会执行以下 3 种操作:
- 处理根路径 (
/
) GET 请求 - 注册网页“访问”(创建/存储
Visit
对象) - 显示最近的 10 次访问(采用预定义的模板
index.html
)
webapp2
框架使用基于类的执行模型,其中为每个支持的 HTTP 方法创建处理程序。在我们的简单示例中,我们只有 GET
,因此定义了 get()
方法:
- 之前:
class MainHandler(webapp2.RequestHandler):
def get(self):
store_visit(self.request.remote_addr, self.request.user_agent)
visits = fetch_visits(10) or () # empty sequence if None
tmpl = os.path.join(os.path.dirname(__file__), 'index.html')
self.response.out.write(template.render(tmpl, {'visits': visits}))
如上所述,Flask 采用自己的路由。您可以编写函数,并使用它们应调用的路由修饰这些函数,而不是处理程序类。用户可以指定在修饰器调用中处理的 HTTP 方法,即 @app.route('/app/', methods=['GET', 'POST'])
。由于默认值为 GET
(也是隐式 HEAD
),因此可以省略。
在迁移到 Flask 时,请将 MainHandler
类及其 get()
方法替换为以下 Flask 路由函数:
- 之后:
@app.route('/')
def root():
store_visit(request.remote_addr, request.user_agent)
visits = fetch_visits(10) or () # empty sequence if None
return render_template('index.html', visits=visits)
当然,这并不代表您的应用,它必定比此示例更加复杂。这些教程的主要目标是帮助您入门、构建一些“记忆内存”,并了解在 App Engine 特定代码中进行更改的位置。如需确认您是否已正确做出此项更改,请将此部分与模块 1 main.py
进行比较。
5. 辅助文件
.gcloudignore
文件没有任何变化。其目的是指定不需要部署到 App Engine 的文件,这些文件对于部署和执行应用是不必要的,包括但不限于辅助 Python、源代码控制、存储库样板文件和其他文件。以下 .gcloudignore
所示(为了简便起见,移除了评论):
.gcloudignore
.git
.gitignore
.hgignore
.hg/
*.pyc
*.pyo
__pycache__/
/setup.cfg
README.md
1.移动模板文件
在基准代码库文件夹(模块 0)中,index.html
模板文件与应用文件位于同一文件夹中。由于 Flask 要求将 HTML 文件放在 templates
文件夹中,因此您必须创建该文件夹 (mkdir templates
),并在其中移动 index.html
。在符合 POSIX 标准的系统(如 Linux 或 Mac OS X)中,命令如下所示:
mkdir templates
mv index.html templates
2.更新模板文件
将 index.html
移到 templates
后,可以进行小巧但需要的修改。下面我们来看一下原始模板文件:
<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
<body>
<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
<li>{{ visit.timestamp.ctime }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>
</body>
</html>
webapp2
使用 Django 模板来执行可访问对象(如 visit.timestamp.ctime
),但不带括号 ( )
,而 Jinja2 明确需要它们。虽然这有点细微的调整,但 Jinja 模板更加强大,因为您可以在调用中传递参数。
在 Django 中,您需要创建“模板标记”或编写过滤条件。了解这一点后,即可向 visit.timestamp.ctime
调用添加一对英文括号,以更新 index.html
:
- 之前:
<li>{{ visit.timestamp.ctime }} from {{ visit.visitor }}</li>
- 之后:
<li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
这是唯一需要进行的更改;所有迁移的 Codelab 都不需要对 index.html
进行额外更改。
部署应用
完成本教程中的所有更改后,应用文件夹中的文件应该与模块 1 代码库文件夹中的文件相同(或几乎相同)。现在进行部署,您可以看到模块 1 Flask 应用与模块 0 webapp2
版本的运行方式相同。
使用 gcloud app deploy
命令,就像我们部署原始模块 0 代码时一样。您可以通过 PROJECT_ID.appspot.com
访问应用,无论是通过网络浏览器访问 curl
,还是使用 wget
命令确认该应用是否按预期运行。
如果出现服务器错误,这通常表示 Python 代码中存在某种拼写错误。查看应用日志进行调查。同时将您的文件与模块 1 代码库中的链接(如上所述)进行比较。
可选:清理
何不准备好清理,以避免在进入下一个迁移 Codelab 时继续计费?作为现有开发者,您可能已经对 App Engine 的价格信息已经跟上了速度。
可选:停用应用
如果您还没准备好学习下一个教程,请停用应用,以免产生费用。准备好运行下一个 Codelab 后,可以重新启用它。在您的应用被停用的情况下,它不会获取任何流量来产生费用,但是您还需要计费的另一事项是,如果 Datastore 使用量超出免费配额,因此请删除一部分使用量,以使其低于该限制。
另一方面,如果您不打算继续迁移,并想删除所有内容,则可以关停项目。
后续步骤
有两个迁移模块,涉及已完成的模块 1 代码、模块 2 和模块 7:
- 模块 2(如果您使用 Datastore,则必须提供该模块)
- 从 App Engine
ndb
迁移到 Cloud NDB - 切换到 Cloud NDB 后,许多其他选项可用
- 将应用容器化为在 Cloud Run 上运行
- 将应用迁移到 Cloud Datastore 客户端库
- 将应用迁移到 Cloud Firestore 以访问 Firebase 功能
- 从 App Engine
- 模块 7(如果您使用 [推送] 任务队列,则必需)
- 添加 App Engine(推送)
taskqueue
用量 - 准备模块 1 中的应用以迁移到模块 8 中的 Cloud Tasks
- 添加 App Engine(推送)
App Engine 迁移模块 Codelab 问题/反馈
如果您遇到此 Codelab 的任何问题,请先搜索您的问题,然后再提交。用于搜索新问题和创建新问题的链接:
迁移资源
模块 0 (START) 和模块 1 (FINISH) 的代码库文件夹的链接如下表所示。您也可以通过所有 App Engine 迁移的代码库访问这些代码库,以便克隆或下载 ZIP 文件。
Codelab | Python 2 | Python 3 |
模块 0 | (不适用) | |
模块 1 | (不适用) |
App Engine 资源
以下是有关此具体迁移的其他资源:
- Python 微型 Web 框架
- (旧)从 Python 2.5 迁移到 2.7,再从
webapp
迁移到webapp2
- 迁移到 Python 3 和 GAE 下一代运行时
- 常规