2024.01.15

Terraform 폴더 구조

개요


tfstate 란

tfstate 란 git 같이 인프라 상태를 저장한 파일이다. 우리가 코드를 수정하면 인프라 상태를 tfstate라는 파일을 기준으로 검사하고, 이 파일과 다른 부분이 실제 인프라에 적용되는 구조이다. 즉, tfstate의 내용이 실제 인프라와 동일하도록 적용되기 때문에 여러명이서 작업할 때 이 tfstate가 공유한다면, 인프라 상태가 공유되기 때문에 작업을 더 안전하게 할 수 있게 된다.



폴더 구조가 가지는 의의

Terraform에서 폴더구조는 생각보다 중요하다. 폴더 내부 위치에 따라 tfstate가 공유되기 때문이다. 만약 특정 폴더에 있던 리소스가 다른 폴더로 옮겨가는 경우, 기존 폴더(tfstate)에서는 사라지는 것이기 때문에 destroy로 처리됩니다. 만약 이런 경우, 서비스 중이던 인프라가 삭제될 수 있기 때문에 신중하게 가져가는 것이 좋습니다.

dev
├── acm.tf
├── ecs_cluster.tf
├── elasticache.tf
├── iam_groups.tf
├── kms.tf
├── loadbalancer.tf
├── main.tf
├── security_groups.tf
├── service_server.tf
├── vpc.tf
└── s3_bucket.tf

위 구조에 문제점

  • 하나의 파일에 너무 많은 리소스가 존재함.
  • 서비스 영향 범위를 알기 어려움.
  • 서비스가 많아지고, 커질 수록 파일 관리가 어려움.

장점

  • 모든 리소스에 대한 arn, id 등 변수 공유가 자유로움

→ 서비스가 적고 구조가 단순한 경우 상관이 없지만, 서비스가 추가되고 구조가 점점 복잡해짐에 따라서 관리에 어려움이 있을 수 있다. 공통적인 구조를 가지고 있는 서비스(ex: ECS)의 경우 module을 이용하여 간단하게 생성도 가능



폴더 구조

Terraform에서 폴더에 따라서 리소스가 공유되거나 격리가 가능합니다. 각각 아래와 같은 폴더 구조를 소개합니다.

  • 서비스 별 폴더 구조
  • 리소스 별 폴더 구조
  • 혼합형 폴더 구조



서비스 별 폴더 구조

아래와 같이 환경(dev, prod, secure)로 나뉘고 하위에 modules와 services로 나누어지는 구조로, modules에는 공통적인 구조를 가지는 리소스가 정의되어 템플릿으로 사용되고, services에는 하위에는 실제 구현할 서비스 리소스가 모이게 됩니다.

dev
├── modules
└── services
      ├── lzone
      ├── tas
      └── carrier-check
            ├── ecs.tf
            ├── alb.tf
            └── s3.tf

장점

  • 서비스 별 파악이 좋다
  • 같은 구조를 가지는 서비스라면 modules를 이용해서 간단 생성 가능(ecs 의 경우 tf 파일 없이도 가능할수도)
  • 서비스 별로 tfstate가 분리되기 때문에 다른 서비스 수정 시 영향(lock 등)을 받지 않음

단점

  • 공통 리소스(vpc 등)에 대한 관리는 여전히 필요하고, 이를 각 서비스에 전달하는 곳에서 중복 코드가 발생할 수 도 있다.



리소스별 폴더 구조

리소스 별로 모아두는 폴더구조입니다. 리소스 별로 모아두는 경우는 많지 않습니다. 아래와 같은 구조를 가지는 경우는 하나의 서비스내부에 복잡한 구조를 가지고 있는 경우 사용합니다. 하지만, 아래와 같은 폴더 구조 보다는 서비스+리소스 폴더구조를 선호합니다.

dev
├── modules
└── resources
      ├── vpc
      ├── alb
      └── s3
          ├── carrier-check.tf
          ├── tas.tf
          └── terraform.tf

장점

  • 공통된 타입의 리소스들을 빠르게 확인하기 쉽다.
  • 복잡한 서비스의 경우 리소스 별로 처리하기 좋다.
  • 의존 관계가 단순함(각 폴더 끼리만 변수 공유를 하면 되기 때문에 단순하다.)

단점

  • 서비스 별 파악이 어렵다.
  • 공통된 구조를 가지더라도 활용하기 어려움.



혼합형 폴더구조

서비스 별 + 리소스 별 폴더구조입니다. 서비스 별로 묶어두어도 영향범위를 찾는 경우는 간편하지만, 각 리소스 별 의존 관계나 변수를 잘 관리하기 위해서 사용합니다. 하지만, 이 경우 간단한 리소스 생성에 output.tf, variables.tf 와 같은 파일을 필수적으로 생성해야하기 때문에 간단한 구조에서는 오히려 독이됩니다.

dev
├── modules
└── services
      ├── lzone
      ├── tas
      └── carrier-check
            ├── ecs
            ⎮   ├── variables.tf
            ⎮   ├── main.tf
            ⎮   └── output.tf
            ├── alb
            └── s3

장점

  • 서비스 - 리소스별 처리가 가능
  • 하나의 리소스에 대해서 세부적으로 관리할 때 좋음.
  • iam 와 같이 policy 등 정의해야하는 파일이 많은 경우 유리.

단점

  • 다른 리소스의 값을 가져오는 부분이 복잡함. - 의존 관계 설정에 어려움
  • 하나의 리소스에 파일이 너무 많이 생김
  • 공통 리소스(vpc 등)에 대한 관리는 여전히 필요하고, 이를 각 서비스에 전달하는 곳에서 중복 코드가 발생할 수 도 있다.



폴더 구조를 어떻게…?

그래서 우리는 어떤 방향으로 관리하는 것이 좋을까? 라고 생각을 해보면, 현재 단계에서는 서비스 별 폴더구조를 가지는 것이 좋다고 생각된다. 리소스별, 혼합형 폴더 구조도 복잡한 설계에서는 괜찮은 방식이지만, 오히려 의존관계나 값 공유에 있어서 관리해야할 부분이 존재하기 때문에 서비스별로 관리하는 것이 좋을 것이다.

공통 리소스 관리

공통 리소스(vpc 등)을 어떻게 관리할 것인가? 공통된 리소스의 경우 넓은 범위를 가지는 iam, network 등에 해당된다. 그렇기 때문에 별도의 폴더로 구분하고 다른 리소스에 값을 잘 전달할 수 있는 구조가 좋다.

vpc, iam 과 같이 common 이나 global에 모아두는 것이 좋을 것 같다.

dev
├── modules
└── global
│      ├── vpc
│      └── iam
└── services
      ├── lzone
      ├── tas
      └── carrier-check
            ├── ecs.tf
            ├── alb.tf
            └── s3.tf

리소스별, 혼합형에서 관리 포인트

terraform destroy 시 alb에 연결된 ecs가 있는 경우 삭제가 되지 않는 것처럼… 리소스 별로 의존관계에 따라서 인프라를 제거 수정해야한다. 리소스별 구조에서는 비슷한 타입별로 모여있어 리소스를 찾기 쉽기 때문에 괜찮지만, 혼합형에서는 서비스를 찾고, 그안에서 리소스 위치로 가서 명령어를 쳐야 하기 때문에 문제가 있을 수 있다.

혼합형을 써본 입장으로서 하나의 리소스를 만들기 까지 준비해야하는 파일이 많았기에 빠른 대응에는 어려울 수 있습니다. 또한, Terraform에 익숙하지 않을 수록 이러한 구조는 더 구조에 대한 이해와 관리에 어려움을 주는 경우가 많아서 추천하지 않습니다.


환경 변수 관리

Terraform 을 이용하다보면 우리가 작성한 리소스의 값이 아닌 이미 생성이 되어있는 값을 써야할 때가 있다. 모든 인프라가 100% 코드로 작성된 것이 아니라면, 환경변수 관리가 필요하다.

관리를 위해서는 아래 2가지 옵션이 있다.

  • direnv 또는 비슷한 환경변수 라이브러리
  • tfvars

direnv 의 경우 폴더별로 환경변수를 지정할 수 있는 라이브러리로, 보안을 위해 깃허브에 arn 등이 올라가면 안되는 환경일때 사용한다. 하지만, env 파일 공유에 어려움이 있고 sync에도 문제가 생긴다. tfvars 는 Terraform에서 가이드해주는 환경 변수 관리 방법으로 깃허브 등에 값이 노출되지만, 관리가 편리하기 때문에 private 레포인 경우 tfvars 를 이용한 것이 가장 좋다.


결론

새로운 리소스 추가 등에서 아래와 같은 구조로 관리 할 수 있도록 개선해나가고, 고정적인 환경변수 등은 tfvars 를 이용해서 관리해나가면 좋을 것 같다. 동일한 구조는 modules 를 통해 템플릿으로 통합하자.

dev
├── modules
└── global
│      ├── vpc
│      └── iam
└── services
      ├── lzone
      ├── tas
      └── carrier-check
            ├── TF.tfvars
            ├── ecs.tf
            ├── alb.tf
            └── s3.tf

위에서 설명한 대로 현재 모든 리소스가 한 공간(tfstate)에 있기 때문에 분리에 시간이 걸립니다. 가장 빠른 방법은 각 서비스 인프라를 제거하고 다시 생성하는 방식인데요… 그러려면 인프라 서버 앞단(cloudfront, nginx 등)에서 점검 페이지나 준비 과정이 필요합니다.



Categories:

Updated:

Leave a comment