‘AWS Cloudformation으로 인프라 구축하기#1 – Network 구성하기’에 이어서 이번 글에서는 이전에 생성한 네트워크를 바탕으로 Server를 구축하여 3tier를 구축해보겠습니다.
3tier Architecture
3tier Architecture는 한 서버에 3가지 로직을 구현한 것이 아닌, 3계층(Presentation 계층, Application 계층, Data 계층)으로 나누어 논리적 또는 물리적 장치에서 구축하는 것입니다. 3개의 계층을 사용하면 수평 확장성, 성능 및 가용성이 향상될 수 있습니다. 각 계층이 다른 계층에 영향을 주지 않고 변경되거나 재배치 될 수 있기 때문에 애플리케이션을 지속적으로 발전시키기 더 쉽습니다.
이번 글에서 Cloudformation 템플릿에서 빌드되는 내용은 아래와 같습니다.
– EC2 인스턴스 5개
– RDS 1개
– 외부 ALB, 내부 ALB 각 1대
– EC2 인스턴스와 RDS에 적용할 보안그룹 4개 (Bastion, WEB, WAS, DB)
[Cloudformation Template] – Parameters
Bespin-network 스택에서 VPC, Subnet 정보를 가져오기 위해서 파라미터로 지정합니다. 파라미터 섹션에는 서버 접속 키, AMI 이미지, RDS 설정 정보를 사용자 정의 값으로 설정했습니다.
Parameters: NetworkStackName: Description: >- Name of an active CloudFormation stack that contains the networking resources, such as the VPC and subnet that will be used in this stack. Type: String MinLength: 1 MaxLength: 128 AllowedPattern: '^[a-zA-Z][-a-zA-Z0-9]*$' Default: Bespin-network KeyName: Description: Name of an existing EC2 KeyPair to enable SSH access to the instance Type: AWS::EC2::KeyPair::KeyName Default: 'bespin-key' AMI: Type: AWS::EC2::Image::Id Default: ami-0fd0765afb77bcca7 DBInstanceClass: Default: db.m5.large Description: DB instance class Type: String AllowedValues: - db.m5.large - db.m5.xlarge DBUsername: Description: Username for DB Access Type: String MinLength: 1 MaxLength: 64 AllowedPattern: '^[a-zA-Z][-a-zA-Z0-9]*$' DBPassword: NoEcho: true Description: Password for DB Access Type: String MinLength: 8 MaxLength: 40 AllowedPattern: '^[a-zA-Z][-a-zA-Z0-9]*$'
[Cloudformation Template] – Resources
EC2 인스턴스 생성 시 AMI 이미지, 서버 접속 키 정보를 파라미터 값에서 참조합니다. 내장 함수 Fn::ImprotValue 사용하여 네트워크를 구성한 스택에서 내보낸 출력의 값을 반환합니다. 내장 함수 !Sub를 사용하여 입력문자열의 변수를 지정한 값으로 변경합니다.
Resources: ########### # Instance ########### BastionHostInstance: Type: AWS::EC2::Instance Properties: InstanceType: t2.micro ImageId: !Ref AMI SubnetId: Fn::ImportValue: !Sub ${NetworkStackName}-PublicSubnet1 KeyName: !Ref KeyName SecurityGroupIds: - !Ref BastionHostSecurityGroup Tags: - Key: "Name" Value: "Bespin-BastionHost" WebInstance2a: Type: AWS::EC2::Instance Properties: InstanceType: t2.micro ImageId: !Ref AMI SubnetId: Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnet1 KeyName: !Ref KeyName SecurityGroupIds: - !Ref WebSecurityGroup Tags: - Key: "Name" Value: "Bespin-WebServer" WebInstance2c: Type: AWS::EC2::Instance Properties: InstanceType: t2.micro ImageId: !Ref AMI SubnetId: Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnet2 KeyName: !Ref KeyName SecurityGroupIds: - !Ref WebSecurityGroup Tags: - Key: "Name" Value: "Bespin-WebServer" WasInstance2a: Type: AWS::EC2::Instance Properties: InstanceType: t2.micro ImageId: !Ref AMI SubnetId: Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnet3 KeyName: !Ref KeyName SecurityGroupIds: - !Ref WasSecurityGroup Tags: - Key: "Name" Value: "Bespin-WasServer" WasInstance2c: Type: AWS::EC2::Instance Properties: InstanceType: t2.micro ImageId: !Ref AMI SubnetId: Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnet4 KeyName: !Ref KeyName SecurityGroupIds: - !Ref WasSecurityGroup Tags: - Key: "Name" Value: "Bespin-WasServer"
ALB 로드밸런서를 생성합니다. ALB는 External, Internal 하나씩 생성합니다.
########### # Load Balancer ########### WEBALB: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: Scheme : internet-facing Subnets: - Fn::ImportValue: !Sub ${NetworkStackName}-PublicSubnet1 - Fn::ImportValue: !Sub ${NetworkStackName}-PublicSubnet2 SecurityGroups: - !Ref ELBEXSecurityGroup WEBALBListener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - Type: forward TargetGroupArn: !Ref WEBTargetGroup LoadBalancerArn: !Ref WEBALB Port: 80 Protocol: HTTP WEBTargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: HealthCheckIntervalSeconds: 30 HealthCheckTimeoutSeconds: 5 HealthyThresholdCount: 5 Matcher: HttpCode : '200' Port: 80 Protocol: HTTP UnhealthyThresholdCount: 2 VpcId: Fn::ImportValue: !Sub ${NetworkStackName}-VPC TargetGroupAttributes: - Key: deregistration_delay.timeout_seconds Value: '300' Targets: - Id: !Ref WebInstance2a Port: 80 - Id: !Ref WebInstance2c Port: 80 Tags: - Key: Name Value: WEBTargetGroup - Key: Port Value: 80 WASALB: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: Scheme : internal Subnets: - Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnet1 - Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnet2 SecurityGroups: - !Ref ELBINSecurityGroup WASALBListener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - Type: forward TargetGroupArn: !Ref WASTargetGroup LoadBalancerArn: !Ref WASALB Port: 8080 Protocol: HTTP WASTargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: HealthCheckIntervalSeconds: 30 HealthCheckTimeoutSeconds: 5 HealthyThresholdCount: 5 Matcher: HttpCode : '200' Port: 8080 Protocol: HTTP UnhealthyThresholdCount: 2 VpcId: Fn::ImportValue: !Sub ${NetworkStackName}-VPC TargetGroupAttributes: - Key: deregistration_delay.timeout_seconds Value: '300' Targets: - Id: !Ref WasInstance2a Port: 8080 - Id: !Ref WasInstance2c Port: 8080 Tags: - Key: Name Value: WASTargetGroup - Key: Port Value: 8080
각 EC2 인스턴스와 RDS에 연결할 Security group을 생성합니다.
########### # Security Group ########### BastionHostSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: BastionHost GroupDescription: Enable access to BastionHost VpcId: Fn::ImportValue: !Sub ${NetworkStackName}-VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 0.0.0.0/0 Tags: - Key: "Name" Value: "Bespin-BastionHost-SG" WebSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: Web GroupDescription: Enable access to Web VpcId: Fn::ImportValue: !Sub ${NetworkStackName}-VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 SourceSecurityGroupId: !Ref ELBEXSecurityGroup - IpProtocol: tcp FromPort: 22 ToPort: 22 SourceSecurityGroupId: !Ref BastionHostSecurityGroup Tags: - Key: "Name" Value: "Bespin-WEB-SG" WasSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: Was GroupDescription: Enable access to Was VpcId: Fn::ImportValue: !Sub ${NetworkStackName}-VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 8080 ToPort: 8080 SourceSecurityGroupId: !Ref ELBINSecurityGroup - IpProtocol: tcp FromPort: 22 ToPort: 22 SourceSecurityGroupId: !Ref BastionHostSecurityGroup Tags: - Key: "Name" Value: "Bespin-WAS-SG" ELBEXSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: ELB-EX GroupDescription: Enable access to ELB-EX VpcId: Fn::ImportValue: !Sub ${NetworkStackName}-VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0
RDS를 생성합니다. RDS의 인스턴스 type, DB 관리자 계정 및 비밀번호는 파라미터로 값을 받겠습니다. 템플릿에는 DB 인스턴스 1대만 구축하였습니다.
########### # RDS ########### SubnetGroup: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: SubnetGroup for MySQL RDS DBSubnetGroupName: Bespin-SubnetGroup SubnetIds: - Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnet5 - Fn::ImportValue: !Sub ${NetworkStackName}-PrivateSubnet6 RDS: Type: AWS::RDS::DBInstance Properties: DBInstanceIdentifier: Bespin-Mysql DBName: Bepsindb DBInstanceClass: !Ref DBInstanceClass Engine: MySQL EngineVersion: 8.0.29 MasterUsername: !Ref DBUsername MasterUserPassword: !Ref DBPassword AllocatedStorage: 5 DBSubnetGroupName: !Ref SubnetGroup VPCSecurityGroups: - !Ref DBSecurityGroup
[AWS Cloudformation 스택 생성 결과]
AWS Cloudformation 스택 생성이 완료되었습니다.
[AWS 종속성 오류 문제]
AWS Console에서 리소스를 제거하게 된다면 종속성 오류 문제가 발생할 수 있습니다. AWS Cloudformation는 스택을 삭제하면 생성한 모든 리소스가 자동으로 삭제된다는 점에서 장점이 있습니다.
[참고자료]