AWS Cloudformation으로 인프라 구축하기#1 – Network 구성하기

이번 글에서는 AWS에서 제공하는 IaC 도구 서비스인 Cloudformation을 이용하여 인프라를 구축하는 내용입니다.


What is Cloudformation?

AWS 및 서드 파티 리소스를 손쉽게 모델링, 프로비저닝 및 관리할 수 있는 코드형 인프라(IaC) 서비스입니다. 리소스를 코드로 관리하여 휴먼에러를 최소화하고 버전관리로 협업에 유리하며 템플릿을 사용하여 같은 구성을 다른 환경에서도 사용할 수 있다는 장점이 있습니다. 생성한 AWS 리소스에 대해서 사용 비용을 지불하고, 레지스트리 확장을 사용하는 경우에는 핸들러 작업당 요금이 부과됩니다.


Cloudformation 작동 방식

1. Cloudformation 템플릿을 작성합니다.
2. 템플릿을 로컬 또는 S3버킷에 저장합니다.
3. 템플릿 파일의 위치를 지정하여 Cloudformation 스택을 생성합니다.


사용자가 수행할 수 있는 권한이 있는 작업만 수행이 가능합니다. IAM User에 VPC 생성 권한을 주지 않고Cloudformation에서 VPC를 생성했을 때, 아래와 같이 생성이 실패하고 에러메시지에 생성 권한이 없다고 나옵니다.


Cloudformation 템플릿 구조

JSON 또는 YAML 형식의 Cloudformation 템플릿을 작성하여 프로비저닝합니다.

AWSTemplateFormatVersion: "version date"   # 최신 템플릿 버전은 2010-09-09입니다.
 
Description:   	# 템플릿에 대한 설명
  String
Metadata:      	# 템플릿에 대한 세부 정보
  template metadata
Parameters:    	# 스택을 생성하거나 업데이트 시 템플릿에 사용자 지정 값을 입력
  set of parameters
Mappings:      	# 리전별로 리소스를 지정할 때 사용(Key-Value 형식)
  set of mappings
Conditions:    	# 스택을 생성하거나 업데이트 시 템플릿에 전달된 파라미터 검증
  set of conditions
Transform:     	# 템플릿을 처리하는 데 사용하는 매크로 지정
  set of transforms
Resources:    	# AWS 리소스 정의
  set of resources
Outputs:      	# 출력 값을 반환하거나 다른 스택에서 활용 가능
  set of outputs

Cloudformation 템플릿을 작성하고 인프라를 구축해보면서 더 자세히 알아보겠습니다.
이번 글에서 생성할 AWS 리소스와 아키텍처는 다음과 같습니다.

– VPC
– Internet Gateway
– NAT Gateway
– Route Table
– Subnet
– EIP


[Cloudformation Template] – Parameters

AvailabilityZone, VPC IPv4 주소 범위, 각 Subnet Ipv4 주소 범위를 파라미터로 정의합니다. Cloudformation 스택을 생성할 때 파라미터 값을 수정할 수 있습니다.

Parameters:
AZ1:
    Description: AvailabilityZone for public
    Type: AWS::EC2::AvailabilityZone::Name
AZ2:
    Description: AvailabilityZone for private
    Type: AWS::EC2::AvailabilityZone::Name
 
VpcCIDR:
    Description: Please enter the IP range (CIDR notation) for this VPC
    Type: String
    Default: 172.16.0.0/16
 
PublicSubnet1CIDR:
    Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone
    Type: String
    Default: 172.16.10.0/24
 
PublicSubnet2CIDR:
    Description: Please enter the IP range (CIDR notation) for the public subnet in the second Availability Zone
    Type: String
    Default: 172.16.20.0/24
 
PrivateSubnet1CIDR:
    Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone
    Type: String
    Default: 172.16.40.0/24
 
PrivateSubnet2CIDR:
    Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone
    Type: String
    Default: 172.16.60.0/24
 
PrivateSubnet3CIDR:
    Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone
    Type: String
    Default: 172.16.80.0/24
 
PrivateSubnet4CIDR:
    Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone
    Type: String
    Default: 172.16.100.0/24
 
PrivateSubnet5CIDR:
    Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone
    Type: String
    Default: 172.16.120.0/24
 
PrivateSubnet6CIDR:
    Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone
    Type: String
    Default: 172.16.140.0/24

[Cloudformation Template] – Resources

Resources 섹션에서 VPC를 구성합니다. VPC의 CidrBlock은 !Ref를 사용하여 위에서 정의한 Parameters 값을 적용하겠습니다. 

Resources:
###########
# VPC
###########
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCIDR
      EnableDnsSupport: true  
      EnableDnsHostnames: true      
      Tags:
        - Key: "Name"
          Value: "Bespin-VPC"

Internet Gateway를 생성하고 VPC에 연결합니다. InternetGatewayId는 InternetGateway가 생성되고 생성된 ID를 참조하도록 합니다.

###########
# Internet Gateway
###########
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: "Name"
          Value: "Bespin-IG"
 
  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

Subnet을 생성합니다. Public Subnet에는 MapPublicIpOnLaunch: true 옵션을 사용하여 인스턴스가 생성될 때 Public IP를 부여할 수 있도록 합니다.

###########
# Subnet
###########
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Ref AZ1
      CidrBlock: !Ref PublicSubnet1CIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: "Name"
          Value: "Bespin-PublicSubnet1"
 
  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Ref AZ2
      CidrBlock: !Ref PublicSubnet2CIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: "Name"
          Value: "Bespin-PublicSubnet2"
 
  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Ref AZ1
      CidrBlock: !Ref PrivateSubnet1CIDR
      MapPublicIpOnLaunch: false
      Tags:
        - Key: "Name"
          Value: "Bespin-PrivateSubnet1"
 
  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Ref AZ2
      CidrBlock: !Ref PrivateSubnet2CIDR
      MapPublicIpOnLaunch: false
      Tags:
        - Key: "Name"
          Value: "Bespin-PrivateSubnet2"
 
  PrivateSubnet3:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Ref AZ1
      CidrBlock: !Ref PrivateSubnet3CIDR
      MapPublicIpOnLaunch: false
      Tags:
        - Key: "Name"
          Value: "Bespin-PrivateSubnet3"
 
  PrivateSubnet4:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Ref AZ2
      CidrBlock: !Ref PrivateSubnet4CIDR
      MapPublicIpOnLaunch: false
      Tags:
        - Key: "Name"
          Value: "Bespin-PrivateSubnet4"
 
  PrivateSubnet5:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Ref AZ1
      CidrBlock: !Ref PrivateSubnet5CIDR
      MapPublicIpOnLaunch: false
      Tags:
        - Key: "Name"
          Value: "Bespin-PrivateSubnet5"
 
  PrivateSubnet6:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Ref AZ2
      CidrBlock: !Ref PrivateSubnet6CIDR
      MapPublicIpOnLaunch: false
      Tags:
        - Key: "Name"
          Value: "Bespin-PrivateSubnet6"

NAT Gateway에 연결할 EIP를 생성합니다. DependsOn 옵션을 사용하여 생성 순서를 정할 수 있습니다. Internet Gateway가 VPC 연결된 후에 EIP를 생성하도록 합니다.

###########
# NAT Gateway
###########
  NatGatewayEIP:
    Type: AWS::EC2::EIP
    DependsOn: InternetGatewayAttachment
    Properties:
      Domain: vpc
 
  NatGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt NatGatewayEIP.AllocationId
      SubnetId: !Ref PublicSubnet2
      Tags:
        - Key: "Name"
          Value: "Bespin-NAT"

다음은 Route Table 생성(AWS::EC2::RouteTable) –> Routing 설정(AWS::EC2::Route) –> 서브넷 연결(AWS::EC2::SubnetRouteTableAssociation) 순으로 구성됩니다. PublicRouteTable은 Public Subnet에 생성되는 인스턴스가 Internet Gateway를 통해 인터넷에 액세스하도록 설정합니다. PrivateRouteTable은 Private Subnet에 생성되는 인스턴스가 NAT Gateway를 통해 인터넷에 액세스하도록 설정합니다.

###########
# RouteTable
###########
  PublicRouteTable:     # 라우팅 테이블을 생성합니다.
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: "Name"
          Value: "Bespin-PublicRouteTable"
 
  DefaultPublicRoute:     # 라우팅 테이블에 라우팅 설정을 합니다.
    Type: AWS::EC2::Route
    DependsOn: InternetGatewayAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
 
  PublicSubnet1RouteTableAssociation:     # 라우팅 테이블에 서브넷을 연결 합니다.
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet1
 
  PublicSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet2
 
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: "Name"
          Value: "Bespin-PrivateRouteTable"
 
  DefaultPrivateRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGateway
 
  PrivateSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnet1
 
  PrivateSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnet2
 
  PrivateSubnet3RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnet3
 
  PrivateSubnet4RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnet4
 
  PrivateSubnet5RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnet5
 
  PrivateSubnet6RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnet6

[Cloudformation Template] – Outputs

VPC, Subnet을 Outputs에 정의하고 출력 정보를 받아서 server를 구축할 때 이용할 것입니다.

###########
# Outputs
###########        
Outputs:
  VPC:
    Description: A reference to the created VPC
    Value: !Ref VPC
    Export:
      Name: !Sub '${AWS::StackName}-VPC'
 
  PublicSubnet1:
    Description: A reference to the public subnet in the 1st Availability Zone
    Value: !Ref PublicSubnet1
    Export:
      Name: !Sub '${AWS::StackName}-PublicSubnet1'
 
  PublicSubnet2:
    Description: A reference to the public subnet in the 2nd Availability Zone
    Value: !Ref PublicSubnet2
    Export:
      Name: !Sub '${AWS::StackName}-PublicSubnet2'
 
  PrivateSubnet1:
    Description: A reference to the private subnet in the 1st Availability Zone
    Value: !Ref PrivateSubnet1
    Export:
      Name: !Sub '${AWS::StackName}-PrivateSubnet1'
 
  PrivateSubnet2:
    Description: A reference to the private subnet in the 2nd Availability Zone
    Value: !Ref PrivateSubnet2
    Export:
      Name: !Sub '${AWS::StackName}-PrivateSubnet2'
 
  PrivateSubnet3:
    Description: A reference to the private subnet in the 1st Availability Zone
    Value: !Ref PrivateSubnet3
    Export:
      Name: !Sub '${AWS::StackName}-PrivateSubnet3'
 
  PrivateSubnet4:
    Description: A reference to the private subnet in the 2nd Availability Zone
    Value: !Ref PrivateSubnet4
    Export:
      Name: !Sub '${AWS::StackName}-PrivateSubnet4'
 
  PrivateSubnet5:
    Description: A reference to the private subnet in the 1st Availability Zone
    Value: !Ref PrivateSubnet5
    Export:
      Name: !Sub '${AWS::StackName}-PrivateSubnet5'
 
  PrivateSubnet6:
    Description: A reference to the private subnet in the 2nd Availability Zone
    Value: !Ref PrivateSubnet6
    Export:
      Name: !Sub '${AWS::StackName}-PrivateSubnet6'

[AWS Cloudformation 스택 생성 결과]

스택이 정상적으로 생성되면 CREATE_COMPLETE 상태가 됩니다.

리소스 항목에서 생성된 리소스를 확인할 수 있습니다.


[마무리]

AWS Cloudformation을 이용해서 간단하게 Network를 구축해봤습니다. 변경사항이 생기면 코드만 수정해주고 업데이트를 해주면 되니 AWS Console에서 수동으로 리소스를 생성하는 것보다 쉽게 인프라를 구성할 수 있습니다. 


[참고자료]

Leave a Comment