안녕하세요, 클라우드로입니다!
이번 글에서는 테라폼의 개념과 작성 방법에 대해 알아보겠습니다.
테라폼(Terrafom)이란?
테라폼은 HashiCorp에서 만든 오픈소스 IaC소프트웨어 툴 중 하나로, IaC종류에는 Ansible, Chef, Terraform 등이 있습니다. 여기서 IaC란 Infrastructure as Code의 줄임말로, 프로그래밍 코드로 인프라를 구축, 운영한다는 의미를 담고 있습니다. IaC 중에서도 테라폼은 특정 클라우드 환경에 인프라를 배포하는 것에 초점을 두고 있으며, HCL(HashiCorp Language)를 사용해 코드를 작성할 수 있습니다.
테라폼 사용의 장점으로는 크게 2개를 생각할 수 있습니다. 첫 번째 장점은 코드로 인프라를 구축하기 때문에 환경 변경 및 배포와 같은 반복 작업에 드는 시간을 줄일 수 있다는 점입니다. 두 번째 장점은 인프라가 코드로 기록 및 관리 되기 때문에 자동으로 문서화가 된다는 점입니다. 테라폼을 배포한 사람이 아니더라도 코드를 읽을 수 있다면 어떤 내용으로 배포가 되어 있는지 확인할 수 있습니다.
테라폼을 통해 인프라를 배포할 경우, “Write, Plan, Apply”의 3단계를 거치게 됩니다. 먼저 개발자가 테라폼 구성 파일을 작성(Write)합니다. 이 때, 작성할 코드의 provider와 버전에 맞는 라이브러리를 설치하는 과정이 진행됩니다. 작성된 테라폼 파일대로 배포했을 때 설정값이 어떻게 나오는지 확인(Plan)하는 단계를 거친 후, 인프라를 배포(Apply)하게 됩니다. 배포 후에 “terraform.tfstate” 파일이 생성되는데, 이는 가장 최근에 배포된 테라폼 코드의 형상을 의미합니다. 이 tfstate파일과 실제 배포되어 있는 인프라를 비교하는 방식으로 인프라를 관리하게 됩니다.
테라폼 명령어
테라폼의 모든 동작은 사용자가 CLI 명령어를 입력하는 것으로 시작이 됩니ㅏㄷ. 테라폼에서 기능하는 주요 명령어는 "terraform init, terraform validate, terraform plan, terraform apply, terraform destroy"가 있고 이 외에도 다른 명령어들이 있습니다. 아래 글에서는 이 5가지 명령어와 추가적으로 주요하게 사용되는 "terraform import" 명령어에 대해 알아보겠습니다.
(1) terraform init
"terraform init" 명령어는 테라폼을 시작하는 가장 기본 단계에서 사용하는 명령어입니다. 사용자가 지정한 provider와 버전에 맞게 테라폼 라이브러리를 설치하여 현재 경로에서 테라폼을 사용할 수 있도록 설정합니다. 여러 번 반복해서 init명령어를 적용해도 무관합니다. 해당 명령어가 실행되면 ".terraform"폴더와 ".terraform.lock.hcl"파일이 생성됩니다. 해당 폴더, 파일은 설치된 테라폼 라이브러리에 대한 내용이 담겨있습니다.
(2) terraform validate
"terraform validate" 명령어는 init 명령어를 실행한 폴더 내부 구성만을 참조하여 원격 상태, provider API 등과 같은 구성 파일의 유효성을 검사합니다. 이 유효성 검사는 사용자가 테라폼 파일을 어떤 값을 사용하여 작성했는지와 상관없이, 구성이 문법적으로 유효하고 내부적으로 일관성이 있는지를 확인합니다. 따라서 속성 이름 및 값 유형의 정확성을 포함하여 재사용 가능 모듈의 일반적인 검증에 주로 유용합니다.
(3) terraform plan
"terraform plan" 명령어는 validate 명령어와 다르게 실제로 작성한 리소스 이름, 변수 등 코드의 유효성을 검증하는 명령어입니다. 유효성 검증을 통과하게 되면, 이미 존재하는 원격 객체의 현재 상태를 읽어 테라폼 상태가 최신인지 확인하고, 현재 구성을 이전 상태와 비교하여 차이를 확인할 수 있게 됩니다. 사용자는 그 차이점을 통해 인프라와 적용될 변경 사항을 미리 볼 수 있게 됩니다. 해당 명령어의 결과는 실제 인프라에 당장 반영되는 것은 아니며, 기본적으로 기록되거나 저장되지 않습니다. 결과를 저장하고 싶다면 "-out=$PATH" 옵션을 사용하시면 됩니다. terraform plan 명령어의 옵션 "-out=$PATH"은 plan의 결과를 사용자가 -out 뒤 지정한 경로에 기록하는 명령어입니다.
(4) terraform apply
"terraform apply" 명령어는 plan에서 확인한 내용을 실제로 인프라에 배포하도록 하는 명령어입니다. 실제 배포 명령어인 만큼, plan명령어의 결과와 함께 정말 배포할 것인지 확인하는 과정을 거칩니다. 해당 과정에서 "yes"를 입력하면 실제 인프라가 배포됩니다. apply를 성공하면, terraform.tfstate 파일이 생성되고 이 파일에 배포된 리소스들의 구성정보들이 저장됩니다. apply 명령어의 여러 옵션 중 눈여겨볼 것은 "-auto-approve" 와 "-refresh-only" 옵션입니다. "-auto-approve" 옵션을 사용하면, "yes"를 입력하는 과정을 생략할 수 있습니다. "-refresh-only" 옵션은 테라폼으로 관리하는 리소스에 변화가 생겼는지 감지하여 동기화하는 옵션입니다.
(5) terraform destroy
테라폼으로 리소스를 배포했다면 테라폼으로 지울 수 있어야 하는데, 이 때 사용하는 명령어가 "terraform destroy" 입니다. destrory 명령어를 사용하면 배포 후에 생긴 tfstate 파일의 모든 리소스가 삭제됩니다. 해당 명령어도 apply 명령어와 마찬가지로 삭제 여부를 한번 더 확인하고, 인프라에 배포되어 있는 리소스가 삭제될 때마다 tfstate 파일의 해당 내용도 삭제됩니다.
(6) terraform import
위 5가지 명령어는 터미널의 활용하여 직접 리소스를 관리하는 느낌의 명령어였다면, "terraform import"는 조금 결이 다를 수 있습니다. terraform import 명령어는 클라우드 콘솔에서 직접 배포한 리소스를 테라폼으로 관리하고 싶을 때 사용합니다. import 명령어의 선행 조건은 불러올 리소스 정보를 저장할 리소스 블록을 생성해야 한다는 것입니다. 이 후, 터미널 창에 테라폼 import CLI 명령어를 입력하면 해당 리소스 블록으로 정보가 들어가게 됩니다. import CLI 명령어도 클라우드와 리소스마다 상이한 부분이 존재하기 때문에 terraform registry 홈페이지를 참고하셔야 합니다.
테라폼 구성
한 인프라를 구축, 즉 배포하기 위한 테라폼 폴더는 일반적으로 main.tf, variables.tf, outputs.tf 3개의 테라폼 파일을 갖춰야 합니다. main.tf에서는 리소스 생성 및 접근에 필요한 credential과 provider를 설정하고, 생성할 리소스와 그에 필요한 인자값(arguments)을 설정합니다. variables.tf에서는 main.tf의 인자값 중 사용자에게 변수로 입력받을 형식과 값을 설정합니다. outputs.tf에서는 리소스가 생성된 후, 사용자가 확인하고 싶은 값을 선언합니다. 단, 테라폼에서 지원하는 값만 outputs.tf에서 출력하도록 작성할 수 있습니다.
(1) main.tf
main.tf에는 일반적으로 3가지 종류의 코드 블록이 작성됩니다. 첫 번째, "terraform block"입니다. 이 블록에서 인프라를 배포하기 위해 사용할 provider와 테라폼 버전을 지정합니다. terraform blcok 내부에서 어떤 클라우드 플랫폼을 사용할지 선택하며 아래의 예시는 Azure를 사용하도록 작성되어 있습니다. 두 번째, "provider block"은 지정된 provider에 맞는 크레덴셜 정보를 입력받는 역할을 하며, 테라폼이 리소스를 생성할 관리하기 위한 플러그인이라고 볼 수 있습니다. 마지막은 “resource block”입니다. 인프라의 실질적인 구성요소를 정의하는 블록으로, 리소스 타입, 이름, 인자값으로 구성됩니다. 테라폼에서는 해당 리소스 타입과 이름을 사용하여 리소스 별 고유 아이디를 생성하고 이는 중복되지 않습니다. 어떤 클라우드를 사용하는지, 어떤 버전을 사용하는지, 어떤 리소스를 배포하는지에 따라 블록들의 형태는 상이하기 때문에 terraform registry 홈페이지를 참고해서 작성해야 합니다.
추가적으로 main.tf에 등장할 수 있는 2가지 종류의 코드 블록이 있습니다. 바로 “data source block”과 “local values block”입니다. 먼저 "data source block"의 경우, 기존에 있는 리소스의 특정값이 필요할 때 사용하게 됩니다. 예를 들어, 사용자가 배포하고자 하는 리소스 A가 이미 존재하는 리소스 B의 ID를 필요로 할 때, data block을 활용하여 값을 불러올 수 있습니다. 다만 이 때, 리소스 B의 ID만을 불러오는 것이지, tfstate 파일이 생성되는 것이 아니기에, 리소스 B에 대한 형상관리는 진행되지 않습니다.
"local values block"의 경우, 테라폼 코드 내에서 반복하여 사용하는 값을 간편하게 표현하기 위해 만든 변수입니다. 즉 변수를 할당해 저장해두는 곳이라 할 수 있습니다. 이를 통해 코드의 반복을 줄일 수 있고, 가독성 또한 상승합니다. locals라는 이름으로 블록을 열고, 저장하고 싶은 값을 입력합니다. locals에 저장되어 있는 내용을 불러 사용할 때는, local로 불러와 해당 항목의 key값을 입력하는 방식으로 사용합니다.
(2) variables.tf
variables.tf에서는 “variable block”이 작성되고, 이는 main.tf에서 정의한 인자값에 실제 사용될 값을 받습니다. 이 블록에는 입력할 인자값의 유형을 적는 “type”과 실제 적용될 값인 “default”을 작성하며, type에 사용할 수 있는 값으로는 “string”, “bool”, “number” 또는 “list(string)” 등이 있습니다.
추가적으로 variables.tf을 통해 값을 main.tf에 전달하지 않고 다른 방식을 사용할 수 있습니다. 이 때 등장하는 개념이 tfvars 입니다. ".tfvars" 또는 ".tfvars.json" 확장자를 가진 파일에서 직접 값을 지정한 후, apply 명령어를 사용할 때 해당 파일을 명시해주는 방식입니다. 확장자의 경우, 반드시 위 확장자일 필요는 없지만 terraform에서 정의한 표준 확장자이기 때문에 여기서는 2가지 확장자로 예시를 들어보겠습니다.
# test.tfvars
default_service_name = "vnet_01"
vnet_address_space = ["10.0.0.0/16"]
enable_ddos_protection = true
# test.tfvars.json
{
"default_service_name": "vnet_01",
"vnet_address_space": ["10.0.0.0/16"],
"enable_ddos_protection" : true
}
variables.tf과는 다르게 type과 default를 지정하지 않고 위 코드처럼 사용할 값을 바로 작성합니다. 이 후, 아래 명령어를 사용해서 적용합니다.
terraform apply -var-file="test.tfvars"
terraform apply -var-file="test.tfvars.json"
(3) outputs.tf
outputs.tf에서는 리소스를 배포한 후, 출력하고 싶은 값을 작성하게 됩니다. output라는 이름으로 블록을 열고 내부에 어떤 값을 출력하고 싶은지 작성합니다. 위에서 언급했듯, 출력할 수 있는 인자값은 리소스마다 다르기 때문에 terraform registry 홈페이지 각 리소스 별 “Attribute Reference” 를 확인해야 합니다.
Meta-Arguments
리소스의 종류와 플랫폼에 따라 테라폼 코드의 형태는 다르기 마련입니다. 이와 반대로 리소스, 클라우드 종류와 상관없이 항상 사용할 수 있는 arguments가 있습니다. 이를 "Meta-Arguments"라고 하며, 종류로는 "count, for_each, depends_on, life_cycle" 등 이 있습니다. 이 중 count, for_each, 그리고 depends_on에 대해 알아보겠습니다.
(1) count
Meta-Arguments 중 첫 번째는 "count"입니다. count는 동일한 리소스를 여러 차례 반복 생성하고자 할 때 사용합니다. 총 몇개의 동일한 설정의 리소스를 생성할 것인지 선언하고, 그에 해당하는 index를 이름에 붙여 리소스를 생성한다고 할 수 있습니다. 아래의 예시처럼 count=2 라고 코드를 구성하면 index에 0, 1, 2가 할당되며, 다른 이름의 동일한 설정값을 가진 virtual_network가 생성됩니다.
(2) for_each
두 번째 설명할 Meta-Arguments는 "for_each"입니다. count와 유사하게 동일한 항목의 리소스를 한 번에 배포할 수 있다는 공통점이 있지만, count와는 다르게 리소스 내부 인자값을 일일이 설정할 수 있다는 차이점이 있습니다. for_each는 map 또는 set 형태에 맞게 작성할 수 있습니다. map을 사용할 경우, key와 value 형태로 작성하며, set을 사용할 경우, 가능한 값을 직접 “resource block”에서 받아 작성합니다.
(3) depends_on
테라폼의 Meta-Arguments 중 "depends_on"은 단어 의미대로 특정한 리소스에 의존성을 설정하는 것을 말합니다. 쉽게 설명하면, main.tf에 선언된 서로 다른 리소스 블록들 중 일부에 생성 순서를 발생시키는 것을 의미합니다. 아래의 예시를 보면, peering에 해당하는 리소스 블록에 depends_on이 설정되어 있는 것을 확인할 수 있습니다. 아래의 상황이라면, peering이 생성되기 전, virtual_network이 먼저 생성이 되는 것입니다.
Dynamic Block
terraform registry문서를 통해 테라폼 코드 작성법을 확인하다보면, 한 리소스 블록 내부에 중첩된 여러 블록을 작성해야할 경우가 발생합니다. 이 때 활용할 수 있는 방법이 dynamic block을 활용하는 것입니다. 위에서 count와 for_each는 리소스 블록을 반복했다면, dynamic block은 리소스 내부의 블록을 반복합니다. dynamic block을 사용하는 과정은 3단계로 설명할 수 있습니다. 먼저 리소스 블록 내부에 중첩된 형태 dynamic block를 선언하고 dynamic에 대한 for_each도 같이 작성합니다. 그 다음, dynamic block 내부에 작성할 인자값들을 content라는 이름으로 포함시킵니다. 마지막으로 dynamic의 결과로 생성될 각각의 블록 별, key, value가 상위 map에서 참조할 수 있도록 설정합니다.
이상으로 테라폼의 개념과 작성법에 대해 알아보았습니다.
다음 글에서는 테라폼 Provider를 활용한 인프라 설계 및 배포에 대해 알아보도록 하겠습니다.
감사합니다.
Cloudraw는 쉽게 클라우드 인프라를 그리고 사용할 수 있는 서비스를 제공하기 위해 노력하고 있습니다.
클라우드가 있는 곳 어디든 Cloudraw가 함께합니다.
📨 help@cloudraw.kr
'IaC' 카테고리의 다른 글
[IaC] Terraform으로 Kubernetes 다루기 - 1/5 (0) | 2024.09.19 |
---|---|
[IaC] Terraform import 개념 및 사용방법 (0) | 2024.06.03 |
[IaC] Terraform 작성 및 배포(2) - for_each, flatten, local values (1) | 2024.03.05 |
[IaC] Terraform 작성 및 배포(1) - provider block, resource block, dynamic block, data block, depends_on (0) | 2024.03.04 |