Terraform 모범 사례를 정리해보았습니다

2022.07.30

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

안녕하세요 클래스메소드의 수재입니다.
모든 서비스에는 모범 사례(=best practices)가 있습니다.
해당 서비스를 이용하는데 이러한 모범 사례를 지킨다면 더욱 효율적이거나 100%의 기능을 활용할 수 있게 됩니다.
물론 Terraform 도 이러한 모범 사례가 있습니다.
이번에는 이러한 모범 사례에 대하여 정리해보겠습니다.

참고자료

모범 사례를 찾아보면 상당히 많은 자료를 확인할 수 있습니다.
이번 포스팅은 아래 페이지들을 참고하여 작성하였습니다.

이번 포스팅에서는 모범 사례에 대해 어느정도 간략하게 설명하였기 때문에 위의 페이지들에서 더 자세한 내용까지 확인할 수 있습니다.

읽기 전에

실제로 이러한 모범 사례를 적용하려 해보면 모든 사항을 지킬 수 없는 경우가 많습니다.
모든 모범 사례를 지키는 것이 아니라 모범 사례를 이해하고 프로젝트에 적합한 정도를 지키는 것이 올바른 방향이라고 생각합니다.
이러한 모범 사례를 의식하고 프로젝트의 코드를 작성해봅시다

프로젝트의 구성

Terraform 에는 많은 사람들이 따르는 표준적인 구조가 있습니다.
여기서 말하는 표준적인 구조는 다음과 같습니다.

  • 최상위 디렉터리를 서비스의 실제 구성이 포함된 modules 디렉터리와 각 환경의 루트 구성이 포함된 environments 디렉터리로 나누어 관리
    -- {project name}/
       -- environments/
          -- dev/
             -- backend.tf
             -- main.tf
          -- prod/
             -- backend.tf
             -- main.tf
          -- ...other…
       -- modules/
          -- <service-name>/
             -- main.tf
             -- variables.tf
             -- outputs.tf
             -- provider.tf
             -- README.md
          -- ...other…
    # or
    -- {project name}/
       -- environments/
          -- dev/
             -- main.tf
             -- backend.tf
             -- variables.tf
             -- provider.tf
             -- README.md
             -- outputs.tf
          -- prod/
             -- main.tf
             -- ...
          -- ...other…
       -- modules/
          -- <service-name>/
             -- main.tf
             -- variables.tf
             -- outputs.tf
           -- ...other…
  • 모든 모듈은 기본적으로 리소스가 있는 위치의 main.tf 파일에서 시작
  • 필요하다면 README.md 모듈에 대한 설명을 작성(출력 값에 대한 설명 등)
  • 변수는 variables.tf에 정의
  • 출력 값은 output.tf에 정의
  • provider.tf, versions.tf 등의 파일에 지속적인 관리를 위한 내용 정의

소규모 프로젝트에서는 전체적으로 단순한 구조를 가지는 것도 좋습니다. 이 경우에는 하나의 디렉터리(혹은 모듈)에서 필요한 파일만(main.tf , variables.tf , README.md 등)을 작성하는 것도 가능합니다.
하지만 규모가 큰 프로젝트거나 앞으로 확장될 가능성이 있는 프로젝트라면 표준적인 구조를 따르는 것이 좋습니다.

이 글에서는 각 파일들에 대해 간략하게 설명하였지만 GCP의 자료를 확인하시면 더 상세한 내용까지 확인하실 수 있습니다.

코드를 단일 리포지토리(mono repo)에 보관할지 여러 리포지토리(multi repo)에 저장할지는 정확한 답이 없습니다. 각 방식의 장단점은 hashicorp의 포스트를 참고해주세요.

모듈

프로젝트가 성장함에 따라 코드를 다른 팀이 보더라도 사용할 수 있도록 재사용 가능한 구조로 만들어야합니다.
일반적으로는 소유권, 책임, 관리 용이성에 따라 모듈을 분리합니다.

provider를 포함하는 모듈은 for_each, count, despends_on 인수와 호환되지 않습니다. 또한 하나 이상의 다른 모듈에 호출되는 공유 모듈은 provider블록을 지정하지 않는 것을 추천합니다. 대신 루트 모듈에 require_providers블록에서 최소한의 필요한 버전을 정의합니다.

모듈화 된 리소스의 참조는 출력(outputs.tf)이나 terraform_remote_state 데이터 소스 등을 활용합니다.
그리고 모듈의 입력과 출력, 모듈에 대한 설명을 README.md에 문서화해야합니다. terraform docs 등을 이용하면 간편하게 문서화가 가능합니다.

여러 환경에서 모듈을 이용하게 된다면 위에서 설명했듯이 환경(environments)를 나누어 상태(tfstate)를 관리하는게 좋습니다. 혹은 terraform에서 제공하고 있는 workspaces 기능을 이용하는 방법도 있습니다.

모든 모듈을 직접 개발할 수 있지만 개발 시간 절약이 필요하다면 공유 모듈을 이용하는 것도 좋은 방법입니다.
사용 가능한 모듈은 terraform registry에서 확인할 수 있습니다.

변수에 대해

처음 기술했듯이 모든 변수는 variables.tf에 선언합니다.
변수 용도 또는 목적과 관련해서 자세한 변수명을 지정(변수 타입, 단위 까지)합니다. 그리고 변수의 설명(description), 타입(type)도 정의합니다. 필요에따라 기본값(default)을 지정합니다.

각 환경에 따라 달라져야 하는 값만 변수로 지정합니다. 즉, 값을 하드 코딩하지 말고 data 블록으로 대체할 수 있다면 그렇게 합니다.
data 블록은 사용되는 리소스의 바로 옆에 작성하며 data 블록이 많아진다면 data.tf 파일로 분리합니다.

루트 모듈의 경우 .tfvars 변수 파일을 사용하여 변수 값을 제공합니다.
일관성을 위해 변수 파일 이름을 terraform.tfvars로 지정합시다. 또한 .tfvars 파일은 깃 허브 등에 업로드하지 않도록 주의합시다.

코드 작성에 대해

모든 terraform 파일은 일관된 형식을 따릅니다. terraform fmtterraform validate 명령어를 이용하여 형식과 유효성을 검사합니다.

객체는 일관된 이름 지정 규칙을 지킵니다.

  • 밑줄 (_)을 구분 기호로 사용 하고 이름에 소문자 사용
  • 리소스 이름에서 리소스 유형을 반복하지 말 것
    • 단독 유형의 리소스 참조를 단순화하기 위해서는 리소스 이름을 main으로 지정
  • 동일 유형의 리소스를 서로 구분하기 위해(예: primary와 secondary) 의미 있는 리소스 이름을 지정
  • 단일 값 변수 및 속성의 경우 단수 명사를 사용하며 목록이나 맵 형식의 경우 복수 명사 를 사용하여 여러 값을 지정
  • 항상 변수와 출력에 설명을 포함
# 올바른 예
resource "aws_~~~" "web_server" { ... } # 밑줄 사용
resource "aws_~~~" "main" { ... } # 단순화 한 이름

# 잘못된 예
resource "aws_~~~" "web-server" { ... } # 밑줄 미 사용
resource "aws_~~~" "main_application_server" { ... } # 복합적인 이름

리소스를 조건에 따라 인스턴스화하려면 count를 사용합니다.
사용자 지정 변수를 사용하여 리소스에 count 변수를 설정하는 방식은 되도록 피합니다(에러가 발생할 수 있습니다). 사용해야하는 경우에는 별도의 `enable_x 변수를 사용해서 조건부 논리를 계산하여 실행합니다.
입력 값에 따라 반복되는 리소스 생성은 for-each 를 사용합니다.

프로젝트의 관리에 대해

작성하는 리소스에는 태그를 지정합니다. 어떠한 문제가 발생하거나 디버깅을 할 때 큰 도움이 됩니다.
이러한 태그는 리소스 코드의 마지막 단에 지정하여 바로 확인할 수 있도록 합니다.
모든 리소스에 일괄적인 태그를 지정할 때는 default_tags를 지정합니다. 다음 문서를 참고해주세요.

상태 파일(state file)에는 민감한 정보가 포함될 수 있습니다. 협업과 보안을 위해 원격 상태(remote state)를 사용하는 것을 추천합니다. terraform은 Terraform Cloud , HashiCorp Consul , Amazon S3, Azure Blob Storage, Google Cloud Storage, Alibaba Cloud OSS 등에 상태 저장을 지원합니다.
저장된 원격 상태 파일을 고객 관리 암호키를 추가함으로써 더욱 보안성을 높일 수도 있습니다.

프로젝트의 규모가 확장되어 갈수록 시스템이 안전하게 유지될 수 있도록 정책을 정의할 필요가 있습니다.
terraform enterprise 에서는 정책을 편하게 관리할 수 있는 정책 프레임워크인 sentinel을 제공하고 있습니다.
이외에도 정책에 대해 사용할 수 있는 여러 툴이 있으니 검토해보는 것을 추천합니다.

보안

보안 향상을 위해 우선적으로 할 수 있는 것은 비밀 관리입니다.
민감한 정보는 텍스트로 저장하여 커밋하지 않습니다. TF_VAR 환경변수를 활용하고 출력에 민감한 변수는 sensitive=true를 설정합니다.
더 나은 방법은 Hashicorp Vault 또는 AWS Secrets Manager, GCP의 gcloud config 같은 보안 비밀 저장소를 이용하는 방법입니다.

문제가 발생하면 정보를 빠르게 수집하기 위해 디버깅을 활성화 해야합니다. 디버깅에 대한 옵션은 공식 문서를 참고해주세요.

테스트

가장 간단히 테스트 하는 방법은 terraform validateterraform plan을 하는 것 입니다.
테스트에 관한 상세한 내용은 hasicorp의 블로그를 한번 읽어보는 것을 추천합니다.

terraform cloud 와 다른 도구

terraform cloud를 이용하면 terraform 을 함께 사용하는데 많은 도움이 됩니다. 자세한 내용은 공식 문서를 참고해주세요.
이 외에도 다른 도구들과 함께 사용하면 코드의 작성부터 테스트까지 더 효율적으로 진행할 수 있습니다. 간단히 몇 가지를 소개하자면

  • tflint – plan 에서 잡을 수 없는 오류에 대한 linter
  • tfenv – Terraform 버전 관리 도구
  • checkov – Terraform 정적 분석 도구
  • terratest – Terraform에 대한 자동화된 테스트를 도와주는 Go 라이브러리
  • pre-commit-terraform – pre-commit 훅에서 실행하는 스크립트를 관리하는 프레임워크
  • terraform-docs – 모듈에서 빠르게 문서 생성
  • spacelift – Terraform 협업 인프라 플랫폼( Terraform Cloud 대안 )
  • terraform-cost-estimation – 비용 추정 서비스

이 외에도 Awesome Terraform에서 더 많은 도구를 확인할 수 있습니다.

마치며

이 글에서는 간략한 내용만 설명되어 있습니다.
상단의 참고 페이지를 방문하여 상세한 내용까지 이해한다면 terraform 을 프로젝트에 이용하는데 많은 도움이 될 것입니다.

긴 글 읽어주셔서 감사합니다.
오탈자 및 내용 피드백은 언제나 환영합니다. must01940 지메일로 연락 주시면 감사합니다!


본 블로그 게시글을 보시고 문의 사항이 있으신 분들은
클래스메소드코리아 (info@classmethod.kr)로 연락 주시면 빠른 시일 내 담당자가 회신 드릴 수 있도록 하겠습니다 !