Terraform? / TIL

Terraform? / 실습

Featured image

Terraform


IaC 도구를 사용하면 GUI를 통하지 않고 구성 파일을 사용하여 를 안전하고 일관되며 반복 가능한 방식으로 빌드, 변경 및 관리할 수 있게 해준다.

Terraform은 IaC 도구이며, 수동 구성이 아닌 코드를 통해 컴퓨터 시스템과 리소스를 생성하고 관리할 수 있다. 이 도구를 사용하면 사람이 읽기 쉬운 선언적 구성 파일에서 리소스와 인프라를 정의할 수 있으며 인프라의 라이프사이클을 관리할 수 있다.

Terraform은 구조화되고 재현 가능한 방식으로 인프라를 정의하고 배포하는 방법과 인프라 관리를 위한 강력한 도구가 되는 몇 가지 주요 기능을 제공한다.

  1. 선언적 구문을 지원하여 사용자가 원하는 인프라 상태를 정의하고 테라폼이 해당 상태를 달성하는 데 필요한 작업을 처리하도록 한다.
  2. 다양한 공급자를 지원하여 다양한 클라우드 플랫폼 및 서비스에서 리소스를 관리할 수 있다.
  3. 인프라 코드의 모듈화 및 재사용을 허용하여 복잡한 시스템을 보다 쉽게 유지 관리하고 확장할 수 있다.
  4. 종속성 관리와 변경 사항을 적용하기 전에 계획하고 미리 볼 수 있는 기능을 제공하여 제어되고 예측 가능한 배포를 보장한다.



필요성


장점


Q. Terraform의 선언적 방식으로 작성된 코드는 항상 인프라의 최신 상태를 의미한다. Terraform은 어떤 방식으로 인프라를 최신 상태로 유지할 수 있는 걸까?


A.

Terraform은 인프라를 최신 상태로 유지하며 변경 사항을 추적하고 관리하므로 구성 파일에 정의된 원하는 상태를 달성하는 데 필요한 리소스를 쉽게 생성, 업데이트 또는 삭제할 수 있다. 이 선언적 접근 방식은 인프라가 항상 원하는 구성과 일치하도록 하여 구성 드리프트의 위험을 줄이고 시간이 지남에 따라 인프라를 보다 쉽게 ​​유지 관리할 수 있도록 한다.


Terraform 기본 설정


Terraform을 구성하기 앞서 기본 설정부터 하도록 하자.

  1. Terraform AWS 공급자를 인증하기 위한 AWS Configure
  2. AWS CLI 설치
  3. Terraform CLI 설치
# Terraform CLI Install

$ sudo apt-get update
$ sudo apt-get install -y gnupg software-properties-common
$ wget -O- https://apt.releases.hashicorp.com/gpg | \
gpg --dearmor | \
sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg
$ gpg --no-default-keyring \
--keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg \
--fingerprint
$ echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
https://apt.releases.hashicorp.com $(lsb_release -cs) main" | \
sudo tee /etc/apt/sources.list.d/hashicorp.list
$ sudo apt-get update
$ sudo apt-get install terraform

Terraform CLI 설치가 완료되면 다음 명령을 이용해 사용 가능한 옵션에 대해 자세히 알아볼 수 있다.

$ terraform -help plan

다음 명령을 이용하여 Terraform CLI 도구에 대한 명령 자동 완성을 사용할 수 있다.

$ touch ~/.bashrc
$ terraform -install-autocomplete


Terraform 명령어


$ terraform plan


$ terraform init


$ terraform apply


$ terraform destroy


Terraform 구성


1. Terraform

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

  required_version = ">= 1.2.0"
}

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

# Define variables
variable "server_port" {
  description = "Port for the web server"
  default     = 80
}

terraform 행은 필요한 공급자와 사용할 Terraform 버전을 구성하는 데 사용된다. required_providers 행은 “aws” 공급자가 필요하고, 공급자의 소스가 “hashicorp/aws”이며 버전이 4.16이어야 함을 지정한다. 즉, 테라폼은 지정된 공급자를 사용하여 인프라의 AWS 리소스와 상호작용 한다.

required_version 행은 이 구성을 실행하는 데 필요한 최소 버전이 1.2.0 이상이어야 함을 지정한다.

provicers 행은 특정 공급자(“aws”)를 구성한다. 테라폼이 AWS 리소스와 상호 작용할 때 “ap-northeast-2” 리전을 기본값으로 사용한다.

variable 행은 테라폼에서 변수를 정의하는 데 사용된다. “server_port”라는 변수로 웹 서버의 포트 번호를 정의한다. description은 변수의 용도 설명이고, default는 변수의 기본 값이다. 즉 server_port라는 변수에 기본값으로 80포트를 넣어준 것이다.


2. EC2

# Create EC2
resource "aws_instance" "app_server" {
  ami           = "ami-04cebc8d6c4f297a3"
  instance_type = "t2.micro"

  user_data = <<-EOF
    #!/bin/bash
    echo "Hello, World" > index.html
    nohup busybox httpd -f -p ${var.server_port} &
    EOF

  key_name = "devops04-seay0"
  tags = {
    Name = "full_stack_application"
  }
}

resource는 Terraform에서 리소스를 정의하는 데에 사용된다.

이 행은 EC2를 구성하는 행이며, 리소스 블럭 이름은 “aws_instance”이고 로컬 이름은 “app_server”이다. 이 이름은 테라폼 구성 내에서 이 리소스를 참조하거나 상호 작용하는 데 사용할 수 있다.


3. VPC

# Create VPC
resource "aws_vpc" "my_vpc" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "my_vpc"
  }
}

“aws_vpc”라는 이름의 리소스를 정의하고 있으며, 로컬 이름은 “my_vpc”이다.


4. Security Group

# Create Public Security Group
resource "aws_security_group" "public_sg" {
  vpc_id      = aws_vpc.my_vpc.id
  name        = "public_sg"
  description = "Security group for public web server"

  tags = {
    Name = "public_sg"
  }
}

# Create Private Security Group
resource "aws_security_group" "private_sg" {
  vpc_id      = aws_vpc.my_vpc.id
  name        = "private_sg"
  description = "Security group for private web server"

  tags = {
    Name = "private_sg"
  }
}

두 개(Public, Private)의 AWS 보안 그룹을 생성하기 위한 리소스 블록이다.


5. Subnets

# Create Public Subnets
resource "aws_subnet" "public_subnet" {
  vpc_id = aws_vpc.my_vpc.id
  cidr_block = "10.0.0.0/24"
  availability_zone = "ap-northeast-2c"
  tags = {
    Name = "public_subnet"
  }
}

# Create Private Subnets
resource "aws_subnet" "private_subnet" {
  vpc_id = aws_vpc.my_vpc.id
  cidr_block = "10.0.1.0/24"
  availability_zone = "ap-northeast-2c"
  tags = {
    Name = "private_subnet"
  }                
}

AWS VPC 내에 두 개(Public, Private)의 서브넷을 생성하기 위한 리소스 블록이다.


6. IGW / Route Table

# Create Internet Gateway
resource "aws_internet_gateway" "my_igw" {
  vpc_id = aws_vpc.my_vpc.id

  tags = {
    Name = "my_igw"
  }
}

# Create Public Route Table
resource "aws_route_table" "public_route_table" {
  vpc_id = aws_vpc.my_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.my_igw.id
  }
  tags = {
    Name = "public_route_table"
  }
}

# Create Private Route Table
resource "aws_route_table" "private_route_table" {
  vpc_id = aws_vpc.my_vpc.id

  tags = {
    Name = "private_route_table"
  }
}

# Connect Public Route Table
resource "aws_route_table_association" "publicRTAssociation" {
  subnet_id = aws_subnet.public_subnet.id
  route_table_id = aws_route_table.public_route_table.id
}

# Connect Private Route Table
resource "aws_route_table_association" "privateRTAssociation" {
  subnet_id = aws_subnet.private_subnet.id
  route_table_id = aws_route_table.private_route_table.id
}

AWS Internet Gateway와 라우팅 테이블을 생성하고 이를 AWS VPC의 서브넷과 연결하기 위한 리소스 블록이다.



7. Security Group Rule

# Create Public SG Rule
resource "aws_security_group_rule" "publicSGRulesHTTPingress" {
  type = "ingress"
  from_port = 80
  to_port = 80
  protocol = "TCP"
  cidr_blocks = [ "0.0.0.0/0" ]
  security_group_id = aws_security_group.public_sg.id
  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_security_group_rule" "publicSGRulesHTTPegress" {
  type = "egress"
  from_port = 80
  to_port = 80
  protocol = "TCP"
  cidr_blocks = [ "0.0.0.0/0" ]
  security_group_id = aws_security_group.public_sg.id
  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_security_group_rule" "publicSGRulesHTTPSingress" {
  type = "ingress"
  from_port = 443
  to_port = 443
  protocol = "TCP"
  cidr_blocks = [ "0.0.0.0/0" ]
  security_group_id = aws_security_group.public_sg.id
  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_security_group_rule" "publicSGRulesHTTPSegress" {
  type = "egress"
  from_port = 443
  to_port = 443
  protocol = "TCP"
  cidr_blocks = [ "0.0.0.0/0" ]
  security_group_id = aws_security_group.public_sg.id
  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_security_group_rule" "publicSGRulesSSHingress" {
  type = "ingress"
  from_port = 22
  to_port = 22
  protocol = "TCP"
  cidr_blocks = [ "0.0.0.0/32" ]
  security_group_id = aws_security_group.public_sg.id
  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_security_group_rule" "publicSGRulesSSHegress" {
  type = "egress"
  from_port = 22
  to_port = 22
  protocol = "TCP"
  cidr_blocks = [ "0.0.0.0/32" ]
  security_group_id = aws_security_group.public_sg.id
  lifecycle {
    create_before_destroy = true
  }
}

# Create Private SG rules
resource "aws_security_group_rule" "privateSGRulesRDSingress" {
  type = "ingress"
  from_port = 3306
  to_port = 3306
  protocol = "TCP"
  security_group_id = aws_security_group.private_sg.id
  source_security_group_id = aws_security_group.public_sg.id
  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_security_group_rule" "privateSGRulesRDSegress" {
  type = "egress"
  from_port = 3306
  to_port = 3306
  protocol = "TCP"
  security_group_id = aws_security_group.private_sg.id
  source_security_group_id = aws_security_group.public_sg.id
  lifecycle {
    create_before_destroy = true
  }
}

보안 그룹의 지정된 포트에 대해 수신 트래픽을 허용하는 규칙을 생성하기 위한 리소스 블록이다.