【问题标题】:AWS Network Load Balancer without Target in every AZ每个 AZ 中没有目标的 AWS 网络负载均衡器
【发布时间】:2020-09-16 17:28:12
【问题描述】:

在 AWS 网络负载均衡器文档中,它说在为目标组指定实例时,它必须在负载均衡器注册的每个可用区中包含一个实例。这不是强制执行的。

如果您在 3 个 AZ 中注册了 NLB,但在 AZ1 中只有一个目标 EC2 实例,流量会怎样?如果启用跨 AZ 负载均衡,会有什么不同吗?

【问题讨论】:

    标签: amazon-web-services networking amazon-ec2 nlb


    【解决方案1】:

    如果您在 3 个 AZ 中注册了 NLB,但在 AZ1 中只有一个目标 EC2 实例,流量会怎样?如果启用跨 AZ 负载均衡,会有什么不同吗?

    在这个特定场景中(NLB 在 3 个 AZ 中,单个实例在 1 个 AZ 中),什么都没有发生。从最终用户的角度来看,使用或不使用跨区域负载平衡没有明显区别。在任何一种情况下都可以访问该实例。

    为了证明这一点,我开发了一个简单的 CloudFormation 模板,用于创建 NLB,带或不带跨区域负载平衡,以及 1 个实例。该模板允许对 NLB、跨区域和实例位置的不同设置进行简单实验。我在us-east-1区域和默认VPC中使用了模板。

    为模板指定几个参数,包括:

    • NLBSubnetsIds - 启用 NLB 的子网。您必须先在控制台中检查,哪些子网在哪些可用区中。

    • InstanceSubnetId - 实例的子网。如果您想使用实例位置,您可以再次检查哪个子网位于哪个 AZ。您必须确保在为您的 NLB 设置的可用区之一中创建实例。

    • CrossZoneEnabled - 启用或禁用 NLB 的跨区域平衡。

    从模板和实例运行状况检查通过创建堆栈后(可能需要 1 或 2 分钟),您可以在浏览器中访问 NLB DNS 以查看实例上托管的示例网页

    ---
    
    Parameters:
    
      VpcId:
        Type: AWS::EC2::VPC::Id
        
      NLBSubnetsIds:
        Type: List<AWS::EC2::Subnet::Id>
        
      InstanceSubnetId:
        Type: AWS::EC2::Subnet::Id 
        
      AmazonLinux2AMIId:
        Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
        Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
        
      CrossZoneEnabled:
        Type: String  
        Default: false
        AllowedValues: [true, false]
    
    Resources:
    
    
      BasicSecurityGroup:                                                        
          Type: AWS::EC2::SecurityGroup                                          
          Properties: 
            GroupDescription: Enable www port
            SecurityGroupIngress:
              - IpProtocol: tcp
                FromPort: 80
                ToPort: 80
                CidrIp: 0.0.0.0/0            
            VpcId:  !Ref VpcId
    
      MyInstance1:
        Type: AWS::EC2::Instance
    
        CreationPolicy:
            ResourceSignal:
              Timeout: PT5M
                    
        Properties:                
          ImageId: !Ref AmazonLinux2AMIId  
          InstanceType: t2.micro        
          Monitoring: false
          SecurityGroupIds: [!Ref BasicSecurityGroup]
          SubnetId: !Ref InstanceSubnetId
          UserData: 
            Fn::Base64: !Sub |
                #!/bin/bash -xe
    
                yum install -y httpd aws-cfn-bootstrap
    
                echo "<h2>Hello world from $(hostname -f)</h2>" \
                  > /var/www/html/index.html
    
                systemctl start httpd
    
                # check if website is working
                curl -s localhost | grep "Hello"
    
                # Signal the status from cfn-init
                /opt/aws/bin/cfn-signal -e $? \
                    --stack ${AWS::StackName} \
                    --resource MyInstance1 \
                    --region ${AWS::Region}
                    
                    
      MyNLB:
        Type: AWS::ElasticLoadBalancingV2::LoadBalancer
        Properties: 
          IpAddressType: ipv4
          LoadBalancerAttributes:  
            - Key: load_balancing.cross_zone.enabled
              Value: !Ref CrossZoneEnabled
          Scheme: internet-facing 
          Subnets: !Ref NLBSubnetsIds
          Type: network
          
      MyListner1:      
        Type: AWS::ElasticLoadBalancingV2::Listener
        Properties: 
          DefaultActions: 
            - TargetGroupArn: !Ref MyTargetGroup
              Type: forward 
          LoadBalancerArn: !Ref MyNLB
          Port: 80 
          Protocol: TCP 
    
      MyTargetGroup: 
        Type: AWS::ElasticLoadBalancingV2::TargetGroup
        Properties: 
          HealthCheckEnabled: true
          HealthCheckIntervalSeconds: 10
          HealthCheckPath: /
          HealthCheckProtocol: HTTP 
          HealthyThresholdCount: 2
          UnhealthyThresholdCount: 2
          Port: 80
          Protocol: TCP 
          TargetGroupAttributes: 
            - Key: deregistration_delay.timeout_seconds
              Value: 30
          Targets:
            - Id: !Ref MyInstance1
              Port: 80
          TargetType: instance 
          VpcId: !Ref VpcId
          
          
    Outputs:
        
      DNSName:
        Value: !GetAtt MyNLB.DNSName
    

    从最终用户的角度来看,在您的场景中,在 NLB 中启用或禁用跨区域没有明显区别。但是,长期的差异可能在于高可用性。也就是说,如果您禁用了跨区域,并且如果实例所在 AZ 中的 NLB 节点发生问题,则 NLB 将无法将流量从其他 AZ 路由到您的实例。这是我的猜测,因为这不是您可以手动检查的东西。原因是一旦您将 AZ/子网与您的 NLB 关联,您就无法解除关联,以检查在这种情况下会发生什么。

    相比之下,如果启用跨区域,在上述场景中,来自其他区域的 NLB 节点可能会跨区域将流量路由到实例。

    启用跨区域流量的主要好处是当您different number of instances 在不同的可用区时。在这种情况下,跨区域平衡可以使所有实例获得大致相同的流量。如果没有跨区域平衡,隔离实例将获得比其他 AZ 中的实例集合更多的流量。

    您可以使用第二个模板检查区域平衡的效果。模板与之前几乎相同,但现在 1 个 AZ 将有 3 个实例,而另一个将有 1 个 AZ。

    ---
    
    Parameters:
    
      VpcId:
        Type: AWS::EC2::VPC::Id
        
      NLBSubnetsIds:
        Type: List<AWS::EC2::Subnet::Id>
        
      InstanceSubnetId1:
        Type: AWS::EC2::Subnet::Id 
    
      InstanceSubnetId2:
        Type: AWS::EC2::Subnet::Id     
        
      AmazonLinux2AMIId:
        Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
        Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
        
      CrossZoneEnabled:
        Type: String  
        Default: false
        AllowedValues: [true, false]
    
    Resources:
    
    
      BasicSecurityGroup:                                                        
          Type: AWS::EC2::SecurityGroup                                          
          Properties: 
            GroupDescription: Enable www port
            SecurityGroupIngress:
              - IpProtocol: tcp
                FromPort: 80
                ToPort: 80
                CidrIp: 0.0.0.0/0            
            VpcId:  !Ref VpcId
    
      MyInstance1:
        Type: AWS::EC2::Instance
    
        CreationPolicy:
            ResourceSignal:
              Timeout: PT3M
                    
        Properties:                
          ImageId: !Ref AmazonLinux2AMIId  
          InstanceType: t2.micro        
          Monitoring: false
          SecurityGroupIds: [!Ref BasicSecurityGroup]
          SubnetId: !Ref InstanceSubnetId1
          UserData: 
            Fn::Base64: !Sub |
                #!/bin/bash -xe
    
                yum install -y httpd aws-cfn-bootstrap
    
                echo "<h2>Hello world from $(hostname -f)</h2>" \
                  > /var/www/html/index.html
    
                systemctl start httpd
    
                # check if website is working
                curl -s localhost | grep "Hello"
    
                # Signal the status from cfn-init
                /opt/aws/bin/cfn-signal -e $? \
                    --stack ${AWS::StackName} \
                    --resource MyInstance1 \
                    --region ${AWS::Region}
                    
    
      MyInstance2:
        Type: AWS::EC2::Instance
    
        CreationPolicy:
            ResourceSignal:
              Timeout: PT3M
                    
        Properties:                
          ImageId: !Ref AmazonLinux2AMIId  
          InstanceType: t2.micro        
          Monitoring: false
          SecurityGroupIds: [!Ref BasicSecurityGroup]
          SubnetId: !Ref InstanceSubnetId2
          UserData: 
            Fn::Base64: !Sub |
                #!/bin/bash -xe
    
                yum install -y httpd aws-cfn-bootstrap
    
                echo "<h2>Hello2 world from $(hostname -f)</h2>" \
                  > /var/www/html/index.html
    
                systemctl start httpd
    
                # check if website is working
                curl -s localhost | grep "Hello"
    
                # Signal the status from cfn-init
                /opt/aws/bin/cfn-signal -e $? \
                    --stack ${AWS::StackName} \
                    --resource MyInstance2 \
                    --region ${AWS::Region}
    
    
      MyInstance3:
        Type: AWS::EC2::Instance
    
        CreationPolicy:
            ResourceSignal:
              Timeout: PT3M
                    
        Properties:                
          ImageId: !Ref AmazonLinux2AMIId  
          InstanceType: t2.micro        
          Monitoring: false
          SecurityGroupIds: [!Ref BasicSecurityGroup]
          SubnetId: !Ref InstanceSubnetId2
          UserData: 
            Fn::Base64: !Sub |
                #!/bin/bash -xe
    
                yum install -y httpd aws-cfn-bootstrap
    
                echo "<h2>Hello2 world from $(hostname -f)</h2>" \
                  > /var/www/html/index.html
    
                systemctl start httpd
    
                # check if website is working
                curl -s localhost | grep "Hello"
    
                # Signal the status from cfn-init
                /opt/aws/bin/cfn-signal -e $? \
                    --stack ${AWS::StackName} \
                    --resource MyInstance3 \
                    --region ${AWS::Region}
    
    
      MyInstance4:
        Type: AWS::EC2::Instance
    
        CreationPolicy:
            ResourceSignal:
              Timeout: PT3M
                    
        Properties:                
          ImageId: !Ref AmazonLinux2AMIId  
          InstanceType: t2.micro        
          Monitoring: false
          SecurityGroupIds: [!Ref BasicSecurityGroup]
          SubnetId: !Ref InstanceSubnetId2
          UserData: 
            Fn::Base64: !Sub |
                #!/bin/bash -xe
    
                yum install -y httpd aws-cfn-bootstrap
    
                echo "<h2>Hello2 world from $(hostname -f)</h2>" \
                  > /var/www/html/index.html
    
                systemctl start httpd
    
                # check if website is working
                curl -s localhost | grep "Hello"
    
                # Signal the status from cfn-init
                /opt/aws/bin/cfn-signal -e $? \
                    --stack ${AWS::StackName} \
                    --resource MyInstance4 \
                    --region ${AWS::Region}                
                    
      MyNLB:
        Type: AWS::ElasticLoadBalancingV2::LoadBalancer
        Properties: 
          IpAddressType: ipv4
          LoadBalancerAttributes:  
            - Key: load_balancing.cross_zone.enabled
              Value: !Ref CrossZoneEnabled
          Scheme: internet-facing 
          Subnets: !Ref NLBSubnetsIds
          Type: network
          
      MyListner1:      
        Type: AWS::ElasticLoadBalancingV2::Listener
        Properties: 
          DefaultActions: 
            - TargetGroupArn: !Ref MyTargetGroup
              Type: forward 
          LoadBalancerArn: !Ref MyNLB
          Port: 80 
          Protocol: TCP 
    
      MyTargetGroup: 
        Type: AWS::ElasticLoadBalancingV2::TargetGroup
        Properties: 
          HealthCheckEnabled: true
          HealthCheckIntervalSeconds: 10
          HealthCheckPath: /
          HealthCheckProtocol: HTTP 
          HealthyThresholdCount: 2
          UnhealthyThresholdCount: 2
          Port: 80
          Protocol: TCP 
          TargetGroupAttributes: 
            - Key: deregistration_delay.timeout_seconds
              Value: 30
          Targets:
            - Id: !Ref MyInstance1
              Port: 80
            - Id: !Ref MyInstance2
              Port: 80
            - Id: !Ref MyInstance3
              Port: 80
            - Id: !Ref MyInstance4
              Port: 80                              
          TargetType: instance 
          VpcId: !Ref VpcId
          
          
    Outputs:
        
      DNSName:
        Value: !GetAtt MyNLB.DNSName
    

    如果你使用上面的模板,反复请求NLB url,你会看到隔离实例将获得大约50%的流量,没有跨区域均衡。启用跨区域平衡后,它将约为 20%。以下是我根据 100 个请求得出的结果:

    【讨论】:

    • 哇。希望我能给你一个更大的赏金,以获得如此惊人的答案。并且您看到从 AZ2 向 LB 发送请求时没有流量损失,并且仅在 AZ1 中注册了一个实例?
    • @Gandalf 没问题。一切都按预期工作。 NLB 将“看到”只有一个实例,并将仅在这一个可用区中使用节点。不过,您可以慢慢来,不要着急,使用提供的模板进行测试。该模板可以轻松测试此场景或类似场景(例如,当实例位于未启用 NLB 的可用区时会发生什么情况)。
    • 我不确定这个答案是否正确。我最近遇到了一个问题,即通过 NLB 主机名进行通信时,内部流量没有到达单个目标。当 NLB 主机名解析为可用区 A(具有目标)中 NLB 的 IP 地址时,请求成功。当 NLB 主机名解析为区域 B 中的 IP 时,请求超时。
    • 此处的文档说明 > 默认情况下,每个负载均衡器节点仅在其可用区中的已注册目标之间分配流量。如果您启用跨区域负载均衡,则每个负载均衡器节点都会在所有已启用的可用区中的已注册目标之间分配流量。有关更多信息,请参阅 Elastic Load Balancing 用户指南中的跨区域负载均衡。 docs.aws.amazon.com/elasticloadbalancing/latest/network/…
    • @RhysMadigan 不知道你是什么意思,说它不正确。跨区域平衡不会将流量分配到未为负载平衡器启用的区域。你引用的文档也是这么说的。
    【解决方案2】:

    您的方案不需要启用跨区域负载平衡。正如 Marcin 指出的那样,它对您没有任何作用。事实上,解析您的 NLB 的 DNS,您会看到它只为每个 AZ 返回一条 A 记录,该记录在所有 NLB 的目标组中聚合了一个健康的实例。 Marcin 的回答非常适合深入研究。

    有些人在这里说“是的,但我们仍然会超时。”。这是因为您的场景比 OP 更复杂。简而言之,您的 NLB 可能具有多个目标组,其中不同的目标存在于多个 AZ 中。启用跨区域负载平衡将解决您的次优配置问题,但会产生额外的数据传输费用。鸭带和口香糖包装可在 AWS VPC 中使用。

    更多信息:

    NLB 使用 DNS 是“智能的”,因为它们的 VIP 只会解析到具有健康目标的 A 记录(在及时的原因内)。如果 NLB 具有跨三个 AZ 的多个具有不同实例的目标组,您将获得返回的三个 A 记录(每个具有健康目标的 AZ 一个记录)。这就是 NLB + RR DNS 的工作原理。

    但是,如果您的目标组在单个 AZ 中包含 EC2 实例,那么 DNS 循环将解析正确的 AZ 的可能性为 33%(给定三个 AZ)。

    最好的解决方案是开启跨区负载均衡。这增加了数据传输成本,但与拆分 NLB 的替代方案相比,它没有那么复杂。请注意,启用跨区域负载平衡需要几分钟才能启动。不要启用它,立即启动 telnet,当它不起作用时很难过。等待 5 到 10 分钟,然后启动您的 telnet。

    来源:使用 AWS 和 janky EC2 解决方案的轶事和实践经验。

    【讨论】:

      猜你喜欢
      • 2018-09-03
      • 1970-01-01
      • 2021-05-19
      • 2018-04-02
      • 2019-12-30
      • 2021-12-04
      • 2020-07-24
      • 2021-07-16
      • 2021-01-10
      相关资源
      最近更新 更多