2

目标

我正在为 Kinesis 数据流实施自动缩放解决方案。

我正在关注的一种可能的解决方案在aws-samples/aws-application-auto-scaling-kinesis 存储库中有详细记录。但是,示例代码使用了 yaml 模板。我希望使用定义相同的内容。

到目前为止的故事

尝试为 创建缩放目标时my_custom_resource

resource "aws_appautoscaling_target" "my_custom_resource" {
  resource_id        = "https://${aws_api_gateway_rest_api.my_api.id}.execute-api.${var.region}.amazonaws.com/prod/scalableTargetDimensions/${var.stream}"
  scalable_dimension = "custom-resource:ResourceType:Property"
  service_namespace  = "custom-resource"
}

所有属性都是按照AWS Auto-Scaling 文档构建的

在链接的 AWS 存储库中使用 CloudFormation 创建了相同的资源:

KinesisAutoScaling:
  Type: AWS::ApplicationAutoScaling::ScalableTarget
  DependsOn: LambdaScaler
  Properties:
    ResourceId: !Sub https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/prod/scalableTargetDimensions/${MyKinesisStream}
    ScalableDimension: 'custom-resource:ResourceType:Property'
    ServiceNamespace: custom-resource

注意:为简洁起见,省略了不相关的属性。

问题

terraform apply产生以下错误:

错误:创建应用程序自动缩放目标时出错:  
ValidationException:资源验证失败:  
https://k5df89sd23.execute-api.us-west-1.amazonaws.com/prod/scalableTargetDimensions/my-test-stream,  
可扩展维度:自定义资源:资源类型:属性。  
原因:未找到可扩展资源

  在 application-autoscaling.tf 第 9 行,在资源“aws_appautoscaling_target”“my_custom_resource”中:  
   9:资源“aws_appautoscaling_target”“my_custom_resource”{

terraform 定义中可能有什么问题?


我知道 Terraform 支持使用 CloudFormation 模板aws_cloudformation_stack- 我真正希望避免的解决方法。

4

1 回答 1

1

免责声明:我知道这个问题已经被问过了一段时间,但我还是要回答以防万一它可能对其他人有帮助......它肯定会帮助我


假设您正在尝试复制您在问题中提出的链接的内容,那么这就是我认为可能是错误的:

当然是部分答案:aws_appautoscaling_target.my_custom_resource.resource_id

如果您创建了一个aws_api_gateway_deployment资源来重现aws-sample的MyApi部分,那么您很幸运! 你可以使用这个:

resource "aws_appautoscaling_target" "my_custom_resource" {
  # ...
  resource_id = "${aws_api_gateway_deployment.gateway.invoke_url}/scalableTargetDimensions/${var.stream}"
  # ...

如果您想了解如何让这个 ^ 工作的详细信息,请参见下文......

但请记住,这可能还不够!


也可能是解决方案的一部分:创建aws_appautoscaling_target

总是假设您遵循相同示例,并且您已经使用...terraform

免责声明:我不确定这个答案其余部分的确切原因。我怀疑这与 AWS Auto Scaling 服务的内部 API 的要求有关。


TL;DR:在注册自动缩放目标之前,一切都需要连接并正常工作

This part : of the other project 涵盖了您试图实现的集成的提示 。
暗示
README.md

长版;我在实施中必须解决的问题:

  1. Lambda 响应格式
  2. API 网关设置和接线
  3. 正确的 IAM 权限
  4. Lambda 和 APIGateway 工作正常

1. 确保你有正确的 lambda 响应

lambda 的返回值的返回格式对于创建aws_appautoscaling_target.
它需要完全

    returningJson = {
      "actualCapacity": float(actualCapacity),
      "desiredCapacity": float(desiredCapacity),
      "dimensionName": resourceName,
      "resourceName": resourceName,
      "scalableTargetDimensionId": resourceName,
      "scalingStatus": scalingStatus,
      "version": "MyVersion"
    }
    
    try:
        returningJson['failureReason'] = failureReason
    except:
        pass

(这就是它在示例中定义的方式)......
在我的实现中,我玩过它(在一切部署和完成之前)认为我可以从GET调用中获取更多数据,用于指标和监控......
结果最后,当其他所有事情都完成后,我所要做的就是恢复返回功能,一切都成功连接。

2. API网关设置及接线

这部分给我带来了麻烦。我认为Auto Scaling API 完全正确地连接并找到目标,以便它可以注册它(这就是您要创建的资源所做的)
此 API 定义是一个有效的openapi.yaml定义。
我建议把它放在一个像这样的文件中:

openapi.yaml.template

swagger: '2.0'
info:
  title: "${NAME}"
paths:
  '/scalableTargetDimensions/{scalableTargetDimensionId}':
    get:
      tags:
        - ScalableTargets
      x-tags:
        - tag: ScalableTargets
      security:
        - sigv4: []
      x-amazon-apigateway-any-method:
        produces:
          - application/json
        consumes:
          - application/json
      x-amazon-apigateway-integration:
        httpMethod: POST
        type: aws_proxy
        uri: ${INTEGRATION_URI}
        responses: {}
    patch:
      tags:
        - ScalableTargets
      x-tags:
        - tag: ScalableTargets
      security:
        - sigv4: []
      x-amazon-apigateway-any-method:
        security:
          - sigv4: []
        produces:
          - application/json
        consumes:
          - application/json
      x-amazon-apigateway-integration:
        httpMethod: POST
        type: aws_proxy
        uri: ${INTEGRATION_URI}
        responses: {}
securityDefinitions:
  sigv4:
    type: apiKey
    name: Authorization
    in: header
    x-amazon-apigateway-authtype: awsSigv4

然后你可以这样使用它:

# API Gateway
resource "aws_api_gateway_rest_api" "gateway" {
  name = var.rest_api_name
  body = templatefile("${path.module}/openapi.yaml.template",
    {
      NAME            = var.rest_api_name,
      INTEGRATION_URI = var.integration_uri
    }
  )
}

resource "aws_api_gateway_deployment" "gateway" {
  depends_on = [
    aws_api_gateway_rest_api.gateway,
  ]

  lifecycle {
    create_before_destroy = true
  }

  rest_api_id = aws_api_gateway_rest_api.gateway.id
  stage_name  = var.stage_name
}

3.正确的IAM权限

模板中定义的权限cloudformation和您可以创建的权限terraform并不完全匹配,并且似乎需要一些调整才能使集成工作......(我怀疑这与 AWS 的一些魔力有关,但我觉得总体上是相对的易于转移到 terraform)
所以这是我role最终policies创建的:

# Lambda
resource "aws_lambda_function" "lambda" {
  # ...
  role = aws_iam_role.kinesis_autoscaler_lambda_role.arn
  # ...
}
resource "aws_iam_role" "kinesis_autoscaler_lambda_role" {
  name               = "${var.env}-kinesis-scaler-lambda-role"
  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_lambda_permission" "kinesis_api" {
  statement_id  = "AllowKinesisAPIInvoke"
  function_name = aws_lambda_function.lambda.function_name
  action        = "lambda:InvokeFunction"
  principal     = "apigateway.amazonaws.com"
  source_arn    = "${aws_api_gateway_deployment.gateway.execution_arn}/GET/scalableTargetDimensions/{scalableTargetDimensionId}"
}

resource "aws_lambda_permission" "kinesis_api_patch" {
  statement_id  = "AllowKinesisAPIPatchInvoke"
  function_name = aws_lambda_function.lambda.function_name
  action        = "lambda:InvokeFunction"
  principal     = "apigateway.amazonaws.com"
  source_arn    = "${aws_api_gateway_deployment.gateway.execution_arn}/PATCH/scalableTargetDimensions/{scalableTargetDimensionId}"
}

# Permissions
resource "aws_iam_policy" "lambda_access_stream" {
  name = "${var.stream_name}-access-stream-policy"
                                      
  policy = <<POLICY                 
{                           
  "Version": "2012-10-17",                                          
  "Statement": [                                              
    {                                               
      "Sid": "KinesisConsumerAccess",     
      "Effect": "Allow",                                  
      "Action": [                                     
        "kinesis:DescribeStreamConsumer"               
      ],                                               
      "Resource": "${aws_kinesis_stream.stream.arn}/consumer/*:*"
    },                                        
    {                                                                                                                  
      "Sid": "KinesisStreamAccess",      
      "Effect": "Allow",                                                  
      "Action": [                                             
        "kinesis:DescribeLimits",                               
        "kinesis:DescribeStream",                                                                                     
        "kinesis:DescribeStreamConsumer",               
        "kinesis:DescribeStreamSummary",                                                                               
        "kinesis:UpdateShardCount"                      
      ],                                                      
      "Resource": "${aws_kinesis_stream.stream.arn}" 
    },                               
    {                                                              
      "Sid": "SSMParameterStoreGet",                                                                                
      "Effect": "Allow",                                  
      "Action": [    
        "ssm:GetParameter"           
      ],                                           
      "Resource": [                      
        "${aws_ssm_parameter.number_of_shards.arn}",    
      ]                                                           
    },                                             
    {                                                     
      "Sid": "SSMParameterStorePut",                      
      "Effect": "Allow",                                                                                               
      "Action": [                            
        "ssm:PutParameter"                                    
      ],                                
      "Resource": [                                                         
        "${aws_ssm_parameter.number_of_shards.arn}"
      ]                          
    }                                  
  ]                                 
}                                    
POLICY                                
}                  
# I ended up splitting the policies for `module` reasons...                              
resource "aws_iam_policy_attachment" "attach_lambda_stream_access" {
  name = "${aws_iam_role.kinesis_autoscaler_lambda_role.name}_attach_lambda_stream_access"
  roles = [                                         
    aws_iam_role.kinesis_autoscaler_lambda_role.name                  
  ]                                                       
  policy_arn = aws_iam_policy.lambda_access_stream.arn
}       

最后一个很重要。这是我为使 lambda 工作所做的最后一次调整的结果。

resource "aws_iam_policy" "lambda_access_scaling" {
  name = "${var.stream_name}-lambda-access-scaling-policy"

  policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "FindScalingPolicyARNAndAlarms",
      "Effect": "Allow",
      "Action": [
        "application-autoscaling:DescribeScalingPolicies",
        "cloudwatch:DescribeAlarms"
      ],
      "Resource": "*"
    },
    {
      "Sid": "UpdateAlarms",
      "Effect": "Allow",
      "Action": [
        "cloudwatch:PutMetricAlarm",
        "cloudwatch:DeleteAlarms"
      ],
      "Resource": [
        "${aws_cloudwatch_metric_alarm.alarm_out.arn}",
        "${aws_cloudwatch_metric_alarm.alarm_in.arn}"
      ]
    }
  ]
}
POLICY
}

resource "aws_iam_policy_attachment" "attach_scaling_access" {
  name = "${aws_iam_role.kinesis_autoscaler_lambda_role.name}_attach_scaling_access"
  roles = [
    aws_iam_role.kinesis_autoscaler_lambda_role.name
  ]
  policy_arn = aws_iam_policy.lambda_access_scaling.arn
}

此外,您希望拥有custom_appautoscaling...的正确权限

# For reference:  
resource "aws_appautoscaling_target" "kinesis_stream" {
  min_capacity       = var.min_number_of_shard
  max_capacity       = var.max_number_of_shard
  resource_id        = "${aws_api_gateway_deployment.gateway.invoke_url}/scalableTargetDimensions/${var.stream_name}"           
  role_arn           = aws_iam_role.custom_appautoscaling_service_role.arn
  scalable_dimension = "custom-resource:ResourceType:Property"
  service_namespace  = "custom-resource"
                                                                   
  depends_on = [                                        
    aws_iam_policy_attachment.attach_base_policy,
  ]                                                     
                 
  lifecycle {                                        
    ignore_changes = [
      # This is because the "assume_role_policy" becomes the actual
      # Role "AWSServiceRoleForApplicationAutoScaling_CustomResource"                                               
      # at runtime and is always attemted to be recreated 
      role_arn,
    ]              
  }
}                         
          
# Actual policies:
 
resource "aws_iam_role" "custom_appautoscaling_service_role" {
  name               = "${var.stream_name}-assume-custom-resource"
  assume_role_policy = <<-EOF                   
  {        
    "Version": "2012-10-17",                            
    "Statement": [
      {                                      
        "Action": "sts:AssumeRole",
        "Principal": {
          "Service": "custom-resource.application-autoscaling.amazonaws.com"
        },                                       
        "Effect": "Allow",
        "Sid": ""  
      }
    ]                     
  }             
  EOF
}                           
                        
resource "aws_iam_policy" "base_policy" {
  name = "${var.stream_name}_base_policy"
                                 
  policy = <<POLICY
{                  
  "Version": "2012-10-17",                             
  "Statement": [                                     
    {  
      "Sid": "DescribeAlarms",
      "Effect": "Allow",
      "Action": [
        "cloudwatch:DescribeAlarms"
      ],
      "Resource": "*"
    },                                                             
    {                                                   
      "Sid": "InvokeApiGateway",
      "Effect": "Allow",                                
      "Action": [
        "execute-api:Invoke*"                        
      ],
      "Resource": [
        "${aws_api_gateway_deployment.gateway.execution_arn}/scalableTargetDimensions/${var.stream_name}"
      ]                                                   
    }
  ]                
}
POLICY                    
}               
     
resource "aws_iam_policy_attachment" "attach_base_policy" {
  name = "${var.stream_name}_attach_base_policy"
  roles = [      
    aws_iam_role.custom_appautoscaling_service_role.name  
  ]                                
  policy_arn = aws_iam_policy.base_policy.arn
}                    
      
resource "aws_iam_policy" "alarms_modification" {
  name = "${var.stream_name}_alarms_modification"
                        
  policy = <<POLICY
{                                   
  "Version": "2012-10-17",       
  "Statement": [
    {              
      "Sid": "UpdateAlarms",                           
      "Effect": "Allow",                             
      "Action": [
        "cloudwatch:PutMetricAlarm",
        "cloudwatch:DeleteAlarms"
      ],
      "Resource": [
        "${aws_cloudwatch_metric_alarm.alarm_out.arn}",
        "${aws_cloudwatch_metric_alarm.alarm_in.arn}"
      ]                                                       
    }                                                   
  ]        
}                       
POLICY
}                                                      
 
resource "aws_iam_policy_attachment" "attach_alarms_modification" {                        
  name = "${var.stream_name}_attach_alarms_modification"
  roles = [                                            
    aws_iam_role.custom_appautoscaling_service_role.name
  ]
  policy_arn = aws_iam_policy.alarms_modification.arn
}              

4. Lambda 和 APIGateway 运行良好

最后,如果这一切仍然不起作用,您可能需要对您的 lambda 和 APIGateway 进行故障排除......
我最终在 lambda 控制台中使用了apigateway-aws-proxy 事件模板。编辑一些字段,使其看起来像这样:

{
  "body": {
    "desiredCapacity": "1"
  },
  "resource": "/{proxy+}",
  "path": "/scalableTargetDimensions/my-custom-stream",
  "httpMethod": "PATCH",
...
  "requestContext" {
    ...
    "path": "/<STAGE_NAME_SEE_API_GATEWAY_DEPLOYMENT_RESOURCE>/scalableTargetDimensions/my-custom-stream",
    "resourcePath": "/{proxy+}",
    "httpMethod": "PATCH",
}

同样对于这一部分,我创建了一个具有主体的base64encoded主体(因为我猜它是通过 APIGateway 传递的方式?)...
无论如何,我最终对 lambda 进行了一点调整,因此它接受了两者,这样就可以知道是否兰巴德实际上工作正常。

{
  "body": "eyJkZXNpcmVkQ2FwYWNpdHkiOiIyIn0=",
  "resource": "/{proxy+}",
  "path": "/scalableTargetDimensions/my-custom-stream",
  "httpMethod": "PATCH",
  "isBase64Encoded": true,
...

有了这个,您应该能够对 lambda 进行故障排除,确保正确授予所有权限。
要对 APIGateway 进行故障排除,您始终可以使用postman。它可以很好地处理对 AWS 的身份验证,因此如果您拥有对您创建的资源具有足够访问权限的凭证APIGateway,您应该能够执行一些操作GETPATCH手动触发APIGateway和测试这部分集成。

于 2020-12-15T19:30:11.827 回答