使用Serverless Framework部署两个独立Lambda函数时遭遇CloudFormation资源冲突问题
最近我碰到一个特别头疼的问题:用Serverless Framework部署两个完全独立的Lambda函数(lambda-1和lambda-2),各自绑定不同S3桶的ObjectCreated事件触发器,结果每次部署其中一个的时候都会报错。
问题详情
每次部署lambda-1时,都会收到CloudFormation的创建失败提示:
CREATE_FAILED: CustomDashresourceDashexistingDashs3LambdaFunction (AWS::Lambda::Function)
-custom-resource-existing-s3 already exists in stack arn:aws:cloudformation:eu-central-1:xxx:stack/ - /xxxsome-uuidxxxx
更奇怪的是,我把两个CloudFormation栈都删掉之后,能成功部署其中一个,但再部署另一个就会立刻触发同样的冲突错误。而且我完全没在serverless.yml里定义过这个叫<prefix-of-both-lambdas>-custom-resource-existing-s3的资源,两个S3桶的名字也和它完全不沾边。
两个函数的Serverless配置
lambda-1的serverless.yml关键部分:
functions: lambda-1: handler: src/lambda_handler.main environment: ENV: ${opt:stage} events: - s3: bucket: ${ssm:/panda/${opt:stage}/strings/lambda-1-bucket} event: s3:ObjectCreated:* rules: - prefix: <s3-prefix>/ - suffix: .csv existing: true forceDeploy: true resources: Outputs: ServerlessDeploymentBucketName: Export: Name: "${self:service}-${opt:stage}-${self:custom.serverless-aws-resource-names.variables.lambdaFunctionName}-sls-dpl" PythonRequirementsLambdaLayerHash: Export: Name: "${self:service}-${opt:stage}-${self:custom.serverless-aws-resource-names.variables.lambdaFunctionName}-requirements-LayerHash" PythonRequirementsLambdaLayerQualifiedArn: Export: Name: "${self:service}-${opt:stage}-${self:custom.serverless-aws-resource-names.variables.lambdaFunctionName}-requirements-LayerQualifiedArn" PythonRequirementsLambdaLayerS3Key: Export: Name: "${self:service}-${opt:stage}-${self:custom.serverless-aws-resource-names.variables.lambdaFunctionName}-requirements-LayerS3Key"
lambda-2的serverless.yml关键部分:
functions: lambda-2: package: artifact: ${env:MVN_ARTIFACT_RELATIVE_PATH} handler: lambda_handler.Handler events: - s3: bucket: ${ssm:/panda/${opt:stage}/strings/lambda-2-bucket} event: s3:ObjectCreated:* rules: - suffix: .xlsx existing: true logSubscription: true memorySize: 512 timeout: 120 resources: Outputs: ServerlessDeploymentBucketName: Export: Name: "sls-${self:service}-${opt:stage}-${self:custom.serverless-aws-resource-names.variables.lambdaFunctionName}-ServerlessDeploymentBucketName"
问题根源
后来查了Serverless Framework的文档才明白:当我们给S3触发器设置existing: true(也就是绑定已存在的S3桶)时,框架会自动创建一个自定义Lambda资源,用来处理现有桶的事件通知配置(比如给Lambda加S3权限、配置桶的事件规则)。
这个自定义资源的默认命名规则是{服务前缀}-custom-resource-existing-s3,如果两个Lambda服务的前缀相同,就会生成完全一样的资源名,而AWS不允许同一区域下存在同名的Lambda函数,自然就触发冲突了。
解决办法
这里有几个可行的解决方案,按需选择:
给两个服务设置不同的
service名称
在每个服务的serverless.yml顶层配置里,把service字段改成不一样的名字,比如:# lambda-1的serverless.yml service: lambda-1-service # lambda-2的serverless.yml service: lambda-2-service这样自定义资源的前缀会跟着
service名称变,自然不会冲突。自定义资源名称规则
在每个服务的custom字段里,覆盖默认的自定义资源命名,确保每个服务的资源唯一:custom: existingS3: resourceName: ${self:service}-${opt:stage}-custom-resource-existing-s3这个配置会让自定义资源的名字带上服务名和环境,彻底避免重复。
手动管理S3触发器
如果不想依赖Serverless自动创建资源,可以手动配置:- 登录AWS控制台,找到对应的S3桶,手动添加事件通知,指向目标Lambda函数
- 在
serverless.yml里去掉S3触发器的配置,同时给Lambda函数添加必要的S3权限(比如s3:GetObject、s3:ListBucket等)
验证步骤
不管用哪个方案,建议先把现有的冲突CloudFormation栈删掉,然后分别部署两个Lambda函数,确认不再出现资源冲突的错误。
备注:内容来源于stack exchange,提问作者Alex_Wlt

