terraform.tfstate : 상태파일 => 구축한 사람의 로컬컴퓨터에 저장됨
공유저장소 git을 쓰지않고
┌ s3 , 암호화
└ 다이나모DB
s3에 버킷을 만들어
terraform-state
global/S3 - main.tf , .tfstate
mysql상태
/stage/data-store/mysql - main.tf , .tfstate
/stage/workspace-example - main.tf
~/01-terraform/terraform-state/global/s3 에 tf init, tf apply
~/01-terraform/terraform-state에 mkdir file-layout-example , mkdir workspace-example
mv global file-layout-example
mv stage file-layout-example
cd workspace-example가서
vim main.tf
terraform {
required_version = ">= 1.0.0, < 2.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
backend "s3" {
# 이전에 생성한 버킷 이름으로 변경
bucket = "aws08-terraform-state"
key = "workspace-example/terraform.tfstate"
region = "ap-northeast-2"
# 이전에 생성한 다이나모DB 테이블 이름으로 변경
dynamodb_table = "terraform-locks"
encrypt = true
}
}
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_instance" "example" {
ami = "ami-0ab04b3ccbadfae1f"
instance_type = "t2.micro"
tags = {
Name = "aws08-example"
}
}
tf init
tf apply
tf workspace list
terraform workspace new example1
tf init
tf apply하면 인스턴스가 2개가 러닝상태로됨
작업공간이 default와 example1 각각 다름
workspace
│
├ default - apply , destroy는 workspace-example에 저장
│
├ example1 - apply , destroy는 env: example1에 저장
│
└ example2 - apply , destroy는 env: example2에 저장
tf workspace select example1 // 작업할 공간 선택
tf workspace delete example2 // 작업한 공간 지우는 법
=====================================
■ 테라폼 모듈
~/01-terraform
mkdir terraform-module
cd terraform-module
cp -r ../terraform-state/file-layout-example/stage . // cp -r 하위 디렉토리 그대로복사하는 명령어 뒤는 경로
cp -r ../terraform-state/file-layout-example/global .
cp -r stage prod
mkdir modules
cd modules
ls ../stage
cp -r ../stage/services .
cd services/webserver-cluster
rm -rf .t*
vim main.tf 들어가서 provider 정의 제거
※ 파일 경로 정리
~/01-terraform/terraform-module
ls
global modules prod stage
terraform-module
│
├ modules - services - webserver-cluster => main.tf(provider삭제 , ami Ubuntu 20.04버젼으로 변경) , outputs.tf , variables.tf , user-data.sh
│
│
└ stage => data-store - mysql , services - webserver-cluster => main.tf(module추가,밑에꺼삭제) , outputs.tf , variables.tf , user-data.sh
※ stage/services/webserver-cluster vim main.tf
terraform {
required_version = ">=1.0.0, <2.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
provider "aws" {
region = "ap-northeast-2"
}
module "webserver_cluster" {
source = "../../../modules/services/webserver-cluster"
}
~
========================================================
modules/services/webserver-cluster
vim main.tf
terraform {
required_version = ">=1.0.0, <2.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
resource "aws_launch_template" "example" {
name = "${var.cluster_name}-example"
image_id = "ami-06eea3cd85e2db8ce" # Ubuntu 20.04 version
instance_type = "var.instance_type"
key_name = "aws08-key"
vpc_security_group_ids = [aws_security_group.instance.id]
user_data = base64encode(data.template_file.web_output.rendered)
lifecycle {
create_before_destroy = true
}
}
resource "aws_autoscaling_group" "example" {
availability_zones = ["ap-northeast-2a", "ap-northeast-2c"]
name = "${var.cluster_name}-asg-example"
desired_capacity = var.des_size
min_size = var.min_size
max_size = var.max_size
target_group_arns = [aws_lb_target_group.asg.arn]
health_check_type = "ELB"
launch_template {
id = aws_launch_template.example.id
version = "$Latest"
}
tag {
key = "Name"
value = var.cluster_name
propagate_at_launch = true
}
}
resource "aws_lb" "example" {
name = var.cluster_name
load_balancer_type = "application"
subnets = data.aws_subnets.default.ids
security_groups = [aws_security_group.alb.id]
}
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.example.arn
port = 80
protocol = "HTTP"
default_action {
type = "fixed-response"
fixed_response {
content_type = "text/plain"
message_body = "404: page not found"
status_code = 404
}
}
}
resource "aws_lb_listener_rule" "asg" {
listener_arn = aws_lb_listener.http.arn
priority = 100
condition {
path_pattern {
values = ["*"]
}
}
action {
type = "forward"
target_group_arn = aws_lb_target_group.asg.arn
}
}
resource "aws_lb_target_group" "asg" {
name = var.cluster_name
port = var.server_port
protocol = "HTTP"
vpc_id = data.aws_vpc.default.id
health_check {
path = "/"
protocol = "HTTP"
matcher = "200"
interval = 15
timeout = 3
healthy_threshold = 2
unhealthy_threshold = 2
}
}
resource "aws_security_group" "instance" {
name = "${var.cluster_name}-instance"
}
resource "aws_security_group_rule" "allow_http_inbound" {
type = "ingress"
security_group_id = aws_security_group.alb.id
from_port = var.server_port
to_port = var.server_port
protocol = local.tcp_protocol
cidr_blocks = local.all_ips
}
resource "aws_security_group" "alb" {
name = "${var.cluster_name}-alb"
}
resource "aws_security_group_rule" "allow_httpd_inbound" {
type = "ingress"
security_group_id = aws_security_group.alb.id
from_port = local.http_port # 출발 포트
to_port = local.http_port # 도착 포트
protocol = local.tcp_protocol # 프로토콜
cidr_blocks = local.all_ips # 송신지
}
resource "aws_security_group_rule" "allow_all_outbound" {
type = "egress"
security_group_id \ aws=security_group.alb.id
from_port = local.any_port # 출발 포트
to_port = local.any_port # 도착 포트
protocol = local.any_protocol # 프로토콜
cidr_blocks = local.all_ips # 송신지
}
data "terraform_remote_state" "db" {
backend = "s3"
config = {
bucket = var.db_remote-state_bucket
key = var.db_remote_state_key
region = "ap-northeast-2"
}
}
locals {
http_port = 80
any_port = 0
any_protocol = "-1"
tcp_protocol = "tcp"
all_ips = ["0.0.0.0/0"]
}
data "aws_vpc" "default" {
default = true
}
data "aws_subnets" "default" {
filter {
name = "vpc-id"
values = [data.aws_vpc.default.id]
}
}
data "template_file" "web_output" {
template = file("${path.module}/user-data.sh")
vars = {
server_port = var.server_port
db_address = data.terraform_remote_state.db.outputs.address
db_port = data.terraform_remote_state.db.outputs.port
}
}
vim. variables.tf
variable "cluster_name" {
description = "The name to use for all the cluster resources"
type = string
}
variable "db_remote_state_bucket" {
description = "The name of the S3 bucket for the database's remote state"
type = string
}
variable "db_remote_state_key" {
description = "The path for the database's remote state in S3"
type = string
}
variable "instance_type" {
description = "The type of EC2 Instances to run (e.g. t2.micro)"
type = string
}
variable "des_size" {
description = "The desired capacity of EC2 Instance in the ASG"
type = number
}
variable "min_size" {
description = "The minimum number of EC2 Instances in the ASG"
type = number
}
variable "max_size" {
description = "The maximum number of EC2 Instances in the ASG"
type = number
}
variable "server_port" {
description = "The port the server will use for HTTP requests"
type = number
default = 8080
}
vim outputs.tf
output "alb_dns_name" {
value = aws_lb.example.dns_name
description = "The domain name of the load balancer"
}
output "asg_name" {
value = aws_autoscaling_group.example.name
description = "The name of the Auto Scaling Group"
}
output "alb_security_group_id" {
value = aws_security_group.alb.id
description = "The ID of the Security Group attached to the load balancer"
}
==================================================
stage/services/webserver-cluster
vim main.tf
terraform {
required_version = ">=1.0.0, <2.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
provider "aws" {
region = "ap-northeast-2"
}
module "webserver_cluster" {
source = "../../../modules/services/webserver-cluster"
cluster_name = var.cluster_name
db_remote_state_bucket = var.db_remote_state_bucket
db_remote_state_key = var.db_remote_state_key
instance_type = "t2.micro"
des_size = 1
min_size = 1
max_size = 2
}
resource "aws_security_group_rule" "allow_testing_inbound" {
type = "ingress"
security_group_id = module.webserver_cluster.alb_security_group_id
from_port = 12345
to_port = 12345
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
vim variables.tf
variable "db_remote_state_bucket" {
description = "The name of the S3 bucket used for the database's remote state storage"
type = string
}
variable "db_remote_state_key" {
description = "The name of the key in the S3 bucket used for the database's remote state storage"
type = string
}
variable "cluster_name" {
description = "The name to use to namespace all the resources in the cluster"
type = string
default = "aws08-webservers-stage"
}
====================================================
prod/services/webserver-cluster
vim main.tf
terraform {
# 테라폼 버전 지정
required_version = ">=1.0.0, <2.0.0"
# 공급자 버전 지정
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
provider "aws" {
region = "ap-northeast-2"
}
module "webserver_cluster" {
source = "../../../modules/services/webserver-cluster"
cluster_name = var.cluster_name
db_remote_state_bucket = var.db_remote_state_bucket
db_remote_state_key = var.db_remote_state_key
instance_type = "m4.large"
des_size = 2
min_size = 2
max_size = 10
}
resource "aws_autoscaling_schedule" "scale_out_during_business_hours"{
scheduled_action_name = "scale-out-during-business-hours"
min_size = 2
max_size = 10
desired_capacity = 10
recurrence = "0 9 * * * "
autoscaling_group_name = module.webserver_cluster.asg_name
}
resource "aws_autoscaling_schedule" "scale_in_at_night"{
scheduled_action_name = "scale-in-at-night"
min_size = 2
max_size = 10
desired_capacity = 2
recurrence = "0 17 * * * "
autoscaling_group_name = module.webserver_cluster.asg_name
}
vim variables.tf
variable "db_remote_state_bucket" {
description = "The name of the S3 bucket used for the database's remote state storage"
type = string
}
variable "db_remote_state_key" {
description = "The name of the key in the S3 bucket used for the database's remote state storage"
type = string
}
variable "cluster_name" {
description = "The name to use to namespace all the resources in the cluster"
type = string
default = "aws08-webservers-prod"
}
vim outputs.tf
output "alb_dns_name" {
value = aws_lb.example.dns_name
description = "The domain name of the load balancer"
}
================================================
cd ../../data-stores/mysql
vim main.tf
terraform {
required_version = ">= 1.0.0, < 2.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
backend "s3" {
# 이전에 생성한 버킷 이름으로 변경
bucket = "aws08-terraform-state"
key = "prod/data-stores/mysql/terraform.tfstate"
region = "ap-northeast-2"
# 이전에 생성한 다이나모DB 테이블 이름으로 변경
dynamodb_table = "aws08-prod-terraform-locks"
encrypt = true
}
}
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_db_instance" "example" {
identifier_prefix = "aws08-prod-terraform-example"
engine = "mysql"
allocated_storage = 10 # 스토리지는 10GB
instance_class = "db.t2.micro" # vCPU 1개, 1GB 메모리
skip_final_snapshot = true
name = var.db_name
username = var.db_username
password = var.db_password
}
vim variables.tf
variable "db_username" {
description = "The username for the database"
type = string
sensitive = true
}
variable "db_password" {
description = "The password for the database"
type = string
sensitive = true
}
variable "db_name" {
description = "The name to use for the database"
type = string
default = "aws08_example_database_prod"
}
vim outputs.tf
output "address" {
value = aws_db_instance.example.address
description = "Connect to the database at this endpoint"
}
output "port" {
value = aws_db_instance.example.port
description = "The port the database is listening on"
}
stage/data-stores/mysql
vim main.tf
terraform {
required_version = ">= 1.0.0, < 2.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
backend "s3" {
# 이전에 생성한 버킷 이름으로 변경
bucket = "aws08-terraform-state"
key = "stage/data-stores/mysql/terraform.tfstate"
region = "ap-northeast-2"
# 이전에 생성한 다이나모DB 테이블 이름으로 변경
dynamodb_table = "aws08-stage-terraform-locks"
encrypt = true
}
}
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_db_instance" "example" {
identifier_prefix = "aws08-stage-terraform-example"
engine = "mysql"
allocated_storage = 10 # 스토리지는 10GB
instance_class = "db.t2.micro" # vCPU 1개, 1GB 메모리
skip_final_snapshot = true
name = var.db_name
username = var.db_username
password = var.db_password
}
vim variables.tf
variable "db_username" {
description = "The username for the database"
type = string
sensitive = true
}
variable "db_password" {
description = "The password for the database"
type = string
sensitive = true
}
variable "db_name" {
description = "The name to use for the database"
type = string
default = "aws08_example_database_stage"
}
vim outputs.tf
output "address" {
value = aws_db_instance.example.address
description = "Connect to the database at this endpoint"
}
output "port" {
value = aws_db_instance.example.port
description = "The port the database is listening on"
}
stage/services/webserver-cluster
vim variables.tf
variable "db_remote_state_bucket" {
description = "The name of the S3 bucket used for the database's remote state storage"
type = string
default = "aws08-terraform-state"
}
variable "db_remote_state_key" {
description = "The name of the key in the S3 bucket used for the database's r emote state storage"
type = string
default = "stage/data-stores/mysql/terraform.tfstate"
}
variable "cluster_name" {
description = "The name to use to namespace all the resources in the cluster"
type = string
default = "aws08-webservers-stage"
}
prod/services/webserver-cluster
vim variables.tf
variable "db_remote_state_bucket" {
description = "The name of the S3 bucket used for the database's remote state storage"
type = string
default = "aws08-terraform-state"
}
variable "db_remote_state_key" {
description = "The name of the key in the S3 bucket used for the database's r emote state storage"
type = string
default = "prod/data-stores/mysql/terraform.tfstate"
}
variable "cluster_name" {
description = "The name to use to namespace all the resources in the cluster"
type = string
default = "aws08-webservers-prod"
}