Amazon web services Terraform在每个应用程序上为Lambda重新创建API权限,从而导致停机(Lambda模块、无服务器框架、VPC)

Amazon web services Terraform在每个应用程序上为Lambda重新创建API权限,从而导致停机(Lambda模块、无服务器框架、VPC),amazon-web-services,aws-lambda,terraform,serverless-framework,terraform-provider-aws,Amazon Web Services,Aws Lambda,Terraform,Serverless Framework,Terraform Provider Aws,我有一个Lambda创建通过。它指向一个版本化的Lambda,因为我使用了保留并发。它还驻留在专有网络中 配置如下所示: module "my-lambda" { source = "terraform-aws-modules/lambda/aws" version = "~> v1.45.0" function_name = "${local.lambda_name}" d

我有一个Lambda创建通过。它指向一个版本化的Lambda,因为我使用了保留并发。它还驻留在专有网络中

配置如下所示:

module "my-lambda" {
  source  = "terraform-aws-modules/lambda/aws"
  version = "~> v1.45.0"

  function_name         = "${local.lambda_name}"
  description           = local.lambda_name
  handler               = "handler.handler"
  runtime               = "python3.8"
  hash_extra            = local.lambda_name
  attach_tracing_policy = true
  tracing_mode          = "Active"
  publish               = true
  vpc_security_group_ids = [
// required VPC security groups
  ]
  vpc_subnet_ids = var.private_subnet_ids
  source_path = [
    // ... abriged
  ]

  build_in_docker                           = true
  provisioned_concurrent_executions         = var.provisioned_concurrency_lambdas
  create_current_version_allowed_triggers   = true
  create_unqualified_alias_allowed_triggers = false

  allowed_triggers = {
    APIGateway = {
      service    = "apigateway"
      source_arn = "${module.my_api_gateway.this_apigatewayv2_api_execution_arn}/*"
    }
  }

  attach_policies = true
  policies = [
    // policies needed for a VPC lambda
  ]
}

我发现,在terraform plan中,即使我不做任何更改并反复发布
terraform plan
,这种替换仍在发生,这导致重新创建API网关权限,并基本上减少了停机时间:

  # module.my_entire_api.module.my-lambda.aws_lambda_permission.current_version_triggers["APIGateway"] must be replaced
-/+ resource "aws_lambda_permission" "current_version_triggers" {
      ~ id            = "APIGateway" -> (known after apply)
      ~ qualifier     = "1" -> (known after apply) # forces replacement
        # (5 unchanged attributes hidden)
    }

  #  module.my_entire_api.module.my-lambda.aws_lambda_provisioned_concurrency_config.current_version[0] must be replaced
-/+ resource "aws_lambda_provisioned_concurrency_config" "current_version" {
      ~ id                                = "env-my-lambda:1" -> (known after apply)
      ~ qualifier                         = "1" -> (known after apply) # forces replacement
        # (2 unchanged attributes hidden)
    }

还有一些其他lambda不在VPC中运行。目前,我没有看到这种效果在这些,而我不完全肯定,它从来没有发生过


可以肯定的是,我并不关心并发配置,因为重新创建它不会导致停机。但是我想配置模块,使aws_lambda_权限不会被重新创建。我怎么可能做到这一点呢?

terraform提供商aws中的一个问题:


从文件中

通常,Lambda函数资源会在源代码更改时更新。如果指定了
publish=true
,则还将创建新的Lambda函数版本

因此,每次部署时,都会部署一个新版本,该版本在相应的资源中引用以更新策略。因此,它每次都会触发更新

根据您为部署和发布派生上下文的位置,通常部署意味着使用新代码重新部署lambda,而发布则增加lambda版本(而不是重新部署代码)


我所面临的问题归结为几件事

  • 在设置并发性时,必须“发布”lambda,以便它们具有适当的版本限定符(类似于“1”而不是
    $LATEST
    ),因此,允许网关调用lambda的lambda权限绑定到特定的lambda版本。当您创建另一个版本时,这些权限将被销毁,并为新的Lambda版本重新创建<代码>在销毁之前创建生命周期标志可能会有所帮助。我还没有看到在没有变化的情况下为非专有网络lambda重新创建这些;更改Lambda后,在Lambda中删除和重新创建API网关的重新保存的并发性和权限只需几分钟
  • 此外,即使Lambda没有改变,VPC Lambda也会经历并发性和权限的再创造,Terraform bug
解决方案似乎根本不处理Lambdas的权限,而是提供允许其调用Lambdas的API网关“凭证”(即具有Lambda InvokeFunction权限的角色)。这样,当调用AWS网关“集成”(=Lambda)时,它将承担该角色。在这种情况下,不需要Lambda端的权限。我的测试表明,在这种情况下,更新Lambda的顺序是正确的:没有为VPC Lambda创建不必要的资源,当更新Lambda时,首先部署一个新版本,然后API网关转移到它(因此,不会发生停机)。在特定负载下的生产测试也证实,我们在实践中没有看到停机

下面是允许Lambda调用的API网关配置的代码段。它遵循的是在网站上找到的配方

请注意,最后一条
Resource=
指令将允许此角色调用所有lambda。您可能希望将这些权限限制为lambda的子集,以提高安全性并减少人为错误

设置此角色后,我使用一个常用模块配置API网关:


但我不更新lambda本身,只运行apply几个times@AskarIbragimov在每次部署中,它都会创建一个新版本。不,它不会:)@AskarIbragimov至少docs这么说。@AskarIbragimov
aws_lambda_函数
根据。您共享的部署日志中也显示了同样的内容。
publish               = true
variable "publish" {
  description = "Whether to publish creation/change as new Lambda Function Version."
  type        = bool
  default     = false
}
resource "aws_lambda_permission" "current_version_triggers" {
  for_each = var.create && var.create_function && !var.create_layer && var.create_current_version_allowed_triggers ? var.allowed_triggers : {}

  function_name = aws_lambda_function.this[0].function_name
  qualifier     = aws_lambda_function.this[0].version

resource "aws_iam_role" "api_gateway_credentials_call_lambda" {
  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Effect = "Allow",
        Principal = {
          Service = "lambda.amazonaws.com"
        },
        Action = "sts:AssumeRole"
      },
      {
        Effect = "Allow",
        Principal = {
          Service = "apigateway.amazonaws.com"
        },
        Action = "sts:AssumeRole"
      }
    ]
  })
  inline_policy {
    name = "permission-apigw-lambda-invokefunction"
    policy = jsonencode({
      "Version" : "2012-10-17",
      "Statement" : [
        {
          Effect   = "Allow",
          Action   = "lambda:InvokeFunction",
          Resource = "arn:aws:lambda:*:${data.aws_caller_identity.current.account_id}:function:*"
        }
      ]
    })
  }
}
module "api_gateway" {
  source  = "terraform-aws-modules/apigateway-v2/aws"
  version = "~> 0.14.0"

  # various parameters ...

  # Routes and integrations
  integrations = {

    "GET /myLambda" = {
      integration_type        = "AWS_PROXY"
      integration_http_method = "POST"
      payload_format_version  = "2.0"
      lambda_arn              = my_lambda_qualified_arn
      # This line enables the permissions:
      credentials_arn         = aws_iam_role.api_gateway_credentials_call_lambda.arn
    }