-
05. 테라폼 팁과 요령: 반복문, 조건문, 배포 및 주의사항24년 11월 이전/Terraform Up and Running 2021. 6. 14. 21:53반응형
이 문서는 책 "테라폼 설치에서 운영까지"을 읽고 작성되었습니다. 최대한 요약해서 책 내용을 최소로 하는데 목표를 두고 있어서, 더 친절하고 정확한 내용을 원하신다면 책을 구매하시는 것을 추천드립니다. => 책 링크
Terraform
은 선언형 언어이다. 이는 인프라의 최종 형태를 추론하는데 적절하지만 반대로 절차적인 작업, 반복하거나, 조건에 따라 분기하거나, 무중단 배포 같은 작업들을 표현하기는 어려운 언어이다. 5장에서는Terraform
을 통해서 반복문, 조건문, 무중단 배포 작업 등에 대해 공부할 수 있다.반복문
Terraform
에서 반복문을 표현하는 것 중 가장 간단한 것은 바로count
이다. 예를 들어서AWS IAM user
를 3개를 반복적으로 생성해야 한다고 해보자. 그럼 이렇게 표현할 수 있다.provider "aws" { region = "us-east-1" } resource "aws_iam_user" "example" { count = 3 name = "gurumee" }
이 때,
AWS IAM user
는 안 만들어진다. 왜냐하면, 한 계정당 전역적으로 이름은 유일해야하기 때문이다. 이를 해결하기 위해서 다음과 같이 변경하면 된다.provider "aws" { region = "us-east-1" } resource "aws_iam_user" "example" { count = 3 name = "gurumee.${count.index}" }
이렇게 하면
gurumee.0
,gurumee.1
,gurumee.2
이름의AWS IAM user
가 3개 만들어진다. 그리고 최근 언어들은 모두for-each
형태의 반복문을 지원하는데 가령 리스트, 맵 등의 컬렉션 데이터 타입을 순회하는 반복문을 뜻한다.Golang
으로 따지면 다음의 코드이다.user_names := []string{"gurumee", "brenden", "ingoo"} for idx, user_name := range user_names { fmt.Println("idx: ", idx, " item: ", user_name) }
위와 같은 코드 역시
Terraform
에서 지원한다. 먼저variables.tf
를 다음과 같이 만든다.src/ch05/global/iam/variables.tf
variable "user_names" { type = list(any) description = "create iam users with three names" default = ["gurumee", "brenden", "ingoo"] } # ...
위는 리스트 데이터 타입을 선언한 것이다. 기본 값으로 세개의 이름이 들어있다. 그 후
main.tf
를 다음과 같이 만들면 된다.provider "aws" { region = "us-east-1" } resource "aws_iam_user" "example" { count = length(var.user_names) name = element(var.user_names, count.index) } # ...
여기서
length
함수는 리스트user_names
의 길이를 구할 수 있다. 기본 값이 들어가면count=3
이 된다. 또한,element
함수는 첫 번째 인자로 리스트, 두 번째 인자로 접근해야 할 인덱스를 받는다. 따라서, 차례대로user_names[0]
,user_names[1]
,user_names[2]
의 이름을 할당하게 된다. 따라서AWS IAM user
는 총 3개, 각각의 이름은gurumee
,brenden
,ingoo
가 할당된다.Terraform
역시 이러한for-each
형태의 반복은 리스트 뿐 아니라 맵 데이터 타입도 가능하다.조건문
책에서는 예제가 복잡한데 간소하게 변경해보았다. 먼저
Terraform
은 "조건식"을 제공한다.condition ? true_val : false_val
이런식으로
condition
이 참이면true_val
을 거짓이면false_val
을 할당하는게 가능하다. 예를 들면 다음 코드를 살펴보자.# ... resource "aws_iam_user_policy_attachment" "gurumee_cloudwatch_full_access" { count = var.give_gurumee_cloudwatch_full_access ? 1 : 0 user = aws_iam_user.example.0.name policy_arn = aws_iam_policy.cloudwatch_full_access.arn } resource "aws_iam_user_policy_attachment" "gurumee_cloudwatch_read_only" { count = var.give_gurumee_cloudwatch_full_access ? 0 : 1 user = aws_iam_user.example.0.name policy_arn = aws_iam_policy.cloudwatch_read_only.arn }
위 코드는
give_gurumee_cloudwatch_full_access
변수가 참이면AWS CloudWatch
에 대한 모든 액세스 권한을, 거짓이면 Read Only 권한을 주는 코드이다. 먼저give_gurumee_cloudwatch_full_access
이라는 변수가 src/ch05/global/iam/variables.tf에 작성되어 있다. 이는bool
변수이다.gurumee_cloudwatch_full_access
는give_gurumee_cloudwatch_full_access
가 참이면count
값이 1이 되어 1개 생성된다. 거짓이면 0이라서 생성되지 않는다. 반대로,gurumee_cloudwatch_read_only
는give_gurumee_cloudwatch_full_access
가 참이면count
값이 0이 되어 생성되지 않고 거짓이면 1이라서 1개 생성하게 된다. 이렇게 조건문if-else
를 표현할 수 있다.사실 개인적인 생각으로는 한 조건에 따른 유사한 리소스 묶음을 2개나 만들어야 하는게 비효율적으로 보이긴 한데, 이렇게 쓰라니 넘어가야지..
무중단 배포하기
먼저
AWS ELB
를 통해서, 무중단 배포 과정을 살펴보자.- Launch Configuration이 새로 만들어진다.
- AutoScaling Group이 (1)을 통해서 새로 만들어진다.
- (2)가 만들어지면 ELB는 (2)를 참조하게 된다.
- 이전에 만들어진 Launch Configuration과 AutoScaling Group은 삭제된다.
Terraform
을 통해서 어떻게 표현할 수 있을까? 이를 위해서Terraform
은 메타 아규먼트로lifecycle
이란 것이 있다.lifecycle
에는 다음의 형태로 리소스를 관리할 수 있게 도와준다.- create_before_destroy : 대체하는 리소스 생성 후 파괴한다.
- prevent_destroy : 리소스를 파괴할 수 없게 한다.
- ignore_changes : 리소스가 변경되도 무시한다.
여기서
create_before_destroy
를 이용하면 무중단 배포를 구현할 수 있다. 다음 코드를 살펴보자.src/ch05/modules/services/webserver-cluster/main.tf
# ... resource "aws_launch_configuration" "example" { image_id = "ami-0d5eff06f840b45e9" instance_type = var.instance_type security_groups = [aws_security_group.instance.id] user_data = templatefile("${path.module}/user-data.tpl", { server_port = var.server_port, server_text = var.server_text, db_address = data.terraform_remote_state.db.outputs.address, db_port = data.terraform_remote_state.db.outputs.port, }) # 여기 주목! lifecycle { create_before_destroy = true } } resource "aws_autoscaling_group" "example" { # 여기 주목! name = "${var.cluster_name}-${aws_launch_configuration.example.name}" launch_configuration = aws_launch_configuration.example.id availability_zones = data.aws_availability_zones.all.names load_balancers = [aws_elb.example.name] health_check_type = "ELB" min_size = var.min_size max_size = var.max_size # 여기 주목! lifecycle { create_before_destroy = true } tag { key = "Name" value = var.cluster_name propagate_at_launch = true } } # ...
주목해야 하는 것은
aws_launch_configuration
,aws_autoscaling_group
리소스들의 메타 아규먼트로lifecycle
에create_before_destroy
가 true로 지정된 것이다. 또한aws_autoscaling_group
은launch_configuration
에 할당된 값이 변경되도 수동으로 인스턴스 개수를 조절하지 않는 한 자동으로 무중단 배포를 하지 않는다.name
이 바뀌지 않아서 그러는데 다행히aws_launch_configuration
는 업데이트 될 때마다 이름이 바뀐다. 그래서 이를 참조하도록aws_autoscaling_group
의name
에서aws_launch_configuration
의 이름을 참조하게 만든다.실제 값을 변경하면 무중단 배포가 일어나는 것을 확인할 수 있다. 다음
업데이트 이전 코드
로 인프라스트럭처를 배포한 후업데이트 이후 코드
로 인프라스트럭처를 수정해보자. 변경되는데 5-10분 정도 시간이 걸리니 천천히 확인하면 된다.업데이트 이전 코드
src/ch05/prod/services/webserver-cluster/main.tf
# ... module "webserver_cluster" { source = "github.com/gurumee92/today-i-learned//terraform_up_and_running/src/ch05/modules/services/webserver-cluster" cluster_name = "websever-prod" db_remote_state_bucket = "gurumee-terraform-state" db_remote_state_key = "prod/data-stores/mysql/terraform.tfstate" instance_type = "m4.large" min_size = 2 max_size = 10 server_port = 8080 # 이 부분이 바뀐다. server_text = "Hello Version1" enable_autoscaling = true }
업데이트 이후 코드
src/ch05/prod/services/webserver-cluster/main.tf
# ... module "webserver_cluster" { source = "github.com/gurumee92/today-i-learned//terraform_up_and_running/src/ch05/modules/services/webserver-cluster" cluster_name = "websever-prod" db_remote_state_bucket = "gurumee-terraform-state" db_remote_state_key = "prod/data-stores/mysql/terraform.tfstate" instance_type = "m4.large" min_size = 2 max_size = 10 server_port = 8080 # 이 부분이 바뀐다. server_text = "Hello Version2" enable_autoscaling = true }
주의 사항
책에서 소개된 몇 개의 문제점은 다음과 같다.
- 카운트의 제한
- 무중단 배포의 제한 사항
- 유효한 계획의 실패
- 까다로운 리팩토링
- 강력한 일관성과 최종 일관성
이중
count
의 제한과 무중단 배포 제한 사항은 2021년 현재 해결되었다. 유효한 계획의 실패는 빈번히 일어난다. A가AWS Console
에서 "gurumee" 계정을 이미 만든 상태에서 B가Terraform
으로 "gurumee" 계정을 만들 때terraform plan
은 성공이지만terraform apply
는 실패한다. 이미 같은 이름의 사용자가 있기 때문이다. 이 문제에 대해서는 책에서는 이렇게 대처할 것을 권고한다.- 테라폼을 사용하기 시작했다면 계속 테라폼으로 관리한다.
- 기존 인프라가 있는 경우 terraform import 명령어를 활용한다.
까다로운 리팩토링 문제는 뭐.. 여느 프로그래밍 언어나 솔루션을 이용하는 스크립트나 동일할 것이다. 책에서는 리팩토링 과정에서 다음을 할 것을 추천하고 있다.
- 항상 terraform plan 명령어를 사용한다.
- 삭제 전에 생성한다. (create_before_destroy를 적극 활용하자)
- 모든 식별자는 불변하다.
- 일부 변수는 불변하다.
마지막 문제는 가끔 발생한다. 특히
terraform apply
옵션에-parallelism=n
이 있다. 이 때 n에 숫자를 넣어주면 n 숫자만큼 병렬적으로 인프라스트럭처를 구성하게 된다. 매우 빠르게 구성할 수 있어서 좋지만 많은 리소스들을 병렬적으로 생성하다보면, A 리소스를 참조하는 B 리소스가 A가 채 생성되기도 전에 생성되서 실패하는 경우가 있다. 이 때는 여러번 실행을 한다든가 병렬 옵션을 제거하는 방법 등으로 일차원적으로 해결할 수 있다. 큰 문제는 아니기 때문에, 이 정도만 알고 넘어가자.5장 전체 코드
5장 전체 코드는 다음 링크에서 확인할 수 있다.
728x90'레거시 > Terraform Up and Running' 카테고리의 다른 글
[2판] 01. 왜 테라폼인가 (0) 2021.08.02 06. 테라폼을 팀에서 사용하기 (0) 2021.06.16 04. 테라폼 모듈 (0) 2021.06.11 03. 테라폼 상태 관리 (0) 2021.06.02 02. 테라폼 시작하기 (0) 2021.05.26