https://gasidaseo.notion.site/gasidaseo/CloudNet-Blog-c9dfa44a27ff431dafdd2edacc8a1863
1
# 환경변수에 지정
export TF_VAR_bucket_name=masterseo-t101-tfstate
export TF_VAR_table_name=masterseo-t101-locks
# 환경변수 확인
export | grep TF_VAR_
2
배포
#
cd 03-terraform-state/file-layout-example/global/s3
cat main.tf variables.tf
[root@ip-172-31-61-209 s3]# cat main.tf variables.tf
terraform {
required_version = ">= 1.0.0, < 2.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
provider "aws" {
region = "us-east-2"
}
resource "aws_s3_bucket" "terraform_state" {
bucket = var.bucket_name
// This is only here so we can destroy the bucket as part of automated tests. You should not copy this for production
// usage
force_destroy = true
}
# Enable versioning so you can see the full revision history of your
# state files
resource "aws_s3_bucket_versioning" "enabled" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
# Enable server-side encryption by default
resource "aws_s3_bucket_server_side_encryption_configuration" "default" {
bucket = aws_s3_bucket.terraform_state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
# Explicitly block all public access to the S3 bucket
resource "aws_s3_bucket_public_access_block" "public_access" {
bucket = aws_s3_bucket.terraform_state.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_dynamodb_table" "terraform_locks" {
name = var.table_name
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}
variable "bucket_name" {
description = "The name of the S3 bucket. Must be globally unique."
type = string
}
variable "table_name" {
description = "The name of the DynamoDB table. Must be unique in this AWS account."
type = string
}
[root@ip-172-31-61-209 s3]#
# 초기화 및 검증 : 환경변수 적용 확인
terraform init && terraform plan
# 배포
terraform apply -auto-approve
# 확인
aws s3 ls
2022-12-07 08:20:10 masterseo-t101-tfstate
aws dynamodb list-tables --output text
[root@ip-172-31-61-209 s3]# aws dynamodb list-tables --output text
TABLENAMES masterseo-t101-locks
cd ..
cd ..
1
# [터미널1] RDS 생성 모니터링
while true; do aws rds describe-db-instances --query "*[].[Endpoint.Address,Endpoint.Port,MasterUsername]" --output text ; echo "------------------------------" ; sleep 1; done
2
# [터미널2]
cd /root/terraform-up-and-running-code/code/terraform/04-terraform-module/module-example/stage/data-stores/mysql
cat main.tf variables.tf
[root@ip-172-31-61-209 mysql]# cat main.tf variables.tf
terraform {
required_version = ">= 1.0.0, < 2.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
backend "s3" {
# This backend configuration is filled in automatically at test time by Terratest. If you wish to run this example
# manually, uncomment and fill in the config below.
bucket = "masterseo-t101-tfstate"
key = "stage/data-stores/mysql/terraform.tfstate"
region = "us-east-2"
dynamodb_table = "masterseo-t101-locks"
# encrypt = true
}
}
provider "aws" {
region = "us-east-2"
}
resource "aws_db_instance" "example" {
identifier_prefix = "terraform-up-and-running"
engine = "mysql"
allocated_storage = 10
instance_class = "db.t2.micro"
db_name = var.db_name
username = var.db_username
password = var.db_password
skip_final_snapshot = true
}
# ---------------------------------------------------------------------------------------------------------------------
# REQUIRED PARAMETERS
# You must provide a value for each of these parameters.
# ---------------------------------------------------------------------------------------------------------------------
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
}
# ---------------------------------------------------------------------------------------------------------------------
# OPTIONAL PARAMETERS
# These parameters have reasonable defaults.
# ---------------------------------------------------------------------------------------------------------------------
variable "db_name" {
description = "The name to use for the database"
type = string
default = "example_database_stage"
}
[root@ip-172-31-61-209 mysql]#
# 환경변수에 지정
export TF_VAR_db_username='cloudneta'
export TF_VAR_db_password='cloudnetaQ!'
# 환경변수 확인
export | grep TF_VAR_
# main.tf 에 백엔드 부분 수정
vi main.tf
backend "s3" {
# This backend configuration is filled in automatically at test time by Terratest. If you wish to run this example
# manually, uncomment and fill in the config below.
bucket = "masterseo-t101-tfstate"
key = "stage/data-stores/mysql/terraform.tfstate"
region = "us-east-2"
dynamodb_table = "masterseo-t101-locks"
# encrypt = true
}
# 초기화 및 검증 : 환경변수 적용 확인
terraform init && terraform plan
# 배포 : RDS는 생성 시 6분 정도 시간 소요
terraform apply -auto-approve
터미널2에서 모니터링 결과
terraform-up-and-running20221xxxxxxxx400000001.c7aitcdywcyg.us-east-2.rds.amazonaws.com 3306 cloudneta
------------------------------
terraform-up-and-running202212xxxxxxx400000001.c7aitcdywcyg.us-east-2.rds.amazonaws.com 3306 cloudneta
------------------------------
terraform output
oot@ip-172-31-61-209 mysql]# terraform output
address = "terraform-up-and-running2094400000001.c7aitcdywcyg.us-east-2.rds.amazonaws.com"
port = 3306
aws s3 ls s3://$TF_VAR_bucket_name --recursive --human-readable --summarize
-31-61-209 mysql]# aws s3 ls s3://$TF_VAR_bucket_name --recursive --human-readable --summarize
2022-12-07 08:31:11 4.3 KiB stage/data-stores/mysql/terraform.tfstate
Total Objects: 1
Total Size: 4.3 KiB
# 이동
cd ..
cd ..
1
#
cd /root/terraform-up-and-running-code/code/terraform/05-tips-and-tricks/zero-downtime-deployment/modules/services/webserver-cluster
cat main.tf variables.tf
[root@ip-172-31-61-209 webserver-cluster]# cat main.tf variables.tf
terraform {
required_version = ">= 1.0.0, < 2.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
resource "aws_launch_configuration" "example" {
image_id = var.ami
instance_type = var.instance_type
security_groups = [aws_security_group.instance.id]
user_data = templatefile("${path.module}/user-data.sh", {
server_port = var.server_port
db_address = data.terraform_remote_state.db.outputs.address
db_port = data.terraform_remote_state.db.outputs.port
server_text = var.server_text
})
# Required when using a launch configuration with an auto scaling group.
lifecycle {
create_before_destroy = true
}
}
resource "aws_autoscaling_group" "example" {
# Explicitly depend on the launch configuration's name so each time it's
# replaced, this ASG is also replaced
name = "${var.cluster_name}-${aws_launch_configuration.example.name}"
launch_configuration = aws_launch_configuration.example.name
vpc_zone_identifier = data.aws_subnets.default.ids
target_group_arns = [aws_lb_target_group.asg.arn]
health_check_type = "ELB"
min_size = var.min_size
max_size = var.max_size
# Wait for at least this many instances to pass health checks before
# considering the ASG deployment complete
min_elb_capacity = var.min_size
# When replacing this ASG, create the replacement first, and only delete the
# original after
lifecycle {
create_before_destroy = true
}
tag {
key = "Name"
value = var.cluster_name
propagate_at_launch = true
}
dynamic "tag" {
for_each = {
for key, value in var.custom_tags:
key => upper(value)
if key != "Name"
}
content {
key = tag.key
value = tag.value
propagate_at_launch = true
}
}
}
resource "aws_autoscaling_schedule" "scale_out_during_business_hours" {
count = var.enable_autoscaling ? 1 : 0
scheduled_action_name = "${var.cluster_name}-scale-out-during-business-hours"
min_size = 2
max_size = 10
desired_capacity = 10
recurrence = "0 9 * * *"
autoscaling_group_name = aws_autoscaling_group.example.name
}
resource "aws_autoscaling_schedule" "scale_in_at_night" {
count = var.enable_autoscaling ? 1 : 0
scheduled_action_name = "${var.cluster_name}-scale-in-at-night"
min_size = 2
max_size = 10
desired_capacity = 2
recurrence = "0 17 * * *"
autoscaling_group_name = aws_autoscaling_group.example.name
}
resource "aws_security_group" "instance" {
name = "${var.cluster_name}-instance"
}
resource "aws_security_group_rule" "allow_server_http_inbound" {
type = "ingress"
security_group_id = aws_security_group.instance.id
from_port = var.server_port
to_port = var.server_port
protocol = local.tcp_protocol
cidr_blocks = local.all_ips
}
data "aws_vpc" "default" {
default = true
}
data "aws_subnets" "default" {
filter {
name = "vpc-id"
values = [data.aws_vpc.default.id]
}
}
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 = local.http_port
protocol = "HTTP"
# By default, return a simple 404 page
default_action {
type = "fixed-response"
fixed_response {
content_type = "text/plain"
message_body = "404: page not found"
status_code = 404
}
}
}
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_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_security_group" "alb" {
name = "${var.cluster_name}-alb"
}
resource "aws_security_group_rule" "allow_http_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 = "us-east-2"
}
}
resource "aws_cloudwatch_metric_alarm" "high_cpu_utilization" {
alarm_name = "${var.cluster_name}-high-cpu-utilization"
namespace = "AWS/EC2"
metric_name = "CPUUtilization"
dimensions = {
AutoScalingGroupName = aws_autoscaling_group.example.name
}
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 1
period = 300
statistic = "Average"
threshold = 90
unit = "Percent"
}
resource "aws_cloudwatch_metric_alarm" "low_cpu_credit_balance" {
count = format("%.1s", var.instance_type) == "t" ? 1 : 0
alarm_name = "${var.cluster_name}-low-cpu-credit-balance"
namespace = "AWS/EC2"
metric_name = "CPUCreditBalance"
dimensions = {
AutoScalingGroupName = aws_autoscaling_group.example.name
}
comparison_operator = "LessThanThreshold"
evaluation_periods = 1
period = 300
statistic = "Minimum"
threshold = 10
unit = "Count"
}
locals {
http_port = 80
any_port = 0
any_protocol = "-1"
tcp_protocol = "tcp"
all_ips = ["0.0.0.0/0"]
}# ---------------------------------------------------------------------------------------------------------------------
# REQUIRED PARAMETERS
# You must provide a value for each of these parameters.
# ---------------------------------------------------------------------------------------------------------------------
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 "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 "enable_autoscaling" {
description = "If set to true, enable auto scaling"
type = bool
}
# ---------------------------------------------------------------------------------------------------------------------
# OPTIONAL PARAMETERS
# These parameters have reasonable defaults.
# ---------------------------------------------------------------------------------------------------------------------
variable "ami" {
description = "The AMI to run in the cluster"
type = string
default = "ami-0fb653ca2d3203ac1"
}
variable "server_text" {
description = "The text the web server should return"
type = string
default = "Hello, World"
}
variable "custom_tags" {
description = "Custom tags to set on the Instances in the ASG"
type = map(string)
default = {}
}
variable "server_port" {
description = "The port the server will use for HTTP requests"
type = number
default = 8080
}
[root@ip-172-31-61-209 webserver-cluster]#
2
# 환경변수에 지정
export TF_VAR_db_remote_state_bucket=$TF_VAR_bucket_name # description = "The name of the S3 bucket used for the database's remote state storage"
export TF_VAR_db_remote_state_key='stage/data-stores/mysql/terraform.tfstate' # description = "The name of the key in the S3 bucket used for the database's remote state storage"
# 환경변수 확인
export | grep TF_VAR_
3
# 초기화 및 검증 : 환경변수 적용 확인
terraform init && terraform plan -var server_text='Old server text'
# 배포
terraform apply -auto-approve -var server_text='Old server text'
# ALB DNS주소로 curl 접속 확인
ALBDNS=$(terraform output -raw alb_dns_name)
while true; do curl --connect-timeout 1 http://$ALBDNS ; echo; echo "------------------------------"; date; sleep 1; done
curl -s http://$ALBDNS
4
# [터미널1]
ALBDNS=<직접입력>
while true; do curl --connect-timeout 1 http://$ALBDNS ; echo; echo "------------------------------"; date; sleep 1; done
5
# [터미널2]
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
# 무중단 업그레이드 배포 : 환경변수 수정 적용 >> 오토스케일링그룹 2개중 기존 그룹은 5분 정도 후에 EC2가 삭제됨(오래 걸리니 맘 편히 기다리자)
terraform plan -var server_text='NEW server text'
terraform apply -auto-approve -var server_text='NEW server text'
6
# 삭제
# 각 폴더에서 리소스 삭제
cd /root/terraform-up-and-running-code/code/terraform/04-terraform-module/module-example/stage/data-stores/mysql
웹 , 디비 , 백엔드 저장소 순으로 삭제한다.
stage/services/webserver-cluster$ terraform destroy -auto-approve
stage/data-stores/mysql$ terraform destroy -auto-approve
03-terraform-state/file-layout-example/global/s3$ terraform destroy -auto-approve
다음
https://brunch.co.kr/@topasvga/2850
https://brunch.co.kr/@topasvga/2421
감사합니다.