1
2
3

T101 3기(=Terraform 101 Study)는 Terraform 실무 실습 스터디입니다.
CloudNet@ 유형욱, 윤서율님이 진행하시며, 책 "테라폼으로 시작하는 IaC"을 기반으로 진행하고 있습니다.

테라폼은 HCL(HashiCorp Configuration Language) 을 통해 인프라를 관리하고 프로비저닝합니다. HCL은 흡사 Go언어와 같이 반복문, 조건문, 함수가 제공되며 HCL 특유의 블록들이 구성되어 인프라를 코드로 관리할 수 있게 도와줍니다. 테라폼 기초 문법 이해로 책을 보는 것을 추천드립니다.

이번 포스트에서는 그 다음 단계로, 실무 예제를 통해 HCL 문법이 어떻게 사용되는지 살펴보겠습니다. 또한, Terraform Cloud를 이용하여 인프라를 프로비저닝하는 방법에 대해서도 알아보겠습니다. 실무 예제는 스터디 참고 책 9장(인프라 운영 및 관리)에서 소개해주는 EKS 모듈 예제로 선택하였습니다. 예제는 책 저자님의 레파지토리에서 확인이 가능합니다.

1.1 테라폼 모듈 이해

HCL 문법 사용에 들어가기 전 테라폼 모듈 구조를 알아보겠습니다. 테라폼 모듈은 재사용 가능한, 독립적인 코드 블록을 뜻합니다. 모듈을 사용하면 코드의 중복을 줄이고, 복잡한 인프라를 쉽게 관리할 수 있습니다. 참고 예제에서도 브랜치를 나뉘어 모듈 도입 전,후를 손 쉽게 비교할 수 있습니다. 레파지토리 브랜치 moularity 에서는 자주 사용하는 서비스(VPC 서비스들과 EKS)을 모듈로 구성하였고 이외의 추가 서비스(ECR, DB)를 따로 코드를 통해 구성하는 것을 확인할 수 있습니다. 모듈의 구조는 다음과 같습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
➜ tree .            
.
// 차일드 모듈 
├── modules         
│   ├── common
│   │   ├── README.md
│   │   ├── main.tf
│   │   ├── output.tf
│   │   └── variable.tf
│   └── eks 
│       ├── README.md
│       ├── aws-iam-authenticator
│       ├── iam-policy.json
│       ├── kubectl
│       ├── main.tf
│       ├── output.tf
│       ├── template
│       │   ├── cert-manager.tpl
│       │   ├── crd.tpl
│       │   ├── eniconfig.tpl
│       │   ├── ingress.tpl
│       │   ├── kubeconfig.tpl
│       │   └── sa.tpl
│       └── variable.tf
// 루트 모듈 
├── main.tf       
├── output.tf
└── variable.tf

폴더 modules를 기반으로 모듈을 구분하여 child, root 모듈로 구분합니다. 루트 모듈에는 사용할 변수들과 사용할 서비스를 구성하고, 차일드 모듈을 통해 실제 서비스를 구성합니다. 모듈을 통해 구성되는 서비스의 아키텍처는 다음과 같습니다.

<a href="https://github.com/terraform101/terraform-refactoring-and-modularity/tree/modularity">https://github.com/terraform101/terraform-refactoring-and-modularity/tree/modularity</a>

https://github.com/terraform101/terraform-refactoring-and-modularity/tree/modularity

1.2 모듈 내 문법 이해

EKS 모듈 예제를 통해 어떻게 사용되는 지 확인하겠습니다.

1.2.1 변수 사용

테라폼 구성 변수는 루트 모듈의 varible.tf을 통해 정의합니다. 변수를 살펴보자면, 변수의 민감 변수와 구성 변수로 나뉘어 집니다. 민감 변수는 환경 변수(TF로 시작) 나 테라폼 클라우드에서 저장한 변수를 통해 입력하는 것을 추천합니다. 민감 변수는 AWS 접근 키, 비밀키 등으로 구성됩니다.

1
2
3
4
5
6
7
8
9
// varible.tf 의 민감변수 정의
### FOR TEST
variable "AWS_ACCESS_KEY_ID" {}
variable "AWS_SECRET_ACCESS_KEY" {}
.. 
variable "ucmp-access-secret" {
  default   = ""
  sensitive = true
}

변수 사용의 또 다른 예로 예제 EC2 의 이미지를 data블록을 통해 AWS에 조회하여 사용합니다.

data5.png

data 블록 값들 정보는 테라폼 공식문서에서 확인할 수 있습니다.

data6.png

1.2.2 함수 사용

HCL은 자체적으로 내장 함수와 반복문, 조건문이 지원됩니다.

예제 모듈에서도 해당 문법을 통해 인프라를 프로비저닝하는데 사용됩니다. 사용 예는 다음과 같습니다.

  • count 조건문을 통한 서비스 구성 여부

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    // 변수 구성
    variable "enable_ecr" {
      type        = bool
      default     = false
      description = "ECR 활성/비활성"
    }
    variable "enable_elasticache" {
      type        = bool
      default     = false
      description = "Elasticache 활성/비활성"
    } 
    ..
    ..
    
    // ecr 구성 여부 
    resource "aws_ecr_repository" "ecr" {
      count = var.enable_ecr ? 1 : 0 
      name  = "ecr-${var.env}-${var.pjt}-imagerepository"
    ..
    }
    // elasticache_cluster 구성 여부  
    resource "aws_elasticache_cluster" "replica" {
      count = var.enable_elasticache ? 1 : 0
    
      cluster_id           = "elasticache-${var.env}-${var.pjt}-cluster-${count.index}"
      replication_group_id = aws_elasticache_replication_group.cluster[0].id
    }
    
  • 내장 함수을 통한 인프라 구성

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    // 1. 반복분 
    // 변수 developer_group_users에 정의된 목록만큼 iam user 생성 
    resource "aws_iam_user" "developer" {
      for_each = toset(var.developer_group_users) // toset 은 결과값의 목록을 데이터 타입으로 변환합니다. 
      name     = each.key
    
      tags = {
        Name = "developer_group_user"
      }
    }
    
    // 2. 내장 함수 
    // 접두사 제거로 결과 도메인 https://의 뒷 문자열을 가져와 oidc 구성에 사용됩니다.
    oidc = trimprefix("${aws_eks_cluster.cluster.identity[0].oidc[0].issuer}", "https://") 
    

1.2.3 프로비저너(provisioner)

변수나 함수인 경우 프로그래밍 코드에서의 변수와 함수처럼 직관적입니다. 반면, 인프라 프로비저닝 이후 로컬 머신에서 동작하는 과정은 익숙치가 않아서 인지 이해 하기가 쉽지 않아 따로 정리합니다. 프로비저너(provisioner)는 특정 리소스가 생성된 후에 추가적인 설정이나 작업을 자동화하기 위해 사용됩니다. 모듈 예제에서는 EC2 비밀 키 등록과 노드 그룹의 iam 정책 등록으로 사용되었습니다.

  • EC2 비밀 키 생성 및 등록

    ${self.private_key_pem} 변수가 tls_private_key.key로 생성된 키로 입력됩니다. 하시코프에서 제공하는 tls 리소스로 생성한 변수가 프로비저너를 통해 바로 사용되는 것을 인지해야 합니다. tls 모듈 정보는 테라폼 문서에서 확인이 가능합니다.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    // ssh 접속 키를 생성
    resource "tls_private_key" "key" {
      algorithm = "RSA"
    
      provisioner "local-exec" {
        command = "echo '${self.private_key_pem}' > ./ec2-${var.env}-${var.pjt}-bastion1.pem"
      }
    }
    
    resource "aws_key_pair" "keypair" {
      key_name   = "${var.pjt}-${var.env}-key"
      public_key = tls_private_key.key.public_key_openssh
    }
    
  • EKS addon 쿠버네티스 리소스 생성

    프로비저너의 또 다른 사용 예로 EKS addon 사용에 필요한 쿠버네티스 오브젝트 생성에 사용됩니다. 이는 EKS 생성 이후의 role-arn 등록 및 EKS 인프라 정보가 필요하기 때문입니다. 오브젝트 생성 구성은 동일하게 진행되며 이해를 돕기위해 Secondary CIDR 의 등록 과정을 공유하겠습니다.

    cloud25.png

    테라폼으로 addon 을 구성 했으나, 구성 환경에 따라 addon 경우의 수가 많기에 addon 부분도 모듈화를 하면 좋을 것 같습니다.

2. 테라폼 클라우드로 EKS 프로비저닝

Terraform Cloud는 팀이 Terraform을 공동으로 사용할 수 있게 도와주는 관리형 서비스로, 일관된 환경에서 Terraform 작업을 실행하고 공유 상태 및 비밀 데이터에 쉽게 접근할 수 있습니다. 무료로 제공되는 기본 버전은 버전 관리, 변수 공유, 안정적인 원격 환경에서의 Terraform 실행, 원격 상태 저장을 지원하며, 유료 버전에서는 사용자 수와 권한 설정 등이 확장됩니다. (공식문서 참고)

<a href="https://www.youtube.com/watch?v=SFHR3S-Znrw&amp;t=270s">https://www.youtube.com/watch?v=SFHR3S-Znrw&amp;t=270s</a>

https://www.youtube.com/watch?v=SFHR3S-Znrw&t=270s

예제 EKS 모듈을 테라폼 클라우드로 프로비저닝하겠습니다. 테라폼 클라우드에서 EKS를 프로비저닝하기 위해서는 추가 작업이 필요합니다.

2.1 Provider 설정

먼저 Cloud에서 사용할 조직과 워크스페이스를 설정해야 합니다. 루트 모듈 main.tf에서 옵션을 확인하여 설정해주세요.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
terraform {
  cloud {
    organization = "t101-hanhorang" // organization 설정
    hostname     = "app.terraform.io" // terraform cloud 기본 주소 (변경하지 않는 값입니다.) 
    workspaces { // 워크스페이스 설정
      name = "t101" 
    }
  }
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 3.0.0, < 4.0.0"
    }
  }
}

설정한 조직과 워크스페이스는 테라폼 클라우드 UI에서 확인할 수 있습니다.

cloud1-2.png

2.2 VSC 연동

테라폼 클라우드에서는 VCS 연동 기능을 지원합니다. VCS 연동 기능을 통해 깃허브에서 TF 코드를 관리할 수 있게 됩니다. 설정 방법은 간단합니다. 새로운 워크스페이스를 생성하여 단계별로 설정하면 됩니다.

cloud2-1.png

  • EKS 모듈화 참고 레파지토리를 확인하면 브랜치가 2개입니다. 레파지토리를 fork한 다음 브랜치를 moulraity 로 설정해주세요. 필자의 경우 rebase로 modularity 브랜치를 Main 브랜치로 병합했습니다.

    1
    2
    3
    4
    5
    
    git checkout modulaty
    git rebase main
    git checkout main
    git merge modulaty 
    git push origin main
    

마지막 단계의 Advanced options 에서 VCS 연동 관련 기능을 설정할 수 있습니다. Apply Method 에서는 terraform apply 여부를 설정할 수 있고, VCS Triggers 에서는 git 브랜치 변경에 따라 트리거할 수 있는 옵션을 설정할 수 있습니다. 필자의 경우 깃허브에서 브랜치 변동시 자동으로 terraform plan 까지만 확인할 수 있도록 설정하였습니다.

cloud16.png

옵션 설정 이후, TF 변수 설정도 진행 해주세요.

cloud178.png

2.3 TF 연동

예제 레파지토리를 연동하면 다음의 에러가 발생합니다. 가져온 레파지토리에 수정 작업이 필요합니다.

cloud4.png

  • EC2 이미지 data 값 수정 : 예제에서는 ucmp 라는 관리자 계정에서 이미지를 관리하지만, 필자의 경우 aws 에서 관리하는 이미지를 가져올 수 있도록 수정하였습니다.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    // main.tf
    data "aws_ami" "node_ami_id" {
      count       = var.enable_eks ? 1 : 0
      provider    = aws.ucmp_owner
      most_recent = true
      owners      = ["amazon"]
    
      filter {
        name   = "name"
        values = ["amzn2-ami-hvm*"]
      }
    
    } 
    
  • 사용하지 않는 output 값 제거 : 예제 프로젝트에서는 ecr를 사용하지 않으며, 오직 EKS만 배포할 예정입니다. 해당 옵션에 맞게 모듈을 수정해야합니다.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    // variable.tf 
    
    #####################
    ## eks-node
    #####################
    variable "enable_eks" {
      type        = bool
      default     = true
      description = "EKS 활성/비활성"
    }
    ..
    ..
    // output.tf 
    ~~output "ECR_REPOSITORY" {
      value       = aws_ecr_repository.ecr[0].name
      description = "ECR repository"
    }~~
    
    output "private_key" {
      value       = nonsensitive(tls_private_key.key.private_key_pem)
      description = "테라폼으로 생섣된 SSH 접속용 키값"
    }
    
    output "kubeconfig" {
      value       = module.eks.kubeconfig
      description = "kube-config 내용"
    }
    

코드 수정 이후 깃허브로 브랜치를 푸쉬하면 트리거를 통해 테라폼 클라우드에서 자동으로 검사를 진행합니다.

cloud188.png

테라폼 클라우드에서 확인하면 plan 결과를 확인할 수 있습니다. Plan 이후 프로비저닝 작업은 화면 아래의 기능을 통해 진행하면 됩니다.

cloud101.png

cloud102.png

마치며

테라폼 클라우드 기능 엄청나네요. 코드로 인프라를 관리한다면 도입을 추천드립니다. 책 저자님의 예제 모듈 TF 코드도 예술 작품을 보는 느낌이였습니다. 코드 공유해주셔서 감사합니다. 역량 발전에 큰 도움이 되었습니다.