iT邦幫忙

2021 iThome 鐵人賽

DAY 19
0
DevOps

從煉獄走到天堂的AWS DevOps 工具及應用開發大進擊系列 第 19

多重影分身之術,讓你的分身去做事情

影分身之術

網站正式上線運行時,會開始有使用者訪問瀏覽,流量就會開始出現變化,當網站推廣新活動或周年慶,就有可能出現龐大的流量訪問網站,可能導致原來在ALB後面的機器或Container負荷不了龐大的流量,而出現網站掛掉的可能性。當然可以手動臨時增加機器數量,但AWS還有提供更方便的服務,AutoScaling Group(ASG)可以根據CPU和Memory使用量,以及Request數量,進行增加、減少,可以減少手動時間,進而解決大量流量的問題。

昨天撰寫的CDK,會繼續使用,並且增加ASG這個服務

將Fargate Service加入ASG的設定,並設定最大與最小數量

scale_policy = svc.auto_scale_task_count(
    max_capacity=5,
    min_capacity=1
)

設定ASG觸發規則,這裡採用Request數量去增加和減少Container的數量

scale_policy.scale_on_request_count(
    "asgPolicy",
    requests_per_target=5,
    target_group=tg,
    scale_in_cooldown=core.Duration.seconds(60),
    scale_out_cooldown=core.Duration.seconds(60)
)

requests_per_target: 根據CloudWatch的規則,若ALB後面的target收到超過定義的requests數量,就進行擴增
cooldown: ASG冷卻時間的設定,主要以機器在擴增時,因為大量的流量,而擴增不必要的機器,而導致不必要的花費。需要根據實務情況做設定

以上就是增加ASG的方法,接下來看實際情況

測試方法

這裡主要使用ApacheBench(ab)這個指令,來去測試網站的運行效能,進而模擬大量的Requests數量
https://ithelp.ithome.com.tw/upload/images/20211003/20140172EZ8DTZajE9.png

CloudWatch要觸發Scale-out時候,要先從ok變成alarm,才會觸發,在下方圖片要觸發需要3分鐘內超過5個Requests才做觸發
https://ithelp.ithome.com.tw/upload/images/20211003/20140172TFLFuemtU2.png
原先的Container數量為2,當遇到大量流量時,CloudWatch狀態變更則觸發擴增規則,就會擴增到適合的數量。但這邊有設定上限值為5
https://ithelp.ithome.com.tw/upload/images/20211003/201401726yG0XuStUW.png
CloudWatch要觸發Scale-in減少機器數量時,需要將狀態轉為alarm時,才會觸發ASG,下方圖片規則是15分鐘內requests數量不超過4.5,數量會慢慢減少到適合當前網站的流量
https://ithelp.ithome.com.tw/upload/images/20211003/20140172lwJc29oGmB.png
數量擴增到5個Containers,但當網站流量處於低迷的情況,就會觸發縮減規則,慢慢減少機器/Container的數量,以符合當前流量。最終為減少到最低限制
https://ithelp.ithome.com.tw/upload/images/20211003/20140172fgR1Bceyig.png

結果

https://ithelp.ithome.com.tw/upload/images/20211003/2014017203ZFAnviTQ.png

結論

ASG的擴增減少規則還有其他種類,以及步進的方式,都需要查看/設定CloudWatch的觸發方式,這邊就簡單地揭露,可以更快的操作ASG,且也懂細節的奧妙

完整程式碼

from aws_cdk import core as cdk
from aws_cdk import (
    aws_ec2 as ec2,
    aws_ecs as ecs,
    aws_elasticloadbalancingv2 as elbv2,
    core
    )

class EcsFargateItStack(cdk.Stack):

    def __init__(self, scope: cdk.Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # The code that defines your stack goes here
        
        vpc = ec2.Vpc(self, "VPC", 
        cidr="10.0.0.0/16",
        enable_dns_hostnames=True,
        enable_dns_support=True,
        max_azs=0,
        nat_gateways=1)

        public_subnet = ec2.Subnet(self, "public-subnet1", 
        availability_zone="us-east-2a",
        cidr_block="10.0.10.0/24",
        vpc_id=vpc.vpc_id,
        map_public_ip_on_launch=True)

        public_subnet2 = ec2.Subnet(
            self,
            "PublicSubnet2",
            availability_zone="us-east-2b",
            cidr_block="10.0.20.0/24",
            vpc_id=vpc.vpc_id,
            map_public_ip_on_launch=True
        )

        public_subnet.add_default_internet_route(gateway_id=vpc.internet_gateway_id, gateway_attachment=vpc)
        public_subnet2.add_default_internet_route(gateway_id=vpc.internet_gateway_id, gateway_attachment=vpc)

        sg = ec2.SecurityGroup(self, "CDK-SG", 
        vpc=vpc,
        description="cdk create security group",
        security_group_name="cdkSG")

        sg.add_ingress_rule(peer=ec2.Peer.any_ipv4(), connection=ec2.Port.tcp(80), description="cdk allow anywhere about HTTP protocol")

        alb = elbv2.ApplicationLoadBalancer(self, "CDK-ALB", 
            vpc=vpc,
            internet_facing=True,
            vpc_subnets=ec2.SubnetSelection(
                subnets=[public_subnet, public_subnet2]
            ),
            security_group=sg,
            load_balancer_name="cdkALB"
        )
        
        tg = elbv2.ApplicationTargetGroup(
            self,
            "CDK-TG",
            port= 80,
            protocol=elbv2.ApplicationProtocol.HTTP,
            vpc=vpc
        )

        listener = alb.add_listener(
            "Listener", 
            port=80,
            protocol=elbv2.ApplicationProtocol.HTTP,
            default_target_groups=[tg]
        )

        listener.add_target_groups(
            "CDK-addTG",
            target_groups=[tg]
        )
        

        cluster = ecs.Cluster(self, "Fargate-Cluster",
        enable_fargate_capacity_providers=True,
        vpc=vpc)

        task = ecs.FargateTaskDefinition(self, "fargate-task", 
        cpu=1024,
        memory_limit_mib=2048,
        )

        task.add_container(id="app",
        image=ecs.ContainerImage.from_registry("johnson860312/awswebdb"),
        container_name="mycontainer",
        port_mappings=[
            ecs.PortMapping(container_port=80,
            host_port=80,
            protocol=ecs.Protocol.TCP)
        ],
        cpu=128,
        memory_reservation_mib=256
        )

        svc = ecs.FargateService(self, "fargate-svc",
            task_definition=task,
            cluster=cluster,
            security_groups=[sg],
            assign_public_ip=True,
            vpc_subnets=ec2.SubnetSelection(
                subnets=[public_subnet, public_subnet2]
            ),
            desired_count=2
        )
        
        scale_policy = svc.auto_scale_task_count(
            max_capacity=5,
            min_capacity=1
        )

        scale_policy.scale_on_request_count(
            "asgPolicy",
            requests_per_target=5,
            target_group=tg,
            scale_in_cooldown=core.Duration.seconds(60),
            scale_out_cooldown=core.Duration.seconds(60)
        )

        tg.add_target(svc)

        core.CfnOutput(self, "PublicDNS", value="http://" + listener.load_balancer.load_balancer_dns_name)

上一篇
當火災發生時,勢必要分流人群,讓各個出口可以有效運用,讓人群疏散
下一篇
成為我們的夥伴,一起航向偉大的航道吧
系列文
從煉獄走到天堂的AWS DevOps 工具及應用開發大進擊30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言