본문 바로가기
DevOps/IaC (Infrastructure as Code)

[T1012] Week 4. State & module

by Hwan,. 2023. 7. 29.
728x90
반응형

State  file

 상태 파일은 terraform apply시에 생성되는 .tfstate 파일이다.

해당 파일은 인프라의 상태를 json 형태로 가지고 있는데, 테라폼 내부에서만 사용하기 위한 파일이기 때문에 테라폼 상태 파일을 직접 편집하거나 직접 읽는 코드로 작성해서는 안된다.

 

팀 단위로 작업하는 경우 아래 조건이 갖춰져야 한다.

  1. 상태 파일을 저장하는 공유 스토리지 (Shared storage for state files)
    • 각 팀원이 동일한 테라폼 상태 파일 사용을 위해서, 공유 위치에 저장이 필요
  2. 상태 파일 잠금 (Locking state files)
    • 잠금 기능 없이 동시에 테라폼 실행 시 여러 테라폼 프로세스가 상태 파일을 동시에 업데이트하여 경쟁 상태(race condition)로 인한 충돌 가능
  3. 상태 파일 격리 (Isolating state files)
    • 테스트(dev), 검증(stage), 상용(prodction) 등 각 환경에 대한 상태 파일의 격리가 필요

 

AWS S3, Azure Blob Storage, Google Cloud Storage 등이 원격 백엔드를 지원하기 때문에 많이 쓰인다.

이런 백엔드를 사용할 경우 일반적으로 발생하는 아래 문제들을 해결할 수 있다.

  1. 수동 오류: plan/apply 실행 시 마다 해당 백엔드에서 파일을 자동을 로드, apply 후 상태 파일을 백엔드에 자동 저장
  2. 잠금: apply 실행 시 테라폼은 자동으로 잠금을 활성화, -lock-timout=<TIME> 로 대기 시간 설정 지정 가능
  3. 보안: 대부분 원격 백엔드는 기본적으로 데이터를 보내거나 상태 파일을 저장할 때 암호화 기능을 지원

 

아래 코드를 사용하면 DynamoDB로 잠금 상태를 제어하는 AWS S3 Backend를 만들수 있다.

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

resource "aws_s3_bucket" "mys3bucket" {
  bucket = "hwan001-t101study-tfstate"
}

resource "aws_s3_bucket_versioning" "mys3bucket_versioning" {
  bucket = aws_s3_bucket.mys3bucket.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_dynamodb_table" "mydynamodbtable" {
  name         = "terraform-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

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

S3 bucket 생성
DynamoDB 생성

 

만들어진 백엔드와 DynamoDB 잠금은 아래처럼 사용할 수 있다.

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

terraform {
  backend "s3" {
    bucket = "hwan001-t101study-tfstate"
    key    = "dev/terraform.tfstate"
    region = "ap-northeast-2"
    dynamodb_table = "terraform-locks"
    # encrypt        = true
  }
}

Module

 모듈은 대부분의 프로그래밍 언어에서 쓰이는 라이브러리나 패키지와 역할이 비슷하다.

모듈은 기본적으로 아래와 같은 구조를 가지며 이렇게 모듈화를 할 경우 재활용성이 높아진다.

 

Root-Module과 Child-Module로 나누어지고, root 모듈 내부에서  child 모듈을 불러서 사용할 수 있다.

모듈을 사용하는 방법은 로컬 디렉토리, 테라폼 레지스트리와 github에 있는 모듈 사용 등이 있다.

 

아래는 깃허브를 사용한 방식으로 ec2 인스턴스를 띄워봤다.

먼저 github에 public 리포지토리를 생성한다. 생성 시 .gitignore에서 Terraform을 선택하면 .terraform 등의 과정에서 생성된 파일들이 github push 시에 올라가지 않는다.

 

VPC Module

 

GitHub - aws-hwan-001/vpc

Contribute to aws-hwan-001/vpc development by creating an account on GitHub.

github.com

data "aws_availability_zones" "available" {
  state = "available"
}
# variables.tf
variable "cidr_block" {
  description = "The CIDR block for the VPC"
  type        = string
}

variable "tags" {
  description = "Tags for the VPC"
  type        = map(string)
  default     = {}
}

variable "primary_subnet_cidr" {
  description = "CIDR block for the primary subnet"
  type        = string
}

variable "primary_subnet_tags" {
  description = "Tags for the primary subnet"
  type        = map(string)
  default     = {}
}

variable "secondary_subnet_cidr" {
  description = "CIDR block for the secondary subnet"
  type        = string
}

variable "secondary_subnet_tags" {
  description = "Tags for the secondary subnet"
  type        = map(string)
  default     = {}
}


# main.tf
resource "aws_vpc" "this" {
  cidr_block = var.cidr_block
  tags       = var.tags
}

resource "aws_subnet" "primary" {
  vpc_id            = aws_vpc.this.id
  cidr_block        = var.primary_subnet_cidr
  availability_zone = data.aws_availability_zones.available.names[0]
  tags              = var.primary_subnet_tags
}

resource "aws_subnet" "secondary" {
  vpc_id            = aws_vpc.this.id
  cidr_block        = var.secondary_subnet_cidr
  availability_zone = data.aws_availability_zones.available.names[1]
  tags              = var.secondary_subnet_tags
}

# output.tf
output "vpc_id" {
  description = "The ID of the VPC"
  value       = aws_vpc.this.id
}

output "primary_subnet_id" {
  description = "The ID of the primary subnet"
  value       = aws_subnet.primary.id
}

output "secondary_subnet_id" {
  description = "The ID of the secondary subnet"
  value       = aws_subnet.secondary.id
}

 

Security Group Module

 

GitHub - aws-hwan-001/sg

Contribute to aws-hwan-001/sg development by creating an account on GitHub.

github.com

# variables.tf
variable "description" {
  description = "Description of the security group"
  type        = string
}

variable "vpc_id" {
  description = "VPC ID to associate the security group with"
  type        = string
}

variable "ingress_rules" {
  description = "List of ingress rules"
  type        = list(object({
    from_port   = number
    to_port     = number
    protocol    = string
    cidr_blocks = list(string)
  }))
}

variable "name" {
  description = "Name of the security group"
  type        = string
}

# main.tf
resource "aws_security_group" "this" {
  description = var.description
  vpc_id      = var.vpc_id

  dynamic "ingress" {
    for_each = var.ingress_rules
    content {
      from_port   = ingress.value.from_port
      to_port     = ingress.value.to_port
      protocol    = ingress.value.protocol
      cidr_blocks = ingress.value.cidr_blocks
    }
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = var.name
  }
}

# output.tf
output "security_group_id" {
  description = "The ID of the security group"
  value       = aws_security_group.this.id
}

 

 

EC2 Module

 

GitHub - aws-hwan-001/ec2

Contribute to aws-hwan-001/ec2 development by creating an account on GitHub.

github.com

# variables.tf
variable "instance_type" {
  description = "EC2 instance type"
  type        = string
}

variable "subnet_id" {
  description = "Subnet id to launch the instance in"
  type        = string
}

variable "security_group_ids" {
  description = "List of security group ids to associate with the instance"
  type        = list(string)
}

variable "instance_name" {
  description = "Name to be used on all resources as prefix"
  type        = string
}

variable "associate_public_ip_address" {
  description = "Associate a public ip address with the instance"
  type        = bool
  default     = false
}

# main.tf
data "aws_ami" "ubuntu" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  owners = ["099720109477"]  # Canonical
}

resource "aws_instance" "this" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = var.instance_type
  subnet_id     = var.subnet_id
  vpc_security_group_ids = var.security_group_ids
  associate_public_ip_address = var.associate_public_ip_address

  tags = {
    Name = var.instance_name
  }
}

# output.tf
output "instance_id" {
  description = "The ID of the instance"
  value       = aws_instance.this.id
}

 

이제 만들어진 모듈을 사용해 리소스를 생성해보자.

ec2 모듈에 count = 3을 넣어주어 3개의 인스턴스를 생성했다.

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

terraform {
  backend "s3" {
    bucket = "hwan001-t101study-tfstate"
    key    = "dev/terraform.tfstate"
    region = "ap-northeast-2"
    dynamodb_table = "terraform-locks"
    # encrypt        = true
  }
}


module "vpc" {
  source = "git::https://github.com/aws-hwan-001/vpc.git"

  cidr_block             = "10.10.0.0/16"
  tags                   = { Name = "hwan001-vpc" }
  primary_subnet_cidr    = "10.10.1.0/24"
  primary_subnet_tags    = { Name = "hwan001-subnet-1" }
  secondary_subnet_cidr  = "10.10.2.0/24"
  secondary_subnet_tags  = { Name = "hwan001-subnet-2" }
}


module "security_group" {
  source  = "git::https://github.com/aws-hwan-001/sg.git"
  
  description = "hwan001 Security Group"
  vpc_id      = module.vpc.vpc_id
  name        = "hwan001-sg"
  ingress_rules = [
    {
      from_port   = 22
      to_port     = 22
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    },
    {
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    },
    {
      from_port   = 443
      to_port     = 443
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    },
    {
      from_port   = 8080
      to_port     = 8080
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    }
  ]
}


module "ec2_instance" {
  source  = "git::https://github.com/aws-hwan-001/ec2.git"
  count = 3
  
  instance_type = "t2.micro"
  subnet_id     = module.vpc.primary_subnet_id
  security_group_ids = [module.security_group.security_group_id]
  instance_name = "hwan001-ec2"
  associate_public_ip_address = true
}

 

상태파일은 위에서 만든 백엔드를 사용하고 있는 걸 볼 수 있다.

 


 

728x90
반응형

댓글