教程:部署示例项目
用 CLI 把云函数、静态网站托管、网关和云托管部署到同一个环境,再用云 API 完成一次云端构建部署。全部步骤在 CloudBase CLI 3.8.5 上验证过。
本教程面向第一次部署的用户,照着做可以跑通。参数说明和各选项的完整取值见 CLI 与云 API 部署参考,在控制台创建流水线见 在控制台创建与管理流水线。
前提条件
| 项目 | 要求 |
|---|---|
| Node.js | 18 及以上 |
| CloudBase CLI | npm i -g @cloudbase/cli,tcb -v 能输出版本 |
| 云开发环境 | 一个可用环境,记下环境 ID。tcb env list 可查看 |
| 登录 | 执行 tcb login,按提示完成授权 |
⚠️ 下面的操作会在环境中创建和覆盖资源。建议使用测试环境。
下文用 <envId> 表示环境 ID。
示例项目
项目包含四类资源:
deploy-demo/
├── cloudbaserc.json # 声明云函数、静态托管、网关
├── functions/
│ └── demo-fn/
│ ├── index.js
│ └── package.json
├── web/ # 前端项目
│ ├── index.html
│ ├── package.json
│ ├── vite.config.js
│ └── src/main.js
└── cloudrun/
└── demo-run/ # 云托管服务
├── Dockerfile
├── package.json
└── server.js
tcb config init可以交互式生成cloudbaserc.json和functions/、web/的最小骨架,但生成结果不能直接用于本教程:函数缺少package.json,部署后调用会报0 code exit unexpected;静态托管的deployPath默认为/,会覆盖环境根目录下的站点。本教程按下面的完整文件操作。
云函数
functions/demo-fn/index.js:
exports.main = async () => ({
statusCode: 200,
headers: { 'Content-Type': 'application/json; charset=utf-8' },
body: JSON.stringify({ message: 'hello from cloud function', version: 'v1' })
})
functions/demo-fn/package.json:
{ "name": "demo-fn", "version": "1.0.0", "main": "index.js" }
前端项目
web/package.json:
{
"name": "demo-web",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": { "build": "vite build" },
"devDependencies": { "vite": "^5.4.0" }
}
web/vite.config.js:
import { defineConfig } from 'vite'
export default defineConfig({ base: './' })
base: './' 使构建出的资源使用相对路径。页面部署在 /demo 这样的子路径下,缺少这一行会导致 /assets/* 请求返回 404。
web/index.html:
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>Deploy Demo</title>
</head>
<body>
<h1 id="title">Deploy Demo v1</h1>
<script type="module" src="./src/main.js"></script>
</body>
</html>
web/src/main.js:
console.log('deploy demo v1')
云托管服务
cloudrun/demo-run/server.js:
const http = require('http')
http
.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' })
res.end(JSON.stringify({ message: 'hello from cloudrun', version: 'v1' }))
})
.listen(process.env.PORT || 3000)
cloudrun/demo-run/package.json:
{ "name": "demo-run", "version": "1.0.0", "main": "server.js" }
cloudrun/demo-run/Dockerfile:
FROM node:18-alpine
WORKDIR /app
COPY package.json server.js ./
EXPOSE 3000
CMD ["node", "server.js"]
配置文件
cloudbaserc.json,把 envId 换成你的环境 ID:
{
"version": "2.1",
"envId": "<envId>",
"functionRoot": "./functions",
"functions": [
{
"name": "demo-fn",
"type": "Event",
"runtime": "Nodejs18.15",
"handler": "index.main",
"timeout": 10
}
],
"hosting": [
{
"name": "web",
"root": "./web",
"framework": "vite",
"installCommand": "npm install",
"buildCommand": "npm run build",
"outputDir": "dist",
"deployPath": "/demo"
}
],
"gateway": {
"routes": [
{ "path": "/demo-api", "target": "function:demo-fn" },
{ "path": "/demo", "target": "hosting:web" }
]
}
}
字段说明见 配置文件。云托管不在这份配置里,单独部署,见 部署云托管。
部署云函数、静态托管和网关
1. 校验配置
tcb validate
输出 [PASS] 项目校验通过 和资源概览:云函数 1 个,静态托管 1 个,网关路由 2 条。deployPath 为子路径时会有一条关于前端 base 的警告,前面已经设置了 base: './',可以忽略。
2. 安装依赖并构建
npm install --prefix web
tcb app build
tcb app build 只构建,不安装依赖。依赖要装在 web/ 目录,在项目根目录执行 npm install 会导致构建报错“未检测到 node_modules”。
构建成功后 web/dist/ 目录生成产物。
3. 预览变更
tcb deploy --dry-run
首次部署的计划为 4 步,全部是新建:
[1/4] 云函数 (demo-fn) 新建
[2/4] 静态托管 (web) 直传
[3/4] 网关 (/demo-api) 新建
[4/4] 网关 (/demo) 新建
4. 部署
tcb deploy --yes
执行成功后,命令输出每个资源的访问地址,退出码为 0:
✔ 部署完成:4 个资源成功
✔ 云函数 demo-fn
✔ 静态托管 web
访问地址: https://<envId>-<appId>.tcloudbaseapp.com/demo/
✔ 网关 /demo-api
访问地址: https://<envId>-<appId>.ap-shanghai.app.tcloudbase.com/demo-api
✔ 网关 /demo
访问地址: https://<envId>-<appId>.ap-shanghai.app.tcloudbase.com/demo/
5. 验证
用命令输出的网关地址访问:
curl https://<envId>-<appId>.ap-shanghai.app.tcloudbase.com/demo-api
# {"message":"hello from cloud function","version":"v1"}
curl https://<envId>-<appId>.ap-shanghai.app.tcloudbase.com/demo/
# 返回页面,包含 <h1 id="title">Deploy Demo v1</h1>
在控制台查看资源:
| 资源 | 位置 |
|---|---|
| 云函数 | 云函数列表 |
| 静态网站 | 静态网站托管 → 文件管理 → demo 目录 |
| 网关路由 | HTTP 访问服务 |
静态网站不会出现在“应用”列表,原因见 静态网站托管的两种部署形态。
6. 修改后重新部署
把 functions/demo-fn/index.js、web/index.html 中的 v1 改成 v2,重新构建并部署:
tcb app build
tcb deploy --yes
部署摘要显示云函数“覆盖更新”,两条网关路由配置未变,被“跳过”。再次访问,云函数返回 v2,页面标题为 Deploy Demo v2。
⚠️ 漏掉
tcb app build时,tcb deploy会上传web/dist里上一次的产物,页面仍是v1。命令不会报错,只在输出里提示“检测到构建产物,跳过本地构建直接上传”。
部署云托管
tcb cloudrun deploy -s demo-run --source ./cloudrun/demo-run --port 3000 --force --yes --wait
| 选项 | 说明 |
|---|---|
-s | 服务名称,不存在时自动创建 |
--source | 代码目录,需包含 Dockerfile |
--port | 容器监听端口 |
--force --yes | 跳过确认 |
--wait | 阻塞到部署完成,成功时退出码为 0 |
命令结束时输出 部署成功。完整参数见 代码部署。
查询访问地址:
tcb cloudrun detail -s demo-run -e <envId>
输出 JSON 中的 DefaultDomainName 为默认访问地址,格式形如 https://demo-run-<数字>-<appId>.sh.run.tcloudbase.com,以命令输出为准:
curl <DefaultDomainName>/
# {"message":"hello from cloudrun","version":"v1"}
修改代码后再次执行同一条命令,服务生成新的部署记录:
tcb cloudrun record list -s demo-run -e <envId>
新版本状态为 creating,变为 normal 且流量比例为 100 后开始接收请求。切换完成后的数秒内,仍可能访问到旧版本。
部署为应用
有构建步骤的前端项目,用 tcb app deploy 在云端构建,结果是“应用”列表中的一个应用,有独立域名和版本记录。
cd web
tcb app deploy demo-app \
--framework vite \
--install-command "npm install" \
--build-command "npm run build" \
--output-dir dist \
--deploy-path /demo-app \
--ignore "node_modules,dist" \
--force -e <envId>
必须在 web/ 目录内执行。从项目根目录用 --cwd ./web 或 app.root 指向子目录,云端构建会在安装步骤失败,见 云端构建部署。
部署成功后输出访问地址:
✔ 部署成功!
访问地址: https://demo-app-<envId>.webapps.tcloudbase.com/
版本号: demo-app-001
首次部署成功后,CLI 在 web/ 下写入一份 cloudbaserc.json。该文件和项目根目录的 cloudbaserc.json 重复,需要时删除,避免混淆。
构建失败时,查看日志定位原因:
tcb app versions list demo-app -e <envId>
tcb api tcb DescribeCloudBaseRunBuildLog --api-version 2018-06-08 \
--body '{"EnvId":"<envId>","ServiceName":"demo-app","ServiceVersion":"demo-app-001","BuildId":<BuildId>,"Start":0}'
BuildId 取自 tcb app versions list 的输出。
通过云 API 部署
用云 API 把代码压缩包交给云端构建,适合自建平台调用。下面用 tcb api 调用,它自动处理签名;自行调用时的签名方式见 公共请求参数。
1. 打包代码
cd web
zip -qr /tmp/demo-web.zip . -x "node_modules/*" "dist/*"
压缩包根目录直接是项目文件,包含 package.json。
2. 获取上传地址
tcb api tcb DescribeCloudAppCosInfo --api-version 2018-06-08 --json \
--body '{"EnvId":"<envId>","ServiceName":"demo-api-app","DeployType":"custom","Suffix":".zip"}'
响应中的 UploadUrl 是带签名的上传地址,UnixTimestamp 在下一步使用。UploadHeaders 为空数组时,上传请求不需要额外请求头。
3. 上传压缩包
curl -X PUT --upload-file /tmp/demo-web.zip "<UploadUrl>"
上传成功返回 HTTP 200。
4. 创建构建
tcb api tcb CreateCloudApp --api-version 2018-06-08 --json --body '{
"EnvId": "<envId>",
"ServiceName": "demo-api-app",
"DeployType": "custom",
"Source": {
"Type": "zip",
"CosTimestamp": "<UnixTimestamp>",
"CosSuffix": ".zip",
"PackageFileName": "package.zip"
},
"Commands": {
"InstallCmd": "npm install",
"BuildCmd": "npm run build",
"DeployCmd": "tcb hosting deploy dist /demo-api-app -e <envId>"
},
"NodeJsVersion": "20"
}'
响应返回 BuildId 和 VersionName(如 demo-api-app-001)。
⚠️
DeployCmd里的-e <envId>不能省。构建环境的 CLI 已登录,但没有默认环境,缺少该参数时部署步骤失败,报No valid environment ID was detected。
5. 查询构建状态
tcb api tcb DescribeCloudAppVersion --api-version 2018-06-08 --json \
--body '{"EnvId":"<envId>","ServiceName":"demo-api-app","DeployType":"custom","VersionName":"demo-api-app-001"}'
Status 依次为 BUILDING、SUCCESS 或 FAILED,Steps 列出每一步的状态。每 10 秒查询一次,示例项目约 10 秒内完成。
成功时 Steps 为:
检出 ZIP 包 → install → build → deploy → End,均为 success
6. 验证
DeployType 为 custom 时,产物部署在静态托管的文件目录,访问地址为:
https://<envId>-<appId>.tcloudbaseapp.com/demo-api-app/
该部署不出现在控制台“应用”列表。需要出现在应用列表,使用 部署为应用。
7. 查看构建日志
构建失败时,用 BuildId 查日志:
tcb api tcb DescribeCloudBaseRunBuildLog --api-version 2018-06-08 --json \
--body '{"EnvId":"<envId>","ServiceName":"demo-api-app","ServiceVersion":"demo-api-app-001","BuildId":<BuildId>,"Start":0}'
日志在响应的 Log.Text 中,按步骤分段。参数详见 CLI 与云 API 部署参考。
方式对比
| 方式 | 资源 | 构建位置 | 云端形态 |
|---|---|---|---|
tcb deploy | 云函数、静态托管、网关 | 本地,先 tcb app build | 静态文件目录,无应用实体 |
tcb cloudrun deploy | 云托管 | 云端 | 云托管服务,带部署记录 |
tcb app deploy | 前端应用 | 云端 | 应用,带版本和独立域名 |
云 API CreateCloudApp | 前端项目 | 云端 | 静态文件目录(custom) |
常见问题
tcb app build 报“未检测到 node_modules”
依赖没装在 hosting[].root 目录。执行 npm install --prefix web。
部署后页面还是旧版本
tcb deploy 不构建,上传的是已有的 outputDir。先执行 tcb app build。
页面白屏,/assets/* 返回 404
前端构建的资源路径是绝对路径,而页面部署在子路径下。Vite 项目设置 base: './'。
云端构建报 cd: can't cd to /root/cloudbase-workspace/./web
从项目根目录用 --cwd 或 app.root 指向了子目录。进入子目录后执行 tcb app deploy。
云 API 部署步骤报 No valid environment ID was detected
DeployCmd 中的 CLI 命令缺少 -e <envId>。
部署应用后在“应用”列表找不到
tcb deploy 的 hosting 和云 API 的 custom 类型部署到静态托管文件目录,不是应用。在“静态网站托管 → 文件管理”查看,或改用 tcb app deploy。