【问题标题】:AWS: how do I create a Network Load Balancer Target Group using CloudFormation?AWS:如何使用 CloudFormation 创建网络负载均衡器目标组?
【发布时间】:2022-02-04 03:44:20
【问题描述】:

我们正在使用 CloudFormation 创建一个网络负载均衡器。目标类型为IP,需要指向一个VPC端点的IP地址。

所以我们需要创建一个“ip”类型的目标组,并指定一个目标列表:

  NetworkLoadBalancerTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub '${AWS::StackName}-NLT'
      Port: 443
      Protocol: TLS
      VpcId: !Ref 'VPC'
      TargetGroupAttributes:
        - Key: deregistration_delay.timeout_seconds
          Value: 300
      TargetType: ip
      Targets: # list of the primary IP addresses of the Network interface(s) associated with the VPC endpoint
        - ?????

目标必须是 VPC 终端节点的 IP 地址。如何引用这些?

VPC 端点是这样创建的:

  VpcEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Interface
      SubnetIds:
      - !Ref ProtectedSubnetA
      - !If [IsProd, !Ref ProtectedSubnetB, !Ref 'AWS::NoValue']
      - !If [IsProd, !Ref ProtectedSubnetC, !Ref 'AWS::NoValue']
      SecurityGroupIds:
      - !Ref SecurityGroupHttpsInInternal
      - !Ref SecurityGroupHttpsOutInternal
      PrivateDnsEnabled: true
      ServiceName: !Sub com.amazonaws.${AWS::Region}.execute-api
      VpcId: !Ref VPC
      PolicyDocument: '{
          "Statement": [
            {
              "Action": "*",
              "Effect": "Allow",
              "Resource": "*",
              "Principal": "*"
            }
          ]
        }'

我可以获得这样的网络接口 ID 列表: !GetAtt VpcEndpoint.NetworkInterfaceIds

但是,这是一个字符串列表。如何获取 id 列表中每个网络接口的 PrimaryPrivateIpAddress 属性?

为了完整起见,这里是网络负载均衡器和相关监听器的定义:

  NetworkLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      LoadBalancerAttributes:
        - Key: load_balancing.cross_zone.enabled
          Value: true
      Name: !Sub '${AWS::StackName}-NLB-Protected'
      Scheme: internal
      Subnets:
        - !Ref ProtectedSubnetA
        - !If [IsProd, !Ref ProtectedSubnetB, !Ref 'AWS::NoValue']
        - !If [IsProd, !Ref ProtectedSubnetC, !Ref 'AWS::NoValue']
      Type: network

  NetworkLoadBalancerListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
      - Type: forward
        TargetGroupArn: !Ref NetworkLoadBalancerTargetGroup
      LoadBalancerArn: !Ref NetworkLoadBalancer
      Port: '443'
      Protocol: TLS
      SslPolicy: ELBSecurityPolicy-TLS-1-2-Ext-2018-06
      Certificates:
        - CertificateArn: !Ref ACMCertificate

【问题讨论】:

    标签: amazon-web-services amazon-cloudformation amazon-elb


    【解决方案1】:

    我发现您的帖子正在寻找完全相同的问题的解决方案。让我分享一下我的结论

    一旦GetAtt 无法访问 IP 列表,我们需要编写一个 CustomResource 来获取它们。

    正如 AWS 在 link 建议的那样,我实施了一个 CustomResource 来获取这些 IP。 还有一个相关的issue

    首先,我们需要将 IAM 角色设置为 lambda。请注意,如果没有这些角色,我们将无法在 CloudWatch 上查看日志

    LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
    LambdaPolicy:
        Type: AWS::IAM::Policy
        Properties:
          PolicyName: LambdaPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - ec2:*
                  - logs:*
                Resource: '*'
          Roles:
            - !Ref LambdaRole
    

    并创建将接收 NetworkId 列表并搜索 NetworkInterface IP 的 lambda 函数:

    LambdaFunction:
        Type: AWS::Lambda::Function
        Condition: IsNotProd
        DeletionPolicy: 'Delete'
        Properties:
          Code:
            ZipFile: !Sub |
              import cfnresponse
              import json
              import boto3
              def lambda_handler(event, context):
                  print('REQUEST RECEIVED:\n' + json.dumps(event))
                  responseData = {}
                  if event['RequestType'] == 'Delete':
                    cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
                    return
                  if event['RequestType'] == 'Create':
                    try:
                      ec2 = boto3.resource('ec2')
                      enis = event['ResourceProperties']['NetworkInterfaceIds']
                      for index, eni in enumerate(enis):
                        network_interface = ec2.NetworkInterface(eni)
                        responseData['IP' + str(index)] = network_interface.private_ip_address
                        print(responseData)
                    except Exception as e:
                      responseData = {'error': str(e)}
                      cfnresponse.send(event, context, cfnresponse.FAILED, responseData)
                      return
                    cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)
          Handler: index.lambda_handler
          Role: !GetAtt LambdaRole.Arn
          Runtime: python3.7
          Timeout: 10
    

    然后创建自定义资源

    GetPrivateIPs:
        DependsOn:
          - VPCEndpoint
        Type: AWS::CloudFormation::CustomResource
        Condition: IsNotProd
        Properties:
          ServiceToken: !GetAtt LambdaFunction.Arn
          NetworkInterfaceIds: !GetAtt VPCEndpoint.NetworkInterfaceIds
    

    你终于可以访问这些IP了

    Targets:
        Type: AWS::ElasticLoadBalancingV2::TargetGroup
        Properties:
          HealthCheckEnabled: true
          HealthCheckIntervalSeconds: 10
          HealthCheckPort: 443
          HealthCheckProtocol: TCP
          HealthCheckTimeoutSeconds: 10
          HealthyThresholdCount: 3
          UnhealthyThresholdCount: 3
          Port: 443
          Protocol: TLS
          Targets:
            - Id: !GetAtt GetPrivateIPs.IP0
              Port: 443
            - Id: !GetAtt GetPrivateIPs.IP1
              Port: 443
          TargetType: ip
          VpcId: !Ref VPC
    

    【讨论】:

      【解决方案2】:

      但是,这是一个字符串列表。如何获取 id 列表中每个网络接口的 PrimaryPrivateIpAddress 属性?

      如您所述,AWS::EC2::VPCEndpoint 返回 NetworkInterfaceIds 而不是 IP 地址。因此,要获得实际的 IP 地址,您必须开发一个custom resource

      这将采用 lambda 函数 的形式,您可以将 ENI ID 传递给该函数。该函数将使用 AWS SDK(例如 boto3)来获取相应的 IP 地址。该函数会将 IP 地址返回给您将在目标组中使用的 CFN。

      【讨论】:

      • 谢谢。这真的很烦人,因为它会比一行 YAML 花费更多的时间,但至少我知道它是可行的。
      • @mdarwin 没问题。是的,CFN 相当有限。如果您将大量使用 CFN,那么您将很难为很多事情编写自定义资源。
      猜你喜欢
      • 2019-12-30
      • 2018-09-03
      • 2020-07-24
      • 1970-01-01
      • 1970-01-01
      • 2021-10-15
      • 2021-08-19
      • 2021-05-19
      • 2021-01-10
      相关资源
      最近更新 更多