如何使用AWS CDK定义API Gateway的Canary金丝雀发布阶段配置
可行实现方案
方案1:直接使用CDK原生API Gateway金丝雀配置
- TypeScript CDK的
aws-apigateway模块已经原生支持Canary配置,不需要完全拆成两套独立资源,直接在Stage配置项中指定canarySettings即可,示例代码如下:
import * as apigateway from 'aws-cdk-lib/aws-apigateway'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import { Stack, StackProps } from 'aws-cdk-lib'; import { Construct } from 'constructs'; export class ApiStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); // 正式版本和金丝雀版本的后端处理逻辑,仅差异部分单独定义 const prodLambda = new lambda.Function(this, 'ProdHandler', { runtime: lambda.Runtime.NODEJS_18_X, handler: 'index.handler', code: lambda.Code.fromAsset('lambda/prod'), environment: { STAGE: 'prod' } }); const canaryLambda = new lambda.Function(this, 'CanaryHandler', { runtime: lambda.Runtime.NODEJS_18_X, handler: 'index.handler', code: lambda.Code.fromAsset('lambda/canary'), // 仅代码逻辑有小差异,其他配置和正式一致 environment: { STAGE: 'canary' } }); // 禁用默认部署,自定义部署和阶段配置 const api = new apigateway.RestApi(this, 'BizApi', { deploy: false }); // 公共API结构定义,可批量复用 const prodIntegration = new apigateway.LambdaIntegration(prodLambda); api.root.addMethod('GET', prodIntegration); // 其他API资源、方法定义统一写在这里即可 const deployment = new apigateway.Deployment(this, 'ApiDeployment', { api }); // 正式阶段 new apigateway.Stage(this, 'ProdStage', { deployment, stageName: 'prod' }); // 金丝雀阶段,绑定同一份部署,仅配置差异规则 new apigateway.Stage(this, 'CanaryStage', { deployment, stageName: 'canary', canarySettings: { percentTraffic: 10, // 10%流量切到金丝雀版本 stageVariables: { backendVersion: 'canary' } } }); } }
- 优势:无需重复定义API结构,仅需声明差异配置项,维护成本最低。
方案2:封装自定义构造实现两套完全隔离的资源
- 如果你需要完全独立的两套API资源(比如域名、权限、限流规则完全隔离),可以把公共的API定义逻辑封装成自定义CDK构造,传入阶段标识参数控制差异配置,示例如下:
import { Construct } from 'constructs'; import * as apigateway from 'aws-cdk-lib/aws-apigateway'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as route53 from 'aws-cdk-lib/aws-route53'; // 自定义构造入参,仅声明差异配置项 interface BizApiProps { isCanary: boolean; domainName: string; } class BizApi extends Construct { constructor(scope: Construct, id: string, props: BizApiProps) { super(scope, id); // 公共配置逻辑统一写在这里 const handler = new lambda.Function(this, 'Handler', { runtime: lambda.Runtime.NODEJS_18_X, handler: 'index.handler', code: props.isCanary ? lambda.Code.fromAsset('lambda/canary') : lambda.Code.fromAsset('lambda/prod'), // 差异环境变量配置 environment: { STAGE: props.isCanary ? 'canary' : 'prod', DB_INSTANCE: props.isCanary ? 'canary-db' : 'prod-db' } }); const api = new apigateway.RestApi(this, 'Api', { restApiName: props.isCanary ? 'BizApi-Canary' : 'BizApi-Prod', deployOptions: { stageName: props.isCanary ? 'canary' : 'prod', throttlingRateLimit: props.isCanary ? 100 : 1000 // 差异限流配置 }, domainName: { domainName: props.domainName, certificate: props.isCanary ? canaryCert : prodCert } }); api.root.addMethod('GET', new apigateway.LambdaIntegration(handler)); // 其他公共API结构定义 } } // 实例化两套独立资源 const prodApi = new BizApi(this, 'ProdApi', { isCanary: false, domainName: 'api.xxx.com' }); const canaryApi = new BizApi(this, 'CanaryApi', { isCanary: true, domainName: 'canary.api.xxx.com' });
- 优势:两套资源完全隔离,配置自由度最高,公共逻辑复用不会出现配置不一致问题。
方案3:基于Lambda别名的轻量化金丝雀方案
- 如果两套资源的差异仅在后端处理逻辑,API结构完全一致,可以不用修改API Gateway的定义,直接给Lambda设置别名,通过API Gateway的阶段变量绑定不同的Lambda别名,流量切分直接在API Gateway的金丝雀配置中完成,不需要重复定义任何API资源。
内容的提问来源于stack exchange,提问作者y. bs
相关产品推荐
相关产品推荐

