인프라

험난했던 CloudFormation으로 인프라 구성하기

김도리개발자 2023. 4. 11. 01:54

무려 13번만에 성공한 CloudFormation 스택

13번의 시도 동안 어떤 에러였는지 어떻게 해결했는지 써보겠다!

1. dory-test

  • 대상 : AWS::EC2::EIPAssociation
  • 에러 메시지 : The networkInterface ID 'eni-0b76d551207121168' does not exist (Service: AmazonEC2; Status Code: 400; Error Code: InvalidNetworkInterfaceID.NotFound; Request ID: 5ec40d3a-ccb6-42ce-a75c-f3049cfdd437; Proxy: null)
  • 문제 : networkInterfaceId를 입력하는데 networkInterface는 EIP가 생성되고 나서 나오기때문에 지정하는 것이 불가능함
  • 솔루션 : networkInterfaceId에 EC2 인스턴스를 Ref로 넣어줌

(스포를 하자면 잘못된 해결 방식이었고 밑에서 올바른 해결법이 나옴)

2. dory-test-2

  • 대상 : AWS::ElasticLoadBalancingV2::LoadBalancer
  • 에러 메시지 : 1 validation error detected: Value 'Internet-facing' at 'scheme' failed to satisfy constraint: Member must satisfy enum value set: [internet-facing, internal] (Service: AmazonElasticLoadBalancing; Status Code: 400; Error Code: ValidationError; Request ID: a84e9a53-bc93-4ba1-aaf5-fe6ff3ecd25e; Proxy: null)
  • 문제 : scheme 값의 value "internet-facing"의 "i" "I" 대소문자 잘못 입력으로 인한 오류 
  • 솔루션 : "I"를 "i"로 바꾸기

3. dory-test-3

  • 대상 : AWS::EC2::Instance
  • 에러 메시지 : The subnet ID 'subnet-0b81102228ea3ee85' does not exist (Service: AmazonEC2; Status Code: 400; Error Code: InvalidSubnetID.NotFound; Request ID: c87299f4-1cd1-4d71-bab3-a563174986ef; Proxy: null)
  • 문제 : EC2 인스턴스에 subnet id로 "subnet-oooooooooo~~" 이런식으로 입력함
  • 솔루션 : subnet id로 Ref로 앞으로 만들어질 subnet을 지정

4. dory-test-4

  • 대상 : AWS::EC2::KeyPair, AWS::EC2::Instance
  • 에러 메시지 : The key pair '[prod] gridge bastion host' does not exist (Service: AmazonEC2; Status Code: 400; Error Code: InvalidKeyPair.NotFound; Request ID: 8a17627e-3107-432b-ad80-3fce8bd5d6c8; Proxy: null)
  • 문제 : KeyPair에서 정의한 KeyName과 의 Instance에서 지정한 KeyName을 다르게 입력함
  • 솔루션 : KeyName을 동일하게 잘 입력함

5. dory-test-5, dory-test-6, dory-test-7

  • 대상 : AWS::EC2::EIPAssociation
  • 에러 메시지 : Invalid id: "i-0b9febe9c7baa6a37" (expecting "eni-...") (Service: AmazonEC2; Status Code: 400; Error Code: InvalidNetworkInterfaceId.Malformed; Request ID: 64a53572-1a9d-4119-b6ab-e5cd92b4f838; Proxy: null)
  • 문제 : networkInterfaceId는 "eni-"로 시작해야 하는데 인스턴스의 id는 "i-"로 시작함
  • 솔루션 : dory-test와 엮인 상황 
    • Sub 사용해서 id 앞에 "en"을 붙여줌 (ㅇㅇ틀린 방법임)
    • !Sub "en${EC2Instance}" 이런식으루

  • 에러 메시지 : The networkInterface ID 'eni-09ce3f806a427eb3a' does not exist (Service: AmazonEC2; Status Code: 400; Error Code: InvalidNetworkInterfaceID.NotFound; Request ID: 80a00740-e4b8-48c1-a105-4e8871e99088; Proxy: null)
  • 문제 : 존재하지 않는 네트워크 인터페이스 id라고 함
  • 솔루션
    • 다른 예시의 네트워크 인터페이스를 보니까 ec2 인스턴스의 id값을 따라가는 것이 아니라 vpc, subnet, sg를 따라감
    • 혹시나 해서 걍 NetworkInterfaceId 없애버림

  • 에러 메시지 : Either NetworkInterfaceId or InstanceId must be specified
  • 문제 : NetworkInterfaceId 없음
  • 솔루션 :
    • NetworkInterfaceId나 InstanceId 둘 중 하나는 꼭 정의되어 있어야 한다고 함
    • EC2EIPAssociation 구글링하니까 networkInterfaceId가 required가 아니라 conditional임
    • NetworkInterfaceId 혹은 InstanceId 둘 중 하나만 입력하면 되는 거여서 InstanceId를 입력했음 (앞으로 만들어질 Instance의 id를 입력해야 해서 Ref로 가져옴)

6. dory-test-8

  • 대상 : AWS::EC2::EIPAssociation
  • 에러 메시지 : 10.1.2.145 is not mapped to the interface eni-0fa8f6546dcd80e67(Service: AmazonEC2; Status Code: 400; Error Code: InvalidParameterValue; Request ID: a61e8266-3728-49ea-a534-d135fda42654; Proxy: null)
  • 문제 : PrivateIpAddress를 정의했었는데 이 ip주소와 네트워크 인터페이스가 매핑이 안됨
  • 솔루션 : PrivateIpAddress가 필수값이 아니라서 지워버림

7. dory-test-9

  • 대상 : AWS::ElasticLoadBalancingV2::Listener
  • 에러 메시지 : Resource handler returned message:faf2' is not a valid target group ARNCode: 400, Request ID: c2837691-2147-Request ID: null)" (RequestToken:InvalidRequest) 96219337-6df7-5b1c-788d-f65e84f8a383, HandlerErrorCode: 4137-a261-413fdf41be76, Extended (Service: ElasticLoadBalancingV2, Status'"arn:aws:elasticloadbalancing:ap-northeast-2:647180380627:listener/app/prod-public-gridge-alb/9711b656879151f0/01e2887c278a
  • 문제 : DefaultActions의 TargetGroupArn이 잘못 설정된 것 같음
  • 솔루션 : AWS::ElasticLoadBalancingV2::Listener Action(DefaultActions)는 필수인데 그 안의 TargetGroupArn은 필수값이 아니길래 지워버림

8. dory-test-10

  • 대상 : AWS::ElasticLoadBalancingV2::Listener
  • 에러 메시지 : Resource handler returned message: "Model validation failed (#/DefaultActions: expected type: JSONArray, found: JSONObject #/Port: expected type: Number, found: String)" (RequestToken: e59d4231-9cd7-23eb-574c-0264d20827a6, HandlerErrorCode: InvalidRequest)
  • 문제 : DefaultActions은 list(배열)로 들어와야 하는데 내가 TargetGroupArn을 지울 때 "-"를 잘못 같이 지워버림
  • 솔루션 : 다시 DefaultActions 안에서 type 앞에 "-" 붙여줌

9. dory-test-11

  • 대상 : AWS::ElasticLoadBalancingV2::Listener
  • 에러 메시지 : Resource handler returned message: "A target group ARN must be specified (Service: ElasticLoadBalancingV2, Status Code: 400, Request ID: f458f3d3-834e-4e00-91e5-e3a6492effa7, Extended Request ID: null)" (RequestToken: 46c6a919-e4e3-065a-7828-310324bb0b2, HandlerErrorCode: InvalidRequest)
  • 문제 : 위에서처럼 TargetGroupArn 필수값 아니라 써져있었는데 갑자기 꼭 정의 되어야 한다면서 에러 뱉음
  • 솔루션 :
    • TargetGroupArn에 ElasticLoadBalancingV2TargetGroup Ref로 입력해줌
    • ElasticLoadBalancingV2Listener2는 알고보니 필요 없는거여서 지워줌

10. dory-test-12

  • 대상 : AWS::EC2::NatGateway
  • 에러 메시지 : Resource handler returned message: "Error occurred during operation 'NatGateway nat-072584a0f41e4fce6 is in state failed and hence failed to stabilize. Detailed failure message: Elastic IP address [eipalloc-0401ba7f72a6b5bOc] is already associated'" (RequestToken: 8c447696-80df-f7a9-2269-1741a3c9a551, HandlerErrorCode: GeneralServiceException)
  • 문제 : eip를 다른 ec2 인스턴스와 NatGateway 둘 다 동시에 associate 하고 있었음
  • 솔루션 : eip에 기존 EC2 인스턴스 연결되어 있는거 끊고 NatGateway에만 연결하는것으로 바꿈

 

더보기

yaml 템플릿 코드

AWSTemplateFormatVersion: "2010-09-09"
Description: ""
Resources:
  EC2VPC:
    Type: "AWS::EC2::VPC"
    Properties:
      CidrBlock: "10.1.0.0/16"
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: "default"
      Tags:
        - Key: "Name"
          Value: "[prod] gridge vpc"

  EC2Subnet:
    Type: "AWS::EC2::Subnet"
    Properties:
      AvailabilityZone: "ap-northeast-2b"
      CidrBlock: "10.1.2.0/24"
      VpcId: !Ref EC2VPC
      MapPublicIpOnLaunch: false
      Tags:
        - Key: "Name"
          Value: "[prod] public gridge subnet"

  EC2Subnet3:
    Type: "AWS::EC2::Subnet"
    Properties:
      AvailabilityZone: "ap-northeast-2a"
      CidrBlock: "10.1.1.0/24"
      VpcId: !Ref EC2VPC
      MapPublicIpOnLaunch: false
      Tags:
        - Key: "Name"
          Value: "[prod] private gridge subnet"

  EC2Subnet2:
    Type: "AWS::EC2::Subnet"
    Properties:
      AvailabilityZone: "ap-northeast-2a"
      CidrBlock: "10.1.3.0/24"
      VpcId: !Ref EC2VPC
      MapPublicIpOnLaunch: false
      Tags:
        - Key: "Name"
          Value: "[prod] public gridge alb subnet 1"

  EC2Subnet4:
    Type: "AWS::EC2::Subnet"
    Properties:
      AvailabilityZone: "ap-northeast-2d"
      CidrBlock: "10.1.4.0/24"
      VpcId: !Ref EC2VPC
      MapPublicIpOnLaunch: false
      Tags:
        - Key: "Name"
          Value: "[prod] public gridge alb subnet 2"

  EC2SecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "[prod] gridge bastion host sg"
      GroupName: "[prod] gridge bastion host sg"
      VpcId: !Ref EC2VPC
      SecurityGroupIngress:
        - CidrIp: "0.0.0.0/0"
          Description: "temp"
          FromPort: 80
          IpProtocol: "tcp"
          ToPort: 80
        - CidrIp: "0.0.0.0/0"
          Description: "temp"
          FromPort: 443
          IpProtocol: "tcp"
          ToPort: 443
        - CidrIp: "110.12.32.45/32"
          Description: "terry"
          FromPort: 22
          IpProtocol: "tcp"
          ToPort: 22
        - CidrIp: "123.111.222.168/32"
          Description: "frontone"
          FromPort: 22
          IpProtocol: "tcp"
          ToPort: 22
        - CidrIp: "180.66.136.118/32"
          Description: "eric-temp"
          FromPort: 22
          IpProtocol: "tcp"
          ToPort: 22
        - CidrIp: "211.110.88.8/32"
          Description: "jerry-temp"
          FromPort: 22
          IpProtocol: "tcp"
          ToPort: 22
        - CidrIp: "211.217.241.105/32"
          Description: "gongduck_softsquared"
          FromPort: 22
          IpProtocol: "tcp"
          ToPort: 22
        - CidrIp: "211.217.241.19/32"
          Description: "gongduck_softsquared_lobby"
          FromPort: 22
          IpProtocol: "tcp"
          ToPort: 22
        - CidrIp: "211.58.97.145/32"
          Description: "dory"
          FromPort: 22
          IpProtocol: "tcp"
          ToPort: 22
        - CidrIp: "219.240.165.33/32"
          Description: "henry"
          FromPort: 22
          IpProtocol: "tcp"
          ToPort: 22
        - CidrIp: "222.111.230.94/32"
          Description: "ark_home_nicednb"
          FromPort: 22
          IpProtocol: "tcp"
          ToPort: 22
        - CidrIp: "61.37.43.195/32"
          Description: "papa_dongguk"
          FromPort: 22
          IpProtocol: "tcp"
          ToPort: 22
      SecurityGroupEgress:
        - CidrIp: "0.0.0.0/0"
          IpProtocol: "-1"

  EC2SecurityGroup2:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "[prod] gridge public alb sg"
      GroupName: "[prod] gridge public alb sg"
      VpcId: !Ref EC2VPC
      SecurityGroupIngress:
        - CidrIp: "0.0.0.0/0"
          FromPort: 80
          IpProtocol: "tcp"
          ToPort: 80
        - CidrIp: "0.0.0.0/0"
          FromPort: 443
          IpProtocol: "tcp"
          ToPort: 443
      SecurityGroupEgress:
        - CidrIp: "0.0.0.0/0"
          IpProtocol: "-1"

  EC2SecurityGroup3:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "[prod] gridge private ec2 sg"
      GroupName: "[prod] gridge private ec2 sg"
      VpcId: !Ref EC2VPC
      SecurityGroupIngress:
        - CidrIp: !Sub "${EC2Instance.PrivateIp}/32"
          Description: "[prod] gridge bastion host private ip"
          IpProtocol: "-1"
        - SourceSecurityGroupId: !Ref EC2SecurityGroup2
          Description: "[prod] gridge public alb sg"
          IpProtocol: "-1"
      SecurityGroupEgress:
        - CidrIp: "0.0.0.0/0"
          IpProtocol: "-1"

  EC2Instance:
    Type: "AWS::EC2::Instance"
    Properties:
      ImageId: "ami-0e9bfdb247cc8de84"
      InstanceType: "t3.micro"
      KeyName: "prod-gridge-bastion-host"
      AvailabilityZone: "ap-northeast-2b"
      Tenancy: "default"
      SubnetId: !Ref EC2Subnet
      EbsOptimized: false
      SecurityGroupIds:
        - !Ref EC2SecurityGroup
      SourceDestCheck: true
      BlockDeviceMappings:
        - DeviceName: "/dev/sda1"
          Ebs:
            Encrypted: false
            VolumeSize: 8
            SnapshotId: "snap-0eed95f8d349d4ce5"
            VolumeType: "gp3"
            DeleteOnTermination: true
      Tags:
        - Key: "Name"
          Value: "[prod] gridge bastion host"

  EC2Instance2:
    Type: "AWS::EC2::Instance"
    Properties:
      ImageId: "ami-0eddbd81024d3fbdd"
      InstanceType: "t3.medium"
      KeyName: "prod-gridge-private-ec2"
      AvailabilityZone: "ap-northeast-2a"
      Tenancy: "default"
      SubnetId: !Ref EC2Subnet3
      EbsOptimized: false
      SecurityGroupIds:
        - !Ref EC2SecurityGroup3
      SourceDestCheck: true
      BlockDeviceMappings:
        - DeviceName: "/dev/xvda"
          Ebs:
            Encrypted: false
            VolumeSize: 35
            SnapshotId: "snap-033af589cb06bcf09"
            VolumeType: "gp2"
            DeleteOnTermination: true
      Tags:
        - Key: "Name"
          Value: "[prod] gridge-private-ec2"

  ElasticLoadBalancingV2LoadBalancer:
    Type: "AWS::ElasticLoadBalancingV2::LoadBalancer"
    Properties:
      Name: "prod-public-gridge-alb"
      Scheme: "internet-facing"
      Type: "application"
      Subnets:
        - !Ref EC2Subnet2
        - !Ref EC2Subnet4
      SecurityGroups:
        - !Ref EC2SecurityGroup2
      IpAddressType: "ipv4"

  ElasticLoadBalancingV2Listener:
    Type: "AWS::ElasticLoadBalancingV2::Listener"
    Properties:
      LoadBalancerArn: !Ref ElasticLoadBalancingV2LoadBalancer
      Port: 80
      Protocol: "HTTP"
      DefaultActions:
        - TargetGroupArn: !Ref ElasticLoadBalancingV2TargetGroup
          Type: "forward"

  ElasticLoadBalancingV2TargetGroup:
    Type: "AWS::ElasticLoadBalancingV2::TargetGroup"
    Properties:
      HealthCheckIntervalSeconds: 30
      HealthCheckPath: "/"
      Port: 80
      Protocol: "HTTP"
      HealthCheckPort: "traffic-port"
      HealthCheckProtocol: "HTTP"
      HealthCheckTimeoutSeconds: 5
      UnhealthyThresholdCount: 2
      TargetType: "instance"
      Matcher:
        HttpCode: "200"
      HealthyThresholdCount: 5
      VpcId: !Ref EC2VPC
      Name: "prod-public-gridge-alb-tg"
      HealthCheckEnabled: true
      Targets:
        - Id: !Ref EC2Instance2
          Port: 80

  EC2EIPAssociation:
    Type: "AWS::EC2::EIPAssociation"
    Properties:
      AllocationId: !GetAtt EC2EIP.AllocationId
      InstanceId: !Ref EC2Instance
  
  EC2EIPAssociation2:
    Type: "AWS::EC2::EIPAssociation"
    Properties:
      AllocationId: !GetAtt EC2EIP2.AllocationId
      InstanceId: !Ref EC2Instance2

  EC2KeyPair:
    Type: "AWS::EC2::KeyPair"
    Properties:
      KeyName: "prod-gridge-bastion-host"
      KeyType: "rsa"

  EC2KeyPair2:
    Type: "AWS::EC2::KeyPair"
    Properties:
      KeyName: "prod-gridge-private-ec2"
      KeyType: "rsa"

  EC2VPCGatewayAttachment:
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties:
      InternetGatewayId: !Ref EC2InternetGateway
      VpcId: !Ref EC2VPC

  EC2NatGateway:
    Type: "AWS::EC2::NatGateway"
    Properties:
      SubnetId: !Ref EC2Subnet
      Tags:
        - Key: "Name"
          Value: "[prod] gridge nat"
      AllocationId: !GetAtt EC2EIP.AllocationId

  EC2InternetGateway:
    Type: "AWS::EC2::InternetGateway"
    Properties:
      Tags:
        - Key: "Name"
          Value: "[prod] gridge igw"

  EC2Route:
    Type: "AWS::EC2::Route"
    Properties:
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref EC2InternetGateway
      RouteTableId: !Ref EC2RouteTable

  EC2EIP:
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: "vpc"
      Tags:
        - Key: "Name"
          Value: "[prod] gridge bastion host"

  EC2EIP2:
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: "vpc"
      Tags:
        - Key: "Name"
          Value: "[prod] gridge nat eip"

  EC2RouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref EC2VPC
      Tags:
        - Key: "Name"
          Value: "[prod] public gridge rt"

  EC2RouteTable2:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref EC2VPC
      Tags:
        - Key: "Name"
          Value: "[prod] public gridge alb rt"

  EC2RouteTable3:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref EC2VPC
      Tags:
        - Key: "Name"
          Value: "[prod] private gridge rt"

  EC2Route2:
    Type: "AWS::EC2::Route"
    Properties:
      DestinationCidrBlock: "0.0.0.0/0"
      NatGatewayId: !Ref EC2NatGateway
      RouteTableId: !Ref EC2RouteTable3

  EC2SubnetRouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref EC2RouteTable
      SubnetId: !Ref EC2Subnet

  EC2SubnetRouteTableAssociation2:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref EC2RouteTable2
      SubnetId: !Ref EC2Subnet2

  EC2SubnetRouteTableAssociation3:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref EC2RouteTable2
      SubnetId: !Ref EC2Subnet4

  EC2SubnetRouteTableAssociation4:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref EC2RouteTable3
      SubnetId: !Ref EC2Subnet3

험난했던 CloudFormation로 인프라 구성하기 끗 ㅡ

참고로 스택 삭제하면 생성했던 인프라들도 함께 삭제해줌!