在負載平衡中 AWS 提供了我們三種負載平衡 Application Load Balancer(ALB)、Network Load Balancer(NLB) 與 Classic Load Balancer,而 Classic Load Balancer 屬於上一代的負載平衡所以並不會提到它
而 Network Load Balancer 與 Application Load Balancer 最大的不同點在於 Network Load Balancer 為第四層(Transport Layer) 服務而 Application Load Balancer 處於 第七層(Application Layer) 服務,如果以我們目前介紹的用法來出其實沒有太大的差別,因為我們都是把它當 HTTP 的負載平衡器來使用,不過還是先體驗一下吧!
竟然說到 NLB 當然是先建立它拉!
const lb = new elbv2.NetworkLoadBalancer(this, "LB", {
vpc,
internetFacing: true,
});
new cdk.CfnOutput(this, "NetworkLoadBalancerDNS", {
value: lb.loadBalancerDnsName,
});
建立一個 port 80 的 Listener
const listener = lb.addListener("Listener", {
port: 80,
});
這邊分為兩個建立方法,大家可以評估自己的需求來選用想要用的方法
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),
],
});
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 文件
只要使用以上兩個步驟就可以把整個 Network Load Balancer 完成,感覺是不是很快呢 XD
因為這邊的步驟怕大家搞混,所以我們把 Instance type 建立方法與 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.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),
],
});
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),
],
});
目前拿到的
打開它試試看
http://cdkec-albae-1qhysttihzwc-231561288.us-west-2.elb.amazonaws.com/phpinfo.php
基本上與昨天的結果會是一樣的,因為測試的範例是使用 HTTP 網頁作為範例,所以比較沒有感覺
而 Network Load Balancer 主要是用來解決需要處理第四層以上的服務所生的,想要理解更多可以查看一下 AWS Console 打開 Listeners 可以看到除了 TCP 還可以支援 TLS、UDP 或是同時處理 TCP 與 UDP 的服務
以 TLS 來說我們可能有一個服務是 RTSP 而我們的程式可能因為某些原因不方便直接讓程式支援 TLS 可以又必須支援 RTSP over SSL 的時候所使用
以 UDP 來說可能是 DNS 服務或是 QUIC
可能類似 DNS
以上為今日的 Network Load Balancer 介紹,希望有幫助到大家
文章內容主要是網路或是程式開發類型的文章
本文同步刊載於 Clarence 部落格:Day 15 - 使用 CDK 控制 Elastic Load Balancing - Network Load Balancer
「AWS CDK 完全學習手冊:打造雲端基礎架構程式碼 IaC」
本書改編並延伸自第 12 屆 iT 邦幫忙鐵人賽獲得 DevOps 組冠軍的《用 CDK 定義 AWS 架構》系列文章,以簡單、好讀的行文風格詳述技術細節,並提供完整的程式碼範例與說明,一步一步帶領新手從零開始踏上 AWS CDK 技術達人之路。有興趣的朋友歡迎至天瓏書局選購!