✍ Posted by Immersive Builder Seong
1. 실습 소개
이번 실습에서는 for_each와 map 타입 변수를 활용하여 웹 프로젝트를 배포합니다.
for_each 구문은 반복되는 유사한 리소스 또는 모듈 집합을 구성하기 위한 메타 인수로서,
each object는 key, value 2개의 속성을 가지며 주로 key-value 형태의 map(또는 set) 타입 변수와 함께 활용됩니다.
키 값 기반으로 리소스에 접근하기 때문에 잘못된 Count 사용으로 인한 장애 상황을 예방할 수 있습니다.
실습 진행 순서는 하기와 같습니다.
- Step1. 외부 공개 모듈(module) 사용하기
- Step2. map 타입 변수 선언하기
- Step3. 변수 파일(terraform.tfvars) 작성하기
- Step4. for_each 구문 작성하기
- Step5. 로컬 모듈(module) 사용하기
- Step6. 웹 프로젝트 배포 및 접속 테스트
2. For each와 Map을 활용하여 리소스 관리하기
Step1. 외부 공개 모듈(module) 사용하기
웹 프로젝트를 배포하기 위한 네트워크 구성 환경 및 리소스를 처음부터 작성할 필요는 없습니다.
테라폼 레지스트리에 공개된 AWS 모듈을 사용하여 VPC, Security Group, LoadBalancer를 구성할 예정입니다.
- 모듈 호출
모듈을 선언할 때에는 source와 version을 지정합니다.
source는 모듈이 위치한 레지스트리 경로이며, version은 모듈의 버전을 의미합니다.
위와 같이 VPC, Security Group, LoadBalancer 모듈을 각각 선언하여 호출합니다.
Step2. map 타입 변수 선언하기
개발 환경(dev-vpc)과 스테이징 환경(stg-vpc)을 격리하여 2개의 웹 프로젝트를 배포할 예정입니다.
따라서 동일한 리소스 구성을 가지는 웹 프로젝트를 변수로 선언하고 map 타입으로 지정합니다.
프로젝트(project) 변수는 값으로 object 타입을 가지며, 각 object는 8개의 인수로 구성됩니다.
- vpc_cidr_block : vpc 네트워크 대역
- public_subnet_cidr_blocks : 퍼블릭 서브넷 리스트
- private_subnet_cidr_blocks : 프라이빗 서브넷 리스트
- public_subnets_per_vpc : vpc 당 구성할 퍼블릭 서브넷 개수
- private_subnets_per_vpc : vpc 당 구성할 프라이빗 서브넷 개수
- instances_per_subnet : 서브넷마다 배포할 인스턴스 개수
- instance_type : 인스턴스 타입 ex) t3.medium, c4.large
- environment : 인스턴스 태그 (tag:Name=Environment)
이후 해당 변수는 var.project 형식으로 참조하게 됩니다.
Step3. 변수 파일(terraform.tfvars) 작성하기
VPC, Security Group, LoadBalancer, Instance 등 리소스를 프로비저닝할 때 해당 프로젝트(project) 변수 값을 전달하기 위해 변수 파일(terraform.tfvars)을 작성합니다.
- terraform.tfvars
프로젝트(project) 변수는 map 타입으로 key와 value 값을 가집니다.
'dev-webapp'과 'stg-webapp'이 key에 해당하고, 개발 환경과 스테이징 환경에 적용될 인수들을 각각 object 형태의 value 값으로 전달합니다.
위와 같이 작성하면 개발(dev-vpc, 192.168.0.0/16) 및 스테이징(stg-vpc, 10.10.0.0/16) 2개의 VPC를 구성하고,
VPC마다 퍼블릭/프라이빗 서브넷을 2개씩 구성한 다음 서브넷마다 EC2 인스턴스를 배포할 것을 예상할 수 있습니다.
Step4. for_each 구문 작성하기
- VPC 모듈
다수의 VPC를 구성하고자 할 때 for_each 메타 인수를 사용할 수 있습니다.
다음 코드는 var.project map의 key-value 쌍을 각각 each.key, each.value에 할당합니다.
- for_each : key-value 형태의 반복 메타 인수
- each.value.vpc_cidr_block : 변수 값에서 vpc_cidr_block에 해당하는 네트워크 대역 값
- slice(list, startindex, endindex) : list[startindex] ≤ list[x] < list[endindex], 인덱스 시작 값은 포함하고 끝 값은 제외함
- slice(each.value.public_subnet_cidr_blocks, 0, each.value.public_subnets_per_vpc) : slice(["192.168.1.0/24","192.168.2.0/24","192.168.3.0/24","192.168.4.0/24"],0,2) => [ "192.168.1.0/24","192.168.2.0/24"] # dev-public-subnet
slice(["10.10.1.0/24","10.10.2.0/24","10.10.3.0/24","10.10.4.0/24"],0,2) => ["10.10.1.0/24","10.10.2.0/24"] # stg-public-subnet - slice(each.value.private_subnet_cidr_blocks, 0, each.value.private_subnets_per_vpc) : slice(["192.168.101.0/24","192.168.102.0/24","192.168.103.0/24","192.168.104.0/24"], 0, 2) => ["192.168.101.0/24","192.168.102.0/24"] # dev-private-subnet
slice(["10.10.101.0/24","10.10.102.0/24","10.10.103.0/24","10.10.104.0/24"], 0, 2) => ["10.10.101.0/24","10.10.102.0/24"] # stg-private-subnet
- Security Group 모듈
Security Group name과 vpc_id, ingress_cidr_blocks에서 each.key를 통해 for_each로 구성된 리소스를 구별할 수 있습니다.
- LoadBalancer(ELB) 모듈
마찬가지로 각 VPC에 배포되는 로드밸런서 또한 each.key를 통해 name과 Security Group ID, Subnet, 인스턴스 ID를 구별합니다.
Step5. 로컬 모듈(module) 사용하기
웹 프로젝트를 배포하기 위한 인스턴스 리소스 블록은 아래와 같이 Count가 적용되어 있습니다.
하지만 Count와 for_each는 동일한 블록에 같이 사용할 수 없습니다.
이를 해결하기 위해 aws_instance 리소스를 자식 모듈로 구성하여 호출하겠습니다.
├── examples
│ ├── for_each_webapp
│ ├── main.tf ≫ 자식 모듈 호출
│ ├── outputs.tf
│ ├── terraform.tf
│ ├── terraform.tfvars
│ └── variables.tf
├── modules
└── aws-web-instance
├── main.tf ≪ 여기로 aws_instance 리소스 블록 이동 !
├── outputs.tf
└── variables.tf
- 자식 모듈 호출
모듈을 호출하는 방법은 Step1과 동일하나, source에 로컬 디렉토리 경로를 지정하는 점이 다릅니다.
루트 모듈을 기준으로 상대 경로("../../modules/aws-web-instance")를 지정합니다.
Step6. 웹 프로젝트 배포 및 접속 테스트
웹 프로젝트를 배포하여 결과를 확인합니다.
$ terraform init && terraform apply -auto-approve
배포가 완료되기까지 약 6분 정도 소요됩니다.
- WEB 서비스 테스트
LB DNS로 접속을 시도할 때 WEB 서비스가 정상적으로 제공됨을 확인할 수 있습니다.
$ DEV_LBDNS=$(aws elb describe-load-balancers --load-balancer-names lb-gwB9-dev-webapp-dev | jq -r .LoadBalancerDescriptions[0].DNSName)
$ echo $DEV_LBDNS
lb-gwB9-dev-webapp-dev-894199057.ap-northeast-2.elb.amazonaws.com
$ while true; do curl --connect-timeout 1 http://$DEV_LBDNS; echo "------------------------------"; date; sleep 1; done
------------------------------
Sun Jun 30 02:32:25 KST 2024
<html><body><div>Welcome to Seong's skill builder!</div></body></html>
------------------------------
Sun Jun 30 02:32:26 KST 2024
<html><body><div>Welcome to Seong's skill builder!</div></body></html>
$ STG_LBDNS=$(aws elb describe-load-balancers --load-balancer-names lb-gwB9-stg-webapp-stg | jq -r .LoadBalancerDescriptions[0].DNSName)
$ echo $STG_LBDNS
lb-gwB9-stg-webapp-stg-232613450.ap-northeast-2.elb.amazonaws.com
$ while true; do curl --connect-timeout 1 http://$STG_LBDNS; echo "------------------------------"; date; sleep 1; done
------------------------------
Sun Jun 30 02:36:13 KST 2024
<html><body><div>Welcome to Seong's skill builder!</div></body></html>
------------------------------
Sun Jun 30 02:36:14 KST 2024
<html><body><div>Welcome to Seong's skill builder!</div></body></html>
실습을 완료하고 리소스를 반납합니다.
$ terraform destroy -auto-approve
▶ 소스코드: 개인 GitHub
[출처]
1) https://developer.hashicorp.com/terraform/tutorials/configuration-language/for-each
2) https://developer.hashicorp.com/terraform/language/meta-arguments/for_each
'IaC > Terraform' 카테고리의 다른 글
[Terraform] 4-2. 다중 리전에서 동작하는 S3 버킷 구성하기 (0) | 2024.07.07 |
---|---|
[Terraform] 4-1. AWS S3와 DynamoDB를 활용하여 백엔드 구성하기 (0) | 2024.07.07 |
[Terraform] 2-3. Count 장애 상황 재현하기 (0) | 2024.06.23 |
[Terraform] 2-2. Count 활용과 제약사항 (0) | 2024.06.23 |
[Terraform] 2-1. EC2 웹 서버 커스터마이징하기 (0) | 2024.06.22 |