Day 15 – 使用 CDK 控制 Elastic Load Balancing – Network Load Balancer

2020 12th 鐵人賽

在負載平衡中 AWS 提供了我們三種負載平衡 Application Load Balancer(ALB)、Network Load Balancer(NLB) 與 Classic Load Balancer,而 Classic Load Balancer 屬於上一代的負載平衡所以並不會提到它

https://i1.wp.com/ithelp.ithome.com.tw/upload/images/20201001/20117701qD5IUlduzr.jpg?w=640&ssl=1

而 Network Load Balancer 與 Application Load Balancer 最大的不同點在於 Network Load Balancer 為第四層(Transport Layer) 服務而 Application Load Balancer 處於 第七層(Application Layer) 服務,如果以我們目前介紹的用法來出其實沒有太大的差別,因為我們都是把它當 HTTP 的負載平衡器來使用,不過還是先體驗一下吧!

建立 Load Balancing

建立 Network Load Balancer(NLB)

竟然說到 NLB 當然是先建立它拉!

const lb = new elbv2.NetworkLoadBalancer(this, "LB", {
  vpc,
  internetFacing: true,
});
new cdk.CfnOutput(this, "NetworkLoadBalancerDNS", {
  value: lb.loadBalancerDnsName,
});

新增 Listener

建立一個 port 80 的 Listener

const listener = lb.addListener("Listener", {
  port: 80,
});

新增 Target 到 Listener

這邊分為兩個建立方法,大家可以評估自己的需求來選用想要用的方法

使用 Instance type 方法建立

Target 設定為 Instance 與 IP 最大的不同在於 Security Group 的設定

使用 Instance Type 可以想像所有的流量會從 ALB 外面直接流進機器,而 NLB 不像是 ALB 一樣擁有 Security Group 因此無法使用 Security Group 對接的方法來保護機器,所以說對應到機器 Security Group 的 Port 需要全開,不然會沒有辦法通

listener.addTargets("Targets", {
  port: httpPort,
  targets: [
    new targets.InstanceTarget(ec2Instance),
  ],
});

使用 IP type 方法建立

Target 設定為 IP Type 的好處在於所有的流量會經由內網丟進去,所以在機器 Security Group 的地方就可以選用 VPC CIDR 的方法指定,以安全性來說可以阻止使用者使用機器的 Public IP 連線或許有的人比較喜歡此方法。 有人可能會詢問那這樣內部的機器是不是就沒有辦法知道 client 的 IP 呢?如果要支援大家可以開啟 proxy protocol version 2 的支援就可以了,不過此 Protocol 需要軟體支援大家需要特別注意!

listener.addTargets("Targets", {
  port: httpPort,
  targets: [
    new targets.IpTarget(ec2Instance.instancePrivateIp),
  ],
});

詳細的 Security 規則大家可以參考 AWS 文件

整理整個服務的 Code

只要使用以上兩個步驟就可以把整個 Network Load Balancer 完成,感覺是不是很快呢 XD 因為這邊的步驟怕大家搞混,所以我們把 Instance type 建立方法與 IP type 建立方法獨立成兩種方法讓大家比較好理解

使用 Instance type 方法建立

const vpc = new ec2.Vpc(this, "VPC", {
  maxAzs: 3,
  natGateways: 0,
});

const mySecurityGroup = new ec2.SecurityGroup(this, "SecurityGroup", {
  vpc,
  allowAllOutbound: true,
});
mySecurityGroup.addIngressRule(
  ec2.Peer.anyIpv4(),
  ec2.Port.tcp(22),
  "allow public ssh access"
);

mySecurityGroup.addIngressRule(
  ec2.Peer.anyIpv4(),
  ec2.Port.tcp(80),
  "allow public http access"
);

const asset = new assets.Asset(this, "Asset", {
  path: path.join(__dirname, "../", "ec2-config", "configure.sh"),
});

const ec2Instance = new ec2.Instance(this, "Instance", {
  vpc,
  instanceType: ec2.InstanceType.of(
    ec2.InstanceClass.T3,
    ec2.InstanceSize.NANO
  ),
  machineImage: ec2.MachineImage.latestAmazonLinux({
    generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
  }),
  securityGroup: mySecurityGroup,
  vpcSubnets: {
    subnetType: ec2.SubnetType.PUBLIC,
  },
  keyName: "KeyPair",
});
new cdk.CfnOutput(this, "EC2PublicDns", {
  value: ec2Instance.instancePublicDnsName,
});
new cdk.CfnOutput(this, "EC2PublicIp", {
  value: ec2Instance.instancePublicIp,
});

const localPath = ec2Instance.userData.addS3DownloadCommand({
  bucket: asset.bucket,
  bucketKey: asset.s3ObjectKey,
});
ec2Instance.userData.addExecuteFileCommand({
  filePath: localPath,
  arguments: "--verbose -y",
});
asset.grantRead(ec2Instance.role);

const httpPort = 80;
const lb = new elbv2.NetworkLoadBalancer(this, "LB", {
  vpc,
  internetFacing: true,
});
new cdk.CfnOutput(this, "NetworkLoadBalancerDNS", {
  value: lb.loadBalancerDnsName,
});

const listener = lb.addListener("Listener", {
  port: httpPort,
});

listener.addTargets("Targets", {
  port: httpPort,
  targets: [
    new targets.InstanceTarget(ec2Instance),
  ],
});

使用 IP type 方法建立

const vpc = new ec2.Vpc(this, "VPC", {
  maxAzs: 3,
  natGateways: 0,
});

const mySecurityGroup = new ec2.SecurityGroup(this, "SecurityGroup", {
  vpc,
  allowAllOutbound: true,
});
mySecurityGroup.addIngressRule(
  ec2.Peer.anyIpv4(),
  ec2.Port.tcp(22),
  "allow public ssh access"
);

mySecurityGroup.addIngressRule(
  ec2.Peer.ipv4(vpc.vpcCidrBlock),
  ec2.Port.tcp(80),
  "allow vpc cidr http access"
);

const asset = new assets.Asset(this, "Asset", {
  path: path.join(__dirname, "../", "ec2-config", "configure.sh"),
});

const ec2Instance = new ec2.Instance(this, "Instance", {
  vpc,
  instanceType: ec2.InstanceType.of(
    ec2.InstanceClass.T3,
    ec2.InstanceSize.NANO
  ),
  machineImage: ec2.MachineImage.latestAmazonLinux({
    generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
  }),
  securityGroup: mySecurityGroup,
  vpcSubnets: {
    subnetType: ec2.SubnetType.PUBLIC,
  },
  keyName: "KeyPair",
});
new cdk.CfnOutput(this, "EC2PublicDns", {
  value: ec2Instance.instancePublicDnsName,
});
new cdk.CfnOutput(this, "EC2PublicIp", {
  value: ec2Instance.instancePublicIp,
});

const localPath = ec2Instance.userData.addS3DownloadCommand({
  bucket: asset.bucket,
  bucketKey: asset.s3ObjectKey,
});
ec2Instance.userData.addExecuteFileCommand({
  filePath: localPath,
  arguments: "--verbose -y",
});
asset.grantRead(ec2Instance.role);

const httpPort = 80;
const lb = new elbv2.NetworkLoadBalancer(this, "LB", {
  vpc,
  internetFacing: true,
});
new cdk.CfnOutput(this, "NetworkLoadBalancerDNS", {
  value: lb.loadBalancerDnsName,
});

const listener = lb.addListener("Listener", {
  port: httpPort,
});

listener.addTargets("Targets", {
  port: httpPort,
  targets: [
    new targets.IpTarget(ec2Instance.instancePrivateIp),
  ],
});

測試 NLB 網址

目前拿到的

  • CdkEc2Stack.LoadBalancerDNS: http://CdkEc-LB8A1-11UM2UIFJMS5H-446ba4836b259a16.elb.us-west-2.amazonaws.com/

測試 Test Page

打開它試試看 https://i1.wp.com/ithelp.ithome.com.tw/upload/images/20200929/201177010EWKEhmVm0.png?w=640&ssl=1

測試 phpinfo.php

http://cdkec-albae-1qhysttihzwc-231561288.us-west-2.elb.amazonaws.com/phpinfo.php

https://i1.wp.com/ithelp.ithome.com.tw/upload/images/20200929/20117701nwoy0p1IgZ.png?w=640&ssl=1

結論

基本上與昨天的結果會是一樣的,因為測試的範例是使用 HTTP 網頁作為範例,所以比較沒有感覺

而 Network Load Balancer 主要是用來解決需要處理第四層以上的服務所生的,想要理解更多可以查看一下 AWS Console 打開 Listeners 可以看到除了 TCP 還可以支援 TLS、UDP 或是同時處理 TCP 與 UDP 的服務 https://i1.wp.com/ithelp.ithome.com.tw/upload/images/20200929/20117701sBEAt55WxC.png?w=640&ssl=1

TLS 支援

以 TLS 來說我們可能有一個服務是 RTSP 而我們的程式可能因為某些原因不方便直接讓程式支援 TLS 可以又必須支援 RTSP over SSL 的時候所使用

UDP 支援

以 UDP 來說可能是 DNS 服務或是 QUIC

TCP 與 UDP

可能類似 DNS


以上為今日的 Network Load Balancer 介紹,希望有幫助到大家