Xây dựng cấu trúc High Availability cơ bản trong AWS với CloudFormation
Bài đăng này đã không được cập nhật trong 4 năm
Chào các bạn, hôm nay mình xin giới thiệu về tạo một cấu trúc high availability trong AWS bằng CloudFormation
-
Nói sơ về cấu trúc hệ thống của bài hôm nay
Architecture
Người dùng truy cập tới trang web thông qua DNS của Application Load Balancer (Cái này có thể thay bằng Domain Route 53, vì demo nên mình lấy DNS của ALB luôn, trong hơi xấu xấu tí nhưng không sao !! hehe ). Tiếp đến traffic đưa tới Target Group với Auto Scaling đảm bảo 2 EC2 instance luôn được online.
-
Tạo VPC
VPC.yml
AWSTemplateFormatVersion: '2010-09-09' Description: > VPC Network Parameters: StageName: Type: String Default: dev ApplicationName: Type: String Default: wordpress VPCCIDR: Type: String Default: '192.168.48.0/20' PublicSubnetCIDRs: Type: CommaDelimitedList Default: '192.168.48.0/24,192.168.49.0/24' PrivateSubnetCIDRs: Type: CommaDelimitedList Default: '192.168.50.0/24,192.168.51.0/24' Resources: VPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VPCCIDR EnableDnsSupport: true EnableDnsHostnames: true InstanceTenancy: default Tags: - Key: Name Value: !Sub "${StageName}-${ApplicationName}-VPC" InternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub "${StageName}-${ApplicationName}-IGW" IGAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: InternetGatewayId: !Ref InternetGateway VpcId: !Ref VPC # ROUTE TABLE PublicRouteTable: Type: AWS::EC2::RouteTable DependsOn: IGAttachment Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${StageName}-${ApplicationName}-PublicRouteTable" PrivateRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${StageName}-${ApplicationName}-PrivateRouteTable" PublicRoute: Type: AWS::EC2::Route DependsOn: IGAttachment Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: "0.0.0.0/0" GatewayId: !Ref InternetGateway # SUBNET PublicSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select [0, !GetAZs {Ref: 'AWS::Region'}] CidrBlock: !Select [0, !Ref PublicSubnetCIDRs] VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${StageName}-${ApplicationName}-PublicSubnet1" PublicSubnet1RouteTableAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet1 RouteTableId: !Ref PublicRouteTable PublicSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select [1, !GetAZs {Ref: 'AWS::Region'}] CidrBlock: !Select [1, !Ref PublicSubnetCIDRs] VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${StageName}-${ApplicationName}-PublicSubnet2" PublicSubnet2RouteTableAssociation2: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet2 RouteTableId: !Ref PublicRouteTable PrivateSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select [0, !GetAZs {Ref: 'AWS::Region'}] CidrBlock: !Select [0, !Ref PrivateSubnetCIDRs] VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${StageName}-${ApplicationName}-PrivateSubnet1" PrivateSubnet1RouteTableAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnet1 RouteTableId: !Ref PrivateRouteTable PrivateSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select [1, !GetAZs {Ref: 'AWS::Region'}] CidrBlock: !Select [1, !Ref PrivateSubnetCIDRs] VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${StageName}-${ApplicationName}-PrivateSubnet2" PrivateSubnet2RouteTableAssociation2: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnet2 RouteTableId: !Ref PrivateRouteTable LoadBalancerSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Security Group for LoadBalancer GroupName: !Sub "${StageName}-${ApplicationName}-ALB-SG" VpcId: !Ref VPC SecurityGroupIngress: - CidrIp: '0.0.0.0/0' IpProtocol: tcp FromPort: 80 ToPort: 80 # SG cho Application, chỉ nhận traffic từ Load Balancer vào ApplicationSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Security Group for Application GroupName: !Sub "${StageName}-${ApplicationName}-Application-SG" VpcId: !Ref VPC SecurityGroupIngress: - SourceSecurityGroupId: !Ref LoadBalancerSG IpProtocol: tcp FromPort: 80 ToPort: 80 Description: 'From ALB' Outputs: VPC: Value: !Ref VPC Export: Name: !Sub "${StageName}-${ApplicationName}-VPC" PublicSubnet1: Value: !Ref PublicSubnet1 Export: Name: !Sub "${StageName}-${ApplicationName}-PublicSubnet1" PublicSubnet2: Value: !Ref PublicSubnet2 Export: Name: !Sub "${StageName}-${ApplicationName}-PublicSubnet2" InternetGateway: Value: !Ref InternetGateway Export: Name: !Sub "${StageName}-${ApplicationName}-IGW" PublicRouteTable: Value: !Ref PublicRouteTable Export: Name: !Sub "${StageName}-${ApplicationName}-PublicRouteTable" PrivateRouteTable: Value: !Ref PrivateRouteTable Export: Name: !Sub "${StageName}-${ApplicationName}-PrivateRouteTable" LoadBalancerSG: Value: !Ref LoadBalancerSG Export: Name: !Sub "${StageName}-${ApplicationName}-ALB-SG" ApplicationSG: Value: !Ref ApplicationSG Export: Name: !Sub "${StageName}-${ApplicationName}-Application-SG"
- Create stack
aws cloudformation create-stack \ --stack-name dev-httpd-vpc \ --template-body file://cloudformation/VPC.yml
-
Tạo Target Group, Auto Scaling Group, LoadBalancer...
asg.yml
AWSTemplateFormatVersion: '2010-09-09' Parameters: StageName: Description: An stage name that will be prefixed to resource names Type: String Default: dev ApplicationName: Type: String Default: wordpress KeyName: Description: EC2 SSH KEY Type: AWS::EC2::KeyPair::KeyName # Giá trị này các bạn lấy keypair name đã tạo cho vào Default: <KeyPairName> InstanceType: Type: String AllowedValues: - t2.nano - t2.micro - t2.small - t2.medium - m3.medium - m4.large - c3.medium - c4.medium - c4.large Default: t2.small Ec2ImageId: Type: AWS::SSM::Parameter::Value<String> Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 Resources: TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Name: !Sub ${StageName}-${ApplicationName}-TG Port: 80 Protocol: HTTP VpcId: {"Fn::ImportValue": !Sub "${StageName}-${ApplicationName}-VPC"} TargetType: instance HealthCheckPath: '/var/www/html/index.html' HealthCheckIntervalSeconds: 10 HealthCheckProtocol: HTTP HealthCheckTimeoutSeconds: 5 HealthyThresholdCount: 2 UnhealthyThresholdCount: 2 LoadBalancerListener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: LoadBalancerArn: !Ref LoadBalancer Port: 80 Protocol: HTTP DefaultActions: - Type: forward TargetGroupArn: !Ref TargetGroup LoadBalancer: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: Name: !Sub ${StageName}-${ApplicationName}-ALB Subnets: - {"Fn::ImportValue": !Sub "${StageName}-${ApplicationName}-PublicSubnet1"} - {"Fn::ImportValue": !Sub "${StageName}-${ApplicationName}-PublicSubnet2"} SecurityGroups: - {"Fn::ImportValue": !Sub "${StageName}-${ApplicationName}-ALB-SG"} LaunchConfiguration: Type: AWS::AutoScaling::LaunchConfiguration Properties: KeyName: !Ref KeyName AssociatePublicIpAddress: 'true' SecurityGroups: - {"Fn::ImportValue": !Sub "${StageName}-${ApplicationName}-Application-SG"} InstanceType: !Ref InstanceType ImageId: !Ref Ec2ImageId LaunchConfigurationName: !Sub "${StageName}-${ApplicationName}-LaunchConfig" UserData: Fn::Base64: !Sub | #!/bin/bash yum update -y yum install -y httpd systemctl start httpd.service systemctl enable httpd.service cd /var/www/html echo "<html><h1>Hello</h1></html>" > index.html chkconfig httpd on BlockDeviceMappings: - DeviceName: "/dev/xvda" Ebs: VolumeSize: "8" VolumeType: "gp2" AutoScalingGroup: Type: AWS::AutoScaling::AutoScalingGroup DependsOn: LoadBalancer Properties: LaunchConfigurationName: !Ref LaunchConfiguration MinSize: "2" MaxSize: "2" DesiredCapacity: "2" VPCZoneIdentifier: - {"Fn::ImportValue": !Sub "${StageName}-${ApplicationName}-PublicSubnet1"} - {"Fn::ImportValue": !Sub "${StageName}-${ApplicationName}-PublicSubnet2"} TargetGroupARNs: - !Ref TargetGroup
- Create stack
aws cloudformation create-stack --stack-name ec2-asg \ --template-body file://asg.yml \ --parameters \ ParameterKey=StageName,ParameterValue=dev \ ParameterKey=ApplicationName,ParameterValue=wordpress
Kết quả
-
Copy DNS name rồi paste mở một trình duyệt mới
index.html với Hello đã hiển thị
-
Tiếp theo mình thử terminate 1 EC2 để xem nó có tự scaling lại cho mình một EC2 nữa không!
-
Và sau khoảng 2 phút một em EC2 lại start lên để đảm bảo đủ 2 EC2 cho mình !
Clean up
Vào Cloudformation xoá lần lượt theo thứ tự ngược lại tạo là xong !
Kết
Cảm ơn các bạn đã đọc tới đây. Hẹn gặp lại vào bài tiếp theo!!
All rights reserved