1. 概览
本实验演示了一些特性和功能,这些特性和功能旨在简化在容器化环境中开发 NodeJS 应用的软件工程师的开发工作流。典型的容器开发要求用户了解容器和容器构建流程的详细信息。此外,开发者通常需要中断他们的流程,离开 IDE,以在远程环境中测试和调试其应用。借助本教程中提到的工具和技术,开发者无需离开 IDE 即可高效使用容器化应用。
学习内容
在本实验中,您将学习在 Google Cloud 中使用容器进行开发的方法,包括:
- 创建起始 Nodejs 应用
- 配置 Nodejs 应用以进行容器开发
- 编写简单的 CRUD REST 服务
- 部署到 GKE
- 调试错误状态
- 利用断点 / 日志
- 将更改热部署回 GKE
- 可选:集成 CloudSQL 以实现后端持久性
2. 设置和要求
自定进度的环境设置
- 登录 Google Cloud 控制台,然后创建一个新项目或重复使用现有项目。如果您还没有 Gmail 或 Google Workspace 账号,则必须创建一个。
- 项目名称是此项目参与者的显示名称。它是 Google API 尚未使用的字符串。您可以随时对其进行更新。
- 项目 ID 在所有 Google Cloud 项目中是唯一的,并且是不可变的(一经设置便无法更改)。Cloud 控制台会自动生成一个唯一字符串;通常您不在乎这是什么在大多数 Codelab 中,您都需要引用项目 ID(它通常标识为
PROJECT_ID
)。如果您不喜欢生成的 ID,可以再随机生成一个 ID。或者,您也可以尝试自己的项目 ID,看看是否可用。完成此步骤后便无法更改该 ID,并且该 ID 在项目期间会一直保留。 - 此外,还有第三个值,即某些 API 使用的项目编号,供您参考。如需详细了解所有这三个值,请参阅文档。
- 接下来,您需要在 Cloud 控制台中启用结算功能,以便使用 Cloud 资源/API。运行此 Codelab 应该不会产生太多的费用(如果有费用的话)。如需关停资源,以免产生超出本教程范围的结算费用,您可以删除自己创建的资源或删除整个项目。Google Cloud 的新用户符合参与 $300 USD 免费试用计划的条件。
启动 Cloudshell 编辑器
本实验旨在与 Google Cloud Shell Editor 搭配使用,并经过测试。要访问该编辑器,请按以下步骤操作:
- 通过 https://console.cloud.google.com 访问您的 Google 项目。
- 点击右上角的 Cloud Shell 编辑器图标
- 窗口底部会打开一个新窗格
- 点击“打开编辑器”按钮
- 编辑器将打开,右侧为探索器,中心区域为编辑器
- 屏幕底部还应提供一个终端窗格
- 如果终端未打开,请使用 `ctrl+` 的组合键打开新的终端窗口
设置 gcloud
在 Cloud Shell 中,设置项目 ID 以及要将应用部署到的区域。将它们保存为 PROJECT_ID
和 REGION
变量。
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
预配本实验中使用的基础架构
在本实验中,您会将代码部署到 GKE,并访问存储在 CloudSQL 数据库中的数据。下面的设置脚本会为您准备此基础架构。
- 下载设置脚本并使其可执行。
wget https://raw.githubusercontent.com/GoogleCloudPlatform/container-developer-workshop/main/labs/nodejs/setup_with_cw.sh
chmod +x setup_with_cw.sh
- 打开
setup_with_cw.sh
文件,然后修改当前设为“CHANGEME”的密码的值 - 运行设置脚本,以建立您将在本实验中使用的 GKE 集群和 CloudSQL 数据库
./setup_with_cw.sh &
Cloud Workstations 集群
- 在 Cloud 控制台中打开 Cloud Workstations。等待集群处于
READY
状态。
创建工作站配置
- 如果您的 Cloud Shell 会话已断开连接,请点击“重新连接”然后运行 gcloud cli 命令来设置项目 ID。运行命令之前,请将以下示例项目 ID 替换为您的 qwiklabs 项目 ID。
gcloud config set project qwiklabs-gcp-project-id
- 在终端中下载并运行以下脚本以创建 Cloud Workstations 配置。
wget https://raw.githubusercontent.com/GoogleCloudPlatform/container-developer-workshop/main/labs/nodejs/workstation_config_setup.sh
chmod +x workstation_config_setup.sh
./workstation_config_setup.sh
- 验证“Configurations”(配置)部分下的结果。转变为“就绪”状态需要 2 分钟。
- 在控制台中打开 Cloud Workstations 并创建新实例。
- 将名称更改为“
my-workstation
”,然后选择现有配置:codeoss-js
。
- 验证“Workstations”部分下的结果。
启动工作站
- 启动和启动工作站。启动工作站需要几分钟的时间。
- 通过点击地址栏中的图标允许第三方 Cookie。
- 点击“网站无法访问?”。
- 点击“允许 Cookie”。
- 工作站启动后,您会看到 Code OSS IDE 启动。点击“标为已完成”在“使用入门”页面一是工作站 IDE
3. 创建新的 Nodejs 起始应用
在本部分中,您将创建一个新的 Nodejs 应用。
- 打开一个新的终端。
- 在 Cloud Shell 中,创建一个名为
mynodejsapp
的新目录
mkdir mynodejsapp
如果您看到这条消息,请点击“允许”按钮,以便将其复制并粘贴到工作站中。
- 切换到此目录并将其作为工作区打开。此操作会在新创建的文件夹中创建工作区配置,从而重新加载编辑器。
cd mynodejsapp && code-oss-cloud-workstations -r --folder-uri="$PWD"
- 再次打开新终端。使用 NVM 安装 Node 和 NPM。
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash
# This loads nvm bash_completion
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
nvm install stable
nvm alias default stable
4. 创建新的起始应用
- 初始化应用
通过运行以下命令创建 package.json
文件
npm init
Choose the `entry point: (index.js) src/index.js` and leave default values for the rest of the parameters. This will create the `package.json` file with following contents
{ "name": "mynodejsapp", "version": "1.0.0", "description": "", "main": "src/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" }
- 添加入口点
在 IDE 中打开并修改 package.json
文件,以将启动命令添加到脚本 "start": "node src/index.js",
中。更改后,脚本应如以下代码段所示:
"scripts": {
"start": "node src/index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
- 添加极速依赖项
我们要添加的代码也使用 express
,因此我们将该依赖项添加到此 package.json
文件中。因此,完成所有更改后,package.json
文件应如下所示。
{
"name": "mynodejsapp",
"version": "1.0.0",
"description": "",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.17.3"
}
}
- 创建 index.js 文件
在资源管理器视图中选择“新建文件夹”,创建名为 src 的源目录。
创建 src/index.js 文件
,替换为以下代码
const express = require('express');
const app = express();
const PORT = 8080;
app.get('/', (req, res) => {
var message="Greetings from Node";
res.send({ message: message });
});
app.listen(PORT, () => {
console.log(`Server running at: http://localhost:${PORT}/`);
});
请注意,PORT 已设置为 8080
值
生成清单
Skaffold 提供集成工具以简化容器开发。在此步骤中,您将初始化 Skaffold,它会自动创建基础 Kubernetes YAML 文件。执行以下命令以开始该过程。
在终端中执行以下命令
skaffold init --generate-manifests
当系统提示时:
- 输入 8080 作为端口
- 输入 y 以保存配置
向工作区可视化图表添加了 skaffold.yaml
和 deployment.yaml
两个文件
更新应用名称
配置中包含的默认值目前与应用的名称不匹配。更新文件以引用您的应用名称,而不是默认值。
- 更改 Skaffold 配置中的条目
- 打开
skaffold.yaml
- 选择当前设为“
package-json-image
”的映像名称 - 右键点击并选择“更改所有出现次数”
- 输入新名称
mynodejsapp
- 更改 Kubernetes 配置中的条目
- 打开
deployment.yaml
文件 - 选择当前设为“
package-json-image
”的映像名称 - 右键点击并选择“更改所有出现次数”
- 输入新名称
mynodejsapp
请注意,在 skaffold.yaml
文件中,build
部分使用 buildpacks
将应用容器化。此代码不包含 Dockerfile,并且开发者无需具备任何 Docker 知识就能将此应用容器化。
此外,通过此 Skaffold 配置,会自动在编辑器和正在运行的容器之间启用热同步。无需其他配置即可启用热同步。
5. 开发过程介绍
在本部分中,您将逐步完成使用 Cloud Code 插件的几个步骤,了解基本流程并验证起始应用的配置和设置。
Cloud Code 与 Skaffold 集成,可简化您的开发流程。当您按以下步骤部署到 GKE 时,Cloud Code 和 Skaffold 会自动构建容器映像,将其推送到 Container Registry,然后将应用部署到 GKE。这是在后台将细节从开发者流程中提取出来的。Cloud Code 还可以为基于容器的开发提供传统的调试和热同步功能,从而增强您的开发流程。
登录 Google Cloud
- 点击 Cloud Code 图标,然后选择“Sign in to Google Cloud”:
- 点击“继续登录”。
- 在终端中检查输出并打开链接:
- 使用您的 Qwiklabs 学生凭据登录。
- 选择“允许”:
- 复制验证码并返回“工作站”标签页。
- 粘贴验证码,然后按 Enter 键。
添加 Kubernetes 集群
- 添加集群
- 选择 Google Kubernetes Engine:
- 选择项目。
- 选择“mycluster”创建的新实例
- 该集群现在会显示在 Cloud Code 下的 Kubernetes 集群列表中。从此处导航和探索集群。
使用 gcloud cli 设置当前项目 ID
- 从 Qwiklabs 页面复制此实验的项目 ID。
- 从终端运行 gcloud cli 命令,以设置项目 ID。运行命令之前,替换示例项目 ID。先替换项目 ID,然后再运行以下命令。
gcloud config set project qwiklabs-gcp-project-id
将容器部署到 Kubernetes
- 在 Cloud Shell Editor 底部的窗格中,选择 Cloud Code 
- 在顶部显示的面板中,选择“开发会话”下方的 Run on Kubernetes(在 Kubernetes 上运行)。如果出现提示,请选择“Yes”以使用当前的 Kubernetes 上下文。
- 首次运行此命令时,屏幕顶部会显示一条提示,询问您是否需要当前的 Kubernetes 上下文,请选择“是”接受并使用当前上下文。
- 接下来,系统会显示一条提示,询问要使用哪个容器注册表。按 Enter 键可接受提供的默认值
- 选择下部窗格中的“Output”(输出)标签页,然后选择下拉菜单中的 Kubernetes: Run/Debug(Kubernetes:运行/调试),以查看进度和通知
- 选择“Kubernetes:运行/调试 - 详细”查看右侧渠道下拉菜单中的 其他详细信息和实时从容器流式传输的日志
- 选择“Kubernetes: Run/Debug”返回简化的视图下拉菜单
- 构建和测试完成后,“Output”(输出)标签页会显示
Resource deployment/mynodejsapp status completed successfully
,并列出了一个网址:“Forwarded 网址 from service demo-app: http://localhost:8080” - 在 Cloud Code 终端中,将鼠标悬停在输出中的网址 (http://localhost:8080) 上,然后在显示的工具提示中选择“Follow Link”。
响应将是:
{"message":"Greetings from Node"}
热重载
- 前往
src/index.js
。将问候消息的代码修改为'Hello from Node'
请注意,在 Output
窗口的 Kubernetes: Run/Debug
视图中,Watcher 将更新后的文件与 Kubernetes 中的容器同步
Update initiated File sync started for 1 files for gcr.io/myproject/mynodejsapp:latest@sha256:f554756b3b4d6c301c4b26ef96102227cfa2833270db56241248ae42baa1971a File sync succeeded for 1 files for gcr.io/myproject/mynodejsapp:latest@sha256:f554756b3b4d6c301c4b26ef96102227cfa2833270db56241248ae42baa1971a Update succeeded
- 如果切换到
Kubernetes: Run/Debug - Detailed
视图,您会注意到它可以识别文件更改并重启节点
files modified: [src/index.js] Copying files:map[src/index.js:[/workspace/src/index.js]]togcr.io/myproject/mynodejsapp:latest@sha256:f554756b3b4d6c301c4b26ef96102227cfa2833270db56241248ae42baa1971a Syncing 1 files for gcr.io/myproject/mynodejsapp:latest@sha256:f554756b3b4d6c301c4b26ef96102227cfa2833270db56241248ae42baa1971a Watching for changes... [mynodejsapp] [mynodejsapp]> mynodejsapp@1.0.0 start /workspace [mynodejsapp]> node src/index.js [mynodejsapp] [mynodejsapp]Server running at: http://localhost:8080/
- 刷新浏览器以查看更新后的结果。
调试
- 转到“调试”视图并停止当前线程 。
- 点击底部菜单中的
Cloud Code
,然后选择Debug on Kubernetes
以在debug
模式下运行应用。
- 在
Output
窗口的Kubernetes Run/Debug - Detailed
视图中,请注意 Skaffold 将在调试模式下部署此应用。 - 构建和部署应用需要几分钟的时间。这次您会发现连接了调试程序。
Port forwarding pod/mynodejsapp-6bbcf847cd-vqr6v in namespace default, remote port 9229 -> http://127.0.0.1:9229 [mynodejsapp]Debugger attached.
- 底部状态栏的颜色从蓝色变为橙色,表示它处于调试模式。
- 在
Kubernetes Run/Debug
视图中,请注意启动了一个 Debuggable 容器
**************URLs***************** Forwarded URL from service mynodejsapp-service: http://localhost:8080 Debuggable container started pod/mynodejsapp-deployment-6bc7598798-xl9kj:mynodejsapp (default) Update succeeded ***********************************
利用断点
- 打开
src/index.js
- 找到显示
var message="Hello from Node";
的语句 - 点击行号左侧的空白处,为该行添加断点。系统会显示一个红色指示器,指明断点已设置
- 重新加载浏览器,并注意调试程序会在断点停止进程,并允许您调查在 GKE 中远程运行的应用变量和状态
- 点击“变量”部分,直到找到
"message"
变量。 - 按“Step over ”执行该行
- 观察
"message"
变量的当前值更改为"Hello from Node"
- 双击变量名称“target”在弹出式窗口中,将值更改为其他值,例如
"Hi from Node"
- 点击调试控制台中的“继续”按钮
- 在浏览器中查看响应,浏览器现在会显示您刚刚输入的更新值。
- 停止“调试”进入模式,然后再次点击停止按钮 以移除该断点。
6. 开发简单的 CRUD 静态服务
至此,您的应用已完全针对容器化开发进行了配置,并且您已完成 Cloud Code 的基本开发工作流。在以下部分中,您将通过添加连接到 Google Cloud 中的代管式数据库的 REST 服务端点,练习所学知识。
配置依赖项
应用代码使用数据库来保留其余服务数据。通过在 package.json
文件中添加以下代码,确保依赖项可用
- 将另外两个依赖项
pg
和sequelize
添加到package.json
文件,以构建 CRUD 应用 Postgres。发布更改后,依赖项部分将如下所示。
"dependencies": {
"express": "^4.17.3",
"pg": "^8.8.0",
"sequelize": "^6.25.7"
}
编写 REST 服务代码
- 将 CRUD 应用代码添加到此应用
wget -O app.zip https://github.com/GoogleCloudPlatform/container-developer-workshop/raw/main/labs/nodejs/app.zip
unzip app.zip
此代码包含
- models 文件夹,其中包含
item
的实体模型 - controllers 文件夹,包含执行 CRUD 操作的代码
- routes 文件夹,用于将特定网址格式路由到不同的调用
- 包含数据库连接详情的 config 文件夹
- 请注意,
db.config.js
文件中的数据库配置是指连接到数据库时需要提供的环境变量。此外,您还需要解析传入的网址编码请求。 - 在
src/index.js
中添加以下代码段,以便能够从主 JavaScript 文件连接到 CRUD 代码,就在以app.listen(PORT, () => {
开头的最后一部分之前
const bodyParser = require('body-parser')
app.use(bodyParser.json())
app.use(
bodyParser.urlencoded({
extended: true,
})
)
const db = require("../app/models");
db.sequelize.sync();
require("../app/routes/item.routes")(app);
- 修改
deployment.yaml
文件中的部署,以添加环境变量来提供数据库连接信息。
更新文件末尾的规范条目,以与以下定义匹配
spec:
containers:
- name: mynodejsapp
image: mynodejsapp
env:
- name: DB_HOST
value: ${DB_INSTANCE_IP}
- name: DB_PORT
value: "5432"
- name: DB_USER
valueFrom:
secretKeyRef:
name: gke-cloud-sql-secrets
key: username
- name: DB_PASS
valueFrom:
secretKeyRef:
name: gke-cloud-sql-secrets
key: password
- name: DB_NAME
valueFrom:
secretKeyRef:
name: gke-cloud-sql-secrets
key: database
- 将 DB_HOST 值替换为您的数据库地址
export DB_INSTANCE_IP=$(gcloud sql instances describe mytest-instance \
--format=json | jq \
--raw-output ".ipAddresses[].ipAddress")
envsubst < deployment.yaml > deployment.new && mv deployment.new deployment.yaml
部署并验证应用
- 在 Cloud Shell 编辑器底部的窗格中,选择
Cloud Code
,然后选择屏幕顶部的Debug on Kubernetes
。 - 构建和测试完成后,“Output”(输出)标签页会显示
Resource deployment/mynodejsapp status completed successfully
,并列出了一个网址:“Forwarded 网址 from service mynodejsapp: http://localhost:8080” - 添加几项内容。
从 cloudshell 终端运行以下命令
URL=localhost:8080
curl -X POST $URL/items -d '{"itemName":"Body Spray", "itemPrice":3.2}' -H "Content-Type: application/json"
curl -X POST $URL/items -d '{"itemName":"Nail Cutter", "itemPrice":2.5}' -H "Content-Type: application/json"
- 在浏览器中运行 $网址/items 以测试 GET。您也可以从命令行运行 curl 命令
curl -X GET $URL/items
- 测试删除:现在尝试运行以下命令来删除商品。根据需要更改 item-id 的值。
curl -X DELETE $URL/items/1
This throws an error message
{"message":"Could not delete Item with id=[object Object]"}
找出并解决问题
- 应用正在调试模式下运行。因此,请使用断点找出问题。请参考以下提示:
- 我们知道 DELETE 出错了,因为它没有返回所需的结果。因此,您需要在
itemcontroller.js
->exports.delete
方法中设置断点。 - 逐步执行执行,并观察每一步的变量,观察左侧窗口中局部变量的值。
- 如需观察特定值(如
request.params
),请将此变量添加到“Watch”窗口。
- 请注意,分配给
id
的值为undefined
。更改代码以解决问题。
修复后的代码段将如下所示。
// Delete a Item with the specified id in the request exports.delete = (req, res) => { const id = req.params.id;
- 重启应用后,通过尝试删除应用再次测试。
- 点击调试工具栏 中的红色方块,停止调试会话
7. 清理
恭喜!在本实验中,您从头开始创建了一个新的 Nodejs 应用,并将其配置为采用容器热部署模式。然后,您按照传统应用堆栈中的相同开发者流程,将应用部署并调试到远程 GKE 集群。
在完成实验后进行清理:
- 删除实验中使用的文件
cd ~ && rm -rf mynodejsapp && rm -f setup.sh
- 删除项目以移除所有相关基础架构和资源