테라폼 설치 및 세팅
August 2023 (2208 Words, 13 Minutes)
블로그 내용은 ‘테라폼으로 시작하는 IaC’책을 기준으로 작성되었습니다.
IaC
Infrastructure as Code 의 줄인말로, IT 인프라를 코드로 관리하고 구성하는 접근방식입니다. 기존의 서버, DB, Network 구성과같은 인프라 구성요소들은 수동으로 설정하거나 전용 관리도구를 이용했습니다. 하지만 IaC는 이런 인프라 구성을 코드 형태로 표현함으로서 자동화, 버전 관리, 문서화 등의 이점을 갖습니다.
처음에 DevOps 분야의 공부를 시작했을때, VM 환경에서 코드를 관리하기위해 직접 VM을 생성해 셋팅을 진행했던 기억이 있습니다.
시간이 조금 흐른뒤에는 이런 SandBox 형태를 코드로 선언하는 Vagrant라는 도구를 이용해서 VM생성을 코드로 작성하기도 했었습니다.
이러한 구성을 Cloud와 Production에서 사용하기위해 확장된 형태의 IaC로 Terraform
을 사용하기로 했습니다.
Terraform 환경 구성
MAC 환경
brew 패키지를 이용해 설치합니다.
테라폼 버젼관리도구인 tfenv를 이용해 설치를 진행합니다.
tfenv 설치
brew install tfenv
설치 가능한 버전 확인
tfenv list-remote
버전 확인후 테라폼 설치
tfenv install 1.5.6
설치된 버전 확인
terraform version
자동완성 기능 추가
terraform -install-autocomplete
명령어를 수행하면 mac 의 zshrc 파일에 환경변수로 오토컴플릿 구문이 추가 됩니다
VS Code Extension 설치
VS Code의 Extension → HashiCorp HCL 설치
AWS CLI 설치 및 자격증명
MAC 환경에서 aws cli 설치
brew aws cli 설치
brew install awscli
aws cli 섳치후 버전 확인
aws --version
자격 증명
IAM계정의 정보에서 access key 확인
aws configure 진행
aws configure
... >> 입력
등록 후 등록정보 확인
aws configure list
인스턴스 배포하기
Amazon Linux 2 최신 AMI ID 찾기
자주 업데이트되어 최신화를 진행해줍니다
aws ec2 describe 명령어중, 원하는 패턴의 인스턴스 타입에 맞는 최신 ami id를 획득하는 script는 다음과 같습니다.
aws ssm get-parameters-by-path --path /aws/service/ami-amazon-linux-latest | grep -A 5 -E 'amzn2-ami-kernel-.*x86_64-gp2' | grep "Value" | awk -F' ' '{print $2}' | tr -d '",'
EC2 배포 실행
위에서 획득한 최신 AMI ID를 이용해 HCL (Hashicorp Configuration Language) 코드파일 생성 후 실행합니다.
ami_id=$(aws ssm get-parameters-by-path --path /aws/service/ami-amazon-linux-latest | grep -A 5 -E 'amzn2-ami-kernel-.*x86_64-gp2' | grep "Value" | awk -F' ' '{print $2}' | tr -d '",')
cat <<EOT > main.tf
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_instance" "example" {
ami = "$ami_id"
instance_type = "t2.micro"
}
EOT
배포 과정 : 해당 폴더에서 terraform init을 수행후 (기존에 실행했었으면 . terraform이 생성됨)
# 초기화
terraform init
ls -al
tree .terraform
# plan 확인
terraform plan
# apply 실행
terraform apply
Enter a value: yes 입력
인스턴스 생성
인스턴스 생성 모니터링
export AWS_PAGER=""
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
인스턴스 삭제
terraform destroy
Enter a value: yes 입력
웹서버 배포하기
기존에 배포 했었던 서버에 웹 서버를 추가해 실행합니다.
ami_id=$(aws ssm get-parameters-by-path --path /aws/service/ami-amazon-linux-latest | grep -A 5 -E 'amzn2-ami-kernel-.*x86_64-gp2' | grep "Value" | awk -F' ' '{print $2}' | tr -d '",')
cat <<EOT > main.tf
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_instance" "example" {
ami = "$ami_id"
instance_type = "t2.micro"
user_data = <<-EOF
#!/bin/bash
echo "Hello, T101 Study" > index.html
nohup busybox httpd -f -p 8080 &
EOF
tags = {
Name = "terraform-Study-101"
}
}
EOT
배포 실행
terraform init
terraform plan
terraform apply -auto-approve
하지만 배포를 실행해도 해당 주소 웹서버에 접속이 되지않는다.
Security Group이 없기때문에 코드를 마저 수정해 다시 apply합니다.
ami_id=$(aws ssm get-parameters-by-path --path /aws/service/ami-amazon-linux-latest | grep -A 5 -E 'amzn2-ami-kernel-.*x86_64-gp2' | grep "Value" | awk -F' ' '{print $2}' | tr -d '",')
cat <<EOT > main.tf
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_instance" "example" {
ami = "$ami_id"
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.instance.id]
user_data = <<-EOF
#!/bin/bash
echo "Hello, T101 Study" > index.html
nohup busybox httpd -f -p 8080 &
EOF
tags = {
Name = "Single-WebSrv"
}
}
resource "aws_security_group" "instance" {
name = var.security_group_name
ingress {
from_port = 8080
to_port = 8080
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
variable "security_group_name" {
description = "The name of the security group"
type = string
default = "terraform-example-instance"
}
output "public_ip" {
value = aws_instance.example.public_ip
description = "The public IP of the Instance"
}
그리고 다시 apply를 할때에는 기존의 terraform 코드를 삭제하지않고 바로 업데이트가 가능합니다.
terraform plan
terraform apply -auto-approve
변경된 Security Group 값에 맞는 인스턴스를 신규로 생성해 Public IP가 변경됩니다.
접속완료!
실습 완료후 인스턴스 삭제를 생활화합니다.
terraform destroy -auto-approve
IaC를 이용한 인프라 배포 후, 어플리케이션 설정을 할 수 있는 다양한 방법이 있습니다.
- Terraform Code의 Userdata 사용
- cloud-init 사용
- Packer 활용
- Provisiner Connections 활용
그리고 ansible과 연동으로 인프라 배포후 구성관리를 위한
- terraform-provider-ansible
https://registry.terraform.io/providers/ansible/ansible/latest/docs
https://github.com/ansible/terraform-provider-ansible/tree/main
HCL
HCL(HashiCorp Configuration Language)은 Hashicorp사에서 IaC와 구성 정보를 명시하기 위해 개발된 오픈 소스 도구입니다.
HCL 특징
- IaC는 코드를 통해 인프라를 관리하고 프로비저닝
- HCL이 테라폼에서의 코드영역. 읽기쉽고 빠르게 배울 수 있게 설계
- 선언적(declarative) 특성을 갖고 Turing-complete 언어적 특성
HCL의 장점
- JSON과 YAML은 기계 친화적인 언어로 제작
- HCL은 JSON보다 간결하고 읽기 쉽게 작성 가능 (50~70%가량 간결)
- JSON은 구문이 길어지고 주석이 지원되지 않음
HCL의 표현식은 다음과 같습니다.
// 한줄 주석 방법1
# 한줄 주석 방법2
/*
라인
주석
*/
locals {
key1 = "value1" # = 를 기준으로 키와 값이 구분되며
myStr = "TF ♡ UTF-8" # UTF-8 문자를 지원한다.
multiStr = <<EOF
Multi
Line
String
with anytext
EOF
boolean1 =true # boolean true
boolean2 =false # boolean false를 지원한다.
deciaml = 123 # 기본적으로 숫자는 10진수,
octal = 0123 # 0으로 시작하는 숫자는 8진수,
hexadecimal = "0xD5" # 0x 값을 포함하는 스트링은 16진수,
scientific = 1e10 # 과학표기 법도 지원한다.
# funtion 호출 예
myprojectname = format("%s is myproject name", var.project)
# 3항 연산자 조건문을 지원한다.
credentials = var.credentials == "" ? file(var.credentials_file) : var.credentials
}
테라폼 블록
Terraform에서 사용되는 HashiCorp Configuration Language (HCL)는 다양한 유형의 블록으로 구성되어있습니다. 테라폼의 버전이나 프로바이더 버전과 같은 값들은 기본적으로 설정이되기도하지만, 함께 작업할때는 버전을 명시적으로 선언하고 필요한 조건을 설정하여 실행 오류를 최소화 할것이 권장됩니다.
terraform {
required_version = "~> 1.3.0" # 테라폼 버전
required_providers { # 프로바이더 버전을 나열
random = {
version = ">= 3.0.0, < 3.1.0"
}
aws = {
version = "4.2.0"
}
}
cloud { # Cloud/Enterprise 같은 원격 실행을 위한 정보 [참고: Docs]
organization = "<MY_ORG_NAME>"
workspaces {
name = "my-first-workspace"
}
}
backend "local" { # state를 보관하는 위치를 지정 [참고: Docs, local, remote, s3]
path = "relative/path/to/terraform.tfstate"
}
}
버전을 명시해야 실행되는 시점에서 최신버전이 아닌, 버전을 명시해야 “언제든지 실행가능” 한 Desired & Immutable
상태를 보장 합니다.
버전체계는 시맨틱 버전관리 (Semantic Versioning) 방식을 따릅니다. 시맨틱 버전 관리는 소프트웨어의 버전을 표기할 때 일관된 방식을 제공하는 규칙입니다. 일반적으로 MAJOR.MINOR.PATCH
형태로 표현됩니다.
MAJOR
: 호환되지 않는 API 변경MINOR
: 기존 버전과 호환되는 새로운 기능 추가PATCH
: 기존 버전과 호환되는 버그 수정
테라폼에서의 버전 제약 구문은 다른 프로그래밍 언어의 종속성 관리와 매우 유사하게 동작합니다. 여기 몇 가지 주요 연산자와 그들이 어떻게 작동하는지에 대한 간단한 요약입니다:
=
또는 없음: 이 연산자는 오직 지정된 버전만을 허용합니다. 다른 조건과는 병합될 수 없습니다.!=
: 이 연산자는 지정된 버전을 제외하고 모든 것을 허용합니다.>, >=, <, <=
: 이 연산자들은 기본 수학적 비교를 수행하여 버전을 허용하거나 제외합니다.~>
: 이 연산자는 “거의 동일한” 버전을 허용합니다. 지정된 버전의 마지막 숫자만이 증가할 수 있습니다. 예를 들어,~> 1.2
는 1.3, 1.4 등을 허용하지만 2.0은 허용하지 않습니다.
이 연산자들을 사용하여 버전 의존성 문제를 명확하게 관리합니다
프로바이더 버전
- 테라폼 0.13 버전에서 대대적인 변화가 생겨 0.13 미만의 버전에서는 provider 블록에 함께 버전을 명시했지만, 0.13 버전 이후의 프로바이더 버전은 terraform 블록에서 required_providers에서 정의합니다.
- https://registry.terraform.io/browse/providers 링크를 참조하여 provider version에 맞게 코드를 작성합니다.
Cloud 블록
Terraform Cloud, Terraform Enterprise는 CLI, VCS, API 기반의 실행방식을 지원하며 cloud 블록으로 선언을 합니다.
# v1.1 이전
terraform {
backend "remote" {
hostname = "app.terraform.io"
organization = "my-org"
workspades = {
name = "my-app-prod"
}
}
}
# v1.1 이후
terraform {
cloud {
hostname = "app.terraform.io"
organization = "my-org"
workspades = {
name = "my-app-prod"
}
}
}
백엔드 블록
테라폼의 backend
블록은 Terraform의 상태 파일을 어디에 저장하고 어떻게 로드할지를 설정합니다. 이 상태 파일은 Terraform이 인프라를 관리하기 위해 필요한 메타데이터를 포함합니다.
백엔드 블록은 하나의 백엔드만 허용
됩니다
백엔드 설정은 terraform
블록 내에 위치해야 하며, 일반적으로 Terraform 코드의 최상위 레벨에 위치합니다.
state에는 외부로 노출되면 안되는 pw 및 인증서 정보와 같은 민감한 데이터
들이 포함될 수 있으므로 state의 접근제어 및 안전한 관리방안 대책 수립이 요구됩니다.
리소스 구성
리소스 블록은 Terraform 코드에서 실제 클라우드 리소스를 선언하고 생성합니다.
- 기본 구조:
resource
를 사용하여 리소스 블록을 시작합니다. - 리소스 유형:
resource
프로바이더에 종속적입니다. - 이름: 동일한 유형의 리소스를 구분하기 위해 사용합니다.
- 구성 인수: 중괄호
{}
안에는 해당 리소스의 속성이나 설정을 정의합니다.
코드 예시
resource "local_file" "abc" {
content = "123"
filename = "${path.module}/abc.txt"
}
resource "aws_instance" "web" {
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"
}
주의사항
- 프로바이더 블록 별도 선언 권장:
resource
블록에서 사용되는 리소스 유형은 특정 프로바이더에 종속됩니다. 따라서 별도로 프로바이더를 선언하는 것이 권장됩니다.
추가 구성 - 메타인수
- depends_on: 다른 리소스에 대한 종속성을 명시합니다.
- count: 지정된 개수만큼 리소스를 배포합니다.
- for_each: 데이터 배열을 기준으로 여러 리소스를 생성합니다.
- provider: 여러 프로바이더 중에서 어떤 것을 사용할지 명시합니다.
- lifecycle: 리소스의 수명주기를 관리합니다 (예: 리소스 파괴 방지).
- provisioner: 리소스 생성 후 추가 작업을 정의합니다.
- timeouts: 리소스 작업(생성, 업데이트, 삭제)에 대한 타임아웃을 설정할 수 있습니다.
초기화
terraform init
: 프로바이더와 관련된 설정을 초기화하고 필요한 플러그인을 다운로드합니다.
파일 구조
- 주로
main.tf
파일에 이러한 리소스 블록이 선언됩니다, 그러나 여러.tf
파일에 분리할 수도 있습니다.
이렇게 오늘은 테라폼을 구성하는 기본 구성요소들에 대해 알아보았습니다.