Skip to content

Latest commit

 

History

History
476 lines (370 loc) · 11.2 KB

File metadata and controls

476 lines (370 loc) · 11.2 KB

NestJS HTTP 云函数部署指南

本指南详细介绍如何将 NestJS 应用部署到 CloudBase HTTP 云函数。

📋 前置要求:如果您还没有创建 NestJS 项目,请先阅读 NestJS 项目创建指南

📋 目录导航


部署特性

HTTP 云函数适合以下场景:

  • 轻量级 API:RESTful API 服务、微服务
  • 间歇性访问:不需要持续运行的应用
  • 成本敏感:按请求次数和执行时间计费
  • 快速部署:无需容器化配置

技术特点

特性 说明
计费方式 按请求次数和执行时间
启动方式 冷启动,按需启动
端口要求 固定 9000 端口
扩缩容 自动按请求扩缩
Node.js 环境 预配置 Node.js 运行时

准备部署文件

1. 创建启动脚本

创建 scf_bootstrap 文件(无扩展名):

#!/bin/bash
export PORT=9000
cd /var/user
node dist/main.js

为启动脚本添加执行权限:

chmod +x scf_bootstrap

2. 优化 main.ts

确保 src/main.ts 支持云函数环境:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  // 启用 CORS
  app.enableCors({
    origin: true,
    credentials: true,
  });

  // 配置端口
  const port = process.env.PORT || 3000;
  await app.listen(port, '0.0.0.0');
  
  console.log(`NestJS application is running on port ${port}`);
}
bootstrap();

3. 构建项目

# 构建生产版本
npm run build

# 确保 dist 目录存在
ls -la dist/

4. 依赖管理

确保 package.json 包含必要依赖:

{
  "dependencies": {
    "@nestjs/common": "^11.0.1",
    "@nestjs/core": "^11.0.1",
    "@nestjs/platform-express": "^11.0.1",
    "class-transformer": "^0.5.1",
    "class-validator": "^0.14.3",
    "reflect-metadata": "^0.2.2",
    "rxjs": "^7.8.1"
  }
}

项目结构

cloudrun-nestjs/
├── src/                    # TypeScript 源代码
│   ├── main.ts
│   ├── app.module.ts
│   ├── app.controller.ts
│   ├── app.service.ts
│   ├── health/
│   └── users/
├── dist/                   # 🔑 编译后的 JavaScript 代码
│   ├── main.js
│   └── ...
├── node_modules/           # 🔑 Node.js 依赖包
├── package.json            # 项目配置和依赖
├── scf_bootstrap          # 🔑 云函数启动脚本
└── nest-cli.json          # NestJS CLI 配置

💡 说明

  • scf_bootstrap 是 CloudBase 云函数的启动脚本
  • 设置 PORT=9000 环境变量确保应用监听云函数要求的端口
  • 重要:HTTP 云函数部署时需要包含 dist 目录和 node_modules 目录
  • 使用云函数运行时环境的 Node.js 解释器启动应用

部署步骤

通过控制台部署

  1. 登录 CloudBase 控制台
  2. 选择您的环境,进入「云函数」页面
  3. 点击「新建云函数」
  4. 选择「HTTP 云函数」
  5. 填写函数名称(如:cloudrun-nestjs-app
  6. 选择运行时:Node.js 18(或其他支持的版本)
  7. 提交方法选择:本地上传文件夹
  8. 函数代码选择 cloudrun-nestjs 目录进行上传
  9. 自动安装依赖:开启此选项
  10. 点击「创建」按钮等待部署完成

通过 CLI 部署(敬请期待)

打包部署

如果需要手动打包:

# 构建项目
npm run build

# 创建部署包(包含 dist 和 node_modules)
zip -r cloudrun-nestjs-app.zip . -x ".git/*" "src/*" "test/*" "*.log" "Dockerfile" ".dockerignore" "node_modules/*"

访问应用

获取访问地址

部署成功后,您可以参考通过 HTTP 访问云函数设置自定义域名访问 HTTP 云函数。

访问地址格式:https://your-function-url/

测试接口

  • 根路径/ - NestJS 欢迎页面
  • 健康检查/health - 查看应用状态
  • 用户列表/api/users - 获取用户列表
  • 用户详情/api/users/1 - 获取特定用户
  • 创建用户POST /api/users - 创建新用户

示例请求

# 健康检查
curl https://your-function-url/health

# 获取用户列表
curl https://your-function-url/api/users

# 分页查询
curl "https://your-function-url/api/users?page=1&limit=2"

# 创建新用户
curl -X POST https://your-function-url/api/users \
  -H "Content-Type: application/json" \
  -d '{"name":"测试用户","email":"test@example.com"}'

常见问题

Q: 为什么 HTTP 云函数必须使用 9000 端口?

A: CloudBase HTTP 云函数要求应用监听 9000 端口,这是平台的标准配置。通过在 scf_bootstrap 中设置 PORT=9000 环境变量来控制端口,本地开发时默认使用 3000 端口。

Q: NestJS 应用如何处理静态文件?

A: 在云函数环境中,建议将静态文件托管到 CDN 或对象存储。如果必须在应用中处理静态文件,可以配置 NestJS 的静态文件服务:

import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  
  // 配置静态文件
  app.useStaticAssets(join(__dirname, '..', 'public'));
  
  // ...
}

Q: 如何处理 CORS 跨域问题?

A: 在 main.ts 中配置 CORS:

app.enableCors({
  origin: ['https://yourdomain.com'],  // 生产环境应该设置具体域名
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
});

Q: 构建和依赖如何处理?

A: HTTP 云函数部署时需要包含构建后的 dist 目录和 node_modules 目录。确保在部署前运行 npm run build 构建项目。

Q: 如何查看云函数日志?

A: 在 CloudBase 控制台的云函数页面,点击函数名称进入详情页查看运行日志。

Q: 云函数支持哪些 Node.js 版本?

A: CloudBase 支持 Node.js 14、16、18、20 等版本,建议使用最新的 LTS 版本。

最佳实践

1. 环境变量管理

// config/configuration.ts
export default () => ({
  port: parseInt(process.env.PORT, 10) || 3000,
  database: {
    host: process.env.DATABASE_HOST,
    port: parseInt(process.env.DATABASE_PORT, 10) || 5432,
  },
});
// app.module.ts
import { ConfigModule } from '@nestjs/config';
import configuration from './config/configuration';

@Module({
  imports: [
    ConfigModule.forRoot({
      load: [configuration],
    }),
  ],
})
export class AppModule {}

2. 优化启动脚本

增强 scf_bootstrap 脚本:

#!/bin/bash
export PORT=9000
export NODE_ENV=production

# 检查构建文件
if [ ! -d "dist" ]; then
    echo "Build directory not found"
    exit 1
fi

# 启动应用
cd /var/user
node dist/main.js

3. 全局异常过滤器

// filters/http-exception.filter.ts
import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
} from '@nestjs/common';
import { Request, Response } from 'express';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const status = exception.getStatus();

    response.status(status).json({
      success: false,
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
      message: exception.message,
    });
  }
}

4. 请求日志拦截器

// interceptors/logging.interceptor.ts
import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
  Logger,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  private readonly logger = new Logger(LoggingInterceptor.name);

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const request = context.switchToHttp().getRequest();
    const method = request.method;
    const url = request.url;
    const now = Date.now();

    return next.handle().pipe(
      tap(() => {
        const response = context.switchToHttp().getResponse();
        const delay = Date.now() - now;
        this.logger.log(
          `${method} ${url} ${response.statusCode} - ${delay}ms`,
        );
      }),
    );
  }
}

5. 数据验证管道

// main.ts
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  // 全局验证管道
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
      forbidNonWhitelisted: true,
      transform: true,
    }),
  );
  
  // ...
}

6. 部署前检查清单

  • scf_bootstrap 文件存在且有执行权限
  • 端口配置为 9000
  • 项目已构建(dist 目录存在)
  • package.json 包含所有必需依赖
  • 包含 dist 目录和 node_modules 目录
  • 排除不必要的文件(如 srctestDockerfile
  • 测试本地构建和启动是否正常
  • 检查启动脚本语法是否正确

性能优化

1. 减少冷启动时间

// 使用懒加载模块
@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      cache: true,
    }),
  ],
})
export class AppModule {}

2. 构建优化

# 生产构建优化
npm run build

# 清理开发依赖
npm prune --production

# 清理不必要的文件
rm -rf src test *.md

3. 内存管理

// guards/memory.guard.ts
import { Injectable, CanActivate, ExecutionContext, Logger } from '@nestjs/common';

@Injectable()
export class MemoryGuard implements CanActivate {
  private readonly logger = new Logger(MemoryGuard.name);

  canActivate(context: ExecutionContext): boolean {
    const memUsage = process.memoryUsage();
    this.logger.log(`Memory usage: ${Math.round(memUsage.rss / 1024 / 1024)} MB`);
    return true;
  }
}

4. 响应优化

// interceptors/transform.interceptor.ts
import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, any> {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      map(data => ({
        success: true,
        data,
        timestamp: new Date().toISOString(),
      })),
    );
  }
}

相关文档