본문 바로가기
IaC/Terraform

[Terraform] 2-3. Count 장애 상황 재현하기

by okms1017 2024. 6. 23.
728x90

✍ Posted by Immersive Builder  Seong

 

 

1. 실습 소개 

이번 실습은 악분님이 정리하신 실습(count vs foreach) 내용을 기반으로 진행합니다. 

 

  • Step 1. 변수 파일을 이용하여 서브넷 배포하기 
  • Step 2. Count를 이용하여 서브넷 여러 개 배포하기 
  • Step 3. 가용영역(AZ)을 지정하여 서브넷 배포하기 
  • Step 4. 인덱싱 태그(Tag)를 설정하여 서브넷 배포하기 
  • Step 5. 태그(Tag)를 고유한 이름으로 설정하여 서브넷 배포하기 
  • Step 6. 서브넷 설정 변수를 통합하여 리팩토링하기 
  • Step 7. 잘못된 Count 사용으로 인한 장애 상황 재현하기 

 

2. Count 시나리오

시나리오를 진행하기 위한 테스트 VPC(192.168.0.0/16)를 사전에 생성합니다. 

 

 

Step 1. 변수 파일을 이용하여 서브넷 배포하기 

  • main.tf 

        resource "aws_subnet" "main" {
          vpc_id     = aws_vpc.main.id
          cidr_block = var.subnet_cidr
        }
 

 

  • variables.tf

        variable "subnet_cidr" {
          type = string
        }
 

 

  • terraform.tfvars

        subnet_cidr = "192.168.1.0/24"
 

 


 

 

Step 2. Count를 이용하여 서브넷 여러 개 배포하기 

  • main.tf 

        resource "aws_subnet" "main" {
          count = length(var.subnet_cidr)
          vpc_id     = aws_vpc.main.id
          cidr_block = element(var.subnet_cidr, count.index)
        }
 

 

  • variables.tf

        variable "subnet_cidr" {
          type = list(string)
        }
 

 

  • terraform.tfvars

        subnet_cidr = ["192.168.1.0/24", "192.168.2.0/24"]
 

 


 

 

Step 3. 가용영역(AZ)을 지정하여 서브넷 배포하기

  • main.tf

        resource "aws_subnet" "main" {
          count = length(var.subnet_cidr)
          vpc_id     = aws_vpc.main.id
          cidr_block = element(var.subnet_cidr, count.index)
          availability_zone = element(var.subnet_az, count.index)
        }
 

 

  • variables.tf

        variable "subnet_az" {
          type = list(string)
        }
 

 

  • terraform.tfvars

        subnet_az = ["ap-northeast-2a", "ap-northeast-2c"]
 

 


 

 

Step4. 인덱싱 태그(Tag)를 설정하여 서브넷 배포하기 

  • main.tf 

        resource "aws_subnet" "main" {
          count             = length(var.subnet_cidr)
          vpc_id            = aws_vpc.main.id
          cidr_block        = element(var.subnet_cidr, count.index)
          availability_zone = element(var.subnet_az, count.index)

          tags = {
            Name = "terraform VPC-${count.index}"
          }
        }
 

 


 

 

Step5. 태그(Tag)를 고유한 이름으로 설정하여 서브넷 배포하기 

  • main.tf

        resource "aws_subnet" "main" {
          count             = length(var.subnet_cidr)
          vpc_id            = aws_vpc.main.id
          cidr_block        = element(var.subnet_cidr, count.index)
          availability_zone = element(var.subnet_az, count.index)

          tags = element(var.subnet_tag, count.index)
        }
 

 

  • variables.tf

        variable "subnet_tag" {
          type = list(map(string))
        }
 

 

  • terraform.tfvars

        subnet_tag = [
        {
            Name        = "public-subnet"
            Environment = "dev"
        },
        {
            Name        = "private-subnet"
            Environment = "dev"
        }
        ]
 

 


 

 

 

 

Step 6. 서브넷 설정 변수를 통합하여 리팩토링하기 

  • main.tf 

        resource "aws_subnet" "main" {
          count             = length(var.subnets)
          vpc_id            = aws_vpc.main.id
          cidr_block        = var.subnets[count.index].subnet_cidr
          availability_zone = var.subnets[count.index].subnet_az

          tags = var.subnets[count.index].subnet_tag
        }
 

 

  • variables.tf

        variable "subnets" {
          type = list(object({
            subnet_cidr = string,
            subnet_az   = string,
            subnet_tag  = map(string)
          }))
        }
 

 

  • terraform.tfvars

        subnets = [
        {
            subnet_cidr = "192.168.1.0/24",
            subnet_az   = "ap-northeast-2a",
            subnet_tag = {
            Name        = "public-subnet"
            Environment = "dev"
            }
        },
        {
            subnet_cidr = "192.168.2.0/24",
            subnet_az   = "ap-northeast-2c",
            subnet_tag = {
            Name        = "private-subnet"
            Environment = "dev"
            }
        }
        ]
 

 


 

 

Step 7. 잘못된 Count 사용으로 인한 장애 상황 재현하기 

하기와 같이 테라폼을 이용하여 4개의 서브넷(192.168.1.0/24 ~ 192.168.4.0/24)을 구성하고 두 번째 서브넷(192.168.2.0/24)에 EC2 인스턴스를 배포하였다고 가정합니다. 

 

만약 첫 번째 서브넷(192.168.1.0/24)을 더 이상 사용하지 않아 해당 서브넷을 삭제하면 어떻게 될까요? 

 

  • main.tf

        resource "aws_vpc" "main" {
          cidr_block = var.vpc_cidr

          tags = {
            Name = "terraform VPC"
          }
        }

        resource "aws_subnet" "main" {
          count = length(var.subnets)

          vpc_id     = aws_vpc.main.id
          cidr_block = var.subnets[count.index].cidr
          availability_zone = var.subnets[count.index].az

          tags = var.subnets[count.index].tags
        }

        resource "aws_instance" "server" {
          ami           = "ami-0e8bd0820b6e1360b"
          instance_type = "t4g.nano"
          subnet_id = aws_subnet.main[1].id
          # subnet_id = index(aws_subnet.main.*.cidr_block, "192.168.2.0/24")
          tags = {
            Name = "Terraform demo"
          }
        }
 

 

  • terraform.tfvars : 첫 번째 서브넷(192.168.1.0/24)에 해당하는 부분을 주석처리합니다. 

        vpc_cidr            = "192.168.0.0/16"

        subnets = [
          # {
          #   cidr = "192.168.1.0/24",
          #   az = "ap-northeast-2a",
          #   tags = {
          #     Name = "public-subnet"
          #     Environment = "dev"
          #   }
          # },
          {
            cidr = "192.168.2.0/24",
            az = "ap-northeast-2c",
            tags = {
              Name = "public-subnet"
              Environment = "dev"
            }
          },
          {
            cidr = "192.168.3.0/24",
            az = "ap-northeast-2a",
            tags = {
              Name = "private-subnet"
              Environment = "dev"
            }
          },
          {
            cidr = "192.168.4.0/24",
            az = "ap-northeast-2c",
            tags = {
              Name = "private-subnet"
              Environment = "dev"
            }
          }
        ]
 

 


실행계획에서 기존 인스턴스를 삭제하고 재생성한다는 문구가 출력됩니다. 

 

$ terraform plan

  # aws_instance.server must be replaced
-/+ resource "aws_instance" "server" {

```

      ~ subnet_id                            = "subnet-0f592b5384a29db75" # forces replacement -> (known after apply) # forces replacement

```

 # aws_subnet.main[1] must be replaced
-/+ resource "aws_subnet" "main" {

      ~ arn                                            = "arn:aws:ec2:ap-northeast-2:732659419746:subnet/subnet-0f592b5384a29db75" -> (known after apply)
      ~ availability_zone                              = "ap-northeast-2c" -> "ap-northeast-2a" # forces replacement
      ~ availability_zone_id                           = "apne2-az3" -> (known after apply)
      ~ cidr_block                                     = "192.168.2.0/24" -> "192.168.3.0/24" # forces replacement

```

  # aws_subnet.main[2] must be replaced
-/+ resource "aws_subnet" "main" {
      ~ arn                                            = "arn:aws:ec2:ap-northeast-2:732659419746:subnet/subnet-0ada55ddea4c906ab" -> (known after apply)
      ~ availability_zone                              = "ap-northeast-2a" -> "ap-northeast-2c" # forces replacement
      ~ availability_zone_id                           = "apne2-az1" -> (known after apply)
      ~ cidr_block                                     = "192.168.3.0/24" -> "192.168.4.0/24" # forces replacement

```

  # aws_subnet.main[3] will be destroyed

 

'Apply'를 실행하자 기존 인스턴스와 서브넷이 삭제되고 재생성되는 모습입니다. 

데이터를 미리 백업하지 않았다면 심각한 장애 상황에 해당합니다. 

 

$ terraform apply -auto-approve

aws_instance.server: Destroying... [id=i-04c5072c56f392d94]
aws_instance.server: Still destroying... [id=i-04c5072c56f392d94, 10s elapsed]
aws_instance.server: Still destroying... [id=i-04c5072c56f392d94, 20s elapsed]
aws_instance.server: Still destroying... [id=i-04c5072c56f392d94, 30s elapsed]
aws_instance.server: Still destroying... [id=i-04c5072c56f392d94, 40s elapsed]
aws_instance.server: Destruction complete after 40s
aws_subnet.main[1]: Destroying... [id=subnet-0f592b5384a29db75]
aws_subnet.main[3]: Destroying... [id=subnet-04f9556cad5931e5a]
aws_subnet.main[2]: Destroying... [id=subnet-0ada55ddea4c906ab]
aws_subnet.main[1]: Destruction complete after 1s
aws_subnet.main[1]: Creating...
aws_subnet.main[2]: Destruction complete after 1s
aws_subnet.main[2]: Creating...
aws_subnet.main[3]: Destruction complete after 1s
aws_subnet.main[1]: Creation complete after 0s [id=subnet-0a38c1437f398f8e8]
aws_instance.server: Creating...
aws_instance.server: Still creating... [10s elapsed]
aws_instance.server: Creation complete after 13s [id=i-069d2387642c62473]

│ Error: creating EC2 Subnet: InvalidSubnet.Conflict: The CIDR '192.168.4.0/24' conflicts with another subnet
│       status code: 400, request id: 569fe338-b4d4-4bb6-8f66-e5ee06c01b54
│ 
│   with aws_subnet.main[2],
│   on main.tf line 10, in resource "aws_subnet" "main":
│   10:         resource "aws_subnet" "main" {
│ 

 

기대하던 상황은 첫 번째 서브넷(192.168.1.0/24)이 삭제되고, 나머지 서브넷(192.168.2.0/24 ~ 192.168.4.0/24)들이 존재하는 구성입니다. 그러나, 현재 서브넷은 두 개만 존재할 뿐더러 할당된 네트워크 대역마저 충돌이 일어났습니다. 

 

 

두 번째 서브넷(192.168.2.0/24) 대역에 있던 인스턴스가 삭제되고 다른 서브넷(192.168.3.0/24) 대역에 인스턴스가 재생성된 상황입니다. 

 

 

이처럼 Count를 잘못 사용하면 장애 상황이 발생하게 됩니다. 

 

 


[출처]

1) CloudNet@, T1014 실습 스터디 

2) https://registry.terraform.io/providers/hashicorp/aws/latest/docs

 

Terraform Registry

 

registry.terraform.io

3) https://developer.hashicorp.com/terraform/language/functions/element

 

element - Functions - Configuration Language | Terraform | HashiCorp Developer

The element function retrieves a single element from a list.

developer.hashicorp.com

4) https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet#tags

 

Terraform Registry

 

registry.terraform.io

728x90