[Devops] Terraform 의 원리

J-USER·2023년 3월 8일
0

DevOps

목록 보기
8/10
post-thumbnail

테라폼을 본격적으로 하기에 앞서, 테라폼의 원리를 이해해야 내가 무엇을 해야하고, 무엇이 잘못 되었는지 알 수 있다. (또한 예비군간 내 동기를 위해)

어떻게 테라폼이 작동할까?

테라폼에는 3가지 주체가 있다.

  • Backend
  • Local
  • 실제 리소스

이 세가지 공간은 서로 다르다. 그리고 보통의 테라폼 구현 순서는 아래와 같다.

로컬에서 .tf 파일을 작성 -> terraform apply -> 실제 리소스에 반영

좀더 자세히 나누자면, 아래와 같이 나타날 수 있다.

로컬에서 .tf 파일을 작성 ->terraform init -> terraform apply -> 실제 리소스에 반영

이 과정 중에서 terraform init 은 뭘 해주는걸까? 하나씩 천천히 알아가 BOZA

terraform init

결론부터 말하자면, 내가 작성하고 있는 tf파일의 메타데이터를 저장할 공간을 마련하는 것이다.

s3 버킷 안에 백엔드 파일에

🙋 왜 공간을 만들어주나요?
🤖 테라폼 입장에서 어떤 인프라와 매핑되는지 알 수 없기 때문이다.

tfstate.tf

실제 리소스가 가지고 있는 상태 파일

Naming

로컬과 실제 S3 디렉토리 구조를 똑같이 구성한다.

EX ) provisioning/terrafrorm/eks/dax_{region}/dax000-vbab/backend.tf

리소스 -> tf

1. terraform import

만약 AWS EC2 가 3 개가 있다고 가정 했을때, terraform이 관리하도록 가져오는것이 목적이라 하자.

Terraform aws_instance 문서를 보면 terraform import aws_instance.web i-12345678 처럼 사용방법을 볼 수 있다.

자세히 뜯어 보자면

terraform import {내가 만든 tf 리소스 이름} {AWS에서 만든 인스턴스ID}

과 같은 명령어가 나온다. 좀더 예제를 자세히 하자면,

resource "aws_instance" "web" {}

terraform import aws_instance.web i-12345678 -> 실제 리소스ID인 i-12345678 를 내가 만든 tf파일의 "aws_instance.web" 라는 이름으로 가져오겠다는 뜻이다.

인스턴스ID 는 하나의 리소스와 매핑 되므로, 리소스가 여러개일 경우 각각의 리소스마다 import 를 해줘야한다.

import를 처음 완료하고 terraform.tfstate 파일이 없다면 ,terraform.tfstate 파일을 자동으로 만들어준다.

이어서 나머지 리소스를 import 하게 된다면, 테라폼이 알아서 terraform.tfstate 에 잘 합쳐준다.

2. terraform plan

이제 내가 로컬에 적은 resource "aws_instance" "web" {} 코드와 실제 클라우드 서비스에 있는 EC2 리소스 i-12345678 는 서로 연결 된 상태이다.

그러나 내가 만든 코드는 아무런 내용도 없는 깡통에 불과하다. 그래서 terraform plan 을 해주게 된다면, 엄청나게 많은 리소스들이 함께 지워질 거라는 경고를 해준다.

여기서 힌트를 얻어서 모든 내용을 추가해주면,

최종적으로 terraform planNo changes. Infrastructure is up-to-date. 가 나오면 해당 tf 코드는 성공적으로 작성한 것이 된다.

3. git PR 날리기

git webhook에서 아틀란티스로 자동 연결하면 아틀란티스에서 테라폼을 원격으로 plan해서 보여준다.

그러나 치명적인 단점이 있다. 아틀란티스는 로컬의 plan결과를 캐시해서 pr을 날린 시점의 리소스와 실제 리소스 상태의 차이가 있을 수 있다.

즉, pr날린 시점에서는 terraform plan 을 했을 시 no change 였지만, 누군가 내가 merge 하기전에 pr을 날리고 merge를 하게 되면, (물론 락이 걸려 안되긴 하겠지만) 정상적으로 apply를 할 수 없게 된다.

그래서 깃 댓글로 atlantis plan -p {project name} or atlantis plan 을 해서 변경점을 확인하고,

git rebase를 통해 변경점을 내 코드에 적용 후 merge(apply) 를 진행해야한다.

4. 이후 리펙토링

절대 처음부터 리펙토링 하면 안돼. 팩토링이 우선이다.

리펙의 도구로는

  • module
  • output
  • remote state

을 애용하면 된다.

module

모듈은 한개 또는 여러개의 .tf, .tf.json 파일로 구성 되어 있고 한개 이상의 resource를 포함한다. 쉽게 생각하자면 일종의 library이다. 각 모듈은 자체적으로 인프라스트럭처를 구성하며, 모듈 간에 의존성을 정의할 수 있다.

module 은 크게 두가지로 나눌 수 있는데,

  • Root module : Terraform command 를 수행하는 directory 에 있는 파일들로 구성된 module 을 Root module 이라고 한다.

  • Child module : 다른 module (Root module 포함) 에서 호출하여 사용되는 module 을 Child module 이라고 한다. Child module 은 여러번 호출되어 사용될 수 있고 module 에 따라 다른 configuration 값을 전달하여 사용할 수도 있다.

🙋 만약 리펙토링 하게 되면, 이름이 "resource.arn" 에서 "module.resource.arn" 로 바뀌면, resource가 삭제 됐다가 다시 생성되자나유
🤖 그렇게 되면 다 옷벗어야해. 그렇기 때문에 state mv FROM TO를 통해서 이동 시켜줌
🤖 그러면 key가 바뀌게 되면서

Remote state

다른 디렉토리의 Terraform 모듈에서 생성된 리소스의 state를 공유하는 기능이다. remote state 를 사용해서 다른 모듈의 리소스를 쉽게 가져올 수 있다.

output

Terraform 모듈에서 생성된 값 또는 리소스의 속성을 외부로 노출하는 기능이다. output을 사용하여 생성된 인프라 리소스의 정보를 다른 Terraform 모듈에서 참조할 수 있다.

사용 예제

# main.tf
provider "aws" {
  region = "us-west-2"
}

data "terraform_remote_state" "db" {
  backend = "s3"
  config = {
    bucket = "my-terraform-state-bucket"
    key    = "db/terraform.tfstate"
    region = "us-west-2"
  }
}

module "vpc" {
  source = "./modules/vpc"
}

module "web" {
  source = "./modules/web"
  vpc_id = module.vpc.vpc_id

  # 가져올 remote_state 설정
  db_instance_id = data.terraform_remote_state.db.outputs.db_instance_id
}

output "web_lb_dns_name" {
  value = module.web.lb_dns_name
}

output "web_sg_id" {
  value = module.web.sg_id
}

위 코드에서는 main.tf 파일에서 vpcweb 모듈을 호출하고, web 모듈은 vpc 모듈에서 생성된 VPC ID를 인자로 전달받는다.
또한, data.terraform_remote_state를 사용하여 db 모듈에서 생성된 DB 인스턴스 ID를 가져온다.

output을 사용하여 web 모듈에서 생성된 로드밸런서 DNS 이름과 보안 그룹 ID를 외부로 노출한다. 이러한 출력을 통해 다른 모듈에서 생성된 인프라스트럭처를 참조할 수 있다.

profile
호기심많은 개발자

0개의 댓글