AWS Cloud School 13기 90일차

Forever 김·2026년 5월 8일

AWS Cloud School

목록 보기
80/97

TIL

오늘은 드디어 Terraform(테라폼)을 처음 다뤘다. IaC(Infrastructure as Code)라는 개념 자체는 알고 있었는데, 직접 HCL 코드를 작성해서 VPC를 만들고 EKS 클러스터까지 띄워보니까 "이래서 테라폼 쓰는구나"가 확실히 느껴졌다. 콘솔에서 클릭클릭 하던 걸 코드 몇 줄로 재현할 수 있다는 게 생각보다 훨씬 강력했다.


Terraform이란?

Hashicorp에서 만든 IaC(Infrastructure as Code) 도구다. HCL(HashiCorp Configuration Language)이라는 자체 언어를 사용한다.

IaC의 종류를 넓게 보면 Ansible, Terraform, Vagrant, CloudFormation 같은 도구들이 있고, 더 넓게 보면 k8s 매니페스트, docker-compose, Dockerfile도 IaC의 범주에 들어간다.

테라폼의 핵심 개념은 인프라의 Desired State(원하는 상태)를 코드로 선언(Declarative) 한다는 것이다.

tfstate (매우 중요)

테라폼에서 가장 중요한 개념 중 하나다.

  • Desired State: 내가 원하는 상태. "인스턴스가 3개였으면 좋겠다"는 바람
  • Current State: 현재 실제 인프라 상태. "현재 A, B, C라는 ID를 갖는 인스턴스 3개가 존재한다"
  • tfstate: 테라폼이 관리하는 Current State 파일. 인프라의 현재 상태를 담고 있지만, 이 파일 자체가 인프라를 보장하지는 않는다

tfstate가 실제 인프라와 항상 동기화되어 있어야 한다. 그래서 팀 작업 시에는 Terraform Cloud나 S3 같은 원격 백엔드에 tfstate를 저장해서 공유한다.

IaC의 장점

  • 재현 가능성: 매번 똑같은 환경을 찍어낼 수 있다. Staging과 Production 환경을 동일하게 구성 가능
  • 휴먼 에러 감소: 콘솔에서 수동으로 클릭하다 실수하는 일이 없어진다
  • 온디맨드(On-demand) 인프라: 필요할 때 코드 한 줄로 인프라를 올리고 내릴 수 있다
  • 멱등성: 같은 코드를 여러 번 실행해도 결과가 동일하다
  • 무결성: tfstate와 lock 파일로 프로바이더 버전을 관리한다

💡 lock이란? a와 b가 동시에 apply를 하면 먼저 실행한 쪽이 lock을 걸어서 b는 apply를 못하게 된다. 동시 수정으로 인한 충돌을 방지하는 메커니즘이다.

테라폼의 특징

클라우드 인프라 프로비저닝 및 관리에 특화된 도구다. VPC 생성부터 EKS 클러스터 생성, Helm을 통한 애드온 설치, ALB, 매니페스트 apply, ACM 생성 및 연동, Route53을 통한 배포까지 네트워크 구축부터 도메인 배포까지 한 번에 가능하다.

Terraform Cloud

GitHub 같은 SCM 레포지토리를 통해 tfstate 및 lock 파일을 공유한다. 팀원들과 현재 인프라 상태를 공유하고 무결성을 보장하려면 이 방식이 필요하다.

Pulumi

내게 익숙한 언어(Python, TypeScript, Go 등)로 인프라를 프로비저닝할 수 있는 도구다. 테라폼 vs Pulumi 중 하나를 선택하는 게 좋은데, 특정 프로그래밍 언어를 잘 다룬다면 Pulumi도 좋은 선택이다.


테라폼 파일 구조

📌 공식 문서 참고: Terraform Language - Values

윈도우 환경에서 VSCode로 테라폼 코드를 작성했다. 기본적인 파일 구조는 이렇다.

파일역할
main.tf메인 파일. 주로 리소스(클래스나 함수처럼 생각하면 됨)를 정의
variables.tf변수들을 모아놓는 파일
locals.tf해당 모듈 내에서만 유효한 변수를 모아놓는 파일
outputs.tf특정 모듈에서 생성한 리소스를 다른 모듈에서 참조할 때 사용

outputs.tf가 왜 필요한지 예를 들면, vpc 모듈에서 eks-vpc라는 VPC를 생성했다면, 이후 eks 모듈에서 그 VPC에 클러스터를 구성해야 한다. vpc 모듈로 생성된 서브넷이나 VPC의 ID를 output 처리한 후 가져다 쓰면 된다.

variables / locals / outputs 차이

공식 문서에서 세 가지를 이렇게 정의한다.

구분방향설명
variable (Input Variables)외부 → 모듈외부에서 모듈로 값을 주입. 함수의 인자(argument)와 같다
locals (Local Values)모듈 내부해당 모듈 안에서만 유효한 중간 계산값. 반복되는 표현식을 한 곳에서 관리
output (Output Values)모듈 → 외부모듈 내부의 값을 외부로 노출. 다른 모듈에서 참조하거나 CLI에서 확인 가능
# variables.tf — 외부에서 주입받는 값
variable "cluster_name" {
  type        = string
  description = "EKS 클러스터 이름"
  default     = "my-cluster"
}

# locals.tf — 모듈 내부에서만 쓰는 계산값
locals {
  common_tags = {
    Project     = var.cluster_name
    Environment = "production"
    ManagedBy   = "terraform"
  }
}

# outputs.tf — 외부로 노출할 값
output "vpc_id" {
  value       = aws_vpc.this.id
  description = "생성된 VPC의 ID"
}

locals를 쓰면 같은 표현식을 여러 곳에서 반복하지 않아도 된다. 예를 들어 공통 태그를 locals로 정의해두면 모든 리소스에서 tags = local.common_tags로 재사용할 수 있다.


테라폼 핵심 명령어 4가지

1. terraform init

초기화 명령어. 명시된 모든 프로바이더(라이브러리) 및 모듈을 검색 후 다운로드 및 설치한다.

C:\code\terra\vpc> terraform init

2. terraform plan

Desired State(테라폼 코드)와 tfstate의 Current State 간의 차이점을 보여주고 시뮬레이션한다. "인프라가 이렇게 바뀔 것 같아"라고 미리 알려주는 것이다. plan이 성공하면 보통 apply도 성공하지만 항상 그런 건 아니다.

C:\code\terra\vpc> terraform plan

# 출력 예시 — 기호로 변경 내용을 한눈에 파악 가능
# + create (새로 생성)
# ~ update in-place (수정)
# - destroy (삭제)
# -/+ destroy and then create replacement (재생성)

Terraform will perform the following actions:

  # aws_vpc.this will be created
  + resource "aws_vpc" "this" {
      + cidr_block           = "10.10.0.0/16"
      + enable_dns_hostnames = true
      + id                   = (known after apply)
      ...
    }

Plan: 1 to add, 0 to change, 0 to destroy.

3. terraform apply

tf의 변경사항을 실제 인프라에 적용해서 리소스를 생성, 수정, 삭제한다.

C:\code\terra\vpc> terraform apply
# 매번 yes를 입력해야 함

C:\code\terra\vpc> terraform apply --auto-approve
# yes 없이 자동 적용

4. terraform destroy

모든 인프라를 삭제한다.

C:\code\terra\vpc> terraform destroy --auto-approve

메타 인수(Meta-Arguments) — 리소스 동작 제어

📌 공식 문서 참고: Terraform - lifecycle reference

리소스 블록 안에서 쓸 수 있는 특수 인수들이다. 프로바이더와 무관하게 테라폼 코어 언어 차원에서 동작한다.

count vs for_each — 여러 리소스 한 번에 만들기

📌 공식 문서 참고: HashiCorp - count versus for_each

둘 다 여러 개의 리소스를 반복 생성할 때 쓴다. 하지만 동작 방식이 달라서 잘못 선택하면 예상치 못한 리소스 삭제가 발생할 수 있다.

count — 숫자 인덱스로 추적

# 서브넷 3개를 count로 생성
resource "aws_subnet" "public" {
  count             = 3
  vpc_id            = aws_vpc.this.id
  cidr_block        = "10.10.${count.index}.0/24"
  availability_zone = data.aws_availability_zones.available.names[count.index]

  tags = {
    Name = "pub-sub${count.index + 1}"
  }
}
# 참조: aws_subnet.public[0], aws_subnet.public[1], aws_subnet.public[2]

for_each — 문자열 키로 추적 (권장)

# 서브넷을 for_each로 생성
variable "subnets" {
  default = {
    "pub-sub1" = "10.10.1.0/24"
    "pub-sub2" = "10.10.2.0/24"
    "pub-sub3" = "10.10.3.0/24"
  }
}

resource "aws_subnet" "public" {
  for_each   = var.subnets
  vpc_id     = aws_vpc.this.id
  cidr_block = each.value

  tags = {
    Name = each.key
  }
}
# 참조: aws_subnet.public["pub-sub1"], aws_subnet.public["pub-sub2"]

왜 for_each가 더 안전한가?

count는 리스트의 인덱스로 리소스를 추적한다. 중간 항목을 삭제하면 뒤에 있는 모든 리소스의 인덱스가 밀려서 테라폼이 "변경됨"으로 인식하고 불필요한 destroy/recreate를 실행한다.

for_each는 문자열 키로 추적하기 때문에 특정 항목을 삭제해도 다른 리소스에 영향이 없다. 공식 문서에서도 "컬렉션의 멤버가 변경될 수 있다면 for_each를 사용하라" 고 권장한다.

구분countfor_each
추적 방식숫자 인덱스문자열 키
중간 항목 삭제 시뒤 항목 전체 영향해당 항목만 삭제
적합한 경우완전히 동일한 리소스 N개각각 다른 설정이 필요한 경우
입력 타입숫자map 또는 set of strings

lifecycle — 리소스 생명주기 제어

📌 공식 문서 참고: HashiCorp Developer - lifecycle reference

테라폼의 기본 동작은 "변경됐으면 destroy 후 recreate"다. lifecycle 블록으로 이 동작을 세밀하게 제어할 수 있다.

resource "aws_instance" "web" {
  # ...
  lifecycle {
    create_before_destroy = true   # 새 리소스 먼저 만들고 기존 것 삭제
    prevent_destroy       = true   # 실수로 삭제 방지
    ignore_changes        = [tags] # 외부에서 변경된 속성 무시
  }
}

create_before_destroy

기본적으로 테라폼은 기존 리소스를 먼저 삭제하고 새 리소스를 만든다. create_before_destroy = true로 설정하면 새 리소스를 먼저 만든 뒤 기존 것을 삭제한다. 다운타임 없는 배포(Zero-downtime deployment)에 필수다.

resource "aws_launch_template" "app" {
  # ...
  lifecycle {
    create_before_destroy = true
  }
}

prevent_destroy

prevent_destroy = true로 설정하면 해당 리소스를 삭제하려는 plan이 에러를 반환한다. RDS 같이 재생성 비용이 큰 리소스에 걸어두면 실수로 날리는 걸 방지할 수 있다.

resource "aws_db_instance" "main" {
  # ...
  lifecycle {
    prevent_destroy = true
    # terraform destroy 실행 시 에러 발생
  }
}

⚠️ 주의: prevent_destroy는 설정이 코드에 있을 때만 동작한다. 리소스 블록 자체를 코드에서 제거하면 테라폼은 그냥 삭제한다.

ignore_changes

테라폼 외부에서 변경된 속성을 무시할 때 쓴다. 예를 들어 Auto Scaling이 인스턴스 수를 조정하거나, 외부 에이전트가 태그를 수정하는 경우에 유용하다.

resource "aws_instance" "example" {
  # ...
  lifecycle {
    ignore_changes = [
      tags,          # 외부에서 태그를 관리하는 경우
      instance_type  # 수동으로 인스턴스 타입을 변경한 경우
    ]
  }
}

Remote Backend — S3 + DynamoDB로 tfstate 팀 공유

📌 공식 문서 참고: AWS Prescriptive Guidance - Terraform Backend

로컬에서 테라폼을 실행하면 tfstate가 내 PC에 저장된다. 혼자 쓸 때는 괜찮지만 팀 작업이 되는 순간 문제가 생긴다.

  • 두 명이 동시에 apply를 실행하면 state 파일이 충돌한다
  • 내 PC에만 있는 state를 팀원이 볼 수 없다
  • state 파일을 git에 올리면 민감한 정보(DB 비밀번호 등)가 노출된다

이 문제를 해결하는 게 Remote Backend다. AWS 환경에서는 S3 + DynamoDB 조합이 표준이다.

  • S3: tfstate 파일을 저장 (버전 관리, 암호화 지원)
  • DynamoDB: state locking 담당 (동시 수정 방지)

S3 Remote Backend 설정

# backend.tf
terraform {
  backend "s3" {
    bucket         = "my-terraform-state-bucket"
    key            = "vpc/terraform.tfstate"
    # state 파일 경로. 프로젝트/환경별로 다르게 설정
    region         = "ap-northeast-2"
    encrypt        = true
    # state 파일 암호화 (민감 정보 보호)
    dynamodb_table = "terraform-state-lock"
    # DynamoDB 테이블로 state locking
  }
}

S3 버킷 + DynamoDB 테이블 생성

# S3 버킷 생성 (state 저장용)
resource "aws_s3_bucket" "terraform_state" {
  bucket = "my-terraform-state-bucket"

  lifecycle {
    prevent_destroy = true
    # 실수로 state 버킷 삭제 방지
  }
}

resource "aws_s3_bucket_versioning" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id
  versioning_configuration {
    status = "Enabled"
    # 버전 관리로 이전 state로 롤백 가능
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

# DynamoDB 테이블 생성 (state locking용)
resource "aws_dynamodb_table" "terraform_state_lock" {
  name         = "terraform-state-lock"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"
  # 반드시 LockID 여야 한다

  attribute {
    name = "LockID"
    type = "S"
  }
}

State Locking 동작 방식

DynamoDB locking의 흐름은 이렇다.

  1. plan / apply / destroy 실행 전 → DynamoDB에 lock 레코드 생성
  2. 다른 프로세스가 같은 state에 접근 시도 → lock 레코드 발견 → 에러 반환
  3. 작업 완료 후 → DynamoDB에서 lock 레코드 삭제

두 명이 동시에 apply를 실행하면 먼저 실행한 쪽이 lock을 잡고, 나중에 실행한 쪽은 아래 에러를 보게 된다.

Error: Error acquiring the state lock
Error message: ConditionalCheckFailedException: The conditional request failed
Lock Info:
  ID:        xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  Path:      my-terraform-state-bucket/vpc/terraform.tfstate
  Operation: OperationTypeApply
  Who:       user@hostname
  Created:   2026-05-08 10:00:00

💡 Terraform 1.10부터 DynamoDB 없이 S3 자체 locking(use_lockfile = true)도 지원한다. 하지만 DynamoDB 방식이 더 오래되고 검증된 방법이라 아직은 DynamoDB를 쓰는 게 일반적이다.


실습 — VPC 모듈 구성

프로바이더 설정

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 6.28.0"
    }
  }
}

VPC + IGW 생성

### 1. VPC 생성 ###
resource "aws_vpc" "this" {
  cidr_block           = "10.10.0.0/16"
  enable_dns_hostnames = true
  tags = {
    Name = "eks-vpc"
  }
}

### 2. Internet Gateway 생성 ###
resource "aws_internet_gateway" "this" {
  vpc_id = aws_vpc.this.id
  # vpc_id를 하드코딩해도 되지만 재사용성이 떨어지므로 참조 방식 사용
}

테라폼에서는 IGW를 만들기만 해도 알아서 VPC에 붙여준다.

퍼블릭 서브넷 생성

### 3. 퍼블릭 서브넷 생성 ###
resource "aws_subnet" "pub_sub1" {
  vpc_id            = aws_vpc.this.id
  cidr_block        = "10.10.1.0/24"
  # vpc의 대역 내여야 함
  availability_zone = "ap-northeast-2a"
  # 가용영역 a

  enable_resource_name_dns_a_record_on_launch = true
  map_public_ip_on_launch                     = true

  depends_on = [aws_internet_gateway.this]
  # 의존성. igw를 먼저 만든 후 subnet을 만들겠다

  tags = {
    Name = "eks-vpc-pub-sub1"
  }
}

라우팅 테이블 생성 및 연결

### 4. 퍼블릭 라우팅 테이블 생성 ###
resource "aws_route_table" "pub_rt" {
  vpc_id = aws_vpc.this.id

  route {
    cidr_block = "0.0.0.0/0"
    # 목적지 = 모든 외부 트래픽
    gateway_id = aws_internet_gateway.this.id
    # next hop = igw
  }

  tags = {
    Name = "eks-vpc-pub-rt"
  }
}
# 💡 local route(VPC 내부 통신)는 VPC 생성 시 자동으로 추가된다.
# gateway_id = "local" 로 명시적으로 선언하면 오히려 에러가 발생하므로 생략한다.

### 5. 라우팅 테이블 연결 ###
resource "aws_route_table_association" "pub1_rt_asso" {
  subnet_id      = aws_subnet.pub_sub1.id
  route_table_id = aws_route_table.pub_rt.id
}

resource "aws_route_table_association" "pub2_rt_asso" {
  subnet_id      = aws_subnet.pub_sub2.id
  route_table_id = aws_route_table.pub_rt.id
}

NAT Gateway + 프라이빗 서브넷

### 6. NAT-GW를 위한 EIP 생성 ###
resource "aws_eip" "this" {
  tags = {
    Name = "eks-vpc-eip"
  }
}

### 7. NAT-GW 생성 ###
resource "aws_nat_gateway" "this" {
  allocation_id = aws_eip.this.id
  subnet_id     = aws_subnet.pub_sub1.id
  # 정석대로 하면 퍼블릭 서브넷마다 만들어줘야 하지만, 실습이라 하나만 생성

  tags = {
    Name = "eks-vpc-natgw"
  }
}

### 8. 프라이빗 서브넷 생성 ###
resource "aws_subnet" "pri_sub1" {
  vpc_id            = aws_vpc.this.id
  cidr_block        = "10.10.2.0/24"
  availability_zone = "ap-northeast-2a"
  enable_resource_name_dns_a_record_on_launch = true
  tags = {
    Name = "eks-vpc-pri-sub1"
  }
}

resource "aws_subnet" "pri_sub2" {
  vpc_id            = aws_vpc.this.id
  cidr_block        = "10.10.12.0/24"
  availability_zone = "ap-northeast-2c"
  enable_resource_name_dns_a_record_on_launch = true
  tags = {
    Name = "eks-vpc-pri-sub2"
  }
}

### 9. 프라이빗 라우팅 테이블 ###
resource "aws_route_table" "pri_rt" {
  vpc_id = aws_vpc.this.id

  route {
    cidr_block     = "0.0.0.0/0"
    # 목적지 = 외부(전체)
    nat_gateway_id = aws_nat_gateway.this.id
    # next hop = nat-gw (NAT GW는 nat_gateway_id 사용, gateway_id 아님)
  }

  tags = {
    Name = "eks-vpc-pri-rt"
  }
}

resource "aws_route_table_association" "pri1_rt_asso" {
  subnet_id      = aws_subnet.pri_sub1.id
  route_table_id = aws_route_table.pri_rt.id
}

resource "aws_route_table_association" "pri2_rt_asso" {
  subnet_id      = aws_subnet.pri_sub2.id
  route_table_id = aws_route_table.pri_rt.id
}

보안 그룹

### 10. 보안 그룹 생성 ###
resource "aws_security_group" "pub-sg" {
  vpc_id = aws_vpc.this.id
  tags = {
    Name = "eks-vpc-pub-sg"
  }
}

### 11. 보안 그룹 정책 ###
# 인바운드 - HTTP 허용
resource "aws_security_group_rule" "http-ingress" {
  type              = "ingress"
  from_port         = 80
  to_port           = 80
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.pub-sg.id
}

# 인바운드 - SSH 허용
resource "aws_security_group_rule" "ssh-ingress" {
  type              = "ingress"
  from_port         = 22
  to_port           = 22
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.pub-sg.id
}

# 아웃바운드 - 모두 허용
resource "aws_security_group_rule" "all-egress" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  # 모든 프로토콜 = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.pub-sg.id
}

💡 AWS 콘솔이나 CLI로 보안 그룹을 만들면 아웃바운드 규칙이 자동으로 생기는데, 테라폼으로 만들면 그렇지 않다. 아웃바운드 정책도 따로 구성해줘야 한다.

outputs.tf — 모듈 간 값 전달

VPC 모듈에서 생성한 리소스의 ID를 EKS 모듈에서 참조하려면 output을 구성해야 한다.

output "eks-vpc-id" {
  value = aws_vpc.this.id
}

output "pub-sub1-id" {
  value = aws_subnet.pub_sub1.id
}

output "pub-sub2-id" {
  value = aws_subnet.pub_sub2.id
}

output "pri-sub1-id" {
  value = aws_subnet.pri_sub1.id
}

output "pri-sub2-id" {
  value = aws_subnet.pri_sub2.id
}

terraform apply 후 output에 해당하는 값들이 터미널에 출력된다.


실습 — EKS 모듈 구성

VPC 모듈과 같은 레벨에 eks 디렉토리를 만들고 EKS 클러스터를 정의한다.

디렉토리 구조:

terra/
├── root.tf       ← 루트 모듈 (vpc + eks 모듈 호출)
├── vpc/
│   ├── main.tf
│   └── outputs.tf
└── eks/
    ├── main.tf
    └── variables.tf

EKS 모듈 — main.tf

Terraform Registry의 공식 EKS 모듈을 활용한다.

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "21.20.0"

  cluster_name    = "pub-cluster"
  cluster_version = "1.34"

  vpc_id = var.eks-vpc-id
  # eks를 설치할 vpc의 아이디는 vpc 모듈에서 출력한 값

  subnet_ids = [
    var.pub-sub1-id,
    var.pub-sub2-id
  ]

  eks_managed_node_groups = {
    eks_ng = {
      min_size       = 2
      max_size       = 4
      desired_size   = 2
      instance_types = ["t3.micro"]
      # v20+ 모듈에서는 instance_type(단수)이 아닌 instance_types(복수) 사용
    }
  }

  cluster_endpoint_public_access = true
  # 외부에서 kubectl 명령을 허용
}

terraform init.terraform 디렉토리에 모듈을 다운로드 받는다. 용량이 크기 때문에 GitHub에 push할 때는 .gitignore에 추가해야 한다.

root.tf — 모듈 조합

terra 루트 디렉토리에 root.tf를 만들어서 vpc 모듈과 eks 모듈을 함께 호출한다.

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

module "eks-vpc" {
  source = "./vpc"
}

module "pub-cluster" {
  source = "./eks"

  eks-vpc-id  = module.eks-vpc.eks-vpc-id
  pri-sub1-id = module.eks-vpc.pri-sub1-id
  pri-sub2-id = module.eks-vpc.pri-sub2-id
  pub-sub1-id = module.eks-vpc.pub-sub1-id
  pub-sub2-id = module.eks-vpc.pub-sub2-id
}

vpc 모듈에서 개별적으로 생성한 인프라는 삭제하고, root.tf에서 통합 관리한다.

# vpc 모듈에서 개별 생성한 인프라 삭제
C:\code\terra\vpc> terraform destroy --auto-approve

# 루트에서 통합 실행
C:\code\terra> terraform init
C:\code\terra> terraform plan
C:\code\terra> terraform apply --auto-approve

kubectl 설치 및 클러스터 연결

EKS 클러스터가 생성되면 kubectl을 설치하고 kubeconfig를 업데이트한다.

# kubectl 설치 (EKS 1.34 버전에 맞는 kubectl)
c:\bin> curl.exe -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.34.6/2026-04-08/bin/windows/amd64/kubectl.exe

# kubeconfig 업데이트
c:\bin> aws eks update-kubeconfig --region ap-northeast-2 --name pub-cluster

💡 eksctl create로 만들었을 때는 내 IAM 계정이 자동으로 클러스터 접근 권한에 추가되는데, 테라폼으로 만들면 그렇지 않다. EKS 콘솔에서 수동으로 IAM 사용자를 Access Entry에 추가해줘야 한다.

또 한 가지 흥미로운 점은, 테라폼으로 EKS를 생성하면 CloudFormation 스택이 생기지 않는다. eksctl은 내부적으로 CloudFormation을 사용하지만, 테라폼은 AWS API를 직접 호출하기 때문이다.


정리

오늘 핵심은 "인프라를 코드로 선언하면 언제든 동일한 환경을 재현할 수 있다" 는 것이다. 콘솔에서 VPC 만들고 서브넷 만들고 라우팅 테이블 연결하고... 이 과정을 매번 반복하는 게 얼마나 번거로운 일인지 알고 있는데, 테라폼으로 하면 terraform apply 한 번으로 끝난다.

특히 모듈 구조가 인상적이었다. vpc 모듈과 eks 모듈을 분리해서 각자 독립적으로 관리하고, root.tf에서 조합하는 방식이 코드의 재사용성을 높여준다. 나중에 다른 프로젝트에서 vpc 모듈만 가져다 쓸 수도 있다.

공식 문서를 찾아보면서 추가로 알게 된 것들도 있었다. count보다 for_each가 안전한 이유, lifecycle 블록으로 실수로 인한 리소스 삭제를 방지하는 방법, 팀 작업 시 S3 + DynamoDB로 tfstate를 공유하는 패턴까지. 이론으로만 알던 것들이 실습과 문서를 같이 보니까 훨씬 명확해졌다.

다음 단계는 테라폼 코드를 GitHub에 올리고 S3 Remote Backend를 실제로 연동해서 팀 단위로 tfstate를 공유하는 방식을 익히는 것이다.

이번 velog는 AWS Kiro를 통해 작성하였다.

profile
나를 한줄로

0개의 댓글