AWS

Terraform

YUNZEE 2025. 5. 28. 10:25
728x90

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 사용자에게 권한 부여

🛠 해결 순서 요약

  1. AWS 콘솔 로그인
  2. IAM > 사용자 > ecr 사용자 클릭
  3. 권한 탭에서 아래 중 하나 수행:
    • 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

 

 

728x90