Hashicorp에서 만든 Terraform을 올해 사용해 볼 기회가 좀 있었다. AWS Lambda를 사용하려고 Apex를 쓰고 있는데 Lambda 외에 다른 AWS 리소스를 설정하는 도구로 Apex가 Terraform을 쓰고 있기 때문이다. Apex를 쓰면서 접한 거라 검색해서 가져다가 대충 만들어서 사용하다가 이번에 오픈 컨테이너 코리아에서 인프라 자동화 스터디를 하면서 주제로 Terraform을 좀 더 살펴보고 공유를 했다.
Provision
최근 Hashicorp가 정의한 DevOps 단계에 따르면 Terraform은 Provision 부분을 담당하고 있다. 물론 이 단계는 Hashicorp가 자사의 제품을 기준으로 정의한 느낌이긴 하다.
인프라 쪽이나 DevOps를 원래 하던 편은 아니라서 그런지 나는 Provision이란 단어의 의미가 모호하게 느껴졌다. Configuration Management가 더 익숙하게 느껴졌는데 Provison과 Configuration Management의 차이가 헷갈려서 찾아봤는데 다른 사람은 크게 헷갈리지 않는지 괜찮은 글은 찾기가 어려웠다. What's Deployment Versus Provisioning Versus Orchestration?를 보면 다음과 같이 설명하고 있다.
Config Management is part of provisioning. Basically, that’s using a tool like Chef, Puppet or Ansible to configure your server. “Provisioning” often implies it’s the first time you do it. Config management usually happens repeatedly.
Config Management를 Provision 일부로 보고 있는 것 같고 다른 글에서도 비슷한 설명이 좀 있었다. 저 글도 그렇지만 사람들은 Provision과 Orchestration을 더 헷갈리는 것 같았다. 개념을 도구에 대입해서 그런지 Orchestration은 더 명확하게 느껴지는 편인데...
Terraform
Terraform is a tool for building, changing, and versioning infrastructure safely and efficiently.
테라폼 사이트에서 가져온 정의이다. 테라폼은 인프라를 만들고 바꾸고 버전 관리하는 도구다. 테라폼 사이트에서는 테라폼이 제공하는 기능을 다음과 같이 설명하고 있다.
- Infrastructure as Code
- Execution Plans
- Resource Graph
- Change Automation
이 기능을 이용해서 인프라스트럭처를 관리할 수 있는데 사용할 수 있는 인프라스트럭처를 테라폼에서는 Provider라고 부른다. 테라폼이 지원하는 프로바이더라고는 AWS, BitBucket, Chef, CloudFlare, Consul, DigitalOcean, Docker, GitHub, Google Cloud, Grafana, InfluxDB, Heroku, Microsoft Azure, MySQL, PostgreSQL 등 개발에서 보통 사용하는 솔루션은 거의 모두 포함되어 있다. 각 프로바이더에 맞게 설정을 해주어야 하고 추상화가 되어 있어서 하나를 설정하면 프로바이더를 바꾸어서 사용할 수 있는 개념은 아니다.
HashiCorp configuration language(hcl)
Terraform의 설정 파일은 HashiCorp가 만든 설정 언어인 HCL을 사용하고 있는데 이는 Teffrform 형식인 .tf
와 JSON 형식인 .tf.json
을 모두 사용할 수 있다. 보통 작성할 때는 .tf
를 사용하고 자동생성되는 설정에서 JSON을 사용한다. HCL은 사람이 읽기 좀 더 쉬운 구조로 되어 있고 주석을 사용할 수 있는 등의 장점이 있다.
지정한 폴더에 .tf
와 .tf.json
를 넣어두면 Terraform이 알파벳 순서로 로드하는데 변수나 리소스 정의의 순서는 상관이 없다. 즉, 로드 순서를 신경 써서 알파벳 순서에 맞게 파일을 만들 필요는 없다. 그 외에 다른 설정 파일을 덮어쓸 수 있는 오버라이드 파일이 있는데 오버라이드 파일은 파일명이 override
이거나 _override
로 끝나야 하는데 이는 다른 파일을 다 로드하고 오버라이드 파일을 알파벳순으로 로드한다.
먼저 간단할 hcl 파일을 보자.
# An AMI
// An AMI
variable "ami" {
description = "the AMI to use"
}
/* A multi
line comment. */
resource "aws_instance" "web" {
ami = "${var.ami}"
count = 2
source_dest_check = false
description = <<EOF
...
...
EOF
connection {
user = "root"
}
}
- 주석은
#
,//
나/* */
를 사용한다. - 3번 라인은 ami라는 변수를 선언한 것이다.
- 값 할당은
key = value
형식을 사용하는데 여기서value
는 문자열, 숫자, 불리언, 리스트, 맵의 형식을 사용할 수 있다. - 문자열을 쌍따옴표를 사용하고 스트링 인터폴레이션에는
${}
문법을 사용한다. - 멀티라인 문자열은 here doc 형식으로
<<EOF
,EOF
를 사용한다.
리소스 설정 문법은 다음과 같다.
resource TYPE NAME {
CONFIG ...
[count = COUNT]
[depends_on = [RESOURCE NAME, ...]]
[provider = PROVIDER]
[LIFECYCLE]
[CONNECTION]
[PROVISIONER ...]
}
resource
가 키워드이고 TYPE
는 프로바이더에 맞게 Terraform에서 정의한 리소스의 타입 이름이다. NAME
은 개발자가 임의로 주면 되는 이름이다. 2번 라인의 CONFIG
는 KEY = VALUE
형식이나 KEY { CONFIG }
형식이 된다. 그래서 AWS 인스턴스를 정의하는 리소스 설정의 예시를 보면 다음과 같다.
resource "aws_instance" "web" {
ami = "ami-408c7f28"
instance_type = "t1.micro"
}
이는 AWS에서 ami-408c7f28
라는 AMI를 사용해서 t1.micro
로 EC2 인스턴스를 정의한 것이다.
Provider
Provider는 리소스의 라이프 사이클을 관리할 책임을 지고 있고 TYPE
의 접두사가 Provider를 가리킨다. 그래서 aws_instance
는 aws
프로바이더의 리소스를 의미한다.
provider NAME {
CONFIG ...
[alias = ALIAS]
}
프로바이더의 정의 형식은 위와 같고 AWS 프로바이더를 설정한다면 다음과 같이 설정한다.
provider "aws" {
access_key = "foo"
secret_key = "bar"
region = "us-east-1"
}
사용하는 CONFIG의 Key는 문서를 참고해야 한다.
데이터소스
Terroform에서는 프로바이더에서 데이터를 가져올 수 있는데 이를 데이터소스라고 부른다.
data "aws_ami" "web" {
filter {
name = "state"
values = ["available"]
}
filter {
name = "tag:Component"
values = ["web"]
}
most_recent = true
}
위처럼 데이터소스를 정의하면 AWS AMI에 대한 데이터소스를 정의한 것인데 태그에 Component = web
로 설정한 AMI를 조회할 수 있다.
변수
변수 선언 문법은 다음과 같다.
variable NAME {
[type = TYPE]
[default = DEFAULT]
[description = DESCRIPTION]
}
이렇게 설정한 변수를 다른 설정에서 사용할 수 있고 CLI에서 변수를 덮어쓸 수도 있다.
variable "key" {
type = "string"
}
variable "images" {
type = "map"
default = {
us-east-1 = "image-1234"
us-west-2 = "image-4567"
}
}
variable "zones" {
default = ["us-east-1a", "us-east-1b"]
}
type
은 변수의 타입인데 정의해줘도 되지만 정의하지 않으면 알아서 추론한다. default
가 실제 해당 변수의 값이 된다.
모듈
모듈의 형식은 다음과 같다.
module NAME {
source = SOURCE_URL
CONFIG ...
}
기존에 정의한 리소스 그룹을 가져와서 모듈화해서 사용할 수 있는데 실제로 어떻게 사용하는지는 아직 파악 못 했다. 실제 모듈은 다음과 같이 정의하고 source
에는 모듈을 다운받을 경로를 지정한다.
module "consul" {
source = "github.com/hashicorp/consul/terraform/aws"
servers = 5
}
테라폼 설치
Terraform 다운로드 페이지에서 OS에 맞게 다운로드받아서 PATH에 넣으면 된다. 현재 최신 버전은 v0.8.2다.
$ terraform -v
Terraform v0.8.2
정상적으로 설치했으면 버전을 확인할 수 있고 사용할 수 있는 명령어를 볼 수 있다.
$ terraform
Usage: terraform [--version] [--help] <command> [args]
The available commands for execution are listed below.
The most common, useful commands are shown first, followed by
less common or more advanced commands. If you're just getting
started with Terraform, stick with the common commands. For the
other commands, please read the help and docs before usage.
Common commands:
apply Builds or changes infrastructure
console Interactive console for Terraform interpolations
destroy Destroy Terraform-managed infrastructure
fmt Rewrites config files to canonical format
get Download and install modules for the configuration
graph Create a visual graph of Terraform resources
import Import existing infrastructure into Terraform
init Initializes Terraform configuration from a module
output Read an output from a state file
plan Generate and show an execution plan
push Upload this Terraform module to Atlas to run
refresh Update local state file against real resources
remote Configure remote state storage
show Inspect Terraform state or plan
taint Manually mark a resource for recreation
untaint Manually unmark a resource as tainted
validate Validates the Terraform files
version Prints the Terraform version
All other commands:
debug Debug output management (experimental)
state Advanced state management
Comments