Terraform
Terraform의 핵심 개념
테라폼의 특징 - 클라우드 인프라 프로비저닝 및 관리에 특화된 도구
- 클라우드 인프라를 프로비저닝 하는 것
- 예를 들어 vpc 생성부터 eks 클러스터 생성, helm을 통한 애드온 설치, ALB 생성 및 매니페스트 apply, acm생성 및 연동, route 53을 통해 배포
IaC | 인프라를 코드로 관리, 사람마다 손으로 만드는 게 아니라 코드를 공유해서 정확하게 똑같이 만들 수 있음. |
Provider | AWS, Azure, GCP 등 어디에 만들 건지 정해주는 플러그인 같은 거, 우리는 주로 AWS Provider를 사용. |
Resource | 실제로 만들고 싶은 AWS 리소스 (예: EC2, S3, RDS 등)를 코드로 정의함. |
.tf 파일 | Terraform 코드 파일 확장자예요. 여기에 어떤 리소스를 만들지 정의함. |
Plan & Apply | 코드를 실행하면 먼저 계획(plan)을 보여주고, 괜찮으면 적용(apply)해서 실제 리소스를 만듦. |
State 파일 | 지금까지 만든 리소스들의 상태를 기록하는 파일이에요. Terraform이 변경사항을 추적하는 데 사용함. |
IaC(Infrastructure as Code) 도구
- 일반적인 범주 -
Terraform, Ansible, Vagrant (베이그라인트) 등 (고유한 언어)
- 넓은 범주(선언적, declarative) -
k8s 매니페스트, docker-compose, Dockerfile 등
= Desired State를 문서로 작성
desired state <=> status
desired state = 파드 3개(원하는 상태)
status = 파드가3개 동작중(현재 상황을 묘사)
테라폼에서는 'status'가 'tfstate'와 비슷함
tfstate = 현재 상황 + 의존성관계
IaC의 장점
- 매번 똑같은 환경을 재현 가능 = 개발자끼리 환경을 통일 = 일관성 유지
- dev, stating, production 환경을 동일하게 찍어낼 수 있음
- 휴먼에러를 줄일 수 있음
- 온디맨드(On-demand)로 인프라를 제공
- 멱등성: 중복 실행 방지
- 무결성: tfstate, lock파일(프로바이더의 버전을 관리하고 유지) , lock이라는 파일이 따로 존재함
테라폼 클라우드
- 깃허브 같은 scm레포지토리나 s3를 통해 tfstate 및 lock파일을 공유 = 팀원들과 현재의 인프라상태를 공유하고 무결성을 보장
- 나중에 어느정도 테라폼 코드에 익숙해지고 난 후에 다루면 좋음
pulumi
내게 익숙한 언어로 인프라를 프로비저닝 할 수 있는 도구, 다양한 언어를 지원. 테라폼 vs pulumi 둘 중에 선택을 하는 게 좋음.

- install하고 다시 시작
https://developer.hashicorp.com/terraform/install
Install | Terraform | HashiCorp Developer
Explore Terraform product documentation, tutorials, and examples.
developer.hashicorp.com

- 윈도우







- C:\bin의 모든 실행 파일들은 어디서든 실행 가능

- cmd을 열어서 확인

- 새 폴더 만들기
- 테라폼 코드를 짤 경로
- 혹시 리눅스에 설치하고 싶다면
wget https://releases.hashicorp.com/terraform/1.7.1/terraform_1.7.1_linux_amd64.zip
sudo unzip terraform_*
sudo mv terraform /usr/local/bin
terraform -version

- Open Folder 해서 아까 만들어둔 terra 폴더를 선택함

- vpc를 구성하기 위해 필요한 리소스들을 준비할 예정
내 마음대로 분류해도 사실 무방함, 심지어 하나의 파일에 아래의 내용을 다 몰아놔도 무방함
main.tf -> 메인 파일, 주로 리소스를 정의(클래스나 함수로 생각해도 무방)
variables.tf -> 변수들을 모아놓음
locals.tf -> 해당 모듈에서만 유효한 변수를 모아놓음
outputs.tf -> 특정 모듈에서 생성한 리소스를 다른 모듈에서 참조하거나 가져다 쓰고 싶을 때 output을 사용
vpc라는 모듈에서 'eks-vpc'라는 이름의 vpc를 생성했다면, 이후에 eks라는 모듈에서 'eks-vpc'라는 vpc에 클러스터를 구성하고 싶다면, vpc모듈로 인해 생성된 서브넷이나 vpc의 id를 output으로 뽑아내야 됨
딴 집에 있는 무언가를 가져오려면 그 집에 허용이 필요한데 그게 output. 그걸 잘 뽑아내야 됨
vpc면 그 아이디가 출력이 되는데 eks클러스터를 구성을 하고 수동으로 로드밸런스를 생성하고 싶을 때 거기에도 허용이 필요함.

우리는 테라폼을 통해 aws에 리소스를 만들고 싶기 때문에 당연히 aws configure정보가 terraform을 치는 환경(윈도우)에 설치되어있어야 함.
https://docs.aws.amazon.com/ko_kr/cli/latest/userguide/getting-started-install.html


- 설치


main.tf 파일에서 아래의 내용을 복붙
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
- 나는 aws 프로바이더를 쓸건데, 그건 hashicorp/aws

- 참조하기 좋은 명령어

- 우리가 눈여겨봐야 할 곳들
- 다양한 argument들을 찾아서 필요하다면 구성해야 함.
ex) vpc 대역 = cidr_block이라는 argument

- 우리가 vpc를 만들면 기본적으로 했던 설정들.
- 이 설정에 해당하는 argument를 찾아서 설정을 해줘야 함.

resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "eks-vpc"
}
}
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "eks-vpc"
}
}
- 아까 위에서 작성한 명령어 포함해서 한 번에 가져와봄

- 코드를 구성했다면 일단 main.tf 파일이 있는 경로로 이동후, init을 해야 됨

- init 후에는 .terraform이라는 숨김 폴더에 프로바이더가 설치
terraform plan - desired state(원하는 상태, 테라폼 코드에 들어있음)와 tfstate에 있는 '현재 상황'과의 차이점을 보여줌.
'인프라가 이렇게 바뀔 것 같아' 보통은 terraform plan이 성공하면 terraform apply도 성공하지만 꼭 그런 건 아님
terraform apply
- 옵션을 --auto-approve를 주면
PS C:\terra\vpc> terraform apply --auto-approve

해결 방법
1. IAM 사용자에게 권한 부여
🛠 해결 순서 요약
- AWS 콘솔 로그인
- IAM > 사용자 > ecr 사용자 클릭
- 권한 탭에서 아래 중 하나 수행:
- AmazonVPCFullAccess 또는 AdministratorAccess 정책 추가
- 또는 직접 작성한 커스텀 정책을 추가 (ec2:CreateVpc 포함)



- 잘 생성

- 내가 원했던 설정이 잘 적용됐는지 확인

- 이번에는 false로 바꾸고 다시 가서 확인

- 확인했다면 다시 true로 원상복구 하세요!!
C:\terra\vpc>terraform destroy --auto-approve
- 인프라 삭제


- 잘 삭제된 걸 확인할 수 있음

- aws_vps는 결국 '클래스', main은 인스턴스 정도로 생각해도 무방함.

- 이름은 정하기 나름인데 만약에 유일하다면 보통 this로 정함.
- 나중에 호출해서 쓸 때 굳이 해당 리소스의 이름을 생각할 필요가 없음.

igw를 만들고 싶다면 aws_vpc 리소스 문서를 찾아가서 신택스에 맞게 만들어 주면 됨.
vpc-resource의

- 이 항목을 찾아서 보면 됨.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
resource "aws_vpc" "this" {
cidr_block = "10.50.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "eks-vpc"
}
}
resource "aws_internet_gateway" "this" {
vpc_id = aws_vpc.this.id
tags = {
Name = "eks-vpc-igw"
}
}
- igw 생성
resource "aws_subnet" "main" {
vpc_id = aws_vpc.this.id
cidr_block = "10.50.1.0/24"
#VPC랑 같은 대역이여야함
tags = {
Name = "eks-vpc-pub-sub1"
}
}
- 퍼블릭 서브넷. 우리가 퍼블릭 서브넷을 만들었을 때 했었던 '서브넷 설정 편집'을 떠올려야 함.
오류/ 해결방안
UnauthorizedOperation: You are not authorized to perform: ec2:DescribeAddressesAttribute
UnauthorizedOperation: You are not authorized to perform: ec2:DescribeAddressesAttribute
- aws_eip.lb 리소스를 생성하거나 조회하려고 할 때,
- 현재 사용 중인 IAM 사용자(arn:aws:iam::922805825674:user/ecr)에게
ec2:DescribeAddressesAttribute 권한이 없어서 발생한 오류
또 권한 문제 발생

근데 여기서 문제점이 뭐였냐면 애초에 관리자 권한으로 내가 aws계정 등록을 해야 했는데 안 함
방금 위에서 했던 건 ECR권한임 그래서 권리자 권한으로 바꿔주면 따로 권한 추가를 안 해줘도 됨

얘를 넣어주면 따로 권한을 추가 안해줘도 괜찮음
kubernetes.io/cluster/pric owned
kubernetes.io/role/elb 1


- 가용영역도 정해줘야 함.


- 자동 할당 IP 설정

- 리소스기반 이름 설정.
resource "aws_subnet" "pub_sub1" {
vpc_id = aws_vpc.this.id
cidr_block = "10.50.1.0/24"
#vpc의 대역이여야함.
map_public_ip_on_launch = true
# 퍼블릭 ip 자동 할당
enable_resource_name_dns_a_record_on_launch = true
# 영문주소 부여
availability_zone = "ap-northeast-2a"
# 가용영역
tags = {
Name = "eks-vpc-pub-sub1"
"kubernetes.io/cluster/pri-cluster" = "owned"
"kubernetes.io/role/elb" = "1"
# 쿠버네티스 클러스터에 필요한 태그
# 나중에 eks 클러스터 생성시 pri-cluster라는 이름으로 생성해야함.
}
depends_on = [ aws_internet_gateway.this ]
# 의존성. igw 먼저 만든후 subnet을 만들겠다.
}
실습 1)
두 번째 퍼블릭 서브넷은 여러분들이 만들어보세요.
이름 = eks-vpc-pub-sub2
AZ = ap-northeast-2c
cidr = 10.50.11.0/24
resource "aws_subnet" "pub_sub2" {
vpc_id = aws_vpc.this.id
cidr_block = "10.50.11.0/24"
map_public_ip_on_launch = true
enable_resource_name_dns_a_record_on_launch = true
availability_zone = "ap-northeast-2c"
tags = {
Name = "eks-vpc-pub-sub2"
"kubernetes.io/cluster/pri-cluster" = "owned"
"kubernetes.io/role/elb" = "1"
}
depends_on = [ aws_internet_gateway.this ]
}
서브넷과 라우팅테이블 생성


- 코드 복사
resource "aws_route_table" "pub_rt" {
vpc_id = aws_vpc.this.id
route {
cidr_block = "10.50.0.0/16"
gateway_id = "local"
# gateway_id = next hop
}
route {
cidr_block = "0.0.0.0/0"
# 외부(전체)로 빠져나가고 싶으면
gateway_id = aws_internet_gateway.this.id
# gateway_id = igw로 나가라
}
tags = {
Name = "eks-vpc-pub-rt"
}
}
- 라우팅 테이블 생성

- 서브넷과 라우팅테이블을 연결
resource "aws_route_table_association" "pub1_rt_asso" {
subnet_id = aws_subnet.pub_sub1.id
# 위에서 만든 pub_sub1 이라는 서브넷 리소스
route_table_id = aws_route_table.pub_rt.id
# 위에서 만든 pub_rt라는 라우팅 테이블 리소스
}
resource "aws_route_table_association" "pub2_rt_asso" {
subnet_id = aws_subnet.pub_sub2.id
route_table_id = aws_route_table.pub_rt.id
}
apply.

NAT-GW 생성


# NAT-GW 생성
resource "aws_nat_gateway" "this" {
allocation_id = aws_eip.this.id
subnet_id = aws_subnet.pub_sub1.id
# 정석대로하면 pub-sub가용영역 전부에 만들어야 하지만 한개(az-a)만 생성.
tags = {
Name = "eks-vpc-natgw"
}
}
프라이빗 서브넷
resource "aws_subnet" "pri_sub1" {
vpc_id = aws_vpc.this.id
cidr_block = "10.50.2.0/24"
# 퍼블릭 아이피 할당x
enable_resource_name_dns_a_record_on_launch = true
availability_zone = "ap-northeast-2a"
tags = {
Name = "eks-vpc-pri-sub1"
"kubernetes.io/cluster/pri-cluster" = "owned"
"kubernetes.io/role/internal-elb" = "1"
}
depends_on = [ aws_nat_gateway.this ]
}
- 프라이빗 서브넷 1 생성.
# 가용영역 c에 프라이빗 서브넷 생성(pri_sub2)
resource "aws_subnet" "pri_sub2" {
vpc_id = aws_vpc.this.id
cidr_block = "10.50.12.0/24"
enable_resource_name_dns_a_record_on_launch = true
availability_zone = "ap-northeast-2c"
tags = {
Name = "eks-vpc-pri-sub2"
"kubernetes.io/cluster/pri-cluster" = "owned"
"kubernetes.io/role/internal-elb" = "1"
}
depends_on = [ aws_nat_gateway.this ]
}
# 프라이빗 라우팅 테이블 생성(pri_rt)
resource "aws_route_table" "pri_rt" {
vpc_id = aws_vpc.this.id
route {
cidr_block = "10.50.0.0/16"
gateway_id = "local"
}
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_nat_gateway.this.id
}
tags = {
Name = "eks-vpc-pri-rt"
}
}
# 프라이빗 라우팅 테이블 서브넷 연결(association).
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
}

- nat-gw가 잘 붙어있는지, 서브넷 설정편집에서 영문주소 받게 되어있는지 확인.
보안그룹



- 동일한 모듈에서는 바로 참조 가능.
# 전체 대역에 80번 허용
resource "aws_security_group_rule" "eks-vpc-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.eks-vpc-pub-sg.id
}
# 전체 대역에 22번 허용
resource "aws_security_group_rule" "eks-vpc-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.eks-vpc-pub-sg.id
}
- 인바운드 정책 생성
resource "aws_security_group_rule" "eks-vpc-all-egress" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.eks-vpc-pub-sg.id
}
- 아웃바운드 정책 생성
- 각 리소스가 잘 생성됐는지 확인.


- outputs.tf 파일을 만들자!
output "eks-vpc-id" {
value = aws_vpc.this.id
# eks-vpc-id 라는 키값에 aws_vpc.this.id가 value로 들어갈 예정.
# eks-vpc-id를 호출하면 vpc-2948328423849 라는 id가 특정될 예정.
}
# 퍼블릭 서브넷들
output "pri-sub1-id" {
value = aws_subnet.pri_sub1.id
}
output "pri-sub2-id" {
value = aws_subnet.pri_sub2.id
}
# 프라이빗 서브넷들
output "pub-sub1-id" {
value = aws_subnet.pub_sub1.id
}
output "pub-sub2-id" {
value = aws_subnet.pub_sub2.id
}

- outputs.tf를 통해 출력된 값. 나중에 eks를 생성할 때 참조할 예정
EKS 클러스터 생성



- 아무것도 없는 부분 클릭하고 새로운 폴더 생성
PS C:\terra\vpc> cd ..
PS C:\terra> cd eks
terraform init으로 초기화
terra\eks\main.tf
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "20.36.0"
cluster_name = "pri-cluster"
#vpc를 생성할때 이미 정해놓은 이름
cluster_version = "1.32"
}
오류/ 해결방안 - git 설치

-eks 모듈을 github에서 git도구를 통해 다운로드하려고 했는데 git이 윈도우에 설치가 안되어있음




- 요거 체크











- git이 잘 설치됨
EKS


- 인풋에서 어떤 값을 넣어줘야 하는지, 항목을 찾는 게 좋음
클러스터를 제대로 구성하려면, 결국에는 온전한 클러스터를 eksctl 통해서 만들어보고, aws 콘솔로 접속해서 gui 항목들을 내가 테라폼으로 만든 eks 클러스터와 대조를 해보면서 계속 만들어야 하는 리소스나 인풋을 구성해 주는 게 좋음
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "20.36.0"
cluster_name = "pri-cluster"
#vpc를 생성할때 이미 정해놓은 이름
cluster_version = "1.32"
vpc_id = var.eks-vpc-id
subnet_ids = [
var.pri-sub1-id,
var.pri-sub2-id
#일단은 변수를 가져오도록 하고, 나중에 vpc 모듈에서 뽑아낸 output값을 넣어줄 예정
]
eks_managed_node_groups = {
pri-cluster-nodegroups = {
min_size = 2
max_size = 4
desired_size = 2
instance_type = ["t3.micro"]
}
}
}


앞에서 사용할 거라고 선언만 해줬지 변수 선언을 안 해줬기 때문에 variables파일에 변수 선언을 해주도록 하겠음
variable "eks-vpc-id" {}
variable "pri-sub1-id" {}
variable "pri-sub2-id" {}
variable "pub-sub1-id" {}
variable "pub-sub2-id" {}
- 빈 변수를 선언해 두고 나중에 값이 채워질 예정

- terra라는 폴더, eks나 vpc폴더와 동등한 위치에 root.tf파일을 생성
- root.tf파일은 두 개의 모듈(eks.vpc)을 호출해서 vpc와 eks를 생성할 예정
C:\terra\eks>cd ..
C:\terra>cd vpc
C:\terra\vpc>terraform destroy
- 기존에 생성한 vpc는 삭제를 하자. 어차피 root.tf에서 한꺼번에 만들 예정이기 때문
--auto-approve 옵션을 붙이지 않았을 땐 'yes'를 누르면 삭제됨
terra\root.tf
provider "aws" {
region = "ap-northeast-2"
}
module "eks-vpc" {
source = "./vpc"
}
module "pri-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의 아웃풋을 참조해서 eks 모듈을 돌릴때 변수에 값을 넣어주겠다다
}
PS C:\terra\vpc> cd ..
PS C:\terra>
- terraform init 할 경로
- terraform(숨김 폴더) 경로에는 용량이 많이 나가는 모듈들이 들어있음. 따라서 내가 어딘가에 테라폼코드를 올린다면, 기본적으로 gitignore 처리를 하는 게 좋음
C:\terra>terraform plan
- 생길 리소스를 확인.
C:\terra>terraform apply

- yes
- 어느 정도 시간이 흐르면 vpc를 생성 후 eks클러스터를 생성하기 시작

- aws의 콘솔에서도 확인 가능

- cloudformation(aws의 IaC도구)에 당연히 스택 구성이 없음. 우리가 만든 테라폼 모듈을 쓰고 있기 때문임
- 사실 우리가 생성한 클러스터는 정상 동작하지 않을 확률이 큼
왜냐하면 아주 기본적인 설정들만 해줬기 때문임
온전하게 eks클러스터를 생성하려면 결국 eks모듈에 대한 이해가 필요함
eksctl로 만든 온전한 클러스터와 terraform으로 만든 불완전한 클러스터를 aws콘솔에서 계속 대조하면서 메꾸는 형식


PS C:\terra> aws eks update-kubeconfig --name pri-cluster
Added new context arn:aws:eks:ap-northeast-2:922805825674:cluster/pri-cluster to C:\Users\USE

- kubectl 명령이 없으므로 설치.

cmd창을 관리자 권한으로 실행
curl.exe -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.32.0/2024-12-20/bin/windows/amd64/kubectl.exe

- kubectl 설치했고, kubeconfig 잘 가져왔고, aws configure(액세스, 시크릿 키)도 잘 구성되어 있는 상태임
- 근데 안됨

- 내 계정에 대한 권한이 없는 걸 확인할 수 있음


- 계속 안됨,,,

cluster_endpoint_public_access = true
- 이 옵션 추가. 외부(public)에서도 api요청을 받을 수 있게.
PS C:\terra> terraform apply --auto-approve
