Cloud Tech

Terraform 내부 로직 이해

Hanhorang31 2024. 6. 15. 20:31

Overview 

Terraform 내부 로직을 이해하기 위해 학습한 내용을 공유합니다.

 

Terraform

Terraform(이하 테라폼)은 코드형 인프라스트럭처(IaC) 도구입니다. 코드를 통해 인프라를 구성하고 관리하는데 사용됩니다. 테라폼 아키텍처는 다음과 같습니다.

  • Terraform Core : 테라폼 파일(.tf)를 구문 분석하고 실행 계획을 생성하며 리소스의 현재 상태(.tfstate)를 관리합니다.
  • Plugins : Terraform과 클라우드 제공업체, SaaS 제공업체, 그리고 기타 API 서비스 간의 상호작용을 담당합니다.
  • Upstream API: 클라우드 제공업체의 API 로, 테라폼이 리소스를 관리하기 위해 호출하는 API 입니다.

사용자가 정의한 테라폼 파일(.tf)을 통해 인프라의 상태를 정의한다면, Terraform Core 와 Plugins 를 거쳐 클라우드 제공업체 SDK API를 호출하여 인프라를 관리하는 구조로 되어 있습니다.

동작 이해를 위해 AWS EC2 인스턴스를 구성하여 내부 원리를 확인하겠습니다.

provider "aws" {
  region = "ap-northeast-2"
}

resource "aws_instance" "example" {
  ami           = "ami-0edc5427d49d09d2a" # Amazon Linux 2023 AMI 2023.4.20240611.0 x86_64 HVM kernel-6.1
  instance_type = "t2.micro"
}

 

위 테라폼 코드를 통해 AWS EC2를 구성한다면, AWS 프로바이더에서 aws_instance 리소스를 확인하여 AWS SDK API를 호출하게 됩니다.

AWS 프로바이더의 동작은 terraform-provider-aws 에서 확인이 가능합니다.

아래 코드를 보면 Resoruce aws_instance 에 대한 로직을 확인할 수 있습니다.

1) 리소스 구성 함수

기본적으로 Create, Read, Update, Delete가 제공됩니다.

호출되는 함수는 Terraform Core가 State를 확인하여 함수를 호출하게 됩니다.

2) 구성 스키마

위 resource aws_instance 의 구성 스키마 부분을 코드로 확인할 수 있습니다.

스키마를 확인하면 ami 과 다른 인스턴스 구성 속성을 확인할 수 있습니다.

3) Create 로직 이해

AWS 프로바이더의 EC2 Create 함수을 보면 AWS SDK API(RunInstancesWithContext)를 호출하는 것을 확인할 수 있습니다.

해당 함수는 AWS SDK 라이브러리 문서에서 확인할 수 있습니다.

위 함수는 RunInstacne라는 이벤트를 통해 인스턴스를 생성하는 함수입니다.

테라폼을 통해 인프라를 생성하고 CloudTrail에서 이벤트를 확인하면 해당 API를 확인할 수 있습니다.

위 과정을 통해 테라폼은 AWS SDK를 감싸서 HCL (HashiCorp Configuration Language)이라는 언어를 통해 사용자가 쉽게 인프라를 구성할 수 있게 도와주는 도구임을 확인하였습니다. 이를 통해 사용자는 복잡한 API 호출을 직접 작성할 필요 없이 간단한 선언형 구문으로 인프라를 정의하고 관리할 수 있습니다.

 

 

State(상태)

State는 테라폼이 인프라의 실제 상태와 선언된 코드를 비교하고 필요한 변경 사항을 추적 및 관리하는 데 사용됩니다. State 는 테라폼 명령어 plan 과 apply 를 통해 관리됩니다.

  • terraform plan: 현재 상태와 원하는 상태를 비교하여 변경 사항을 계산하고 계획을 보여줍니다.
  • terraform apply : plan에서 계산된 변경 사항을 실제로 인프라에 적용합니다.

테라폼 명령어를 기반으로 State 관리 방법과 명령어 내부 동작을 코드로 통해 확인하겠습니다.

위 EC2 생성 예제에서 plan 명령어를 실행하면 찰나의 틈에 tfstate 파일이 생기고 사라지는 것을 확인할 수 있습니다. 처음 생성될 때에는 비교할 상태가 없어 바로 넘어가지만, 기존에 tfstate가 있다면 임시로 생성한 상태와 비교하고 변경 사항을 계산하여 사용자에게 보여주게 됩니다.

Terraform Core에서 Plan 명령어를 코드로 확인하면 다음과 같습니다.

  1. 코드 유효성 검사 : 플래그 파싱 및 유효성 검사를 진행합니다.
  2. 상태 로드 및 백엔드 준비: 프로바이더 플러그인을 로드하며, 백엔드에 저장된 현재 상태를 가져옵니다.
  3. 계획 생성: 현재 상태와 원하는 상태와 비교하여 계획을 세우고, 그 계획을 실제로 적용합니다.

Apply 명령어도 코드로 과정을 확인하면 다음과 같습니다.

  1. 코드 유효성 검사 : 플래그 파싱 및 유효성 검사를 진행합니다.
  2. 상태, 플러그인, 계획 로드 : 프로바이더 플러그인 및 백엔드에 저장된 현재 상태, 그리고 위 Plan 명령어를 통해 작업한 계획 작업을 로드합니다.
  3. 인프라 적용 : 위 로드한 데이터를 기반으로 실제 인프라에 적용하는 작업을 진행합니다.

위 명령어를 확인하면 Terraform Core에서 State를 기반으로 인프라를 확인하는 것을 알 수 있습니다.

처음 Terraform Apply를 실행하면 terraform.tfstate 파일이 생성되며, 인프라 상태를 확인할 수 있습니다.

# terraform.tfstate 
{
  "version": 4,
  "terraform_version": "1.8.5",
  "serial": 1,
  "lineage": "4d89c74b-ea0a-260c-8c09-c862248e7ad7",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "aws_instance",
      "name": "example",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [
        {
          "schema_version": 1,
          "attributes": {
            "ami": "ami-0edc5427d49d09d2a",
            ...
          },
          "sensitive_attributes": [],
          "private": "*"
        }
      ]
    }
  ],
  "check_results": null
}

인프라 적용된 상태를 가져오기에 상태 파일 보관에 주의가 필요합니다.

상태파일에서 순서(serial) 과 resource에서 파씽되어 인프라에서 어떻게 적용되었는 지에 대해 확인할 수 있습니다.

 

Plan에서  유효하나 실제 인프라에서 오류가 나는 경우 ? 

Apply 계획에서도 문제가 없으나 인프라 적용시 에러가 날 수 있습니다. 원인은 콘솔상에서의 추가 작업과 충돌로 일어나는데요. 위 EC2 예제에서 콘솔상에서 인스턴스 타입을 변경하고 apply를 하면 다음과 같이 에러가 발생할 수 있습니다.

Plan은 유효한 이유는 현재 상태로만 비교하기 때문인데요. 위 Apply 단계에서 검토시 코드상 변경없는 부분이 변경이 일어난다면 인프라상 히스토리 확인이 필요합니다.

위와 같이 콘솔 작업과 충돌이 일어난 경우 해결 방법으로

테라폼만 사용 또는 import 방법이 있지만, terraform refresh 라는 명령어를 통해 상태 충돌을 해결하겠습니다.

  • terraform refresh: 현재 상태 파일을 실제 인프라의 상태와 동기화하는 명령어 입니다.
  • 동작 원리는 상태 파일 로드 -> 실제 인프라 상태 조회 -> 상태 비교 및 동기화 -> 상태 파일 업데이트

콘솔에서 인스턴스를 t3.mirco로 유형을 변경하여 refresh 동작 원리를 확인하겠습니다.

테라폼으로 구성한 인프라 상태는 t2.micro로 state 파일 확인시 t2.micro로 확인됩니다.

여기서 refresh를 하면 state가 콘솔에서 수정한 인프라 상태로 변경됨을 확인할 수 있습니다.

> terraform refresh
aws_instance.example: Refreshing state... [id=i-00b94dfad58799293]

이후 코드를 콘솔 형태와 맞추어 apply를 수행하게 되면 변경사항 없이 상태를 관리할 수 있습니다.

테라폼은 콘솔에서 추가한 리소스들도 관리할까?

Terraform은 기본적으로 상태 파일(terraform.tfstate)에 기록된 리소스만 관리합니다.

예제에서 생성된 상태를 확인하면 코드로 추가한 인스턴스만 확인할 수 있습니다. 검증을 위해 콘솔에서 임의의 인스턴스를 추가하고 다시 apply를 하면 변경 사항이 없는 것을 확인하겠습니다.

콘솔에서 작업한 리소스들은 Import를 통해 코드로 가져올 수 있지만, 모듈과의 충돌로 추천하지 않는 방법입니다. 모듈이 있는 경우, 코드로 콘솔에서 작업한 내용을 추가하여 refresh 로 상태를 최신화하는 것을 추천드립니다.

 

 

변경사항 관리 방법

테라폼으로 인프라를 관리할 수 있지만, 운영 측면에서 replace가 되는 경우 특히 주의가 필요합니다. replace는 인프라 리소스가 대체되는 경우로 서비스 특성상 순단이 있을 수 있으며, 인프라 위 애플리케이션 레벨에서 백업 설정이 없는 경우 데이터가 전부 날라가 혼란을 초래할 수 있습니다.

일반적으로, Replace 는 Terraform Plan 에서 확인이 가능합니다.

  • ami 속성 끝 주석 # force replacement 표기 확인

Replace 되는 속성들은 AWS 프로바이더 코드에서도 확인할 수 있습니다.

각 Reousrce 스키마에서 forceNew 로 표기된 속성들은 값이 변경될 때 기존 리소스를 삭제하고 새로 생성해야 합니다.

위에서 생성한 Resource 인스턴스 스키마로 확인했을 때 ami, associcate_public_ip_address, AZ 등이 수정될 때 다시 삭제되고 생성되는 속성임을 확인할 수 있습니다.

서비스 중단을 최소화하기 위해 테라폼에서는 create_before_destroy 옵션을 제공합니다.

해당 옵션은 새로운 리소스를 생성한 후 예전 리소스를 삭제시켜주는 옵션입니다.

다만, 맹신하지 말아야 할 것이 AWS 리소스 별로 고려사항이 필요합니다. 예를들어 EKS node 또는 ASG에서는 동작과 다르게 동작할 수 있으니 반드시 AWS SDK 확인 및 콘솔에서 선행 테스트 이후 작업하시는 것을 추천드립니다.

테라폼으로 EKS 클러스터 버전 업그레이드시 SDK 확인

불가피하게 변경이 필요한 경우 ignore_changes 옵션으로 테라폼 동작상의 변경사항을 무시할 수 있습니다.

ASG로 예를 들면, create_before_destroy 옵션을 사용함에도 일시적으로 desired_capacity 값이 min 값으로 설정될 수 있기에 ignore_changes 옵션을 통해 Terraform이 ASG의 인스턴스 수를 임시로 변경하지 않도록 합니다.

 

resource "aws_autoscaling_group" "example" {
  desired_capacity     = 2
  max_size             = 20
  min_size             = 2
  vpc_zone_identifier  = ["subnet-0bb1c79de3EXAMPLE"]

  launch_configuration = aws_launch_configuration.example.name

  lifecycle {
    create_before_destroy = true
    ignore_changes = [desired_capacity]  # desired_capacity 변경 무시
  }

  tag {
    key                 = "Name"
    value               = "example-asg"
    propagate_at_launch = true
  }