Serverless Framework部署绑定DLQ的SQS时,首次部署RedrivePolicy失败
问题现象
首次完整部署包含死信队列(DLQ)的SQS队列及关联Lambda时,出现CREATE_FAILED错误,CloudFormation返回如下报错:
Resource handler returned message: "Invalid request provided: Error occurred while ReceiveMessage. SQS Error Code: AWS.SimpleQueueService.NonExistentQueue. SQS Error Message: The specified queue does not exist for this wsdl version.(Service: Lambda, Status Code: 400)(HandlerErrorCode: InvalidRequest)"
但如果先部署好DLQ和SQS队列,再单独添加RedrivePolicy配置,部署就能正常完成。
相关配置代码
serverless.ts
import type { AWS } from '@serverless/typescript'; // Lambdas import getMessage from './src/functions/add-to-pros'; const serverlessConfiguration: AWS = { service: 'dlq-service', frameworkVersion: '3', custom: { stageType: '${opt:stage, env:AWS_STAGE, "dev"}', envType: '${env:ENV_TYPE, "dev"}', apiDomainName: 'api.${self:custom.hostedZoneName}', apiBasePath: 'api-${self:custom.stageType}-${self:service}', // SQS Queues getMessageQueue: 'GetMessageQueue', getMessageQueueName: '${self:service}-add-to-pros-queue-${self:provider.stage}', getMessageQueueArn: { 'Fn::Join': [':', ['arn', 'aws', 'sqs', { Ref: 'AWS::Region' }, { Ref: 'AWS::AccountId' }, '${self:custom.getMessageQueueName}']], }, getMessageQueueDLQ: 'GetMessageQueueDLQ', getMessageQueueDLQName: '${self:service}-add-to-pros-queue-dlq-${self:provider.stage}', esbuild: { bundle: true, minify: false, sourcemap: true, exclude: ['aws-sdk'], target: 'node16', define: { 'require.resolve': undefined }, platform: 'node', concurrency: 10, loader: { '.html': 'text' }, }, }, plugins: ['serverless-esbuild', 'serverless-deployment-bucket', 'serverless-offline'], provider: { name: 'aws', runtime: 'nodejs16.x', stage: '${self:custom.stageType}', region: 'eu-west-3', deploymentBucket: { name: '${self:service}-${self:custom.envType}-${self:provider.region}-deployment-bucket-2', }, }, functions: { getMessage, }, package: { // When true optimise lambda performance but increase deployment time individually: !!process.env.STAGE_TYPE && process.env.STAGE_TYPE !== 'dev', }, resources: { Resources: { // Dead Letter Queues GetMessageQueueDLQ: { Type: 'AWS::SQS::Queue', Properties: { QueueName: '${self:custom.getMessageQueueDLQName}', MessageRetentionPeriod: 1209600, // 14 days in seconds }, }, // Queues GetMessageQueue: { DependsOn: ['${self:custom.getMessageQueueDLQ}'], Type: 'AWS::SQS::Queue', Properties: { QueueName: '${self:custom.getMessageQueueName}', RedrivePolicy: { deadLetterTargetArn: { 'Fn::GetAtt': ['${self:custom.getMessageQueueDLQ}', 'Arn'], }, maxReceiveCount: 3, }, VisibilityTimeout: 900, }, }, }, }, }; module.exports = serverlessConfiguration;
getMessage lambda src/functions/handler.ts
export async function main(event: SQSEvent): Promise<void> { try { logger.info(event); } catch (error) { logger.error(error); } }
getMessage lambda src/functions/index.ts
import { getCurrentFolderPath } from '@libs/utils/handler-resolver'; export default { handler: `${getCurrentFolderPath(__dirname)}/handler.main`, memorySize: 256, events: [ { sqs: { arn: '${self:custom.getMessageQueueArn}', }, }, ], };
问题原因
Lambda事件中引用的SQS队列ARN是通过Fn::Join手动拼接的,这种方式无法让CloudFormation自动建立Lambda与SQS队列的依赖关系。部署时,CloudFormation可能优先创建Lambda资源,此时SQS队列尚未完成创建,导致Lambda尝试关联不存在的队列,触发报错。
而后续单独添加RedrivePolicy时,队列已经存在,所以不会出现问题。
解决方案
方案1:修改SQS ARN的引用方式
在serverless.ts的custom配置中,将手动拼接的getMessageQueueArn改为通过Fn::GetAtt引用队列资源的ARN,确保CloudFormation识别依赖关系:
// 替换原来的getMessageQueueArn getMessageQueueArn: { 'Fn::GetAtt': ['GetMessageQueue', 'Arn'], },
方案2:直接在Lambda事件中引用队列资源
在Lambda的事件配置中,直接通过Fn::GetAtt获取队列ARN,跳过自定义的ARN变量:
// 修改src/functions/index.ts中的events配置 events: [ { sqs: { arn: { 'Fn::GetAtt': ['GetMessageQueue', 'Arn'], }, }, }, ],
方案3:显式添加Lambda对SQS队列的依赖
如果需要保留原有的ARN拼接方式,可以在Lambda资源中显式添加DependsOn,确保Lambda在队列创建完成后再部署:
在serverless.ts的resources.Resources中添加Lambda的资源定义(如果尚未定义),并指定依赖:
resources: { Resources: { // ... 原有DLQ和队列定义 GetMessageFunction: { Type: 'AWS::Lambda::Function', DependsOn: ['GetMessageQueue'], // 其他Lambda配置(Serverless自动生成的资源逻辑ID通常为{函数名}Function) }, }, },
验证修改
修改完成后,重新执行serverless deploy,此时CloudFormation会按顺序创建DLQ、主SQS队列,最后创建关联的Lambda,避免队列不存在的错误。
内容的提问来源于stack exchange,提问作者TheSmartMonkey

