• Home
  • About
    • lahuman photo

      lahuman

      열심히 사는 아저씨

    • Learn More
    • Facebook
    • LinkedIn
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

#4 모듈

06 Nov 2022

Reading time ~8 minutes

모듈

  • 참고 링크
    • [Docs] Modules - 링크 / Overview - 링크
    • Terraform Registry - 링크 / Modules Browse - 링크

테라폼 모듈 소개 추천 영상 : (당근페이) 박병진님 - 확장 가능한 테라폼 코드 관리를 위한 원칙

https://youtu.be/yWhwZpzJ3no?t=2504

소개

배경 : 둘 이 상의 환경에서 코드 재사용, 여러 테라폼 리소스를 하나의 논리적 그룹으로 관리하기 위해 사용

graph LR; A(((User))); F{Elastic Load Balancer}; G{Elastic Load Balancer}; A --> F; A --> G; subgraph Staging; F --> B(EC2 Instance); F --> C(EC2 Instance); F --> D(EC2 Instance); F --> E(EC2 more...); B --> L[(MySQL on RDS)]; C --> L; D --> L; E --> L; end; subgraph Production; G --> H(EC2 Instance); G --> I(EC2 Instance); G --> J(EC2 Instance); G --> K(EC2 more...); H --> M[(MySQL on RDS)]; I --> M; J --> M; K --> M; end;

Staging 와 Production 환경에서 동일한 코드를 복사하여 사용하는 대신, 두 환경에서 동일한 모듈의 코드를 재사용할 수 있음

분류 : Root 모듈(실제로 수행하게 되는 작업 디렉터리의 테라폼 코드 모음) , Child 모듈, Published 모듈 - 링크

  • Child : 다른 모듈의 테라폼 코드 내에서 호출(참조)하기 위한 목적으로 작성된 테라폼 코드 모음

    모듈 소스 : Local paths, Terraform Registry, Github, G3 Buckets, GCS buckets 등 - 링크

  • Terraform Registry : 하시코프에서 공식적으로 운영하는 테라폼 프로바이더 및 모듈 저장소, 공개된 모듈을 쉽게 활용 가능 - 링크
    • [Github] terraform-aws-modules : 하시코프 ambassador 중 한 명인 Anton Babenko 가 리드, 가장 인기 있는 AWS 테라폼 모듈을 관리하는 Github 조직 - 링크

      학습 내용 : 모듈 기본 basics, 모듈 입력 inputs, 모듈과 로컬 locals, 모듈 출력 outputs, 모듈 주의 사항 gotchas, 모듈 버전 관리 versioning

[실습1] 로컬 Local

참고 링크 : spacelift gentledev10 tutorials

실습 따라하기 : iam user 생성

  • 신규 폴더 생성 후 작업
cat <<EOT > iamuser.tf
provider "aws" {
  region = "ap-northeast-2"
}

locals {
  name = "mytest"
  team = {
    group = "dev"
  }
}

resource "aws_iam_user" "myiamuser1" {
  name = "\${local.name}1"
  tags = local.team
}

resource "aws_iam_user" "myiamuser2" {
  name = "\${local.name}2"
  tags = local.team
}
EOT

확인

#
terraform init && terraform plan && terraform apply -auto-approve

# iam 사용자 리스트 확인
aws iam list-users | jq

# 삭제
terraform destroy -auto-approve

모듈 기본 Module Basics

모듈 : 폴더에 있는 모든 테라폼 구성 파일이 모듈임. 참고로 현재 작업 디렉터리의 모듈은 루트 root 모듈이라고 함

설명 : 예를 들어 ASG, ALB, 보안 그룹 등 리소스의 코드 stage/services/webserver-cluster 를 재사용 가능한 모듈로 바꾸어 보기

  • stage/services/webserver-cluster 리소스를 먼저 정리
  • modules 라는 최상위 폴더를 생성하고 모든 파일을 stage/services/webserver-cluster 에서 modules/services/webserver-cluster 로 이동함
  • 이 과정은 스테이징 환경과 프로덕션 환경을 참조하기 위한 modules 디렉터리를 생성한 것임.
  • 세 번째로 modules**/services/webserver-cluster 에서 main.tf 파일을 생성하고 **provider 정의를 제거함.
  • 이제 스테이징 환경에서 이 모듈을 사용 가능. 사용 구문은 아래와 같음.
module "<NAME>" {
  source = "<SOURCE>"

  [CONFIG ...]
}
  • NAME은 테라폼 코드 전체에서 참조하기 위해 사용하는 식별자이고, SOURCE는 modules**/services/webserver-cluster 같은 모듈 코드를 찾을 수 있는 **경로이며,
  • CONFIG는 그 모듈과 관련된 특정한 하나 이상의 인수로 구성됨.

Staging 예시

예를 들어 stage/services/webserver-cluster/main.tf 에 새 파일을 만들고 다음과 같이 webserver-cluster 모듈을 사용 할 수 있다

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

module "webserver_cluster" {
  source = "../../../modules/services/webserver-cluster"
}

Prodution 예시

그리고 아래와 같이 prod/services/webserver-cluster/main.tf 에 새 파일을 만들어 동일한 모듈을 프로덕션 환경에서 재사용할 수 있음

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

module "webserver_cluster" {
  source = "../../../modules/services/webserver-cluster"
}
  • 모듈을 적용하거나 source 파라미터를 수정하는 경우 반드시 terrraform init 실행이 필요하다
    • 즉, init 명령어 하나로 손쉽게 공급자와 모듈을 다운로드하고 백엔드를 구성함. ```bash $ terraform init Initializing modules…
  • webserver_cluster in ../../../modules/services/webserver-cluster

Brikman, Yevgeniy. Terraform: Up and Running (p. 214). O’Reilly Media. Kindle Edition.

    
### 마지막으로 실제 적용 시 실패 → **이유는 이름이 하드코딩 되어 있어서 중복 이름으로 실패** ⇒ **모듈**에도 **입력** 매개 변수를 만들자
    

## 모듈 입력 Module Inputs

### `입력` : 모듈에도 입력 변수 input variables 사용 가능

#### `설명` : 모듈에서 입력 변수 활용
##### 1. ***modules**/services/webserver-cluster/**variables**.tf* 에 새로운 **입력 변수 3개**를 추가

```bash
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
}
2. modules**/services/webserver-cluster/**main**.tf 에 하드 코딩된 이름 대신 **var.cluster_name 을 사용 → 아래는 ALB가 사용할 보안 그룹 이름에 사용
resource "aws_security_group" "alb" {
  name = "${var.cluster_name}-alb"

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
  • 그외 AWS 리소스도 하드 코딩되어 중복되지 않게 변경 : ex) 인스턴스 tag 리소스 : ”${var.cluster_name}-instance”
3. terrroform_remote_state 데이터소스 업데이트 : bucket 과 key 매개 변수 수정
data "terraform_remote_state" "db" {
  backend = "s3"

  config = {
    bucket = var.db_remote_state_bucket
    key    = var.db_remote_state_key
    region = "us-east-2"
  }
}
4. [스테이징 환경] stage/services/webserver-cluster/main.tf 에서 아래와 같이 새로운 입력 변수를 설정
module "webserver_cluster" {
  source = "../../../modules/services/webserver-cluster"

  cluster_name           = "webservers-stage"
  db_remote_state_bucket = "(YOUR_BUCKET_NAME)"
  db_remote_state_key    = "stage/data-stores/mysql/terraform.tfstate"
}
5. [프로덕션 환경] prod/services/webserver-cluster/main.tf 에서 아래와 같이 새로운 입력 변수를 설정
module "webserver_cluster" {
  source = "../../../modules/services/webserver-cluster"

  cluster_name           = "webservers-prod"
  db_remote_state_bucket = "(YOUR_BUCKET_NAME)"
  db_remote_state_key    = "prod/data-stores/mysql/terraform.tfstate"
}
  1. (옵션) 환경별 인스턴스 타입과 min/max 사이즈를 다르게 설정해보기. 이를 위해 modules/services/webserver-cluster/variables.tf 에 입력 변수 3개 추가.
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
}
7. 이후 modules/services/webserver-cluster/main.tf 에서 시작 구성을 업데이트
resource "aws_launch_configuration" "example" {
  image_id        = "ami-0fb653ca2d3203ac1"
  instance_type   = var.instance_type
  security_groups = [aws_security_group.instance.id]

  user_data = templatefile("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
  })

  # Required when using a launch configuration with an auto scaling group.
  lifecycle {
    create_before_destroy = true
  }
}
8. 마찬가지로 ASG 에 업데이트
resource "aws_autoscaling_group" "example" {
  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

  tag {
    key                 = "Name"
    value               = var.cluster_name
    propagate_at_launch = true
  }
}
9. [스테이징 환경] 에서 stage/services/webserver-cluster/main.tf 에 설정
module "webserver_cluster" {
  source = "../../../modules/services/webserver-cluster"

  cluster_name           = "webservers-stage"
  db_remote_state_bucket = "(YOUR_BUCKET_NAME)"
  db_remote_state_key    = "stage/data-stores/mysql/terraform.tfstate"

  instance_type = "t2.micro"
  min_size      = 2
  max_size      = 2
}
10. [프로덕션 환경] 에서 prod/services/webserver-cluster/main.tf 에 설정
module "webserver_cluster" {
  source = "../../../modules/services/webserver-cluster"

  cluster_name           = "webservers-prod"
  db_remote_state_bucket = "(YOUR_BUCKET_NAME)"
  db_remote_state_key    = "prod/data-stores/mysql/terraform.tfstate"

  instance_type = "m4.large"
  min_size      = 2
  max_size      = 10
}

모듈과 지역변수 Module Locals

지역변수 : 입력 변수 대신 모듈에서 변수를 정의

설명 : 로컬 Locals 는 코드를 보다 쉽게 읽기 유지 관리할 수 있게 도움을 줌

1. 현재 ALB 보안 그룹에 하드 코딩된 값
resource "aws_security_group" "alb" {
  name = "${var.cluster_name}-alb"

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
2. 입력 변수 대신 locals 블록에서 로컬 값 local values 으로 정의
locals {
  http_port    = 80
  any_port     = 0
  any_protocol = "-1"
  tcp_protocol = "tcp"
  all_ips      = ["0.0.0.0/0"]
}
3. 모듈에서 로컬 값 사용 시 이름은 모듈 내에서만 표시되므로 다른 모듈에는 영향을 미치지 않으며, 모듈 외부에서 이 값을 재정의할 수 없습니다.
  • 로컬 값을 읽으려면 아래 구문으로 된 로컬 참조 local reference 를 사용해야 함.
local.<NAME>
4. ALB 보안그룹을 포함해 모듈의 모든 보안 그룹에 적용
resource "aws_security_group" "alb" {
  name = "${var.cluster_name}-alb"
  
  ingress {
    from_port   = local.http_port
    to_port     = local.http_port
    protocol    = local.tcp_protocol
    cidr_blocks = local.all_ips
  }

  egress {
    from_port   = local.any_port
    to_port     = local.any_port
    protocol    = local.any_protocol
    cidr_blocks = local.all_ips
  }

모듈 출력 Module Outputs

출력 : 모듈에 출력 Output 값을 활용

- 설명 : 모듈에 ASG 이름 출력 후 [프로덕션] 환경의 ASG에 스케줄 기반 증감 설정 → 5장 조건부 활용

1. (예시) [프로덕션] 환경의 ASG에 스케줄 기반 증감 정책 : 매일 오전 8시 10대 증가 → 매일 오후 9시 2대로 감소
  • autoscaling_group_name 정보가 필요
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 8 * * *"
}

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 21 * * *"
}
2. 모듈에서 ASG 이름을 출력 변수로 설정 : /modules/services/webserver-cluster/outputs.tf
output "asg_name" {
  value       = aws_autoscaling_group.example.name
  description = "The name of the Auto Scaling Group"
}
3. 사용 방법
# 사용 구문
module.<MODULE_NAME>.<OUTPUT_NAME>

# 다음과 같이 사용
module.frontend.asg_name
4. ASG 코드 내용에 추가 : prod/services/webserver-cluster/main.tf
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
}
5. (옵션) ALB DNS 노출 : /modules/services/webserver-cluster/outputs.tf
output "alb_dns_name" {
  value       = aws_lb.example.dns_name
  description = "The domain name of the load balancer"
}
  • 그런 다음 에 output 추가 : stage/services/webserver-cluster/outputs.tf , prod/services/webserver-cluster/outputs.tf
output "alb_dns_name" {
  value       = module.webserver_cluster.alb_dns_name
  description = "The domain name of the load balancer"
}


terraformstudy Share Tweet +1