Search

테라폼 기본 기능

CloudNetaStudy에서 진행하는 테라폼 스터디를 하며 관련한 내용을 정리한다. 원래 가시다님이 스터디를 진행했고, 지난 쿠버네티스 데이터베이스 오퍼레이터 스터디를 한번 해봤다. 하지만 이번엔 다른 분(유형욱님)이 진행하시고 이전보다 프라이빗?하게 진행하는 느낌이다. 스터디 관련해서 공개할 링크 같은게 없다…
아무튼 이번 스터디는 테라폼에 관련한 스터디이고 단순히 오픈소스 버전 테라폼 사용이 아닌 테라폼 클라우드(TFC) 사용까지 다룬다. 이 부분에 관심이 있어 스터디를 시작했다. 내가 업무에 쓰기 위해 PoC 할 때와 달리, 지금은 free tier 혜택이 많이 는거 같다
또 이 스터디는 테라폼으로 시작하는 IaC라는 책의 내용을 기반으로 한다. 따라서 “책”이라 언급하면 이 책을 뜻한다.
이번 글은 첫번째, 1주차 스터디를 마치고 그 내용을 정리한다. 1주차 스터디에서 다뤘던 내용의 일부를 정리할 것이다.
스터디는 핸즈온 위주로 진행된다. 하지만 나는 테라폼을 이미 어느정도 사용하고 있고 기초적인 사용법을 따라할 수 있게끔 정리하진 않으려 한다. 반대로 개념 위주로 도식을 통해 그리고 나의 개인적인 경험과 생각을 정리하려고 한다.

하시코프의 철학과 테라폼

하시코프 철학(Tao)은 현재 8가지 꼭지로 정리되며 테라폼은 그 중 세가지를 바탕으로 만들었다고 한다. 책과 위 링크의 이름이 완벽히 맞진 않아서 책(링크) 로 표기한다.

워크플로에 집중(Workflows, not technologies)

“기술이 아니라” 워크플로(작업 형식/방식)에 집중하여 문제(인프라 프로비저닝, 관리, 삭제)를 해결한다.
기술에 구애 받지 않는다. 워크플로를 더 간소화 하기 위해 도구(기술)을 선택하거나 직접 개발할 뿐이다. 문제 해결 자체에 더 집중할 수 있다.
문제 해결에 집중하는 방법, 생각이 인상적이다. 그런데 테라폼이 어떻게? 과연 그런가? 는 내가 사용하며 검증할 부분이다

코드형 인프라

이건 아마 Tao에서 다음 꼭지들로 나뉜게 아닌가 싶다.
Simple, Modular, Composable
Immutability
Versioning through Codification
Automation through Codification
넓게 보면 Communicating Sequential Processes와 Resilient Systems도 포함될 거 같다
각 꼭지에 대한 설명은 생략한다. 나는 개발을 했어서 당연하게 느껴지는 부분이 많다. 그리고 그런 장점들을 인프라 관리에 녹이는 것은 아주 좋아 보인다!
사실 테라폼의 state가 다른 코드에 비하면 생소한 개념인데, 어떤 철학에 부합해서 만들어진 것인지 봐야겠다.

실용주의(Pragmatism)

그저 허울 좋은 말일 수도 있으나 워크플로에서부터 강조한, 문제 해결 자체에 집중하겠다는, 가장 중요한 철학이라 생각한다.
물론 관건은 다시 테라폼이 어떻게? 테라폼이 정말 그런가? 이다.
사실 이 스터디 “아 테라폼 많이들 쓴다는데 생각보다 어려운데? 별론데?” 같은 마음이 들어 시작한 것이기 때문에, 계속 진행하며 초반에 살펴 본 이 철학들을 바탕으로 검증을 하는 것이 필요하다.
단순히 까려는게 아니라 더 잘 쓰기 위함이다.

테라폼 주요 명령(primary commands)

사전 준비

스터디의 환경 구성을 간단히 설명한다. 핸즈온에 집중한 글이 아니라고 했으니 자세한 세팅 대신 어떤 것을 쓴다고 언급만 한다.
terraform binary: 맥에서 tfenv 로 latest 설치(1.5.6)
AWS 계정
terraform binary의 가장 중요한 4개의 서브커맨드이다.
작업 공간을 준비한다(initialize a working directory)
테라폼 구성(Terraform configuration)
백엔드의 state 접근(accessing state in the configured backend)
플러그인 다운로드 및 설치(downloading and installing provider plugins)
모듈 다운로드(downloading modules)
실행 계획 즉, 변경사항의 미리보기다.
실제 리소스를 생성하지 않는다(dry-run)
변경사항을 미리 확인하거나
팀 동료와 공유하여 같이 검토하는데 쓸 수 있다.
plan 실행 시
원격 객체(리소스)의 현재 상태(current state)에 대하 Terraform state가 최신인지 확인
이전 state와 비교하여 현재 구성(코드)의 차이점 출력
적용(apply)할 경우, 원격 객체를 구성처럼 하는데에 대한 변경작업(actions) 제안
리소스 관점에서 plan 은 리소스를 실제로 만들거나 변경, 삭제하지 않고 그 계획을 diff 스타일로 보여준다(e.g. AWS EC2 생성)
plan 의 변경 작업을 실행한다.
위 예시의 plan 이라면 AWS EC2를 실제로 생성한다.
apply -destroy 의 alias이다.
-destroy 옵션은 원격 객체를 모두 없애고 Terraform state를 비운다.
작업 디렉토리의 모든 객체와 state를 없앨 때 사용한다.

HCL

HCL은 JSON에 완전 호환성이 있는 언어이다. JSON과는 달리 몇가지 장점이 있다.
변수 interpolation이 쉽다.
블록 타입이 있어 단순히 키-값 맵의 중첩보다 짧아진다.
하시코드 문서 곳곳에선 HCL 파일의 내용을 구성(configurations)이라 하는 코드와 같다고 볼 수 있다. 어차피 한번에 모든 문법을 다 살펴보는 것은 불가능하고 많이 쓰이는 블록에 대해 한 파일로 설명한다(어라? 노션 코드블록에서 지원하지 않는 타입이다 . 개인적으로 Vagrantfile을 보며 루비의 영향도 많이 받지 않았을까.. 싶었다).
terraform { # terraform 블록 required_version = "~> 1.3.0" # terraform 버전, semver based constraints required_providers { # providers 버전 random = { version = ">= 3.0.0, < 3.1.0" } aws = { version = "4.2.0" } } cloud { # 아직 배우지 않은 TFC 관련 구성 cloud 블록 organization = "<MY_ORG_NAME>" workspaces { name = "my-first-workspace" } } backend "local" { # backend 블록 path = "relative/path/to/terraform.tfstate" } } provider "aws" { # provider 블록, provider의 parameters 선언 region = "ap-northeast-2" } resource "aws_instance" "example" { # resources 블록 ami = "ami-1234567890abcdef" instance_type = "t2.micro" tags = { Name = "example-instance" } }
Ruby
복사

도전과제

[도전과제1] EC2 웹 서버 배포 + 리소스 생성 그래프 확인

Ubuntu 에 apache(httpd) 를 설치하고 index.html 생성(닉네임 출력)하는 userdata 를 작성해서 설정 배포 후 웹 접속 - 해당 테라폼 코드(파일)를 작성
terraform { required_version = "~> 1.5.6" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.15" } } } provider "aws" { region = "ap-northeast-2" } resource "aws_key_pair" "ssh_key" { key_name = "ssh_key" public_key = file("${path.module}/week-one-challenge.pub") } resource "aws_instance" "challenge_one" { ami = "ami-01e0199be84ecbd0d" instance_type = "t4g.nano" key_name = aws_key_pair.ssh_key.key_name vpc_security_group_ids = [ aws_security_group.challenge_one.id, ] user_data = <<-EOF #!/bin/bash sudo apt -y update sleep 15 sudo apt -y update sudo apt -y install apache2 sudo systemctl start apache2 sudo chown -R ubuntu:ubuntu /var/www/html chmod +x *.sh cat << EOM > /var/www/html/index.html <html> <head><title>Meow!</title></head> <body> <div style="width:800px;margin: 0 auto"> <!-- BEGIN --> <center><img src="http://placekitten.com/600/400"></img></center> <center><h2>flavono123</h2></center> <!-- END --> </div> </body> </html> EOM EOF tags = { Name = "week-one-challenge" } } resource "aws_security_group" "challenge_one" { name = "week-one-challenge" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["xxx.xxx.xxx.xxx/32"] } ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["xxx.xxx.xxx.xxx1/32"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } output "public_ip" { value = aws_instance.challenge_one.public_ip description = "The public IP of the Instance" }
Go
복사

[도전과제2] AWS S3/DynamoDB 백엔드

AWS S3/DynamoDB 백엔드 설정 실습
# 우선 아래 코드로 apply하여 backend에 필요한 s3 버킷과 dynomodb 생성 resource "aws_s3_bucket" "tfstate" { bucket = "week-one-challenge-username" resource "aws_s3_bucket_versioning" "tfstate_versioning" { bucket = aws_s3_bucket.tfstate.id versioning_configuration { status = "Enabled" } } resource "aws_dynamodb_table" "tfstate_lock" { name = "tfstate-lock" billing_mode = "PAY_PER_REQUEST" hash_key = "LockID" attribute { name = "LockID" type = "S" } } output "s3_bucket_name" { value = aws_s3_bucket.tfstate.bucket description = "The ARN of the S3 bucket" } output "dynamodb_table_name" { value = aws_dynamodb_table.tfstate_lock.name description = "The name of the DynamoDB table" } # 그 후 아래 backend 블록 추가하 후 tf init -reconfigure로 재구성 terraform { backend "s3" { bucket = "week-one-challenge-username" key = "terraform.tfstate" region = "ap-northeast-2" dynamodb_table = "tfstate-lock" encrypt = true } }
Ruby
복사
tf state pull 등 state 명령으로 확인
삭제 시 역순으로 진행
backend 블록 삭제 후 tf init -migrate-state(=default local backend 사용, state 파일 가져옴)
버킷의 terraform.tfstate 파일(키) 직접 삭제
그 후 리소스 블록 삭제

[도전과제3] lifecycle의 precondition

lifecycle의 precondition 실습 내용에서 step0.txt ~ step6.txt 총 7개의 파일 이름 중 하나가 일치 시 검증 조건 만족으로 코드 작성
variable "file_name" { type = string } resource "local_file" "abc" { content = "lifecycle - step in 0 ~ 6" filename = "${path.module}/${var.file_name}" lifecycle { precondition { condition = can(regex("step[0-6].txt", var.file_name)) error_message = "file name is not in \"step0.txt\" ~ \"step6.txt\"" } } }
Ruby
복사
검증 실패 시 에러(e.g. step10.txt)
╷ │ Error: Resource precondition failed │ │ on main.tf line 15, in resource "local_file" "abc":15: condition = can(regex("step[0-6].txt", var.file_name)) │ ├──────────────── │ │ var.file_name is "step10.txt" │ │ file name is not in "step0.txt" ~ "step6.txt"
Ruby
복사

[도전과제4] AWS 서비스 리소스 배포 + 리소스 생성 그래프 확인

Hashicorp AWS Provider Document 에 Example Usage 중 아무거나 1개의 AWS 서비스 리소스 배포 실습 - 링크
위 도전과제들로 대체