평소에 테라폼에 관심이 많아, 자격증도 취득하고 공부를 했다. 공부를 했지만, 아직 이것저것 헷갈리는 게 많다. 이를 구체화시키고, 실무에 대한 조언을 들을 겸 스터디에 참가하게 되었다. 나중에는 Golang 연습할 겸 코드도 뜯어보고 싶다. 스터디는 CloudNet에서 주관하고 유형욱님과 윤서율님이 진행해주신다.
1주차에서는 테라폼에 대해 알아보고, 실행 환경을 세팅한다. 이후 EC2를 배포해보면서 기본 문법과 명령어에 대해 학습한다.
On-premise : Terraform이라 불리는 형태로, 사용자의 컴퓨팅 환경에 오픈소스 바이너리툴인 테라폼을 통해 사용
라이선스를 변경되어서, 오픈소스 → 커뮤니티 에디션으로 변경된다.
Hosted SaaS : Terraform Cloud로 불리는 SaaS로 제공되는 구성 환경으로 하시코프가 관리하는 서버 환경이 제공
Private Install : Terraform Enterprise로 불리는 서버 설치형 구성 환경으로, 기업의 사내 정책에 따라 프로비저닝 관리가 외부 네트워크와 격리 - 링크
2,3 번은 기본적으로 GUI가 제공되며 Terraform Cloud는 Free 티어가 있다.
테라폼 클라우드 가격정책 비교
AWS_PAGER 옵션 제거
페이저를 비활성화하는 이유는 여러 가지가 있을 수 있습니다.
export AWS_PAGER=""
적용하면, 페이저가 없이 값만 출력된다.
AWS 계정 선택
여러 AWS 계정을 쓰는 경우, Profile을 환경변수로 선택할 수 있다.
export AWS_PROFILE="study"
Terraform에서도 별도의 provider block에서 세팅해줘야한다.
provider "aws" {
profile = "eks"
region = "ap-northeast-2"
}
추가) vscode aws toolkit 설정으로, 현재의 profile을 확인할 수 있다. 여러 AWS 계정을 쓸 경우, 편리하다.
HCL은 JSON을 본따 만든 언어이며, JSON보다 사람 친화적인 언어이다.
validate
-no-color
: 대부분의 명령과 함께 사용 가능, 로컬이 아닌 외부 실행 환경(젠킨스, Terraform Cloud, Github Action 등)을 사용하는 경우, 색상 표기 문자 ←[0m←[1m 가 표기 될 수 있다. 이 경우 -no-color 옵션으로 색상 표기 문자 없이 출력함. [참고]plan
-detailed-exitcode
: plan 추가 옵션으로, 파이프라인 설계에서 활용 가능, exitcode가 환경 변수로 구성됨apply
or destroy
-auto-approve
: 자동 승인 기능 부여 옵션우선, 해당 실습에서는 default VPC를 사용한다. 만약 사용하는 리전의 default VPC가 없다면 아래의 명령어를 실행하여 생성한다.
$ aws ec2 create-default-vpc
{
"Vpc": {
"CidrBlock": "172.31.0.0/16",
...
],
"IsDefault": true,
"Tags": []
}
}
EC2를 프로비저닝하는 기본적인 코드이다. 해당 코드를 실행하면, EC2를 생성할 수 있으며 접속할 public IP를 받을 수 있다.
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_instance" "example" {
ami = "*ami-0c9c942bd7bf113a2*"
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"
}
$ tf apply
...
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
public_ip = "54.180.106.217"
💡 만약 "빈번하게 서버의 포트가 변경되면" 어떻게 해야 될까요? 이런 "불편함을 해결"하려면 어떻게 해야 될까요?
→ user_data_replace_on_change = false
옵션을 추가한다!
코드를 아래와 같이 변경한다.
resource "aws_instance" "example" {
ami = "ami-0c9c942bd7bf113a2"
...
user_data_replace_on_change = false
tags = {
Name = "Single-WebSrv"
}
}
...
resource "aws_security_group" "instance" {
name = var.security_group_name
...
# lifecycle을 추가하면 다운타임을 줄일 수 있다.
lifecycle {
create_before_destroy = true
}
}
변경하기 이전과 이후에 생성과정을 보면 다른 것을 알 수 있다. 이전처럼 파괴하고 재생성이 아닌, 변경된 값을 업데이트한다.
# 이전
aws_security_group.instance: Destroying... [id=sg-03c90b3d559abb123]
aws_security_group.instance: Destruction complete after 1s
aws_security_group.instance: Creating...
aws_security_group.instance: Creation complete after 1s [id=sg-0de633908986b76ad]
# 라이프사이클 적용 후
...
aws_security_group.instance: Modifying... [id=sg-0de633908986b76ad]
aws_security_group.instance: Modifications complete after 1s [id=sg-0de633908986b76ad]
💡 만약 "user_data_replace_on_change = false" 설정 상태에서 userdata 를 변경 후 apply 하면 어떻게 될까요?
이 설정은 user_data
가 변경될 때 EC2 인스턴스를 새로 생성할 것인지, 아니면 기존 인스턴스를 유지할 것인지를 결정한다. false
는 인스턴스를 유지하는 옵션이며, 인스턴스를 유지한다.
# 적용 전
aws_instance.example: Destroying... [id=i-0794bd9bb343a948b]
...
aws_instance.example: Creating...
# "user_data_replace_on_change = false" 적용 후
aws_instance.example: Modifying... [id=i-0568341d533d4ca58]
aws_instance.example: Still modifying... [id=i-0568341d533d4ca58, 10s elapsed]
aws_instance.example: Still modifying... [id=i-0568341d533d4ca58, 20s elapsed]
Terraform 블록, 아래와 같이 내용을 잘 정리해주셨다.
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"
}
}
required_providers
에 정의terraform {
cloud {
hostname = "[app.terraform.io](http://app.terraform.io/)"
organization = "my-org"
workspades = {
name = "my-app-prod"
}
}
}
협업을 위해서는 s3 등 원격으로 저장해서 관리함, 기본적으로 lock을 지원한다. 로컬에서 간단하게 테스트할 수 있는 데, 로컬에서 apply
명령어를 실행하고, 승인을 기다릴 때 ls -al 명령어로 작업 디렉터리의 파일을 확인하면 .terraform.tfstate.lock.info
파일이 생성된 것을 확인할 수 있다.
cat .terraform.tfstate.lock.info | jq .
{
"ID": "b4dbfee6-a28f-04da-d235-5591414dbcbc",
"Operation": "OperationTypeApply",
"Info": "",
"Who": "kane@kanes-MacBook-Pro.local",
"Version": "1.5.6",
"Created": "2023-08-27T14:07:14.110318Z",
"Path": "terraform.tfstate"
}
-migrate-state
는 terraform.tfstate의 이전 구성에서 최신의 state 스냅샷을 읽고 기록된 정보를 새 구성으로 전환한다.-reconfigure
는 init을 실행하기 전에 terraform.tfstate 파일을 삭제해 테라폼을 처음 사용할 때처럼 이 작업 공간(디렉터리)을 초기화 하는 동작이다.Terraform의 .tfstate
파일 내의 serial
값은 상태 파일의 버전을 나타내며, 동시성 제어와 데이터 무결성 확인에도 중요한 역할을 합니다. 이 값은 Terraform 명령이 실행될 때마다 자동으로 증가하여, 상태 파일의 최신성과 일관성을 유지합니다. 그렇기에 backup 파일이 현재 state 파일보다 serial 번호가 낮다.
위의 EC2 실습에서 user_data 부분만 변경했다.
user_data = <<-EOF
#!/bin/bash
echo "T101 Study Kane" > index.html
nohup busybox httpd -f -p 8080 &
EOF
AWS S3/DynamoDB 백엔드
아래는 관련 코드이다.
provider "aws" {
profile = "eks"
region = "ap-northeast-2"
}
resource "aws_s3_bucket" "mys3bucket" {
bucket = "kane-t101study-tfstate"
}
# Enable versioning so you can see the full revision history of your state files
resource "aws_s3_bucket_versioning" "mys3bucket_versioning" {
bucket = aws_s3_bucket.mys3bucket.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_dynamodb_table" "mydynamodbtable" {
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}
output "s3_bucket_arn" {
value = aws_s3_bucket.mys3bucket.arn
description = "The ARN of the S3 bucket"
}
output "dynamodb_table_name" {
value = aws_dynamodb_table.mydynamodbtable.name
description = "The name of the DynamoDB table"
}
EC2를 배포하는 코드에 아래와 같은 원격 backend를 설정한다.
terraform {
backend "s3" {
bucket = "kane-t101study-tfstate"
key = "dev/terraform.tfstate"
region = "ap-northeast-2"
dynamodb_table = "terraform-locks"
# encrypt = true
}
}
배포한 후, AWS 콘솔에 접속하면 아래와 같이 table을 확인할 수 있다.
이제, s3를 모니터링하며 Terraform을 배포하여 state 파일이 정상적으로 변경되는 지 확인한다.
while true; do aws s3 ls s3://$NICKNAME-t101study-tfstate --recursive --human-readable --summarize ; echo "------------------------------"; date; sleep 1; done
Total Objects: 0
Total Size: 0 Bytes
------------------------------
Mon Aug 28 00:50:18 KST 2023
Total Objects: 0
Total Size: 0 Bytes
------------------------------
...
# 리소스 생성
------------------------------
Mon Aug 28 00:53:16 KST 2023
2023-08-28 00:50:56 21.1 KiB dev/terraform.tfstate
Total Objects: 1
Total Size: 21.1 KiB
------------------------------
Mon Aug 28 00:53:17 KST 2023
2023-08-28 00:53:18 22.4 KiB dev/terraform.tfstate
------------------------------
...
# 리소스 업데이트
------------------------------
Total Objects: 1
Total Size: 22.4 KiB
------------------------------
# 리소스 삭제
...
------------------------------
Mon Aug 28 00:56:04 KST 2023
2023-08-28 00:56:03 180 Bytes dev/terraform.tfstate
Total Objects: 1
Total Size: 180 Bytes
------------------------------
Mon Aug 28 00:56:05 KST 2023
아래와 같이 콘솔에서도 확인할 수 있다. 생성 순서는 아래에서부터 위 방향이다.