Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.

Terraform에서 Map 타입 변수의 사용

Terraform으로 AWS 리소스를 사용하면서 점점 변수를 많이 사용하게 된다. Terraform으로 리소스를 생성하면 그 리소스의 이름으로 참조할 수 있지만 모든 리소스를 다 Terraform으로 관리할 수 있는 것은 아니다. 경험했을 때는 사용자 이름이나 AMI 이름 등이 대표적이다.

resource "aws_instance" "demo" {
  ami = "ami-785c491f"
  instance_type = "t2.micro"
}

EC2 인스턴스를 위처럼 정의할 수 있는데 여기서 ami-785c491f는 Ubuntu Server 16.04 LTS (HVM) 이지만 이 AMI 아이디만으로는 알기가 어려우므로 이렇게 사용한다면 주석으로 어떤 AMI 인지를 적어두는 것이 차후에 관리하기가 좋을 것이다.

resource "aws_instance" "demo" {
  ami = "ami-785c491f" # Ubuntu Server 16.04 LTS (HVM)
  instance_type = "t2.micro"
}

일회성의 경우 애매하기는 하지만 가능하면 아래처럼 변수를 사용하는 것이 더 좋다고 생각한다.

variable "ubuntu_ami" {
  default = "ami-785c491f"
  description = "Ubuntu Server 16.04 LTS (HVM)"
}

resource "aws_instance" "demo" {
  ami = "${var.ubuntu_ami}"
  instance_type = "t2.micro"
}

변수를 사용하는 경우 쉽게 어떤 AMI를 사용하는지(이름을 잘 지어야겠지만) 알 수 있고 여러 곳에서 사용하는 경우 한 번에 이미지를 바꾸는 것도 가능하다.

여기서는 변수에 특정 값을 할 당했는데 Terraform의 변수는 맵을 지원하기 때문에 맵을 사용해서 여러 값을 넣는 것도 가능하다.

variable "ubuntu_ami" {
  default = {
    us-east-1 = "ami-d15a75c7"
    us-west-2 = "ami-8b92b4ee"
    ap-northeast-1 = "ami-785c491f"
    ap-northeast-2 = "ami-94d20dfa"
  }
  description = "Ubuntu Server 16.04 LTS (HVM)"
}

여러 리전을 사용하는 경우 하나의 변수를 Map으로 정의해서 리전 별로 지정하면 var.ubuntu_ami["ap-northeast-1"]와 같이 참조할 수 있고 사용할 때도 의미를 알기 쉬워서 아주 좋다.

variable "availability_zones" {
  type = "map"
  default = {
    "eu-west-1" = "eu-west-1a,eu-west-1b,eu-west-1c"
    "us-west-1" = "us-west-1b,us-west-1c"
    "us-west-2" = "us-west-2a,us-west-2b,us-west-2c"
    "us-east-1" = "us-east-1c,us-west-1d,us-west-1e"
  }
}

위처럼 Availability Zones을 리전별로 정의해 놓고 참조해서 사용하는 것도 가능하다. 맵에서 키를 찾는 lookup함수를 제공하므로 var.availability_zones["us-east-1"]대신 아래와 같이 값을 가져올 수도 있다.

> lookup(var.availability_zones, "us-east-1")
us-east-1c,us-west-1d,us-west-1e

Availability Zones는 배열로 만들어서 사용하고 싶다면 split을 사용하면 배열로 변환해서 할당할 수 있다.

> split(",", lookup(var.availability_zones, "us-east-1"))
[
  us-east-1c,
  us-west-1d,
  us-west-1e
]


2017/07/30 21:27 2017/07/30 21:27

Terraform간에 리소스 공유

Terraform으로 AWS 등 인프라를 관리하다 보면 하나의 Terrafrom, 조금 더 정확히는 terraform.tfstate 파일에 모두 관리하지 않고 나누어서 관리하게 된다.

As Terraform configurations get larger, it's much more manageable and safer to split one large configuration into many smaller ones linked together with terraform_remote_state data sources.

Terrafrom 문서에서도 위처럼 관리하다 보면 점점 리소스가 많아지게 되는데 하나의 큰 설정으로 관리하지 않고 더 작은 설정으로 나누고 terraform_remote_state로 연결해서 사용하라고 권하고 있다.

일단 하나의 .tfstate로 관리하는 리소스가 많아지면 plan이나 apply를 할 때도 시간이 점점 오래 걸리게 되므로 자연히 여러 .tfstate로 나누게 된다. 이렇게 나누는 명확한 기준은 아직 찾지 못했지만 만들다 보면 같이 사용하는 리소스끼리 관리하면서 필요에 따라 나누게 된다. 위의 문서도 초반에 읽었었지만, 그때는 terraform_remote_state로 연결한다는 의미를 잘 이해하지 못했는데 이 글에서는 이렇게 나눈 .tfstate를 연결해서 사용하는 방법을 설명한다.

예제를 위한 VPC용 Terrafrom 구성

먼저 VPC를 하나 만들기 위해 vpc폴더 아래 Terraform을 구성해 보자. 다음과 같은 vpc/terraform.tf 파일을 하나 만들어보자.(파일명을 중요하지 않다.)

terraform {
  backend "s3" {
    bucket = "kr.ne.outsider.terraform"
    key = "vpc/terraform.tfstate"
    region = "ap-northeast-1"
    encrypt = true
    lock_table = "TerraformLock"
    acl = "bucket-owner-full-control"
  }
}

이는 전에 설명한대로 .tfstate 파일을 S3에 저장하는 설정이다. 여기서 kr.ne.outsider.terraform S3 버킷과 TerraformLock DynamoDB 테이블은 이미 만들어져 있다고 가정한다. 이 설정을 두고 초기화를 하면 .tfstate를 저장할 백엔드를 초기화한다.

$ terraform init
Initializing the backend...


Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your environment. If you forget, other
commands will detect it and remind you to do so if necessary.

이제 VPC를 만들어 보자. 다음은 vpc/vpc.tf 파일이다.

resource "aws_vpc" "example" {
  cidr_block = "172.10.0.0/20"
  tags {
    Name = "example"
  }
}

resource "aws_subnet" "example_a" {
  vpc_id = "${aws_vpc.example.id}"
  cidr_block = "172.10.0.0/24"
  availability_zone = "ap-northeast-1a"
}

resource "aws_subnet" "example_c" {
  vpc_id = "${aws_vpc.example.id}"
  cidr_block = "172.10.1.0/24"
  availability_zone = "ap-northeast-1c"
}

이를 사용해서 terraform apply를 하면 AWS에 VPC가 만들어진다.

별도의 tfstate로 EC2 관리

위에서 만든 VPC 안에 EC2 인스턴스를 관리하기 위해 ec2 폴더를 만들어서 관리하기 위해 ec2/terraform.tf를 만들어서 백엔드를 초기화한다(terraform init).

terraform {
  backend "s3" {
    bucket = "kr.ne.outsider.terraform"
    key = "ec2/terraform.tfstate"
    region = "ap-northeast-1"
    encrypt = true
    lock_table = "TerraformLock"
    acl = "bucket-owner-full-control"
  }
}

이제 EC2 인스턴스를 띄우는 Terraform 구성을 만들 차례로 다음과 같은 ec2/ec2.tf 파일을 생성한다.

resource "aws_instance" "example-server" {
  ami = "ami-785c491f"
  instance_type = "t2.micro"
  subnet_id = "subnet-6315f62a"
  key_name = "key-pair-name"
  tags {
    Name = "examples"
  }
}

여기서 보면 subnet_id를 직접 하드 코딩해서 subnet-6315f62a로 지정했다. 이 서브넷은 앞에서 만든 VPC의 서브넷이기는 하지만 한 폴더에서 관리했다면 ${aws_subnet.example_a.id}처럼 참조해서 사용했겠지만 여기서는 별도의 폴더로 분리했으므로(.tfstate가 다르므로) 이렇게 참조를 할 수가 없다. AWS 웹 콘솔에 들어가서 서브넷 ID를 찾아서 지정해 줄 수는 있지만, 매번 할 때도 귀찮고 이 아이디를 보고 어떤 서브넷인지 알기가 어려우므로 코드를 읽기도 좋지 않다.

terraform_remote_state 데이터 소스

이 문제는 terraform_remote_state 데이터 소스를 사용하면 해결할 수 있다. terraform_remote_state는 원격으로 관리하는 tfstate를 데이터소스로 가져와서 참조하는 역할을 한다.

먼저 원격 상태 파일을 다른 설정에서 가져다 쓰려면 내보낼 값을 지정해 주어야 한다. 여기서는 ec2에서 vpc를 가져다 사용할 것이므로 vpc쪽에서 외부에서 참조할 수 있는 값을 지정해 주어야 한다. 다음과 같은 vpc/output.tf 파일을 생성한다.

output "vpc_subnet_a_id" {
  value = "${aws_subnet.example_a.id}"
}

이는 aws_subnet.example_a.id의 값을 vpc_subnet_a_id라는 이름으로 외부에 노출하는 설정이다. 이 설정을 원격 상태 파일에 적용하려면 terraform refresh를 실행해야 한다.

$ terraform refresh
Acquiring state lock. This may take a few moments...
aws_vpc.example: Refreshing state... (ID: vpc-f7c49293)
aws_subnet.example_a: Refreshing state... (ID: subnet-6315f62a)
aws_subnet.example_c: Refreshing state... (ID: subnet-720e282a)

Outputs:

vpc_subnet_a_id = subnet-6315f62a

실행하면 마지막에 output으로 노출한 설정값이 표시된 것을 볼 수 있다.

이제 ec2에서 이 VPC의 원격 상태 파일을 참조하는 설정을 ec2/data.tf로 만든다.

data "terraform_remote_state" "vpc" {
  backend = "s3"
  config {
    bucket = "kr.ne.outsider.terraform"
    key = "vpc/terraform.tfstate"
    region = "ap-northeast-1"
    encrypt = true
    lock_table = "TerraformLock"
    acl = "bucket-owner-full-control"
  }
}

이는 데이터소스로 위에서 S3에 지정한 tfstate를 참조한 설정이다. config에는 tfstate를 정의한 설정을 같게 지정해서 사용하면 된다. 이 데이터는 원격에 있으므로 ec2폴더 쪽에서도 데이터 갱신을 한번 해주어야 한다.

$ terraform refresh
data.terraform_remote_state.vpc: Refreshing state...

이제 terraform console에서 이 값을 확인해 보면 앞에서 하드 코딩한 값이 제대로 출력되는 것을 볼 수 있다.

$ terraform console
> data.terraform_remote_state.vpc.vpc_subnet_a_id
subnet-6315f62a

data.terraform_remote_state.vpc.vpc_subnet_a_id 참조를 사용하면 subnet-6315f62a를 사용하는 대신 더 읽기 쉬운 이름을 지정할 수 있고 각 tfstate를 연결해서 사용할 수 있다.

2017/07/28 03:42 2017/07/28 03:42