AWS Elastic Kubernetes Service +8 Terraform with EKS (AEWS 2기)

LEE EUI JOO·2024년 4월 23일
0

K8S

목록 보기
17/17


이제 어느덧 가시다 Team 과의 마지막 스터디 주차입니다. 더 많은 인사이트를 얻을 수 있는 기회였고, 빡쎈(?)일정이었지만 매주 재밌는 내용 덕분에 마지막 주차 까지 무사히 달려올 수 있던 것 같습니다. 이번 주, 마지막 주차는 Terraform with EKS 입니다.

** Terraform 의 기본 문법을 습득하고 EKS 의 Module 에서 어떤 API 를 호출하는지 파악 하는 것이 KeyPoint 입니다.


0. Terraform 기본 사용

  • HashCorp 에서 공개한 IaC 도구

  • Terraform 워크플로의 세가지 stage
    • Write - Plan - Apply


실습 환경 준비 - Local 에서 진행합니다.

# tfenv 설치
brew install tfenv

# 설치 가능 버전 리스트 확인
tfenv list-remote

# 테라폼 1.5.1 버전 설치
tfenv install 1.8.1

# 테라폼 1.5.1 버전 사용 설정 
tfenv use 1.8.1

# tfenv로 설치한 버전 확인
tfenv list

# 테라폼 버전 정보 확인
terraform version

# 자동완성
terraform -install-autocomplete
## 참고 .zshrc 에 아래 추가됨
cat ~/.zshrc
autoload -U +X bashcompinit && bashcompinit
complete -o nospace -C /usr/local/bin/terraform terraform
  • 기타 설치
# AWSCLI : 자신의 PC OS에 맞게 설
brew install awscli

# EKSCTL : 자신의 PC OS에 맞게 설치
brew install eksctl

# Kubectl
brew install kubernetes-cli

# Helm
brew install helm

# jquery, tree, watch
brew install tree jq watch
  • Visual Studio Code 설치
  • Extentions
    • HashiCorp HCL : syntax highlighting for HCL files - 링크
    • HashiCorp Terraform : Highlighting syntax from Terraform 등 - 링크

# default VPC를 생성
aws ec2 create-default-vpc

# default Subnet 생성
aws ec2 create-default-subnet --availability-zone ap-northeast-2a
aws ec2 create-default-subnet --availability-zone ap-northeast-2b
aws ec2 create-default-subnet --availability-zone ap-northeast-2c
aws ec2 create-default-subnet --availability-zone ap-northeast-2d

1. Get started AWS

  • 기본 환경 준비
mkdir learn-terraform
cd learn-terraform
touch main.tf
  • Amazon Linux 2 최신 ami id 찾기
#aws ec2 describe-images --owners self amazon
aws ec2 describe-images --owners self amazon --query 'Images[*].[ImageId]' --output text
aws ec2 describe-images --owners amazon --filters "Name=name,Values=amzn2-ami-hvm-2.0.*-x86_64-gp2" "Name=state,Values=available"
aws ec2 describe-images --owners amazon --filters "Name=name,Values=amzn2-ami-hvm-2.0.*-x86_64-gp2" "Name=state,Values=available" --query 'Images|sort_by(@, &CreationDate)[-1].[ImageId, Name]' --output text
ami-0217b147346e48e84	amzn2-ami-hvm-2.0.20240412.0-x86_64-gp2

aws ec2 describe-images --owners amazon --filters "Name=name,Values=amzn2-ami-hvm-2.0.*-x86_64-gp2" "Name=state,Values=available" --query 'Images|sort_by(@, &CreationDate)[-1].[ImageId]' --output text
ami-0217b147346e48e84

AL2ID=`aws ec2 describe-images --owners amazon --filters "Name=name,Values=amzn2-ami-hvm-2.0.*-x86_64-gp2" "Name=state,Values=available" --query 'Images|sort_by(@, &CreationDate)[-1].[ImageId]' --output text`
echo $AL2ID

ami-0217b147346e48e84
  • EC2 생성 모니터링
# [터미널1] EC2 생성 모니터링
export AWS_PAGER=""
while true; do aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text ; echo "------------------------------" ; sleep 1; done
  • 테라폼 코드 파일 작성 : HCL Hashicorp Configuration Language 코드 파일 생성, Block로 구성
    • provider : Terraform으로 정의할 Infrastructure Provider를 의미 - Docs
    • resource : 실제로 생성할 인프라 자원을 의미
> **resource “<PROVIDER>_<TYPE>” “<NAME>{
  [CONFIG ...]
}**
> 
> - PROVIDER : ‘aws’ 같은 공급자의 이름
> - TYPE : ‘security_group’ 같은 리소스의 유형
> - NAME : 리소스의 이름
> - CONFIG : 한개 이상 arguments
cat <<EOT > main.tf
provider "aws" {
  region = "ap-northeast-2"
}

resource "aws_instance" "example" {
  ami           = "$AL2ID"
  instance_type = "t2.micro"
}
EOT
  • 배포 - 터미널 2에서 실행합니다.
# 초기화
terraform init
ls -al
tree .terraform

# plan 확인
terraform plan

# apply 실행
terraform apply
 Enter a value: yes 입력

# ec2 생성 확인 : aws 웹 관리 콘솔에서도 확인 - 서울 리전 선택
export AWS_PAGER=""
aws ec2 describe-instances --output table

# 테라폼 정보 확인
terraform state list
aws_instance.example

terraform show
# aws_instance.example:
resource "aws_instance" "example" {
    ami                                  = "ami-0217b147346e48e84"
    arn                                  = "arn:aws:ec2:ap-northeast-2:236747833953:instance/i-02d7966d0aa05c36a"
    associate_public_ip_address          = true
    availability_zone                    = "ap-northeast-2a"
    cpu_core_count                       = 1
    cpu_threads_per_core                 = 1
    disable_api_stop                     = false
    disable_api_termination              = false
    ebs_optimized                        = false
    get_password_data                    = false
    hibernation                          = false
    host_id                              = null
    iam_instance_profile                 = null
    id                                   = "i-02d7966d0aa05c36a"
    instance_initiated_shutdown_behavior = "stop"
    instance_lifecycle                   = null
    instance_state                       = "running"
    instance_type                        = "t2.micro"
    ipv6_address_count                   = 0
    ipv6_addresses                       = []
    key_name                             = null
    monitoring                           = false
    outpost_arn                          = null
    password_data                        = null
    placement_group                      = null
    placement_partition_number           = 0
    primary_network_interface_id         = "eni-004bfc2194f717ff9"
    private_dns                          = "ip-172-31-2-126.ap-northeast-2.compute.internal"
    private_ip                           = "172.31.2.126"
    public_dns                           = "ec2-43-203-182-137.ap-northeast-2.compute.amazonaws.com"
    public_ip                            = "43.203.182.137"
    secondary_private_ips                = []
    security_groups                      = [
        "default",
    ]
    source_dest_check                    = true
    spot_instance_request_id             = null
    subnet_id                            = "subnet-06ef0432e1e25e0d4"
    tags_all                             = {}
    tenancy                              = "default"
    user_data_replace_on_change          = false
    vpc_security_group_ids               = [
        "sg-0239c0a5e05197b25",
    ]

    capacity_reservation_specification {
        capacity_reservation_preference = "open"
    }

    cpu_options {
        amd_sev_snp      = null
        core_count       = 1
        threads_per_core = 1
    }

    credit_specification {
        cpu_credits = "standard"
    }

    enclave_options {
        enabled = false
    }

    maintenance_options {
        auto_recovery = "default"
    }

    metadata_options {
        http_endpoint               = "enabled"
        http_protocol_ipv6          = "disabled"
        http_put_response_hop_limit = 1
        http_tokens                 = "optional"
        instance_metadata_tags      = "disabled"
    }

    private_dns_name_options {
        enable_resource_name_dns_a_record    = false
        enable_resource_name_dns_aaaa_record = false
        hostname_type                        = "ip-name"
    }

    root_block_device {
        delete_on_termination = true
        device_name           = "/dev/xvda"
        encrypted             = false
        iops                  = 100
        kms_key_id            = null
        tags                  = {}
        tags_all              = {}
        throughput            = 0
        volume_id             = "vol-08a58616c9cc54c21"
        volume_size           = 8
        volume_type           = "gp2"
    }
}

terraform show aws_instance.example

  • EC2 Tag 정보 수정 : Code 수정
cat <<EOT > main.tf
provider "aws" {
  region = "ap-northeast-2"
  profile = "ejl-personal"
}

resource "aws_instance" "example" {
  ami           = "$AL2ID"
  instance_type = "t2.micro"

  tags = {
    Name = "aews-study"
  }

}
EOT
  • 배포 실행
# plan 실행 시 아래와 같은 정보가 출력
terraform plan
aws_instance.example: Refreshing state... [id=i-02d7966d0aa05c36a]

Terraform used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_instance.example will be updated in-place
  ~ resource "aws_instance" "example" {
        id                                   = "i-02d7966d0aa05c36a"
      ~ tags                                 = {
          + "Name" = "aews-study"
        }
      ~ tags_all                             = {
          + "Name" = "aews-study"
        }
        # (38 unchanged attributes hidden)

        # (8 unchanged blocks hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.
...
# apply 실행
terraform apply
 Enter a value: yes 입력

# 모니터링 : [터미널1]에 Name 확인
  • None 에서 aews-study 로 tag 가 변경 된 것을 확인할 수 있습니다.

  • 삭제
# 리소스 삭제
terraform destroy
 Enter a value: yes 입력

혹은
terraform destroy -auto-approve

HCL : HashiCorp configuration language은 하시코프사에서 IaC와 구성 정보를 명시하기 위해 개발된 오픈 소스 도구

  • IaC는 수동 프로세스가 아닌 코드를 통해 인프라를 관리하고 프로비저닝 하는 것을 말함

  • 테라폼에서 HCL이 코드의 영역을 담당한다. HCL은 쉽게 읽을 수 있고 빠르게 배울 수 있는 언어의 특징을 가진다.

  • 인프라가 코드로 표현되고, 이 코드는 곧 인프라이기 때문에 선언적 특성을 갖게 되고 튜링 완전한 Turing-complete 언어적 특성을 갖는다.

  • 즉, 일반적인 프로그래밍 언어의 조건문 처리 같은 동작이 가능하다. 자동화와 더불어, 쉽게 버저닝해 히스토리를 관리하고 함께 작업 할 수 있는 기반을 제공.

  • HCL에서 변수와 문자열 값을 함께 사용하는 인터폴레이션 interpolation 표현 방식을, JSON을 사용하는 다른 IaC 도구와 비교

  • HCL을 사용하면 동일한 내용을 JSON으로 표현하는 것보다 더 간결하고 읽기 쉽게 작성할 수 있다

HCL 표현식

// 한줄 주석 방법1
# 한줄 주석 방법2

/*
라인
주석
*/

locals {
  key1     = "value1"     # = 를 기준으로 키와 값이 구분되며
  myStr    = "TF ♡ UTF-8" # UTF-8 문자를 지원한다.
  multiStr = <<EOF
  Multi
  Line
  String
  with anytext
EOF

  boolean1    = true   # boolean true
  boolean2    = false  # boolean false를 지원한다.
  deciaml     = 123    # 기본적으로 숫자는 10진수,
  octal       = 0123   # 0으로 시작하는 숫자는 8진수,
  hexadecimal = "0xD5" # 0x 값을 포함하는 스트링은 16진수,
  scientific  = 1e10   # 과학표기 법도 지원한다.

  # funtion 호출 예
  myprojectname = format("%s is myproject name", var.project)

  # 3항 연산자 조건문을 지원한다.
  credentials = var.credentials == "" ? file(var.credentials_file) : var.credentials
}
  • HCL 표현식에서는 코드에서 사용되는 주석 표기부터 변수 정의 등을 포함하고 프로그래밍적인 연산과 구성 편의성을 높이기 위한 function 제공 - 링크
  • 테라폼으로 인프라를 구성하기 위한 선언 블록도 다수 존재 : terraform, resource, data, variable, local, output

  • Block : 테라폼 구성을 명시하는 데 사용
    • 테라폼 버전이나 프로바이더 버전과 같은 값들은 자동으로 설정되지만, 함께 작업할 때는 버전을 명시적으로 선언하고 필요한 조건을 입력하여 실행 오류를 최소화 할 것을 권장한다.
    • 오늘 실행하던, 3년 후에 실행하던 동일한 결과를 얻을 수 있어야 한다!
terraform {
  required_version = "~> 1.3.0" # 테라폼 버전

  required_providers { # 프로바이더 버전을 나열
    random = {
      version = ">= 3.0.0, < 3.1.0"
    }
    aws = {
      version = "4.2.0"
    }
  }

  cloud { # Cloud/Enterprise 같은 원격 실행을 위한 정보
    organization = "<MY_ORG_NAME>"
    workspaces {
      name = "my-first-workspace"
    }
  }

  backend "local" { # state를 보관하는 위치를 지정
    path = "relative/path/to/terraform.tfstate"
  }
}
  • 테라폼 내에서 버전이 명시되는 terraform, module에서 사용 가능하며 버전에 대한 제약을 둠으로써 테라폼, 프로바이더, 모듈이 항상 의도한 정의대로 실행되는 것을 목적으로 한다.
  • 버전 체계는 시맨틱 버전 관리 Semantic Versioning 방식을 따른다
# version = Major.Minor.Patch
version = 1.3.4
  • 시맨틱 버전 관리 방식
    • Major 버전 : 내부 동작의 API가 변경 또는 삭제되거나 하위 호환이 되지 않는 버전
    • Minor 버전 : 신규 기능이 추가되거나 개선되고 하위 호환이 가능한 버전
    • Patch 버전 : 버그 및 일부 기능이 개선된 하위 호환이 가능한 버전
  • 버전 제약 구문은 다른 프로그램 언어에서의 종속성 관리 시스템과 흡사하다.
    • = 또는 연산자 없음 : 지정된 버전만을 허용하고 다른 조건과 병기할 수 없다.
    • != : 지정된 버전을 제외한다.
  • 테라폼 버전 관리로 비유된 선언 방식의 의미
선언된 버전의미고려 사항
1.0.0테라폼 v1.0.0만을 허용한다테라폼을 업그레이드하기 위해서는 선언된 버전을 변경해야만 한다
>= 1.0.0테라폼 v1.0.0 이상의 모든 버전을 허용한다v1.0.0 버전을 포함해 그 이상의 모든 버전을 허용해 실행된다
~> 1.0.0.테라폼 v1.0.0을 포함한 v1.0.x 버전을 하용하고 v1.x는 허용하지 않는다부버전에 대한 업데이트는 무중단으로 이루어진다
>= 1.0, < 2.0.0테라폼 v1.0.0 이상 v2.0.0 미만인 버전을 허용한다주버전에 대한 업데이트를 방지한다

  • Version
  • 디렉터리 이동
cd ..
cd 03.start
  • 테라폼 버전에 대한 변경 내역과 기존 버전 정보 확인 - 링크
# 현재 버전 정보 확인
terraform version
Terraform v1.5.1
  • 코드 파일 수정 main.tf
terraform {
  required_version = "< 1.0.0"
}

resource "local_file" "abc" {
  content  = "abc!"
  filename = "${path.module}/abc.txt"
}
  • init 실행
# 실행 결과는?
terraform init
...
  • 코드 파일 수정
terraform {
  required_version = ">= 1.0.0"
}

resource "local_file" "abc" {
  content  = "abc!"
  filename = "${path.module}/abc.txt"
}
  • init 실행
# 실행 결과는?
terraform init
...

  • 프로바이더 버전
    • 테라폼 0.13 버전 이전에는 provider 블록에 함께 버전을 명시했지만 해당 버전 이후 프로바이더 버전은 terraform 블록에서 required_providers에 정의

  • 각 프로바이더의 이름에 소스 경로와 버전을 명시하며, 테라폼 레지스트리 공식 페이지에서 원하는 프로바이더를 선택한 다음 화면에서 우측 상단의 [USE PROVIDER]를 클릭하면 테라폼 코드에 해당 버전을 사용하는 샘플 코드가 표기됩니다.

  • 코드 파일 수정 : 프로바이더 버전을 과하게 높게 설정 - 링크
terraform {
  required_version = ">= 1.0.0"
  required_**providers** {
    local = {
      source  = "hashicorp/local"
      **version = ">=10000.0.0"**
    }
  }
}

resource "local_file" "abc" {
  content  = "123!"
  filename = "${path.module}/abc.txt"
}
  • init
# 실행 결과는 에러가 발생합니다.
terraform init -upgrade
  • 코드 파일 수정 : local 프로바이더 버전을 >= 2.0.0으로 수정
terraform {
  required_version = ">= 1.0.0"
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = ">= 2.0.0"
    }
  }
}

resource "local_file" "abc" {
  content  = "123!"
  filename = "${path.module}/abc.txt"
}
  • init
terraform init -upgrade

리소스 구성

  • resource : 리소스 블록은 선언된 항목을 생성하는 동작을 수행
  • 실습을 위해서 03.end 디렉터리를 신규 생성 후 열기 → main.tf 파일 생성
mkdir resource
cd resource  
touch main.tf  
  • 리소스 블록은 resource로 시작한다. 이후 리소스 블록이 생성할 ‘리소스 유형’을 정의한다.
  • 리소스 선언 : 리소스 유형(프로바이더이름_제공리소스유형), 동일한 유형에 대한 식별자 역할로 고유한 이름, 구성 인수들이 이름 뒤에 중괄호 내에 선언됨
resource "<리소스 유형>" "<이름>" {
  <인수> = <>
}

resource "local_file" "abc" {
  content  = "123"
  filename = "${path.module}/abc.txt"
}
  • 리소스에서 사용되는 유형들은 프로바이더에 종속성을 갖는다. 특정 프로바이더의 유형만 추가해도 init 수행 시 해당 프로바이더를 설치한다.
resource "local_file" "abc" {
  content  = "123"
  filename = "${path.module}/abc.txt"
}

resource "aws_instance" "web" {
  ami = "ami-a1b2c3d4"
  instance_type = "t2.micro"  
}
  • init
# init 시 프로바이더 선언 없이도 리소스 추가로 자동 인식된 프로바이더 요구사항과 초기화
terraform init
tree .terraform
  • 리소스 동작 보조 추가 메타인수를 정의 할 수 있다 → 뒤에서 자세히 설명
    • depends_on : 종속성을 선언하며, 선언된 구성요소와의 생성 시점에 대해 정의
    • count : 선언된 개수에 따라 여러 리소스를 생성
    • for_each : map 또는 set 타입의 데이터 배열의 값을 기준으로 여러 리소스를 생성
    • provider : 동일한 프로바이더가 다수 정의되어 있는 경우 지정
    • lifecycle : 리소스의 수명주기 관리
    • provisioner : 리소스 생성 후 추가 작업 정의
    • timeouts : 프로바이더에서 정의한 일부 리소스 유형에서는 create, update, delete에 대한 허용 시간 정의 가능

종속성

  • 테라폼 종속성은 resource, module 선언으로 프로비저인되는 각 요소의 생성 순서를 구분짓는다.

  • 기본적으로 다른 리소스에서 값을 참조해 불러올 경우 생성 선후 관계에 따라 작업자가 의도하지는 않았지만 자동으로 연관 관계가 정의되는 암시적 종속성을 갖게 되고, 강제로 리소스 간 명시적 종속성을 부여할 경우에는 메타인수인 depends_on을 활용한다.

  • 코드 파일 수정

resource "local_file" "abc" {
  content  = "123!"
  filename = "${path.module}/abc.txt"
}

resource "local_file" "def" {
  content  = "456!"
  filename = "${path.module}/def.txt"
}

  • apply 실행 : 서로 선후 관계가 없는 동일한 수준으로, 병렬(동시) 실행
  • VS Code 확장 graphviz 설치
#
terraform apply -auto-approve
...
Plan: 2 to add, 0 to change, 0 to destroy.
local_file.def: Creating...
local_file.abc: Creating...
local_file.abc: Creation complete after 0s [id=5f30576af23a25b7f44fa7f5fdf70325ee389155]
local_file.def: Creation complete after 0s [id=b9fbde4d33ab9c450a7ce303fb4788c9d2db9aed]

# 리소스 확인
ls *.txt
terraform state list
local_file.abc
local_file.def

# graph 확인 > graph-1.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph
terraform graph > graph-1.dot

# 모든 리소스 제거
terraform destroy -auto-approve
ls *.txt
terraform state list

  • 리소스 참조값을 설정해 두 개의 리소스 간 암시적 종속성 부여
resource "local_file" "abc" {
  content  = "123!"
  filename = "${path.module}/abc.txt"
}

resource "local_file" "def" {
  content  = local_file.abc.content
  filename = "${path.module}/def.txt"
}
  • Apply
#
terraform apply -auto-approve
...
Plan: 2 to add, 0 to change, 0 to destroy.
local_file.abc: Creating... <- 먼저 만들고
local_file.abc: Creation complete after 0s [id=5f30576af23a25b7f44fa7f5fdf70325ee389155]
local_file.def: Creating... <- 그 다음 만듬
local_file.def: Creation complete after 0s [id=5f30576af23a25b7f44fa7f5fdf70325ee389155]

ls *.txt
terraform state list
cat abc.txt
123!%
cat def.txt
123!%
diff abc.txt def.txt

# graph 확인 > graph-2.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph

digraph G {
  rankdir = "RL";
  node [shape = rect, fontname = "sans-serif"];
  "local_file.abc" [label="local_file.abc"];
  "local_file.def" [label="local_file.def"];
  "local_file.def" -> "local_file.abc";
}

terraform graph > graph-2.dot

  • Dot 확인
    • 종속성 관계가 생성 되어 있습니다.

  • 리소스의 속성을 주입하지 않아도 두 리소스 간에 종속성이 필요한 경우에, depends_on 선언으로 적용 가능
resource "local_file" "abc" {
  content  = "123!"
  filename = "${path.module}/abc.txt"
}

resource "local_file" "def" {
  depends_on = [
    local_file.abc
  ]

  content  = "456!"
  filename = "${path.module}/def.txt"
}
  • apply
#
terraform destroy -auto-approve
terraform apply -auto-approve
...

# graph 확인 > graph-3.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph

digraph G {
  rankdir = "RL";
  node [shape = rect, fontname = "sans-serif"];
  "local_file.abc" [label="local_file.abc"];
  "local_file.def" [label="local_file.def"];
  "local_file.def" -> "local_file.abc";
}

terraform graph > graph-3.dot


리소스 속성 참조

  • 리소스 구성에서 참조 가능한 값은 인수속성이다
    • 인수 : 리소스 생성 시 사용자가 선언하는 값
    • 속성 : 사용자가 설정하는 것은 불가능하지만 리소스 생성 이후 획득 가능한 리소스 고유 값
  • 리소스 인수의 선언과 참조 가능한 인수 및 속성 패턴
# Terraform Code
resource "<리소스 유형>" "<이름>" {
  <인수> = <>
}

# 리소스 참조
<리소스 유형>.<이름>.<인수>
<리소스 유형>.<이름>.<속성>
  • 아래 코드는 쿠버네티스 프로바이더의 Namespace 리소스를 생성하고 이후 Secret을 해당 Namespace에 생성하는 종속성을 리소스 인수 값 값으로 생성하는 예이다. Namespace의 이름만 변경해도, 해당 Namespace를 참조하는 모든 리소스가 업데이트되어 영향을 받는다.
resource "kubernetes_namespace" "example" {
  metadata {
    annotations = {
      name = "example-annotation"
    }
    name = "terraform-example-namespace"
  }
}

resource "kubernetes_secret" "example" {
  metadata {
    namespace = kubernetes_namespace.example.metadata.0.name # namespace 리소스 인수 참조
    name      = "terraform-example"
  }
  data = {
    password = "P4ssw0rd"
  }
}
  • 리소스가 생성될 때, 사용자가 입력한 ‘인수’를 받아 실제 리소스가 생성되면 일부 리소스는 자동으로 기본값이나 추가되는 ‘속성’이 부여된다.
  • 각 리소스마다 문서를 확인해보면 인수Arguments로 표현되어 있으며, 리소스 생성 후 추가되는 속성 값으로 Attributes에 안내되어 있다 - 링크
  • 리소스 속성을 참조하는 다른 리소스 또는 구성요소에서는 생성 후의 속성 값들도 인수로 가져올 수 있다.

데이터 소스 구성
데이터 소스는 테라폼으로 정의되지 않은 외부 리소스 또는 저장된 정보를 테라폼 내에서 참조할 때 사용

  • 실습을 위해서 3.5 디렉터리를 신규 생성 후 열기 → main.tf 파일 생성
mkdir 3.5 && cd 3.5
  • 데이터 소스 블록은 data로 시작, 이후 ‘데이터 소스 유형’을 정의 ← Resource 블록 정의와 유사
    • 데이터 소스 유형은 첫 번째 _를 기준으로 앞은 프로바이더 이름, 뒤는 프로바이더에서 제공하는 리소스 유형을 의미한다.
    • 데이터 소스 유형을 선언한 뒤에는 고유한 이름을 붙인다. 리소스의 이름과 마찬가지로 이름은 동일한 유형에 대한 식별자 역할을 하므로 중복될 수 없다.
    • 이름 뒤에는 데이터 소스 유형에 대한 구성 인수들은 { } 안에 선언한다. 인수가 필요하지 않은 유형도 있지만, 그때에도 { } 는 입력한다
data "local_file" "abc" {
  filename = "${path.module}/abc.txt"
}
  • 데이터 소스를 정의할 때 사용 가능한 메타인수는 다음과 같다.
    • depends_on : 종속성을 선언하며, 선언된 구성요소와의 생성 시점에 대해 정의
    • count : 선언된 개수에 따라 여러 리소스를 생성
    • for_each : map 또는 set 타입의 데이터 배열의 값을 기준으로 여러 리소스를 생성
    • lifecycle : 리소스의 수명주기 관리

데이터 소스 속성 참조

  • 데이터 소스로 읽은 대상을 참조하는 방식은 리소스와 구별되게 data가 앞에 붙는다. 속성 값은 다음과 같이 접근할 수 있다.
# Terraform Code
data "<리소스 유형>" "<이름>" {
  <인수> = <>
}

# 데이터 소스 참조
data.<리소스 유형>.<이름>.<속성>
  • Code Example
    • 데이터 소스를 활용해 AWS 가용영역 인수를 정의 → 리전 내에서 사용 가능한 가용영역 목록 가져오기
# Declare the data source
data "aws_availability_zones" "available" {
  state = "available"
}
resource "aws_subnet" "primary" {
  availability_zone = data.aws_availability_zones.available.names[0]
  # e.g. ap-northeast-2a
}
resource "aws_subnet" "secondary" {
  availability_zone = data.aws_availability_zones.available.names[1]
  # e.g. ap-northeast-2b
}
resource "local_file" "abc" {
  content  = "123!"
  filename = "${path.module}/abc.txt"
}

data "local_file" "abc" {
  filename = local_file.abc.filename
}

resource "local_file" "def" {
  content  = data.local_file.abc.content
  filename = "${path.module}/def.txt"
}
  • 구성도

  • apply & 확인
#
terraform apply -auto-approve
terraform state list

data.local_file.abc
local_file.abc
local_file.def

# 파일 확인
ls *.txt
diff abc.txt def.txt

# graph 확인
terraform graph > graph.dot

# 테라폼 콘솔 : 데이터 소스 참조 확인
terraform console
> 
data.local_file.abc.content
...
exit
  • az.tf 파일 생성
data "aws_availability_zones" "available" {
  state = "available"
}
  • 확인
terraform init -upgrade && terraform apply -auto-approve
terraform state list
data.aws_availability_zones.available

terraform state show data.aws_availability_zones.available
# data.aws_availability_zones.available:
data "aws_availability_zones" "available" {
    group_names = [
        "ap-northeast-2",
    ]
    id          = "ap-northeast-2"
    names       = [
        "ap-northeast-2a",
        "ap-northeast-2b",
        "ap-northeast-2c",
        "ap-northeast-2d",
    ]
    state       = "available"
    zone_ids    = [
        "apne2-az1",
        "apne2-az2",
        "apne2-az3",
        "apne2-az4",
    ]
}

terraform console
> data.aws_availability_zones.available.names
tolist([
  "ap-northeast-2a",
  "ap-northeast-2b",
  "ap-northeast-2c",
  "ap-northeast-2d",
])
> data.aws_availability_zones.available.names[0]
"ap-northeast-2a"
> data.aws_availability_zones.available.zone_ids[1]
"apne2-az2"
> data.aws_availability_zones.available.zone_ids[0]
"apne2-az1"

Tip. echo "data.aws_availability_zones.available" | terraform console

{
  "all_availability_zones" = tobool(null)
  "exclude_names" = toset(null) /* of string */
  "exclude_zone_ids" = toset(null) /* of string */
  "filter" = toset(null) /* of object */
  "group_names" = toset([
    "ap-northeast-2",
  ])
  "id" = "ap-northeast-2"
  "names" = tolist([
    "ap-northeast-2a",
    "ap-northeast-2b",
    "ap-northeast-2c",
    "ap-northeast-2d",
  ])
  "state" = "available"
  "timeouts" = null /* object */
  "zone_ids" = tolist([
    "apne2-az1",
    "apne2-az2",
    "apne2-az3",
    "apne2-az4",
  ])
}

변수 선언 방식

  • 입력 변수는 인프라를 구성하는 데 필요한 속성 값을 정의해 코드의 변경 없이 여러 인프라를 생성하는 데 목적이 있다.
  • 테라폼에서는 이것을 입력 변수 Input Variables 로 정의한다.
  • 실습을 위해서 3.6 디렉터리를 신규 생성 후 열기 → main.tf 파일 생성
cd .. && rm -rf 3.5
mkdir 3.6 && cd 3.6
  • 변수는 variable로 시작되는 블록으로 구성된다.
    • 변수 블록 뒤의 이름 값은 동일 모듈 내 모든 변수 선언에서 고유해야 하며, 이 이름으로 다른 코드 내에서 참조된다.
# variable 블록 선언의 예
variable "<이름>" {
 <인수> = <>
}

variable "image_id" {
 type = string
}
  • 테라폼 예약 변수 이름으로 사용 불가능 : source, version, providers, count, for_each, lifecycle, depends_on, locals

  • 변수 정의 시 사용 가능한 메타인수

    • default : 변수 값을 전달하는 여러 가지 방법을 지정하지 않으면 기본값이 전달됨, 기본값이 없으면 대화식으로 사용자에게 변수에 대한 정보를 물어봄
    • type : 변수에 허용되는 값 유형 정의, string number bool list map set object tuple 와 유형을 지정하지 않으면 any 유형으로 간주
    • description : 입력 변수의 설명
    • validation : 변수 선언의 제약조건을 추가해 유효성 검사 규칙을 정의 - 링크
    • sensitive : 민감한 변수 값임을 알리고 테라폼의 출력문에서 값 노출을 제한 (암호 등 민감 데이터의 경우) - 링크
    • nullable : 변수에 값이 없어도 됨을 지정

변수 유형
지원되는 변수의 범주와 형태

  • 기본 유형
    • string : 글자 유형
    • number : 숫자 유형
    • bool : true 또는 false
    • any : 명시적으로 모든 유형이 허용됨을 표시
  • 집합 유형
    • list (<유형>): 인덱스 기반 집합
    • map (<유형>): 값 = 속성 기반 집합이며 키값 기준 정렬
    • set (<유형>): 값 기반 집합이며 정렬 키값 기준 정렬
    • object ({<인수 이름>=<유형>, …})
    • tuple ([<유형>, …])
  • list와 set은 선언하는 형태가 비슷하지만 참조 방식이 인덱스와 키로 각각 차이가 있고, map와 set의 경우 선언된 값이 정렬되는 특징을 가진다.

입력 변수 사용 예시

  • 전달할 값이 number 인지 확인하는 입력 변수의 예
variable "number_example" {
  description = "An example of a number variable in Terraform"
  type        = number
  default     = 42
}
  • 전달할 값이 list 인지 확인하는 입력 변수의 예
variable "list_example" {
  description = "An example of a list in Terraform"
  type        = list
  default     = ["a", "b", "c"]
}
  • 조건 결합 사용 가능. 다음은 리스트의 모든 항목이 number 인 list 의 예
variable "list_numeric_example" {
  description = "An example of a numeric list in Terraform"
  type        = list(number)
  default     = [1, 2, 3]
}
  • 다음은 모든 값이 string 인 map 의 예
variable "map_example" {
  description = "An example of a map in Terraform"
  type        = map(string)

  default = {
    key1 = "value1"
    key2 = "value2"
    key3 = "value3"
  }
}
  • 다음은 object 또는 tuple 제약 조건을 사용하여 보다 복잡한 구조적 유형(structural type) 작성 가능
variable "object_example" {
  description = "An example of a structural type in Terraform"
  type        = object({
    name    = string
    age     = number
    tags    = list(string)
    enabled = bool
  })

  default = {
    name    = "value1"
    age     = 42
    tags    = ["a", "b", "c"]
    enabled = true
  }
}
  • 변수 유형별 선언 방식의 예시 - main.tf 파일
variable "string" {
  type        = string
  description = "var String"
  default     = "myString"
}

variable "number" {
  type    = number
  default = 123
}

variable "boolean" {
  default = true
}

variable "list" {
  default = [
    "google",
    "vmware",
    "amazon",
    "microsoft"
  ]
}

output "list_index_0" {
  value = var.list.0
}

output "list_all" {
  value = [
    for name in var.list : upper(name)
  ]
}

variable "map" { # Sorting
  default = {
    aws   = "amazon",
    azure = "microsoft",
    gcp   = "google"
  }
}

variable "set" { # Sorting
  type = set(string)
  default = [
    "google",
    "vmware",
    "amazon",
    "microsoft"
  ]
}

variable "object" {
  type = object({ name = string, age = number })
  default = {
    name = "abc"
    age  = 12
  }
}

variable "tuple" {
  type    = tuple([string, number, bool])
  default = ["abc", 123, true]
}

variable "ingress_rules" { # optional ( >= terraform 1.3.0)
  type = list(object({
    port        = number,
    description = optional(string),
    protocol    = optional(string, "tcp"),
  }))
  default = [
    { port = 80, description = "web" },
  { port = 53, protocol = "udp" }]
}
  • 확인
#
terraform init && terraform plan && terraform apply -auto-approve
terraform state list

#
terraform output
list_all = [
  "GOOGLE",
  "VMWARE",
  "AMAZON",
  "MICROSOFT",
]
list_index_0 = "google"

유효성 검사
입력되는 변수 타입 지징 이외, 사용자 지정 유효성 검사가 가능

  • 변수 블록 내에 validation 블록에서 조건인 condition에 지정되는 규칙이 true 또는 false를 반환해야 하며, error_message는 condition 값의 결과가 false 인 경우 출력되는 메시지를 정의한다.
  • regex 함수는 대상의 문자열에 정규식을 적용하고 일치하는 문자열을 반환하는데, 여기에 can 함수를 함께 사용하면 정규식에 일치하지 않는 경우의 오류를 검출한다.
  • validation 블록은 중복으로 선언할 수 있다.
  • variable 유효성 검사의 예 - main.tf 코드 파일 내용 수정
  • 확인
#
terraform apply -auto-approve
var.image_id
  The id of the machine image (AMI) to use for the server.

  Enter a value: ami
...

#
terraform apply -auto-approve
var.image_id
  The id of the machine image (AMI) to use for the server.

  Enter a value: ami-
...


#
terraform apply -auto-approve
var.image_id
  The id of the machine image (AMI) to use for the server.

  Enter a value: ami-12345678
...

변수 참조

  • main.tf 코드 파일 내용 수정
mkdir 4
cd 4

variable "my_password" {}

resource "local_file" "abc" {
  content  = var.my_password
  filename = "${path.module}/abc.txt"
}
  • 실행
#
terraform init -upgrade
terraform apply -auto-approve
var.my_password
  Enter a value: qwe123
...

# 확인
terraform state list
terraform state show local_file.abc
cat abc.txt ; echo

# 해당 파일에 다른 내용으로 변경해보기
terraform apply -auto-approve
var.my_password
  Enter a value: t101mypss
...

# 확인
cat abc.txt ; echo

  • Terraform Apply 를 통해 password 변경


민감한 변수 취급
입력 변수의 민감 여부 선언 가능

  • main.tf 코드 파일 내용 수정
    • 기본값 추가로 입력 항목은 발생하지 않지만, 출력에서 참조되는 변수 값이(sensitive)로 감춰지는 것을 확인 할 수 있다
variable "my_password" {
  default   = "password"
  sensitive = true
}

resource "local_file" "abc" {
  content  = var.my_password
  filename = "${path.module}/abc.txt"
}
  • 민감한 정보라 출력 내용에는 안보입니다.

  • 확인 : 민감한 변수로 지정해도 terraform.tfstate 파일에는 결과물이 평문으로 기록되므로 State 파일의 보안에 유의
 cat terraform.tfstate | grep '"content":'
            "content": "password",

변수 입력 방식과 우선순위

  • variable의 목적은 코드 내용을 수정하지 않고 테라폼의 모듈적 특성을 통해 입력되는 변수로 재사용성을 높이는 데 있다.
  • 특히 입력 변수라는 명칭에 맞게 사용자는 프로비저닝 실행 시에 원하는 값으로 변수에 정의할 수 있다.
  • 선언되는 방식에 따라 변수의 우선순위가 있으므로, 이를 적절히 사용해 로컬 환경과 빌드 서버 환경에서의 정의를 다르게 하거나, 프로비저닝 파이프라인을 구성하는 경우 외부 값을 변수에 지정할 수 있다.
  • main.tf 코드 파일 내용 수정
variable "my_var" {}

resource "local_file" "abc" {
  content  = var.my_var
  filename = "${path.module}/abc.txt"
}
  • [우선순위 수준]의 숫자가 작을수록 우선순위도 낮습니다.

[우선순위 수준 1] 실행 후 입력

# 실행
terraform apply -auto-approve
var.my_var
  Enter a value: var1
...

# 확인
terraform state show local_file.abc
cat abc.txt ; echo

[우선순위 수준 2] variable 블록의 default 값

  • main.tf 코드 파일 내용 수정
variable "my_var" {
  default = "var2"
}

resource "local_file" "abc" {
  content  = var.my_var
  filename = "${path.module}/abc.txt"
}
  • 확인
# 실행
terraform apply -auto-approve

# 확인
terraform state show local_file.abc
cat abc.txt ; echo

[우선순위 수준 3] 환경 변수 (TF_VAR 변수 이름)

  • 시스템 환경 변수의 접두사에 TFVAR 가 포함되면 그 뒤의 문자열을 변수 이름으로 인식한다.
  • 앞서 default로 추가한 내용과 어떤 방식이 우선순위가 높은지 확인해보자
# Linux/macOS
export TF_VAR_my_var=var3
terraform apply -auto-approve

# 확인
cat abc.txt ; echo

[우선순위 수준 4] terraform.tfvars에 정의된 변수 선언

  • 루트 모듈의 main.tf 파일과 같은 위치에 terraform.tfvars 파일을 생성해 변수에 대한 값을 추가하고 앞서 선언한 변수 선언과 비교해 우선순위를 확인
#
echo 'my_var="var4"' > terraform.tfvars
cat terraform.tfvars

#
terraform apply -auto-approve

# 확인
cat abc.txt ; echo

[우선순위 수준 5] *.auto.tfvars에 정의된 변수 선언

  • 파일명의 정렬에 따라 우선순위가 적용된다
# a.auto.tfvars 파일 생성
echo 'my_var="var5_a"' > a.auto.tfvars
ls *.tfvars


terraform apply -auto-approve

# 확인
cat abc.txt ; echo

# b.auto.tfvars 파일 생성
echo 'my_var="var5_b"' > b.auto.tfvars
ls *.tfvars

#
terraform apply -auto-approve

# 확인
cat abc.txt ; echo

[우선순위 수준 6] *.auto.tfvars.json에 정의된 변수 선언

  • *.auto.tfvars와 같이 파일명의 정렬에 따라 우선순위가 적용된다
# a.auto.tfvars.json 파일 생성
cat <<EOF > a.auto.tfvars.json
{
  "my_var" : "var6_a"
}
EOF
ls *.tfvars ; ls *.json

#
terraform apply -auto-approve

# 확인
cat abc.txt ; echo

# c.auto.tfvars.json 파일 생성
cat <<EOF > c.auto.tfvars.json
{
  "my_var" : "var6_c"
}
EOF
ls *.tfvars ; ls *.json

#
terraform apply -auto-approve

# 확인
cat abc.txt ; echo

[우선순위 수준 7] CLI 실행 시 -var 인수에 지정 또는 -var-file로 파일 지정

  • 여러 인수가 선언되는 경우 나중에 선언된 변수의 우선순위가 높다
#
terraform apply -auto-approve -var=my_var=var7
cat abc.txt ; echo

#
terraform apply -auto-approve -var=my_var=var7 -var=my_var=var8
cat abc.txt ; echo
  • *.tfvars와 같은 형식의 내용의 파일이라면 -var-file로 지정할 수 있다.
# var9.txt 파일 생성
echo 'my_var="var9"' > var9.txt

#
terraform apply -auto-approve -var=my_var=var7 -var-file="var9.txt"
cat abc.txt ; echo

output 선언
출력 값은 주로 테라폼 코드의 프로비저닝 수행 후의 결과 속성 값을 확인하는 용도로 사용

  • 모듈 내에서 생성되는 속성 값들은 output 블록에 정의된다
output "instance_ip_addr" {
  value = "http://${aws_instance.server.private_ip}"
}
  • 출력되는 값은 value의 값이며 테라폼이 제공하는 조합과 프로그래밍적인 기능들에 의해 원하는 값을 출력할 수 있다.
  • 주의할 점은 output 결과에서 리소스 생성 후 결정되는 속성 값은 프로비저닝이 완료되어야 최종적으로 결과를 확인할 수 있고 terraform plan 단계에서는 적용될 값이 출력하지 않는다는 것이다.
  • 변수 정의 시 사용 가능한 메타인수는 다음과 같다.
    • description : 출력 값 설명
    • sensitive : 민감한 출력 값임을 알리고 테라폼의 출력문에서 값 노출을 제한
    • depends_on : value에 담길 값이 특정 구성에 종속성이 있는 경우 생성되는 순서를 임의로 조정
    • precondition : 출력 전에 지정된 조건을 검증

output 활용

  • main.tf 파일 내용
    • (참고) abspath : 파일 시스템 경로를 포함하는 문자열을 가져와 절대 경로로 변환하는 함수 - 링크
resource "local_file" "abc" {
  content  = "abc123"
  filename = "${path.module}/abc.txt"
}

output "file_id" {
  value = local_file.abc.id
}

output "file_abspath" {
  value = abspath(local_file.abc.filename)
}
  • 실행 : plan 실행 시, 이미 정해진 속성은 출력을 예측하지만 아직 생성되지 않은 file_id 값은 값의 경우는 결과 예측을 할 수 없다

# plan 실행 시, 이미 정해진 속성은 출력을 예측하지만
terraform init && terraform plan
...
Changes to Outputs:
  + file_abspath = "/Users/gasida/Downloads/workspaces/3.8/abc.txt"
  + file_id      = (known after apply)


# 
terraform apply -auto-approve
...
Outputs:
file_abspath = "/Users/gasida/Downloads/workspaces/3.8/abc.txt"
file_id = "6367c48dd193d56ea7b0baad25b19455e529f5ee"

# graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph > graph.dot

#
terraform state list
terraform outputfile_abspath = "/Users/leeeuijoo/Downloads/playground/euijoo/learn-terraform/6/abc.txt"
file_id = "6367c48dd193d56ea7b0baad25b19455e529f5ee"
  • Terraform Console 확인
> local_file.abc
{
  "content" = "abc123"
  "content_base64" = tostring(null)
  "content_base64sha256" = "bKE9UspwyIPg8LsQHkJaiehiTeUdstI5JZOvaoQRgJA="
  "content_base64sha512" = "xwtd2ev7b1HQnUEytxcMnSB1CnhS8AaA9lZY8DEOgQBW5nY8NMmgCw6UAHb1RJXBafwjAszrMSA5JxxDRpUH3A=="
  "content_md5" = "e99a18c428cb38d5f260853678922e03"
  "content_sha1" = "6367c48dd193d56ea7b0baad25b19455e529f5ee"
  "content_sha256" = "6ca13d52ca70c883e0f0bb101e425a89e8624de51db2d2392593af6a84118090"
  "content_sha512" = "c70b5dd9ebfb6f51d09d4132b7170c9d20750a7852f00680f65658f0310e810056e6763c34c9a00b0e940076f54495c169fc2302cceb312039271c43469507dc"
  "directory_permission" = "0777"
  "file_permission" = "0777"
  "filename" = "./abc.txt"
  "id" = "6367c48dd193d56ea7b0baad25b19455e529f5ee"
  "sensitive_content" = (sensitive value)
  "source" = tostring(null)
}
> local_file.abc.id
"6367c48dd193d56ea7b0baad25b19455e529f5ee"

2. VPC 생성 실습

Goal : default VPC 대신 직접 VPC를 만들고, 해당 VPC내에 EC2 1대를 배포

  • VPC 배포 : vpc.tf
# 신규 디렉터리 생성
mkdir my-vpc-ec2
cd my-vpc-ec2
  • vpc.tf 파일 생성
provider "aws" {
  region  = "ap-northeast-2"
  profile = "ejl-personal"
}

resource "aws_vpc" "myvpc" {
  cidr_block       = "10.10.0.0/16"

  tags = {
    Name = "aews-study"
  }
}
  • deploy
# 배포
terraform init && terraform plan && terraform apply -auto-approve
terraform state list
aws_vpc.myvpc
terraform state show aws_vpc.myvpc
# aws_vpc.myvpc:
resource "aws_vpc" "myvpc" {
    arn                                  = "arn:aws:ec2:ap-northeast-2:236747833953:vpc/vpc-0449975439109dbbe"
    assign_generated_ipv6_cidr_block     = false
    cidr_block                           = "10.10.0.0/16"
    default_network_acl_id               = "acl-0aaed6758221148c1"
    default_route_table_id               = "rtb-08199dc361c392daf"
    default_security_group_id            = "sg-0e10474b7c1852e87"
    dhcp_options_id                      = "dopt-0d9ce164026238cd5"
    enable_dns_hostnames                 = false
    enable_dns_support                   = true
    enable_network_address_usage_metrics = false
    id                                   = "vpc-0449975439109dbbe"
    instance_tenancy                     = "default"
    ipv6_association_id                  = null
    ipv6_cidr_block                      = null
    ipv6_cidr_block_network_border_group = null
    ipv6_ipam_pool_id                    = null
    ipv6_netmask_length                  = 0
    main_route_table_id                  = "rtb-08199dc361c392daf"
    owner_id                             = "236747833953"
    tags                                 = {
        "Name" = "aews-study"
    }
    tags_all                             = {
        "Name" = "aews-study"
    }
}

# VPC 확인
export AWS_PAGER=""
aws ec2 describe-vpcs | jq
{
  "Vpcs": [
    {
      "CidrBlock": "10.10.0.0/16",
      "DhcpOptionsId": "dopt-0d9ce164026238cd5",
      "State": "available",
      "VpcId": "vpc-0449975439109dbbe",
      "OwnerId": "236747833953",
      "InstanceTenancy": "default",
      "CidrBlockAssociationSet": [
        {
          "AssociationId": "vpc-cidr-assoc-0c088ee35c010c86d",
          "CidrBlock": "10.10.0.0/16",
          "CidrBlockState": {
            "State": "associated"
          }
        }
      ],
      "IsDefault": false,
      "Tags": [
        {
          "Key": "Name",
          "Value": "aews-study"
        }
      ]
    },
    {
      "CidrBlock": "172.31.0.0/16",
      "DhcpOptionsId": "dopt-0d9ce164026238cd5",
      "State": "available",
      "VpcId": "vpc-05d7198a174e03c3b",
      "OwnerId": "236747833953",
      "InstanceTenancy": "default",
      "CidrBlockAssociationSet": [
        {
          "AssociationId": "vpc-cidr-assoc-093061c3a95ffa41a",
          "CidrBlock": "172.31.0.0/16",
          "CidrBlockState": {
            "State": "associated"
          }
        }
      ],
      "IsDefault": true
    },
    {
      "CidrBlock": "172.16.0.0/16",
      "DhcpOptionsId": "dopt-0d9ce164026238cd5",
      "State": "available",
      "VpcId": "vpc-0fe3665371c65b1ad",
      "OwnerId": "236747833953",
      "InstanceTenancy": "default",
      "CidrBlockAssociationSet": [
        {
          "AssociationId": "vpc-cidr-assoc-0ebe8b3ad61284dfb",
          "CidrBlock": "172.16.0.0/16",
          "CidrBlockState": {
            "State": "associated"
          }
        }
      ],
      "IsDefault": false,
      "Tags": [
        {
          "Key": "Name",
          "Value": "euijoo-vpc"
        }
      ]
    }
  ]
}

aws ec2 describe-vpcs --filter 'Name=isDefault,Values=false' | jq
aws ec2 describe-vpcs --filter 'Name=isDefault,Values=false' --output yaml

  • AWS 관리콘솔에서 VPC 생성 정보 확인 : DNS 옵션값 확인
  • vpc.tf 코드 내용 수정 : VPC DNS 옵션 수정
provider "aws" {
  region  = "ap-northeast-2"
}

resource "aws_vpc" "myvpc" {
  cidr_block       = "10.10.0.0/16"
  enable_dns_support   = true  # Default true 임
  enable_dns_hostnames = true

  tags = {
    Name = "aews-study"
  }
}
  • 배포
terraform plan && terraform apply -auto-approve

  • DNS hostnames : enabled 확인

  • vpc.tf 코드 내용 수정 : 서브넷 2개 생성 추가
provider "aws" {
  region  = "ap-northeast-2"
}

resource "aws_vpc" "myvpc" {
  cidr_block       = "10.10.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "aews-study"
  }
}

resource "aws_subnet" "mysubnet1" {
  vpc_id     = aws_vpc.myvpc.id
  cidr_block = "10.10.1.0/24"

  availability_zone = "ap-northeast-2a"

  tags = {
    Name = "t101-subnet1"
  }
}

resource "aws_subnet" "mysubnet2" {
  vpc_id     = aws_vpc.myvpc.id
  cidr_block = "10.10.2.0/24"

  availability_zone = "ap-northeast-2c"

  tags = {
    Name = "aews-subnet2"
  }
}

output "aws_vpc_id" {
  value = aws_vpc.myvpc.id
}
  • 배포
# 배포
terraform plan && terraform apply -auto-approve
terraform state list
aws_subnet.mysubnet1
aws_subnet.mysubnet2
aws_vpc.myvpc

terraform state show aws_subnet.mysubnet1

terraform output
aws_vpc_id = "vpc-0449975439109dbbe"
terraform output aws_vpc_id
terraform output -raw aws_vpc_id

# graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph > graph.dot

# 서브넷 확인
aws ec2 describe-subnets --output text

# 참고 : aws ec2 describe-subnets --filters "Name=vpc-id,Values=vpc-<자신의 VPC ID>"
VPCID=$(terraform output -raw aws_vpc_id)
aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPCID" | jq
aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPCID" --output table

  • dot 확인

  • 콘솔에서 서브넷 생성 확인

  • vpc.tf 코드 내용 수정 : IGW 인터넷 게이트웨이 추가
provider "aws" {
  region  = "ap-northeast-2"
}

resource "aws_vpc" "myvpc" {
  cidr_block       = "10.10.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "t101-study"
  }
}

resource "aws_subnet" "mysubnet1" {
  vpc_id     = aws_vpc.myvpc.id
  cidr_block = "10.10.1.0/24"

  availability_zone = "ap-northeast-2a"

  tags = {
    Name = "t101-subnet1"
  }
}

resource "aws_subnet" "mysubnet2" {
  vpc_id     = aws_vpc.myvpc.id
  cidr_block = "10.10.2.0/24"

  availability_zone = "ap-northeast-2c"

  tags = {
    Name = "t101-subnet2"
  }
}


resource "aws_internet_gateway" "myigw" {
  vpc_id = aws_vpc.myvpc.id

  tags = {
    Name = "t101-igw"
  }
}

output "aws_vpc_id" {
  value = aws_vpc.myvpc.id
}
  • 배포
# 배포
terraform plan && terraform apply -auto-approve
terraform state list
aws_internet_gateway.myigw
aws_subnet.mysubnet1
aws_subnet.mysubnet2
aws_vpc.myvpc

  • vpc.tf 코드 내용 수정 : IGW 인터넷 게이트웨이로 전달하는 디폴트 라우팅 정보 추가
provider "aws" {
  region  = "ap-northeast-2"
}

resource "aws_vpc" "myvpc" {
  cidr_block       = "10.10.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "t101-study"
  }
}

resource "aws_subnet" "mysubnet1" {
  vpc_id     = aws_vpc.myvpc.id
  cidr_block = "10.10.1.0/24"

  availability_zone = "ap-northeast-2a"

  tags = {
    Name = "t101-subnet1"
  }
}

resource "aws_subnet" "mysubnet2" {
  vpc_id     = aws_vpc.myvpc.id
  cidr_block = "10.10.2.0/24"

  availability_zone = "ap-northeast-2c"

  tags = {
    Name = "t101-subnet2"
  }
}


resource "aws_internet_gateway" "myigw" {
  vpc_id = aws_vpc.myvpc.id

  tags = {
    Name = "t101-igw"
  }
}

resource "aws_route_table" "myrt" {
  vpc_id = aws_vpc.myvpc.id

  tags = {
    Name = "t101-rt"
  }
}

resource "aws_route_table_association" "myrtassociation1" {
  subnet_id      = aws_subnet.mysubnet1.id
  route_table_id = aws_route_table.myrt.id
}

resource "aws_route_table_association" "myrtassociation2" {
  subnet_id      = aws_subnet.mysubnet2.id
  route_table_id = aws_route_table.myrt.id
}

resource "aws_route" "mydefaultroute" {
  route_table_id         = aws_route_table.myrt.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.myigw.id
}

output "aws_vpc_id" {
  value = aws_vpc.myvpc.id
}
  • 배포
# 배포
terraform plan && terraform apply -auto-approve
terraform state list
aws_internet_gateway.myigw
aws_route.mydefaultroute
aws_route_table.myrt
aws_route_table_association.myrtassociation1
aws_route_table_association.myrtassociation2
aws_subnet.mysubnet1
aws_subnet.mysubnet2
aws_vpc.myvpc

terraform state show aws_route.mydefaultroute

# graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph > graph.dot

# 라우팅 테이블 확인
#aws ec2 describe-route-tables --filters 'Name=tag:Name,Values=t101-rt' --query 'RouteTables[].Associations[].SubnetId'
aws ec2 describe-route-tables --filters 'Name=tag:Name,Values=t101-rt' --output table

  • Dot 정보


SG, EC2 배포

  • sg.tf 파일 생성 : 보안그룹 생성
resource "aws_security_group" "mysg" {
  vpc_id      = aws_vpc.myvpc.id
  name        = "T101 SG"
  description = "T101 Study SG"
}

resource "aws_security_group_rule" "mysginbound" {
  type              = "ingress"
  from_port         = 80
  to_port           = 80
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.mysg.id
}

resource "aws_security_group_rule" "mysgoutbound" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.mysg.id
}
  • 보안그룹 배포
# 배포
ls *.tf
terraform plan && terraform apply -auto-approve
terraform state list
aws_internet_gateway.myigw
aws_route.mydefaultroute
aws_route_table.myrt
aws_route_table_association.myrtassociation1
aws_route_table_association.myrtassociation2
aws_security_group.mysg
aws_security_group_rule.mysginbound
aws_security_group_rule.mysgoutbound
aws_subnet.mysubnet1
aws_subnet.mysubnet2
aws_vpc.myvpc
...

terraform state show aws_security_group.mysg
# aws_security_group.mysg:
resource "aws_security_group" "mysg" {
    arn                    = "arn:aws:ec2:ap-northeast-2:236747833953:security-group/sg-04d1521da7cb57363"
    description            = "T101 Study SG"
    egress                 = []
    id                     = "sg-04d1521da7cb57363"
    ingress                = []
    name                   = "T101 SG"
    name_prefix            = null
    owner_id               = "236747833953"
    revoke_rules_on_delete = false
    tags_all               = {}
    vpc_id                 = "vpc-0449975439109dbbe"
}


    ~/Dow/playground/euijoo/learn-terraform/my-vpc-ec2  terraform state show aws_security_group_rule.mysginbound                                                               ✔  ejl-personal ap-northeast-2   15:55:30  
# aws_security_group_rule.mysginbound:
resource "aws_security_group_rule" "mysginbound" {
    cidr_blocks            = [
        "0.0.0.0/0",
    ]
    from_port              = 80
    id                     = "sgrule-3070248179"
    protocol               = "tcp"
    security_group_id      = "sg-04d1521da7cb57363"
    security_group_rule_id = "sgr-0e64cf542b1ec3b4d"
    self                   = false
    to_port                = 80
    type                   = "ingress"
}
    ~/Dow/playground/euijoo/learn-terraform/my-vpc-ec2  terraform state show aws_security_group_rule.mysgoutbound                                                              ✔  ejl-personal ap-northeast-2   15:55:55  
# aws_security_group_rule.mysgoutbound:
resource "aws_security_group_rule" "mysgoutbound" {
    cidr_blocks            = [
        "0.0.0.0/0",
    ]
    from_port              = 0
    id                     = "sgrule-2880335876"
    protocol               = "-1"
    security_group_id      = "sg-04d1521da7cb57363"
    security_group_rule_id = "sgr-0faa376cedd84cc61"
    self                   = false
    to_port                = 0
    type                   = "egress"
}

# graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph > graph.dot
  • Console 확인

  • Dot 확인

  • ec2.tf 파일 생성 : EC2 생성
data "aws_ami" "my_amazonlinux2" {
  most_recent = true
  filter {
    name   = "owner-alias"
    values = ["amazon"]
  }

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-ebs"]
  }

  owners = ["amazon"]
}

resource "aws_instance" "myec2" {

  depends_on = [
    aws_internet_gateway.myigw
  ]

  ami                         = data.aws_ami.my_amazonlinux2.id
  associate_public_ip_address = true
  instance_type               = "t2.micro"
  vpc_security_group_ids      = ["${aws_security_group.mysg.id}"]
  subnet_id                   = aws_subnet.mysubnet1.id

  user_data = <<-EOF
              #!/bin/bash
              wget https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64
              mv busybox-x86_64 busybox
              chmod +x busybox
              echo "Web Server</h1>" > index.html
              nohup ./busybox httpd -f -p 80 &
              EOF

  user_data_replace_on_change = true

  tags = {
    Name = "t101-myec2"
  }
}

output "myec2_public_ip" {
  value       = aws_instance.myec2.public_ip
  description = "The public IP of the Instance"
}
  • 배포
# 
ls *.tf
terraform plan && terraform apply -auto-approve
terraform state list
data.aws_ami.my_amazonlinux2
aws_instance.myec2
aws_internet_gateway.myigw
aws_route.mydefaultroute
aws_route_table.myrt
aws_route_table_association.myrtassociation1
aws_route_table_association.myrtassociation2
aws_security_group.mysg
aws_security_group_rule.mysginbound
aws_security_group_rule.mysgoutbound
aws_subnet.mysubnet1
aws_subnet.mysubnet2
aws_vpc.myvpc
...

terraform state show data.aws_ami.my_amazonlinux2
terraform state show aws_instance.myec2

# 데이터소스 값 확인
terraform console
> 
data.aws_ami.my_amazonlinux2.id
"ami-01c81850a6167bb81"
data.aws_ami.my_amazonlinux2.image_id
data.aws_ami.my_amazonlinux2.name
data.aws_ami.my_amazonlinux2.owners
data.aws_ami.my_amazonlinux2.platform_details
data.aws_ami.my_amazonlinux2.hypervisor
data.aws_ami.my_amazonlinux2.architecture
exit

# graph 확인 > graph.dot 파일 선택 후 오른쪽 상단 DOT 클릭
terraform graph > graph.dot

# 출력된 EC2 퍼블릭IP로 cul 접속 확인
terraform output -raw myec2_public_ip
52.79.154.3

MYIP=$(terraform output -raw myec2_public_ip)
while true; do curl --connect-timeout 1  http://$MYIP/ ; echo "------------------------------"; date; sleep 1; done
  • Console 확인

  • Dot 확인

  • 리소스 삭제 : terraform destroy -auto-approve


프로바이더 소개


테라폼 레지스트리의 프로바이더 목록에는 유지 보수 및 게시에 대한 권한에 따라 Tier 정보가 제공

  • 지정하는 프로바이더의 요구사항을 정의 : 레지스트리의 Overview 항목 오른쪽에 있는 [USE PROVIDER] 클릭해 현재 버전의 정의 방법을 확인

로컬 이름과 프로바이더 지정

  • terraform 블록의 required_providers 블록 내에 <로컬 이름> = { } 으로 여러 개의 프로바이더를 정의할 수 있다. 여기서 사용되는 로컬 이름은 테라폼 모듈 내에서 고유해야 한다. 로컬 이름과 리소스 접두사는 독립적으로 선언되며, 각 프로바이더의 소스 경로가 지정되면 프로바이더의 고유 접두사가 제공된다. 만약 동일한 접두사를 사용하는 프로바이더가 선언되는 경우 로컬 이름을 달리해 관련 리소스에서 어떤 프로바이더를 사용하는지 명시적으로 지정할 수 있다.
  • 예를 들어 다음의 main.tf에서처럼 동일한 http 이름을 사용하는 다수의 프로바이더가 있는 경우 각 프로바이더에 고유한 이름을 부여하고 리소스와 데이터 소스에 어떤 프로바이더를 사용할지 provider 인수에 명시한다. 단, 동일한 source에 대해 다수의 정의는 불가능한다.
  • 동일한 http 접두사를 사용하는 다수의 프로바이더 사용 정의
    terraform {
      required_providers {
        architech-http = {
          source = "architect-team/**http**"
          version = "~> 3.0"
        }
        http = {
          source = "hashicorp/**http**"
        }
        aws-http = {
          source = "terraform-aws-modules/**http**"
        }
      }
    }
    
    data "http" "example" {
      provider = aws-http
      url = "https://checkpoint-api.hashicorp.com/v1/check/terraform"
    
      request_headers = {
        Accept = "application/json"
      }
    }

단일 프로바이더의 다중 정의

  • 동일한 프로바이더를 사용하지만 다른 조건을 갖는 경우, 사용되는 리소스마다 별도로 선언된 프로바이더를 지정해야 하는 경우가 있다. 예를 들면, AWS 프로바이더를 사용하는데 서로 다른 권한의 IAM을 갖는 Access ID 또는 대상 리전을 지정해야 하는 경우다. 이때는 프로바이더 선언에서 alias를 명시하고 사용하는 리소스와 데이터 소스에서는 provider 메타인수를 사용해 특정 프로바이더를 지정할 수 있다. provider 메타인수에 지정되지 않은 경우 alias가 없는 프로바이더가 기본 프로바이더로 동작한다.

  • 실습을 위해서 4.1 디렉터리를 신규 생성 후 열기 → main.tf 파일 생성

    mkdir 4.1 && cd 4.1
  • 아래 예제는 리전을 다르게 구성한 AWS 프로바이더를 aws_instance에 지정하는 방법이다

    provider "aws" {
      region = "ap-southeast-1"
    }
    
    provider "aws" {
      alias = "seoul"
      region = "ap-northeast-2"
    }
    
    resource "aws_instance" "app_server1" {
      ami           = "ami-06b79cf2aee0d5c92"
      instance_type = "t2.micro"
    }
    
    resource "aws_instance" "app_server2" {
      provider      = aws.seoul
      ami           = "ami-0ea4d4b8dc1e46212"
      instance_type = "t2.micro"
    }
  • 실행

    #
    terraform init && terraform plan
    terraform apply -auto-approve
    
    #
    terraform state list
    echo "aws_instance.app_server1.public_ip" | terraform console
    echo "aws_instance.app_server2.public_ip" | terraform console
    aws ec2 describe-instances --filters Name=instance-state-name,Values=running --output table
    aws ec2 describe-instances --filters Name=instance-state-name,Values=running --output table --region ap-southeast-1
    
    # 삭제
    terraform destroy -auto-approve

프로바이더 요구사항 정의

  • 테라폼 실행 시 요구되는 프로바이더 요구사항은 terraform 블록의 required_providers 블록에 여러 개를 정의할 수 있다. source에는 프로바이더 다운로드 경로를 지정하고 version은 버전 제약을 명시한다.

  • 프로바이더 요구사항 정의 블록

    terraform {
      required_providers {
        <프로바이더 로컬 이름> = {
          source = [<호스트 주소>/]<네임스페이스>/<유형>
          version = <버전 제약>
        }
        ...
      }
    }
    • 호스트 주소 : 프로바이더를 배포하는 주소로서 기본값은 registry.terraform.io
    • 네임스페이스 : 지정된 레지스트리 내에서 구분하는 네임스페이스로, 공개된 레지스트리 및 Terraform Cloud의 비공개 레지스트리의 프로바이더를 게시하는 조직을 의미
    • 유형 : 프로바이더에서 관리되는 플랫폼이나 서비스 이름으로 일반적으로 접두사와 일치하나 일부 예외가 있을 수 있음
  • 프로바이더는 기능이나 조건이 시간이 지남에 따라 변경될 수 있다. 이 같은 변경에 특정 버전을 명시하거나 버전 호환성을 정의할 때, version에 명시할 수 있다. 이 값이 생략되는 경우 terraform init을 하는 당시의 가장 최신 버전으로 선택된다. 버전 상세 내용은 3.3장 버전 설정 확인


3. Terraform Module - 전략입니다.

  • 테라폼으로 인프라와 서비스를 관리하면 시간이 지날수록 구성이 복잡해지고 관리하는 리소스가 늘어나게 된다. 테라폼의 구성 파일과 디렉터리 구성에는 제약이 없기 때문에 단일 파일 구조상에서 지속적으로 업데이트할 수 있지만, 다음과 같은 문제가 발생한다.

    • 테라폼 구성에서 원하는 항목을 찾고 수정하는 것이 점점 어려워짐
    • 리소스들 간의 연관 관계가 복잡해질수록 변경 작업의 영향도를 분석하기 위한 노력이 늘어남
    • 개발/스테이징/프로덕션 환경으로 구분된 경우 비슷한 형태의 구성이 반복되어 업무 효율이 줄어듦
    • 새로운 프로젝트를 구성하는 경우 기존 구성에서 취해야 할 리소스 구성과 종속성 파악이 어려움
  • 모듈은 루트 모듈과 자식 모듈로 구분된다

    • 루트 모듈 Root Module : 테라폼을 실행하고 프로비저닝하는 최상위 모듈
    • 자식 모듈 Chile Module : 루트 모듈의 구성에서 호출되는 외부 구성 집합
  • 모듈은 테라폼 구성의 집합이다. 테라폼으로 관리하는 대상의 규모가 커지고 복잡해져 생긴 문제를 보완하고 관리 작업을 수월하게 하기 위한 방안으로 활용

    • 관리성 : 모듈은 서로 연관 있은 구성의 묶음이다. 원하는 구성요소를 단위별로 쉽게 찾고 업데이트할 수 있다. 모듈은 다른 구성에서 쉽게 하나의 덩어리로 추가하거나 삭제할 수 있다. 또한 모듈이 업데이트되면 이 모듈을 사용하는 모든 구성에서 일관된 변경 작업을 진행할 수 있다.
    • 캡슐화 : 테라폼 구성 내에서 각 모듈은 논리적으로 묶여져 독립적으로 프로비저닝 및 관리되며, 그 결과는 은닉성을 갖춰 필요한 항목만을 외부에 노출시킨다.
    • 재사용성 : 구성을 처음부터 작성하는 것에는 시간과 노력이 필요하고 작성 중간에 디버깅과 오류를 수정하는 반복 작업이 발생한다. 테라폼 구성을 모듈화하면 이후에 비슷한 프로비저닝에 이미 검증된 구성을 바로 사용할 수 있다.
    • 일관성표준화 : 테라폼 구성 시 모듈을 활용하는 워크플로는 구성의 일관성을 제공하고 서로 다른 환경과 프로젝트에도 이미 검증한 모듈을 적용해 복잡한 구성과 보안 사고를 방지할 수 있다.

Module 작성 기본 원칙
모듈 작성 기본 원칙 : 모듈은 대부분의 프로그래밍 언어에서 쓰이는 라이브러리나 패키지와 역할이 비슷하다

아래와 같은 기본 작성 원칙을 제안함

  • 모듈 디렉터리 형식을 terraform-<프로바이더 이름>-<모듈 이름> 형식을 제안한다. 이 형식은 Terraform Cloud, Terraform Enterprise에서도 사용되는 방식으로 1) 디렉터리 또는 레지스트리 이름이 테라폼을 위한 것이고, 2) 어떤 프로바이더의 리소스를 포함하고 있으며, 3) 부여된 이름이 무엇인지 판별할 수 있도록 한다.

  • 테라폼 구성은 궁극적으로 모듈화가 가능한 구조로 작성할 것을 제안한다. 처음부터 모듈화를 가정하고 구성파일을 작성하면 단일 루트 모듈이라도 후에 다른 모듈이 호출할 것을 예상하고 구조화할 수 있다. 또한 작성자는 의도한 리소스 묶음을 구상한 대로 논리적인 구조로 그룹화할 수 있다.

  • 각각의 모듈을 독립적으로 관리하기를 제안한다. 리모트 모듈을 사용하지 않더라도 처음부터 모듈화가 진행된 구성들은 떄로 루트 모듈의 하위 파일 시스템에 존재하는 경우가 있다. 하위 모듈 또한 독립적인 모듈이므로 루트 모듈 하위에 두기보다는 동일한 파일 시스템 레벨에 위치하거나 별도 모듈만을 위한 공간에서 불러오는 것을 권장한다. 이렇게 하면 VCS를 통해 관리하기가 더 수월하다.

  • 공개된 테라폼 레지스트리의 모듈을 참고하기를 제안한다. 대다수의 테라폼 모듈은 공개된 모듈이 존재하고 거의 모든 인수에 대한 변수 처리, 반복문 적용 리소스, 조건에 따른 리소스 활성/비활성 등을 모범 사례로 공개해두었다. 물론 그대로 가져다 사용하는 것보다는 프로비저닝하려는 상황에 맞게 참고하는 것을 권장한다.

  • 작성된 모듈은 공개 또는 비공개로 게시해 팀 또는 커뮤니티와 공유하기를 제안한다. 모듈의 사용성을 높이고 피드백을 통해 더 발전된 모듈을 구성할 수 있는 자극이 된다.

  • 모듈을 독립적으로 관리하기 위해 디렉터리 구조를 생성할 때 모듈을 위한 별도 공간을 생성하는 방식으로 진행한다. 특정 루트 모듈 하위에 자식 모듈을 구성하는 경우 단순히 복잡한 코드를 분리하는 요도로 명시되며 종속성이 발생하므로 루트 모듈 사이에 모듈 디렉터리를 지정한다. 아래 구성의 예.


모듈화

  • 모듈의 기본적 구조는 테라폼 구성으로 입력 변수를 구성하고 결과를 출력하기 위한 구조로 구성한다.

  • 모듈화’라는 용어는 이런 구조재활용하기 위한 템플릿 작업을 말한다.

  • 애플리케이션 개발시에도 자주 사용되는 용어로 테라폼은 작성된 모듈을 다른 루트 모듈에서 가져다 사용하며 이를 통해 재사용성과 표준화 구조를 구성할 수 있다.

  • 기존에 작성된 모듈은 다른 모듈에서 참조해 사용할 수 있다. 사용 방식은 리소스와 비슷하다.

  • 모듈에서 필요한 값은 variable로 선언해 설정하고, 모듈에서 생성된 값 중 외부 모듈에서 참조하고 싶은 값은 output으로 설정한다. 마치 자바 개발 시 getter, setter로 캡슐화된 클래스를 활용하는 것과 비슷한다.


모듈 작성 실습

  • 하나의 프로비저닝에서 사용자와 패스워드를 여러 번 구성해야 하는 경우를 가상의 시나리오로 삼아 모듈화를 진행해보겠다.
    • random_pet는 이름을 자동으로 생성하고, random_password는 사용자의 패스워드를 설정한다 - random_pet
    • random_password는 random 프로바이더 리소스로 난수 형태로 패스워드를 만들 수 있다.

자식 모듈 작성

  • 디렉터리 생성 및 06-module-traning/modules/terraform-random-pwgen/main.tf variable.tf output.tf 파일 생성
mkdir -p 06-module-traning/modules/terraform-random-pwgen

# main.tf
resource "random_pet" "name" {
  keepers = {
    ami_id = timestamp()
  }
}

resource "random_password" "password" {
  length           = var.isDB ? 16 : 10
  special          = var.isDB ? true : false
  override_special = "!#$%*?"
}

# variable.tf
variable "isDB" {
  type        = bool
  default     = false
  description = "패스워드 대상의 DB 여부"
}

# output.tf
output "id" {
  value = random_pet.name.id
}

output "pw" {
  value = nonsensitive(random_password.password.result) 
}

ls
main.tf     output.tf   variable.tf

  • 실행 : 자식 모듈 동작 테스트
#
cd 06-module-traning/modules/terraform-random-pwgen

#
ls *.tf
terraform init && terraform plan

# 테스트를 위해 apply 시 변수 지정
terraform apply -auto-approve -var=isDB=true
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
id = "knowing-aardvark"
pw = "Y5eeP0i2KLLE9gBa"

# 확인
terraform state list
terraform state show random_pet.name
terraform state show random_password.password

# tfstate에 모듈 정보 확인
cat terraform.tfstate | grep module

# graph 확인
terraform graph > graph.dot

  • dot 확인

자식 모듈 호출 실습

  • 다수의 리소스를 같은 목적으로 여러 번 반복해서 사용하려면 리소스 수만큼 반복해 구성 파일을 정의해야 하고 이름도 고유하게 설정해줘야 하는 부담이 있지만, 모듈을 활용하면 반복되는 리소스 묶음을 최소화할 수 있다.
  • 디렉터리 생성 및 06-module-traning/06-01-basic/main.tf 파일 생성
mkdir -p 06-module-traning/06-01-basic
touch main.tf

module "mypw1" {
  source = "../modules/terraform-random-pwgen"
}

module "mypw2" {
  source = "../modules/terraform-random-pwgen"
  isDB   = true
}

output "mypw1" {
  value  = module.mypw1
}

output "mypw2" {
  value  = module.mypw2
}
  • 실행 : 자식 모듈을 호출해 반복 재사용하는 루트 모듈의 결과
#
cd 06-module-traning/06-01-basic

#
terraform init && terraform plan && terraform apply -auto-approve
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
mypw1 = {
  "id" = "equipped-mustang"
  "pw" = "OXST1EYqQc"
}
mypw2 = {
  "id" = "diverse-impala"
  "pw" = "y8mEbOJhS6dCTiK#"
}

# 확인
terraform state list

# tfstate에 모듈 정보 확인
cat terraform.tfstate | grep module

# terraform init 시 생성되는 modules.json 파일 확인
tree .terraform
.terraform
├── modules
│   └── modules.json
...

## 모듈로 묶여진 리소스는 module이라는 정의를 통해 단순하게 재활용하고 반복 사용할 수 있다.
## 모듈의 결과 참조 형식은 module.<모듈 이름>.<output 이름>으로 정의된다.
cat .terraform/modules/modules.json | jq
{
  "Modules": [
    {
      "Key": "",
      "Source": "",
      "Dir": "."
    },
    {
      "Key": "mypw1",
      "Source": "../modules/terraform-random-pwgen",
      "Dir": "../modules/terraform-random-pwgen"
    },
    {
      "Key": "mypw2",
      "Source": "../modules/terraform-random-pwgen",
      "Dir": "../modules/terraform-random-pwgen"
    }
  ]
}

# graph 확인
terraform graph > graph.dot


모듈 소스 관리

  • Module 블록에 정의된 소스 구성으로 모듈의 코드 위치를 정의한다. terraform init을 수행할 때 지정된 모듈을 다운로드해 사용한다 - 링크
    • 로컬 디렉터리 경로
    • 테라폼 레지스트리 Terraform Registry
    • 깃허브 Github
    • 비트버킷 Bitbucket
    • 깃 Generic Git Repository
    • HTTP URLs
    • S3 Bucket
    • GCS Bucket google cloud storage
  • 테라폼 공식 문서 안내와 같이 루트 경로에 모듈을 배치하는 것 외에 패키지 하위 디렉터리 경로를 참조하는 것도 하나의 방안이다.

로컬 디렉터리 경로

  • 로컬 경로를 지정할 때는 테라폼 레지스트리와 구분하기 위해 하위 디렉터리는 ./로, 상위 디렉터리는 ../로 시작한다.
  • 대상 모듈은 이미 같은 로컬 파일 시스템에 존재하므로 다운로드 없이 바로 사용한다. 앞서 언급한 대로 재사용성이 고려된다면 상위 디렉터리에 별도 관리하는 것을 권장하고, 항상 루트 모듈과 함께 동작해야 하는 경우 하위 디렉터리에 모듈을 정의한다.
  • 코드 예시) 작업 환경을 기준으로 하는 로컬 경로 상위에 위치한 모듈 경로 선언
module "local_module" {
  source = "../modules/my_local_module"
}

테라폼 레지스트리

  • 테라폼 모듈 레지스트리는 테라폼의 프로토콜을 사용해 모듈을 사용하는 방식이다.
  • 공개된 테라폼 모듈을 사용하거나 Terraform Cloud, Terraform Enterprise에서 제공되는 비공개 테라폼 모듈을 사용할 때 설정하는 소스 지정 방식이다.
  • 공개된 모듈 - 링크 aws_vpc
  • 공개된 테라폼 모듈을 source에 선언할 때는 <네임스페이스>/<이름>/<프로바이더> 형태로 설정한다.
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.1.0"
}

aws vpc 모듈 실습

#
git clone https://github.com/terraform-aws-modules/terraform-aws-vpc/
tree terraform-aws-vpc/examples -L 1

terraform-aws-vpc/examples
├── complete
├── ipam
├── ipv6-dualstack
├── ipv6-only
├── issues
├── manage-default-vpc
├── network-acls
├── outpost
├── secondary-cidr-blocks
├── separate-route-tables
├── simple
└── vpc-flow-logs

tree terraform-aws-vpc/examples -L 2

terraform-aws-vpc/examples
├── complete
│   ├── README.md
│   ├── main.tf
│   ├── outputs.tf
│   ├── variables.tf
│   └── versions.tf
├── ipam
│   ├── README.md
│   ├── main.tf
│   ├── outputs.tf
│   ├── variables.tf
│   └── versions.tf
├── ipv6-dualstack
│   ├── README.md
│   ├── main.tf
│   ├── outputs.tf
│   ├── variables.tf
│   └── versions.tf
├── ipv6-only
│   ├── README.md
│   ├── main.tf
│   ├── outputs.tf
│   ├── variables.tf
│   └── versions.tf
├── issues
│   ├── README.md
│   ├── main.tf
│   ├── outputs.tf
│   ├── variables.tf
│   └── versions.tf
├── manage-default-vpc
│   ├── README.md
│   ├── main.tf
│   ├── outputs.tf
│   ├── variables.tf
│   └── versions.tf
├── network-acls
│   ├── README.md
│   ├── main.tf
│   ├── outputs.tf
│   ├── variables.tf
│   └── versions.tf
├── outpost
│   ├── README.md
│   ├── main.tf
│   ├── outputs.tf
│   ├── variables.tf
│   └── versions.tf
├── secondary-cidr-blocks
│   ├── README.md
│   ├── main.tf
│   ├── outputs.tf
│   ├── variables.tf
│   └── versions.tf
├── separate-route-tables
│   ├── README.md
│   ├── main.tf
│   ├── outputs.tf
│   ├── variables.tf
│   └── versions.tf
├── simple
│   ├── README.md
│   ├── main.tf
│   ├── outputs.tf
│   ├── variables.tf
│   └── versions.tf
└── vpc-flow-logs
    ├── README.md
    ├── main.tf
    ├── outputs.tf
    ├── variables.tf
    └── versions.tf
cd terraform-aws-vpc/examples/simple
ls *.tf
cat main.tf
provider "aws" {
  region = local.region
}

data "aws_availability_zones" "available" {}

locals {
  name   = "ex-${basename(path.cwd)}"
  region = "eu-west-1"

  vpc_cidr = "10.0.0.0/16"
  azs      = slice(data.aws_availability_zones.available.names, 0, 3)

  tags = {
    Example    = local.name
    GithubRepo = "terraform-aws-vpc"
    GithubOrg  = "terraform-aws-modules"
  }
}

################################################################################
# VPC Module
################################################################################

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

  name = local.name
  cidr = local.vpc_cidr

  azs             = local.azs
  private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)]

  tags = local.tags
}
  • 코드 수정 : 서울 리전 변경, main.tf 파일 내용 확인
# 서울 리전 변경
grep 'eu-west-1' main.tf
sed -i -e 's/eu-west-1/ap-northeast-2/g' main.tf

# VPC CIDR 변경
grep '10.0.0.0' main.tf
sed -i -e 's/10.0.0.0/10.10.0.0/g' main.tf

# main.tf 파일 내용 확인
cat main.tf
provider "aws" {
  region = local.region
}

data "aws_availability_zones" "available" {}

locals {
  name   = "ex-${basename(path.cwd)}"
  region = "ap-northeast-2"

  vpc_cidr = "10.10.0.0/16"
  azs      = slice(data.aws_availability_zones.available.names, 0, 3)

  tags = {
    Example    = local.name
    GithubRepo = "terraform-aws-vpc"
    GithubOrg  = "terraform-aws-modules"
  }
}

################################################################################
# VPC Module
################################################################################

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

  name = local.name
  cidr = local.vpc_cidr

  azs             = local.azs
  private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)]

  tags = local.tags
}

# 모듈 확인
tree ../../modules
../../modules
└── vpc-endpoints
    ├── README.md
    ├── main.tf
    ├── outputs.tf
    ├── variables.tf
    └── versions.tf

2 directories, 5 files
  • 배포
#
terraform init
cat .terraform/modules/modules.json| jq

{
  "Modules": [
    {
      "Key": "",
      "Source": "",
      "Dir": "."
    },
    {
      "Key": "vpc",
      "Source": "../..",
      "Dir": "../.."
    }
  ]
}

# 생성된 리소스들 확인해보자!
terraform apply -auto-approve
terraform output
terraform state list

data.aws_availability_zones.available
module.vpc.aws_default_network_acl.this[0]
module.vpc.aws_default_route_table.default[0]
module.vpc.aws_default_security_group.this[0]
module.vpc.aws_route_table.private[0]
module.vpc.aws_route_table.private[1]
module.vpc.aws_route_table.private[2]
module.vpc.aws_route_table_association.private[0]
module.vpc.aws_route_table_association.private[1]
module.vpc.aws_route_table_association.private[2]
module.vpc.aws_subnet.private[0]
module.vpc.aws_subnet.private[1]
module.vpc.aws_subnet.private[2]
module.vpc.aws_vpc.this[0]
# 실습 완료 후 리소스 삭제
terraform destroy -auto-approve


4. Terraform 으로 EKS 배포

첫 번째 EKS 클러스터 배포

# 코드 가져오기
git clone https://github.com/gasida/aews-cicd.git
cd aews-cicd/4

# terraform 환경 변수 저장
export TF_VAR_KeyName=[각자 ssh keypair]
export TF_VAR_KeyName='ejl-eks'
echo $TF_VAR_KeyName

# 
terraform init
terraform plan

# 10분 후 배포 완료
terraform apply -auto-approve

  • 주요 생성 자원 : VPC, Subnet, IGW, NATGW, Routing Table, EKS, EKS SG
  • Terraform 정보 확인
terraform state list
data.aws_caller_identity.current
data.aws_eks_cluster.cluster
data.aws_eks_cluster_auth.cluster
aws_iam_policy.external_dns_policy
aws_iam_role_policy_attachment.external_dns_policy_attach
aws_security_group.node_group_sg
kubernetes_service_account.aws_lb_controller
module.aws_load_balancer_controller_irsa_role.data.aws_caller_identity.current
module.aws_load_balancer_controller_irsa_role.data.aws_iam_policy_document.load_balancer_controller[0]
module.aws_load_balancer_controller_irsa_role.data.aws_iam_policy_document.this[0]
module.aws_load_balancer_controller_irsa_role.data.aws_partition.current
module.aws_load_balancer_controller_irsa_role.aws_iam_policy.load_balancer_controller[0]
module.aws_load_balancer_controller_irsa_role.aws_iam_role.this[0]
module.aws_load_balancer_controller_irsa_role.aws_iam_role_policy_attachment.load_balancer_controller[0]
module.eks.data.aws_caller_identity.current
module.eks.data.aws_eks_addon_version.this["coredns"]
module.eks.data.aws_eks_addon_version.this["kube-proxy"]
module.eks.data.aws_eks_addon_version.this["vpc-cni"]
module.eks.data.aws_iam_policy_document.assume_role_policy[0]
module.eks.data.aws_iam_session_context.current
module.eks.data.aws_partition.current
module.eks.data.tls_certificate.this[0]
module.eks.aws_cloudwatch_log_group.this[0]
module.eks.aws_ec2_tag.cluster_primary_security_group["Environment"]
module.eks.aws_ec2_tag.cluster_primary_security_group["Terraform"]
module.eks.aws_eks_access_entry.this["admin"]
module.eks.aws_eks_access_policy_association.this["admin_myeks"]
module.eks.aws_eks_addon.this["coredns"]
module.eks.aws_eks_addon.this["kube-proxy"]
module.eks.aws_eks_addon.this["vpc-cni"]
module.eks.aws_eks_cluster.this[0]
module.eks.aws_iam_openid_connect_provider.oidc_provider[0]
module.eks.aws_iam_policy.cluster_encryption[0]
module.eks.aws_iam_role.this[0]
module.eks.aws_iam_role_policy_attachment.cluster_encryption[0]
module.eks.aws_iam_role_policy_attachment.this["AmazonEKSClusterPolicy"]
module.eks.aws_iam_role_policy_attachment.this["AmazonEKSVPCResourceController"]
module.eks.aws_security_group.cluster[0]
module.eks.aws_security_group.node[0]
module.eks.aws_security_group_rule.cluster["ingress_nodes_443"]
module.eks.aws_security_group_rule.node["egress_all"]
module.eks.aws_security_group_rule.node["ingress_cluster_443"]
module.eks.aws_security_group_rule.node["ingress_cluster_4443_webhook"]
module.eks.aws_security_group_rule.node["ingress_cluster_6443_webhook"]
module.eks.aws_security_group_rule.node["ingress_cluster_8443_webhook"]
module.eks.aws_security_group_rule.node["ingress_cluster_9443_webhook"]
module.eks.aws_security_group_rule.node["ingress_cluster_kubelet"]
module.eks.aws_security_group_rule.node["ingress_nodes_ephemeral"]
module.eks.aws_security_group_rule.node["ingress_self_coredns_tcp"]
module.eks.aws_security_group_rule.node["ingress_self_coredns_udp"]
module.eks.time_sleep.this[0]
module.eks-external-dns.data.aws_region.current
module.vpc.aws_default_network_acl.this[0]
module.vpc.aws_default_route_table.default[0]
module.vpc.aws_default_security_group.this[0]
module.vpc.aws_eip.nat[0]
module.vpc.aws_internet_gateway.this[0]
module.vpc.aws_nat_gateway.this[0]
module.vpc.aws_route.private_nat_gateway[0]
module.vpc.aws_route.public_internet_gateway[0]
module.vpc.aws_route_table.private[0]
module.vpc.aws_route_table.public[0]
module.vpc.aws_route_table_association.private[0]
module.vpc.aws_route_table_association.private[1]
module.vpc.aws_route_table_association.private[2]
module.vpc.aws_route_table_association.public[0]
module.vpc.aws_route_table_association.public[1]
module.vpc.aws_route_table_association.public[2]
module.vpc.aws_subnet.private[0]
module.vpc.aws_subnet.private[1]
module.vpc.aws_subnet.private[2]
module.vpc.aws_subnet.public[0]
module.vpc.aws_subnet.public[1]
module.vpc.aws_subnet.public[2]
module.vpc.aws_vpc.this[0]
module.eks.module.eks_managed_node_group["default"].data.aws_caller_identity.current
module.eks.module.eks_managed_node_group["default"].data.aws_iam_policy_document.assume_role_policy[0]
module.eks.module.eks_managed_node_group["default"].data.aws_partition.current
module.eks.module.eks_managed_node_group["default"].aws_eks_node_group.this[0]
module.eks.module.eks_managed_node_group["default"].aws_iam_role.this[0]
module.eks.module.eks_managed_node_group["default"].aws_iam_role_policy_attachment.additional["myeksExternalDNSPolicy"]
module.eks.module.eks_managed_node_group["default"].aws_iam_role_policy_attachment.this["AmazonEC2ContainerRegistryReadOnly"]
module.eks.module.eks_managed_node_group["default"].aws_iam_role_policy_attachment.this["AmazonEKSWorkerNodePolicy"]
module.eks.module.eks_managed_node_group["default"].aws_iam_role_policy_attachment.this["AmazonEKS_CNI_Policy"]
module.eks.module.eks_managed_node_group["default"].aws_launch_template.this[0]
module.eks.module.kms.data.aws_caller_identity.current[0]
module.eks.module.kms.data.aws_iam_policy_document.this[0]
module.eks.module.kms.data.aws_partition.current[0]
module.eks.module.kms.aws_kms_alias.this["cluster"]
module.eks.module.kms.aws_kms_key.this[0]
module.eks.module.eks_managed_node_group["default"].module.user_data.null_resource.validate_cluster_service_cidr

terraform console
-----------------
data.aws_caller_identity.current
data.aws_caller_identity.current.arn
data.aws_eks_cluster.cluster
data.aws_eks_cluster.cluster.name
data.aws_eks_cluster.cluster.version
data.aws_eks_cluster.cluster.access_config
data.aws_eks_cluster_auth.cluster
kubernetes_service_account.aws_lb_controller
-----------------

cat terraform.tfstate | jq 
more terraform.tfstate
  • 배포 정보 확인
  • 저는 여러 계정에 Cluster 가 있기 때문에 context 변경을 해줘야 합니다.
# Context 변경
aws eks --region ap-northeast-2 update-kubeconfig --name myeks                                                                                
Added new context arn:aws:eks:ap-northeast-2:236747833953:cluster/myeks to /Users/leeeuijoo/.kube/config
#
kubectl get node -v=6


I0424 17:20:33.518495   74436 loader.go:395] Config loaded from file:  /Users/leeeuijoo/.kube/config
I0424 17:20:34.376202   74436 round_trippers.go:553] GET https://BC66A6F469EDD8D6B70917EF36F12730.gr7.ap-northeast-2.eks.amazonaws.com/api?timeout=32s 200 OK in 853 milliseconds
I0424 17:20:34.400062   74436 round_trippers.go:553] GET https://BC66A6F469EDD8D6B70917EF36F12730.gr7.ap-northeast-2.eks.amazonaws.com/apis?timeout=32s 200 OK in 20 milliseconds
I0424 17:20:34.434645   74436 round_trippers.go:553] GET https://BC66A6F469EDD8D6B70917EF36F12730.gr7.ap-northeast-2.eks.amazonaws.com/api/v1/nodes?limit=500 200 OK in 24 milliseconds
NAME                                               STATUS   ROLES    AGE     VERSION
ip-192-168-1-20.ap-northeast-2.compute.internal    Ready    <none>   7m42s   v1.29.0-eks-5e0fdde
ip-192-168-2-32.ap-northeast-2.compute.internal    Ready    <none>   7m44s   v1.29.0-eks-5e0fdde
ip-192-168-3-187.ap-northeast-2.compute.internal   Ready    <none>   7m45s   v1.29.0-eks-5e0fdde

# EKS 클러스터 인증 정보 업데이트
CLUSTER_NAME=myeks
aws eks update-kubeconfig --region ap-northeast-2 --name $CLUSTER_NAME
kubectl config rename-context "arn:aws:eks:ap-northeast-2:$(aws sts get-caller-identity --query 'Account' --output text):cluster/$CLUSTER_NAME" "Aews-Labs"

CURRENT   NAME                                                        CLUSTER                                                 AUTHINFO                                                    NAMESPACE
*         Aews-Labs                                                   arn:aws:eks:ap-northeast-2:236747833953:cluster/myeks   arn:aws:eks:ap-northeast-2:236747833953:cluster/myeks       
          docker-desktop                                              docker-desktop                                          docker-desktop         

#
kubectl cluster-info
kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone
kubectl get pod -A

NAMESPACE     NAME                       READY   STATUS    RESTARTS   AGE
kube-system   aws-node-2dptb             2/2     Running   0          8m32s
kube-system   aws-node-8m5xd             2/2     Running   0          8m13s
kube-system   aws-node-98qqh             2/2     Running   0          8m23s
kube-system   coredns-67dc5c9f9d-8w7w5   1/1     Running   0          8m32s
kube-system   coredns-67dc5c9f9d-v8h8j   1/1     Running   0          8m33s
kube-system   kube-proxy-kmlcb           1/1     Running   0          8m23s
kube-system   kube-proxy-vzf26           1/1     Running   0          8m32s
kube-system   kube-proxy-zbb4j           1/1     Running   0          8m26s
  • access 앤드리 확인

  • 기타 설치
# ExternalDNS
MyDomain=<자신의 도메인>
MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text)
echo $MyDomain, $MyDnzHostedZoneId
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/aews/externaldns.yaml
MyDomain=$MyDomain MyDnzHostedZoneId=$MyDnzHostedZoneId envsubst < externaldns.yaml | kubectl apply -f -

# kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set env.TZ="Asia/Seoul" --namespace kube-system
kubectl patch svc -n kube-system kube-ops-view -p '{"spec":{"type":"LoadBalancer"}}'
kubectl annotate service kube-ops-view -n kube-system "external-dns.alpha.kubernetes.io/hostname=kubeopsview.$MyDomain"
echo -e "Kube Ops View URL = http://kubeopsview.$MyDomain:8080/#scale=1.5"

# AWS LB Controller
helm repo add eks https://aws.github.io/eks-charts
helm repo update
helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=$CLUSTER_NAME \
  --set serviceAccount.create=false --set serviceAccount.name=aws-load-balancer-controller

# gp3 스토리지 클래스 생성
kubectl apply -f https://raw.githubusercontent.com/gasida/PKOS/main/aews/gp3-sc.yaml
  • (참고 ) 삭제 시 ← 아직 삭제하지 않고, 아래 실습 후 삭제 예정
# CLB는 terraform으로 배포하지 않고 직접 수동으로 배포되었으니, 수동으로 삭제를 해주어야 함
helm uninstall kube-ops-view --namespace kube-system

# 클러스터 삭제
terraform destroy -auto-approve

두 번째 EKS 클러스터 베포 : 코드 재사용 검증

  • 모듈화를 해놓았기 때문에 다른 클러스터를 간단하게 생성 가능 합니다.

# 디렉토리 이동
cd ..
mkdir 5
cd 5
cp ../4/*.tf .
ls



# 배포
terraform init
terraform apply -auto-approve -var=ClusterBaseName=myeks2 -var=KubernetesVersion="1.28"

# EKS 클러스터 인증 정보 가져오기
CLUSTER_NAME2=myeks2
aws eks update-kubeconfig --region ap-northeast-2 --name $CLUSTER_NAME2 --kubeconfig ./myeks2config

Added new context arn:aws:eks:ap-northeast-2:236747833953:cluster/myeks2 to /Users/leeeuijoo/Downloads/playground/euijoo/learn-terraform/aews-cicd/5/myeks2config

# EKS 클러스터 정보 확인
kubectl --kubeconfig ./myeks2config get node 

NAME                                               STATUS   ROLES    AGE     VERSION
ip-192-168-1-163.ap-northeast-2.compute.internal   Ready    <none>   2m32s   v1.28.5-eks-5e0fdde
ip-192-168-2-118.ap-northeast-2.compute.internal   Ready    <none>   2m31s   v1.28.5-eks-5e0fdde
ip-192-168-3-169.ap-northeast-2.compute.internal   Ready    <none>   2m32s   v1.28.5-eks-5e0fdde

kubectl --kubeconfig ./myeks2config get pod -A

NAMESPACE     NAME                       READY   STATUS    RESTARTS   AGE
kube-system   aws-node-2zlpz             2/2     Running   0          2m34s
kube-system   aws-node-b6c5q             2/2     Running   0          2m43s
kube-system   aws-node-h4kxf             2/2     Running   0          2m27s
kube-system   coredns-55474bf7b9-4x4zz   1/1     Running   0          2m44s
kube-system   coredns-55474bf7b9-fm8lb   1/1     Running   0          2m44s
kube-system   kube-proxy-5f6zz           1/1     Running   0          2m36s
kube-system   kube-proxy-7vtww           1/1     Running   0          2m40s
kube-system   kube-proxy-xk9c7           1/1     Running   0          2m44s

삭제 : 디렉터리(폴더) 진입 후 각각 삭제

  • 첫 번째 클러스터 삭제
    # CLB는 terraform으로 배포하지 않고 직접 수동으로 배포되었으니, 수동으로 삭제를 해주어야 함
    helm uninstall kube-ops-view --namespace kube-system
    
    # 클러스터 삭제
    terraform destroy -auto-approve
  • 두 번째 클러스터 삭제
    # 클러스터 삭제
    terraform destroy -auto-approve -var=ClusterBaseName=myeks2 -var=KubernetesVersion="1.28"
profile
무럭무럭 자라볼까

0개의 댓글