<aside> πŸ™‡β€β™€οΈ Kakaoenterprise 인턴 μˆ˜ν–‰ 쀑 μ§„ν–‰ν–ˆλ˜ λ°œν‘œ μžλ£Œμž…λ‹ˆλ‹€.

</aside>

Untitled

**tf-aws-provider**
	γ„΄ πŸ“ global
		γ„΄ πŸ“ s3  # Remote State Storage(s3 bucket) 
			γ„΄ πŸ“„ main.tf
			γ„΄ πŸ“„ output.tf
	γ„΄ πŸ“ stage
		γ„΄ πŸ“ datastores
			γ„΄ πŸ“ mysql # Database
				γ„΄ πŸ“„ main.tf
				γ„΄ πŸ“„ var.tf
				γ„΄ πŸ“„ output.tf
		γ„΄ πŸ“ services
			γ„΄ πŸ“ webserver-cluster # ASG(EC2 instances) + ELB
				γ„΄ πŸ“„ main.tf
				γ„΄ πŸ“„ var.tf
				γ„΄ πŸ“„ data.tf
				γ„΄ πŸ“„ output.tf

0️⃣ AWS Provider μ„€μ •

provider "aws" {
  region = "us-east-1"
}

1️⃣ ASG둜 μ›Ήμ„œλ²„ ν΄λŸ¬μŠ€ν„° κ΅¬μ„±ν•˜κΈ°

aws_security_group : resource

aws_launch_configuration : resource

aws_autoscaling_group : resource

aws_availability_zones : data

stage/services/webserver-cluster/main.tf

## (1) λ³΄μ•ˆ κ·Έλ£Ή μ„€μ • ##
resource "aws_security_group" "elb" {
  name = "terraform-elb-instance"

  # 포트 80번 νŠΈλž˜ν”½μ„ ν™•μΈν•œ ν›„, 연동
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] # 인터넷이 μ—°κ²°λœ μ–΄λŠκ³³μ—μ„œλΌλ„ 접속
  }

  # μ„€μ • λ¦¬μ†ŒμŠ€μ™€ κ΄€λ ¨λœ λ¦¬μ†ŒμŠ€μ΄κΈ°μ— create_before_destroy = true
  lifecycle {
    create_before_destroy = true
  }
}

## (2) EC2 μΈμŠ€ν„΄μŠ€λ₯Ό ASG에 μ„€μ •ν•˜λŠ” μ‹œμž‘ ꡬ성 ##
resource "aws_launch_configuration" "example"{
  image_id = "ami-40d28157"
  instance_type = "t2.micro"
  security_groups = ["${aws_security_group.elb.id}"]

  # EC2 μΈμŠ€ν„΄μŠ€μ— 포트 번호λ₯Ό μ„€μ •ν•˜λŠ” λΉ„μ§€λ°•μŠ€ λΆ€λΆ„
  # - EC2 μΈμŠ€ν„΄μŠ€μ˜ μ‚¬μš©μž 데이터 섀정을 톡해 "Hello, World" 슀크립트λ₯Ό μΈμŠ€ν„΄μŠ€ κΈ°λ™μ‹œ μˆ˜ν–‰ν•  수 있게 λœλ‹€.
  user_data = <<-EOF
#!/bin/bash
echo "Hello, World" >> index.html
nohup busybox httpd -f -p "${var.server_port}" &
EOF

  lifecycle {
    create_before_destroy = true
  }

}

## (3) ASG 생성 ##
resource "aws_autoscaling_group" "example" {
  launch_configuration = "${aws_launch_configuration.example.id}"
  availability_zones = "${data.aws_availability_zones.all.names}"

  max_size = 10
  min_size = 2

  tag {
    key = "Name"
    value = "terraform-asg-example"
    propagate_at_launch =  true
  }
}

stage/services/webserver-cluster/data.tf

## aws κ°€μš©μ˜μ—­ κ°€μ Έμ˜€κΈ° ##
data "aws_availability_zones" "all" {
}

stage/services/webserver-cluster/variable.tf

variable "server_port" {
  description = "The port the server will use for HTTP requests"
  default = 80
}

aws console

Untitled

2οΈβƒ£Β λ‘œλ“œλ°ΈλŸ°μ„œ λ°°ν¬ν•˜κΈ°

이전 ASG 배포의 ν•œκ°€μ§€ 문제점 : μ—¬λŸ¬κ°œμ˜ μ„œλ²„λ₯Ό μš΄μ˜ν•˜κΈ° μœ„ν•΄μ„œλŠ” 각 μ„œλ²„μ˜ 곡인 IPκ°€ μ•„λ‹Œ ν•˜λ‚˜μ˜ λŒ€ν‘œ IPκ°€ μ‘΄μž¬ν•΄μ•Ό 함 β†’ λ‘œλ“œλ°ΈλŸ°μ„œλ‘œ ν•΄κ²°

μ„œλΉ„μŠ€ νŠΈλž˜ν”½μ„ μˆ˜μš©ν•˜κΈ° μœ„ν•΄ λ‘œλ“œ λ°ΈλŸ°μ„œμ˜ IP ν˜Ήμ€ 도메인 이름을 μ„œλΉ„μŠ€ μ•žμ— λ…ΈμΆœν•΄μ•Όν•¨. β†’ 높은 κ°€μš©μ„±κ³Ό λ‹€μ–‘ν•œ ν™•μž₯μ„± 보μž₯

aws_elb resource

stage/services/webserver-cluster/main.tf

## ELB 생성 ##
resource "aws_elb" "example" {
  name = "terraform-asg-example"
  availability_zones = "${data.aws_availability_zones.all.names}"
  # 기본적으둜 λ“€μ–΄μ˜€κ³  λ‚˜κ°€λŠ” νŠΈλž˜ν”½μ— λŒ€ν•΄ ν—ˆμš©ν•˜μ§€ μ•Šκ²Œ ν•˜κΈ° μœ„ν•΄, λ³΄μ•ˆ 그룹에 포트 80번 νŠΈλž˜ν”½μ— λŒ€ν•΄ μ •μ˜ν•΄μ•Ό 함.
  security_groups = ["${aws_security_group.elb.id}"]

  # νŠΉμ • ν¬νŠΈμ— λŒ€ν•œ νŠΈλž˜ν”½μ„ λΌμš°νŒ… ν•˜κΈ° μœ„ν•΄ ν•˜λ‚˜ μ΄μƒμ˜ λ¦¬μŠ€λ„ˆλ₯Ό μ„€μ •
  listener {
    instance_port     = 80
    instance_protocol = "http"
    lb_port           = "${var.server_port}"
    lb_protocol       = "http"
  }

  # μƒνƒœλ₯Ό μ§€μ†μ μœΌλ‘œ μ²΄ν¬ν•˜λŠ” elb의 health check 블둝
  # 30 초 λ§ˆλ‹€ '/' URL둜 HTTP μš”μ²­μ„ ν•˜μ—¬ ASG에 μžˆλŠ” μΈμŠ€ν„΄μŠ€ 응닡이 '200 OK'인지 ν™•μΈν•˜λŠ” μ„€μ •
  health_check {
    healthy_threshold   = 2
    interval            = 30
    target              = "HTTP:${var.server_port}/"
    timeout             = 3
    unhealthy_threshold = 2
  }
}

## ASG에 LB μΆ”κ°€ ##
resource "aws_autoscaling_group" "example" {

  load_balancers = ["${aws_elb.example.name}"]  # μΈμŠ€ν„΄μŠ€κ°€ μ‹œμž‘ν•  λ•Œ ELB에 각 μΈμŠ€ν„΄μŠ€λ₯Ό λ“±λ‘ν•˜λ„λ‘ ASG에 μš”μ²­
  health_check_type = "ELB"

  # ..
}

AWS Console

ELB 생성

ELB 생성

3️⃣ S3둜 Remote State Storage λ§Œλ“€κΈ°

aws_s3_bucket

aws_s3_bucket_versioning

/global/s3/main.tf

provider "aws" {
  region = "us-east-1"
}

resource "aws_s3_bucket" "terraform_state" {
  bucket = "lena-tf-state-bucket"

  lifecycle {
    prevent_destroy = true
  }
}

resource "aws_s3_bucket_versioning" "versioning-ex" {
  bucket = aws_s3_bucket.terraform_state.id

  versioning_configuration {
    status = "Enabled"
  }
}

μƒνƒœνŒŒμΌ 잠금 : aws_dynamodb_table

/global/s3/main.tf

# μƒνƒœ 파일 μž κΈˆμ„ μœ„ν•œ DynamoDB μΆ”κ°€
resource "aws_dynamodb_table" "terraform_lock" {
  name = "lena-tf-state-bucket-lock"
  hash_key = "LockID"
  read_capacity = 2
  write_capacity = 2

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

S3 Backend 섀정에 s3 bucketκ³Ό dynamodb μΆ”κ°€

terraform {
  backend "s3" {
    bucket = "lena-tf-state-bucket"
    key = "stage/services/webserver-cluster/terraform.tfstate"
    region = "us-east-1"
    encrypt = true
    dynamodb_table = "lena-tf-state-bucket-lock"
  }
}

Releasing state lock

Untitled

AWS Console

s3 bucket

s3 bucket

dynamoDB

dynamoDB

4️⃣ RDS의 MySQL 연동

μ›Ή μ„œλ²„ ν΄λŸ¬μŠ€ν„°μ—μ„œ μ›Ή μ„œλ²„μ˜ 배포 μ—…λ°μ΄νŠΈλ₯Ό μ§„ν–‰ν•˜λŠ” μ‹œμ μ— λ°μ΄ν„°λ² μ΄μŠ€μ— λ¬Έμ œκ°€ λ°œμƒν•˜μ§€ μ•ŠκΈ°λ₯Ό μ›ν•œλ‹€λ©΄ λ°μ΄ν„°λ² μ΄μŠ€ λ¦¬μ†ŒμŠ€λ₯Ό μƒμ„±ν•˜κ³ , λ°μ΄ν„°λ² μ΄μŠ€ μƒνƒœνŒŒμΌμ„ λ§Œλ“€μ–΄ μ΄λ˜ν•œ 원격 μŠ€ν† λ¦¬μ§€μ—μ„œ 관리할 ν•„μš”κ°€ 있음.

aws_db_instance : resource

/stage/datastores/mysql/main.tf

## DBκ°€ S3에 λͺ¨λ“  μƒνƒœλ₯Ό μ €μž₯ν•˜λ„λ‘ 원격 μƒνƒœ μ €μž₯μ†Œλ₯Ό ꡬ성
terraform {
  backend "s3" {
    bucket = "lena-tf-state-bucket"
    key = "stage/datastores/mysql/terraform.tfstate"
    region = "us-east-1"
    encrypt = true
    dynamodb_table = "lena-tf-state-bucket-lock"
  }
}

provider "aws" {
  region = "us-east-1"
}

## 데이터 베이슀 λ¦¬μ†ŒμŠ€ 생성 ##
resource "aws_db_instance" "example" {
  engine = "mysql"
  allocated_storage = 10
  instance_class = "db.t2.micro"
  db_name = "example_data"
  username = "admin"
  password = "${var.db_password}"
}

TF_VAR_foo

<aside> 🚫 ν…ŒλΌνΌμ—μ„œ λ°μ΄ν„°λ² μ΄μŠ€ νŒ¨μŠ€μ›Œλ“œμ™€ 같은 μ•”ν˜Έν™”λŠ” μ€‘μš”ν•˜λ‹€! β†’ λ―Όκ°ν•œ μ •λ³΄λŠ” νŒŒμΌμ΄λ‚˜ μ½”λ“œμ— μ €μž₯ν•˜μ§€ 말기

</aside>

/stage/datastores/mysql/var.tf

variable "db_password" {
  description = "The password for the database"
}
export TF_VAR_db_password="(λ°μ΄ν„°λ² μ΄μŠ€ νŒ¨μŠ€μ›Œλ“œ)"

5️⃣ S3 λ²„ν‚·μ˜ State 파일 λ‚΄μš© κ°€μ Έμ˜€κΈ°

ν•„μš”ν•œ 데이터λ₯Ό 좜λ ₯ λ³€μˆ˜λ‘œ λΉΌμ„œ state νŒŒμΌμ— μ €μž₯λ˜λ„λ‘

output "address" {
  value = "${aws_db_instance.example.address}"
}

output "port" {
  value = "${aws_db_instance.example.port}"
}

Untitled