12345678_0001
我开始实施这个并想分享我的完整解决方案。在弄清楚如何使用它之后,我决定不使用它,因为它有几个缺点。无法将新版本的金丝雀公开给用户群的专用部分,这意味着有时他们会使用新版本或旧版本调用发布到 sns 的函数会触发所有下游动作,这可能会获取下游服务的新版本或旧版本,这会在破坏 API 的情况下导致很多问题IAM 更改会立即影响两个版本,可能会破坏旧版本。相反,我将所有内容部署到预生产帐户,运行我的集成和端到端测试,如果它们成功,我将部署到生产创建金丝雀部署的 cdk 代码:const versionAlias = new lambda.Alias(this, 'Alias', { aliasName: "alias", version: this.lambda.currentVersion,})const preHook = new lambda.Function(this, 'LambdaPreHook', { description: "pre hook", code: lambda.Code.fromAsset('dist/upload/convert-pre-hook'), handler: 'main', runtime: lambda.Runtime.GO_1_X, memorySize: 128, timeout: cdk.Duration.minutes(1), environment: { FUNCTION_NAME: this.lambda.currentVersion.functionName, }, reservedConcurrentExecutions: 5, logRetention: RetentionDays.ONE_WEEK,})// this.lambda.grantInvoke(preHook) // this doesn't work, I need to grant invoke to all functions :spreHook.addToRolePolicy(new iam.PolicyStatement({ actions: [ "lambda:InvokeFunction", ], resources: ["*"], effect: iam.Effect.ALLOW,}))const application = new codedeploy.LambdaApplication(this, 'CodeDeployApplication')new codedeploy.LambdaDeploymentGroup(this, 'CanaryDeployment', { application: application, alias: versionAlias, deploymentConfig: codedeploy.LambdaDeploymentConfig.ALL_AT_ONCE, preHook: preHook, autoRollback: { failedDeployment: true, stoppedDeployment: true, deploymentInAlarm: false, }, ignorePollAlarmsFailure: false, // alarms: // autoRollback: codedeploy.A // postHook:})我的pre hook函数的go代码。PutLifecycleEventHookExecutionStatus如果预挂钩成功,则告诉代码部署。不幸的是,如果部署消息失败,您在 cdk 部署输出中获得的消息将毫无用处,因此您需要检查挂钩前/后日志。为了实际运行集成测试,我只需调用 lambda 并检查是否发生错误。package mainimport ( "encoding/base64" "fmt" "log" "os" "github.com/aws/aws-lambda-go/lambda" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/codedeploy" lambdaService "github.com/aws/aws-sdk-go/service/lambda")var svc *codedeploy.CodeDeployvar lambdaSvc *lambdaService.Lambdatype codeDeployEvent struct { DeploymentId string `json:"deploymentId"` LifecycleEventHookExecutionId string `json:"lifecycleEventHookExecutionId"`}func handler(e codeDeployEvent) error { params := &codedeploy.PutLifecycleEventHookExecutionStatusInput{ DeploymentId: &e.DeploymentId, LifecycleEventHookExecutionId: &e.LifecycleEventHookExecutionId, } err := handle() if err != nil { log.Println(err) params.Status = aws.String(codedeploy.LifecycleEventStatusFailed) } else { params.Status = aws.String(codedeploy.LifecycleEventStatusSucceeded) } _, err = svc.PutLifecycleEventHookExecutionStatus(params) if err != nil { return fmt.Errorf("failed putting the lifecycle event hook execution status. the status was %s", *params.Status) } return nil}func handle() error { functionName := os.Getenv("FUNCTION_NAME") if functionName == "" { return fmt.Errorf("FUNCTION_NAME not set") } log.Printf("function name: %s", functionName) // invoke lambda via sdk input := &lambdaService.InvokeInput{ FunctionName: &functionName, Payload: nil, LogType: aws.String(lambdaService.LogTypeTail), // returns the log in the response InvocationType: aws.String(lambdaService.InvocationTypeRequestResponse), // synchronous - default } err := input.Validate() if err != nil { return fmt.Errorf("validating the input failed: %v", err) } resp, err := lambdaSvc.Invoke(input) if err != nil { return fmt.Errorf("failed to invoke lambda: %v", err) } decodeString, err := base64.StdEncoding.DecodeString(*resp.LogResult) if err != nil { return fmt.Errorf("failed to decode the log: %v", err) } log.Printf("log result: %s", decodeString) if resp.FunctionError != nil { return fmt.Errorf("lambda was invoked but returned error: %s", *resp.FunctionError) } return nil}func main() { sess, err := session.NewSession() if err != nil { return } svc = codedeploy.New(sess) lambdaSvc = lambdaService.New(sess) lambda.Start(handler)}