Ver3.0 ๐ Terraform์ผ๋ก ์์ํ๋ Infrastructure as Code์ ํด๋ผ์ฐ๋ ๋ฆฌ์์ค ๊ด๋ฆฌ ์๋ฒฝ ๊ฐ์ด๋

๐ Terraform์ผ๋ก ์์ํ๋ Infrastructure as Code์ ํด๋ผ์ฐ๋ ๋ฆฌ์์ค ๊ด๋ฆฌ ์๋ฒฝ ๊ฐ์ด๋
์ฝ๋ ํ ์ค๋ก ์ธํ๋ผ๋ฅผ ๋๋ฑ! ํด๋ผ์ฐ๋ ์๋์ ํ์ ๋๊ตฌ๋ฅผ ์น๊ตฌ์ฒ๋ผ ์ฝ๊ฒ ์๋ ค๋๋ฆด๊ฒ์ โ๏ธ
์๋
ํ์ธ์! ์ค๋์ ๊ฐ๋ฐ์๋ผ๋ฉด ๊ผญ ์์์ผ ํ Infrastructure as Code(IaC)์ Terraform์ ๋ํด ์ด์ผ๊ธฐํด๋ณผ๊ฒ์. ๐
ํน์ ์๋ฒ ์ค์ ํ ๋๋ง๋ค ์ฝ์ ํ๋ฉด ์์์ ํด๋ฆญ ํด๋ฆญ ํ๋๋ผ ์๋ชฉ์ด ์ํ์
จ๋์? ๐ฑ๏ธ ์๋๋ฉด "์ด? ๊ฐ๋ฐ ํ๊ฒฝ์ด๋ ์ด์ ํ๊ฒฝ์ด ์ ๋ค๋ฅด์ง?"๋ผ๋ฉฐ ๋นํฉํ ์ ์์ผ์ ๊ฐ์? ๊ทธ๋ ๋ค๋ฉด ์ด ๊ธ์ด ๋ฑ ๋ง์ ๊ฑฐ์์!
Terraform์ ๋ง์น ๋ ๊ณ ๋ธ๋ก์ฒ๋ผ ์ธํ๋ผ๋ฅผ ์ฝ๋๋ก ์กฐ๋ฆฝํ ์ ์๊ฒ ํด์ฃผ๋ ๋ง๋ฒ ๊ฐ์ ๋๊ตฌ๋๋๋ค. ๐งฑ ํด๋ผ์ฐ๋ ๋ฆฌ์์ค๋ฅผ ๊ด๋ฆฌํ๋ ๊ฒ ์ด๋ ๊ฒ ์ฌ์ธ ์๊ฐ ์์ด์!
๐ Infrastructure as Code(IaC)๊ฐ ๋ญ๊ธธ๋?
๋จผ์ ๊ธฐ๋ณธ๋ถํฐ ์ง๊ณ ๋์ด๊ฐ๊ฒ์. Infrastructure as Code๋ ๋ง ๊ทธ๋๋ก ์ธํ๋ผ๋ฅผ ์ฝ๋๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ์์ด์์.
์ ํต์ ์ธ ๋ฐฉ์์์๋ ์๋ฒ๋ฅผ ๊ตฌ์ถํ๋ ค๋ฉด ๋ฐ์ดํฐ์ผํฐ์ ๊ฐ์ ๋ฌผ๋ฆฌ์ ์ผ๋ก ์๋ฒ๋ฅผ ์ค์นํ๊ฑฐ๋, ํด๋ผ์ฐ๋ ์ฝ์์ ์ ์ํด์ ๋ง์ฐ์ค๋ก ํด๋ฆญํด๋ฆญํ๋ฉฐ ์ค์ ํ์ฃ . ์ด ๊ณผ์ ์ ์๊ฐ๋ ์ค๋ ๊ฑธ๋ฆฌ๊ณ , ์ฌ๋๋ง๋ค ์ค์ ์ด ๋ฌ๋ผ์ง ์ ์์ด์ ๋ฌธ์ ๊ฐ ๋ง์์ด์. ๐
๐ก IaC์ ํต์ฌ ์ฅ์
1. ์ฌํ์ฑ(Reproducibility) ๐
๊ฐ์ ์ฝ๋๋ฅผ ์คํํ๋ฉด ์ธ์ ์ด๋์๋ ๋์ผํ ์ธํ๋ผ๊ฐ ๋ง๋ค์ด์ ธ์. ๊ฐ๋ฐ ํ๊ฒฝ๊ณผ ์ด์ ํ๊ฒฝ์ด ์๋ฒฝํ๊ฒ ๋๊ฐ์์ง๋ ๊ฑฐ์ฃ !
2. ๋ฒ์ ๊ด๋ฆฌ(Version Control) ๐
Git์ฒ๋ผ ์ธํ๋ผ ๋ณ๊ฒฝ ์ด๋ ฅ์ ์ถ์ ํ ์ ์์ด์. "๋๊ฐ ์ธ์ ๋ญ ๋ฐ๊ฟจ์ง?"๋ผ๋ ์ง๋ฌธ์ ๋ช
ํํ๊ฒ ๋ตํ ์ ์๋ต๋๋ค.
3. ์๋ํ(Automation) โก
์๋ ์์
์ ์ต์ํํ๊ณ ์ค์๋ฅผ ์ค์ผ ์ ์์ด์. ํด๋ฆญ 100๋ฒ ํ ๊ฑฐ ๋ช
๋ น์ด ํ ์ค๋ก ๋!
4. ํ์
์ฉ์ด์ฑ(Collaboration) ๐ค
ํ์๋ค๊ณผ ์ฝ๋ ๋ฆฌ๋ทฐํ๋ฏ์ด ์ธํ๋ผ ๋ณ๊ฒฝ์ฌํญ์ ๊ฒํ ํ๊ณ ์น์ธํ ์ ์์ด์.
๐ง Terraform, ๋๋ ๋๊ตฌ๋?
Terraform์ HashiCorp์ด๋ผ๋ ํ์ฌ์์ ๋ง๋ ์คํ์์ค IaC ๋๊ตฌ์์. 2014๋
์ ์ฒ์ ์ถ์๋์๊ณ , ์ง๊ธ์ ํด๋ผ์ฐ๋ ์ธํ๋ผ ๊ด๋ฆฌ์ ์ฌ์ค์ ํ์ค(de facto standard)์ด ๋์๋ต๋๋ค! ๐
๊ฐ์ฅ ํฐ ํน์ง์ ๋ฉํฐ ํด๋ผ์ฐ๋ ์ง์์ด์์. AWS, Azure, GCP, ์ฌ์ง์ด ์จํ๋ ๋ฏธ์ค ํ๊ฒฝ๊น์ง ํ๋์ ๋๊ตฌ๋ก ๊ด๋ฆฌํ ์ ์์ฃ . ๋ง์น ๋ง๋ฅ ๋ฆฌ๋ชจ์ปจ ๊ฐ์ ๋๋์ด๋๊น์? ๐ฑ
๐ฏ Terraform์ ํต์ฌ ํน์ง
์ ์ธ์ ๊ตฌ๋ฌธ(Declarative Syntax)
"์ด๋ป๊ฒ(How)" ๋ง๋ค์ง๊ฐ ์๋๋ผ "๋ฌด์์(What)" ๋ง๋ค์ง๋ฅผ ์ ์ํด์. ์๋ฅผ ๋ค์ด "EC2 ์ธ์คํด์ค๋ฅผ ์ด๋ ๊ฒ ์ ๋ ๊ฒ ํด๋ฆญํด์ ๋ง๋ค์ด"๊ฐ ์๋๋ผ "t2.micro ํ์
์ EC2 ์ธ์คํด์ค๊ฐ ํ์ํด"๋ผ๊ณ ์ ์ธํ๋ ๊ฑฐ์ฃ .
์คํ ๊ณํ(Execution Plan)
์ค์ ๋ก ๋ณ๊ฒฝํ๊ธฐ ์ ์ "์ด๋ ๊ฒ ๋ฐ๋ ๊ฑฐ์์~"๋ผ๊ณ ๋ฏธ๋ฆฌ ๋ณด์ฌ์ค์. ๋ง์น ์ท์ ์ฌ๊ธฐ ์ ์ ์
์ด๋ณด๋ ๊ฒ์ฒ๋ผ์! ๐
๋ฆฌ์์ค ๊ทธ๋ํ(Resource Graph)
๋ฆฌ์์ค ๊ฐ์ ์์กด์ฑ์ ์๋์ผ๋ก ํ์
ํด์ ์ฌ๋ฐ๋ฅธ ์์๋ก ์์ฑ/์ญ์ ํด์. ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๋จผ์ ๋ง๋ค๊ณ ๊ทธ ๋ค์์ ์ ํ๋ฆฌ์ผ์ด์
์๋ฒ๋ฅผ ๋ง๋๋ ์์ด์ฃ .
์ํ ๊ด๋ฆฌ(State Management)
ํ์ฌ ์ธํ๋ผ์ ์ํ๋ฅผ ํ์ผ๋ก ์ ์ฅํด์ ์ถ์ ํด์. ์ด๊ฒ ์์ด์ผ Terraform์ด "์, ์ด๊ฑด ์ด๋ฏธ ์์ผ๋ ๊ฑด๋๋ฆฌ์ง ๋ง๊ณ , ์ ๊ฑด ์๋ก ๋ง๋ค์ด์ผ๊ฒ ๋ค"๋ผ๊ณ ํ๋จํ ์ ์๋ต๋๋ค.
๐ Terraform์ ๊ธฐ๋ณธ ๊ฐ๋ ๊ณผ ์ฉ์ด
Terraform์ ์ ๋๋ก ์ดํดํ๋ ค๋ฉด ๋ช ๊ฐ์ง ํต์ฌ ๊ฐ๋ ์ ์์์ผ ํด์. ํ๋์ฉ ์น์ ํ๊ฒ ์ค๋ช ํด๋๋ฆด๊ฒ์! ๐
1๏ธโฃ Provider (ํ๋ก๋ฐ์ด๋)
Provider๋ Terraform์ด ํน์ ํ๋ซํผ๊ณผ ์ํตํ ์ ์๊ฒ ํด์ฃผ๋ ํ๋ฌ๊ทธ์ธ์ด์์. AWS, Azure, GCP ๊ฐ์ ํด๋ผ์ฐ๋ ์๋น์ค๋ฟ๋ง ์๋๋ผ GitHub, Datadog ๊ฐ์ SaaS๋ ์ง์ํด์.
์๋ฅผ ๋ค์ด AWS ๋ฆฌ์์ค๋ฅผ ๊ด๋ฆฌํ๋ ค๋ฉด AWS Provider๋ฅผ ์ค์ ํด์ผ ํ๋ ๊ฑฐ์ฃ . ๋ง์น ์ธ๊ตญ์ธ๊ณผ ๋ํํ๋ ค๋ฉด ํต์ญ์ฌ๊ฐ ํ์ํ ๊ฒ์ฒ๋ผ์! ๐
2๏ธโฃ Resource (๋ฆฌ์์ค)
Resource๋ ์ค์ ๋ก ์์ฑํ๊ณ ๊ด๋ฆฌํ ์ธํ๋ผ ๊ตฌ์ฑ ์์์์. EC2 ์ธ์คํด์ค, S3 ๋ฒํท, RDS ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฑ์ด ๋ชจ๋ ๋ฆฌ์์ค๋๋๋ค.
๊ฐ ๋ฆฌ์์ค๋ ๊ณ ์ ํ ํ์
๊ณผ ์ด๋ฆ์ ๊ฐ์ง๊ณ ์์ด์. ์๋ฅผ ๋ค์ด aws_instance๋ EC2 ์ธ์คํด์ค๋ฅผ ๋ํ๋ด๋ ๋ฆฌ์์ค ํ์
์ด์ฃ .
3๏ธโฃ Module (๋ชจ๋)
Module์ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ Terraform ์ฝ๋ ๋ฌถ์์ด์์. ํจ์๋ ํด๋์ค์ฒ๋ผ ์๊ฐํ๋ฉด ๋ผ์! ๐ฆ
์๋ฅผ ๋ค์ด "์น ์๋ฒ ๊ตฌ์ฑ"์ด๋ผ๋ ๋ชจ๋์ ๋ง๋ค์ด๋๋ฉด, ์ฌ๋ฌ ํ๋ก์ ํธ์์ ๊ฐ์ ์ค์ ์ ๋ฐ๋ณตํด์ ์ธ ์ ์์ด์. DRY(Don't Repeat Yourself) ์์น์ ์งํฌ ์ ์์ฃ !
4๏ธโฃ State (์ํ)
State๋ Terraform์ด ๊ด๋ฆฌํ๋ ์ธํ๋ผ์ ํ์ฌ ์ํ๋ฅผ ์ ์ฅํ ํ์ผ์ด์์. ๊ธฐ๋ณธ์ ์ผ๋ก terraform.tfstate๋ผ๋ JSON ํ์ผ๋ก ์ ์ฅ๋์ฃ .
์ด ํ์ผ์ด ์์ด์ผ Terraform์ด "ํ์ฌ ๋ญ๊ฐ ์๊ณ , ๋ญ ์ถ๊ฐ/๋ณ๊ฒฝ/์ญ์ ํด์ผ ํ๋์ง" ์ ์ ์์ด์. ๋งค์ฐ ์ค์ํ ํ์ผ์ด๋ผ์ ํ ์์
์์๋ S3๋ Terraform Cloud ๊ฐ์ ์๊ฒฉ ์ ์ฅ์์ ๋ณด๊ดํ๋ ๊ฒ ์ข๋ต๋๋ค! ๐
5๏ธโฃ Variable (๋ณ์)
Variable์ ์ฝ๋๋ฅผ ์ ์ฐํ๊ฒ ๋ง๋ค์ด์ฃผ๋ ๋งค๊ฐ๋ณ์์์. ํ๊ฒฝ๋ณ๋ก ๋ค๋ฅธ ๊ฐ์ ์ฌ์ฉํ๊ฑฐ๋, ๋ฏผ๊ฐํ ์ ๋ณด๋ฅผ ๋ถ๋ฆฌํ ๋ ์ ์ฉํ์ฃ .
์๋ฅผ ๋ค์ด ๊ฐ๋ฐ ํ๊ฒฝ์์๋ t2.micro ์ธ์คํด์ค๋ฅผ, ์ด์ ํ๊ฒฝ์์๋ t2.large ์ธ์คํด์ค๋ฅผ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด ๋ณ์๋ก ์ฒ๋ฆฌํ๋ฉด ๋ผ์!
6๏ธโฃ Output (์ถ๋ ฅ)
Output์ Terraform ์คํ ํ ํ์ํ ์ ๋ณด๋ฅผ ์ถ๋ ฅํ๋ ๊ธฐ๋ฅ์ด์์. ์๋ฅผ ๋ค์ด ์์ฑ๋ ์๋ฒ์ IP ์ฃผ์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์๋ํฌ์ธํธ ๊ฐ์ ์ ๋ณด๋ฅผ ํ์ธํ ์ ์์ฃ .
๋ค๋ฅธ ๋ชจ๋์ด๋ ์์คํ
์์ ์ด ์ ๋ณด๋ฅผ ํ์ฉํ ์๋ ์์ด์. ๋ง์น ํจ์์ return ๊ฐ ๊ฐ์ ๊ฐ๋
์ด๋๋๋ค! ๐ค
๐ป Terraform ์ค์น์ ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
์, ์ด์ ์ค์ ์ผ๋ก ๋ค์ด๊ฐ๋ณผ๊น์? Terraform์ ์ค์นํ๊ณ ๊ฐ๋จํ ์์ ๋ฅผ ์คํํด๋ณด๊ฒ ์ต๋๋ค! ๐ฎ
๐ฝ ์ค์น ๋ฐฉ๋ฒ (์ด์์ฒด์ ๋ณ)
macOS (Homebrew ์ฌ์ฉ)
ํฐ๋ฏธ๋์ ์ด๊ณ ๋ค์ ๋ช
๋ น์ด๋ฅผ ์
๋ ฅํ์ธ์:
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
Windows (Chocolatey ์ฌ์ฉ)
PowerShell์ ๊ด๋ฆฌ์ ๊ถํ์ผ๋ก ์ด๊ณ :
choco install terraform
Linux (Ubuntu/Debian)
๋ค์ ๋ช
๋ น์ด๋ค์ ์์๋๋ก ์คํํ์ธ์:
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform
์ค์น๊ฐ ์๋ฃ๋์๋์ง ํ์ธํ๋ ค๋ฉด:
terraform version
์ด๋ ๊ฒ ํ๋ฉด ์ค์น๋ Terraform ๋ฒ์ ์ด ํ์๋ ๊ฑฐ์์! โ
๐ ์ฒซ ๋ฒ์งธ Terraform ์ฝ๋ ์์ฑํ๊ธฐ
๊ฐ๋จํ AWS EC2 ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ ์์ ๋ฅผ ๋ง๋ค์ด๋ณผ๊ฒ์. ๋จผ์ ์์
๋๋ ํ ๋ฆฌ๋ฅผ ๋ง๋ค๊ณ main.tf ํ์ผ์ ์์ฑํ์ธ์.
# Provider ์ค์
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "ap-northeast-2" # ์์ธ ๋ฆฌ์
}
# EC2 ์ธ์คํด์ค ๋ฆฌ์์ค ์ ์
resource "aws_instance" "my_server" {
ami = "ami-0c9c942bd7bf113a2" # Amazon Linux 2023
instance_type = "t2.micro"
tags = {
Name = "MyFirstTerraformServer"
Environment = "Development"
}
}
# ์์ฑ๋ ์ธ์คํด์ค์ ํผ๋ธ๋ฆญ IP ์ถ๋ ฅ
output "instance_public_ip" {
description = "EC2 ์ธ์คํด์ค์ ํผ๋ธ๋ฆญ IP ์ฃผ์"
value = aws_instance.my_server.public_ip
}
์ฝ๋๋ฅผ ํ๋์ฉ ๋ฏ์ด๋ณผ๊น์? ๐
terraform ๋ธ๋ก: ํ์ํ Provider์ ๋ฒ์ ์ ๋ช
์ํด์. ์ด๊ฑด ๋ง์น ํ๋ก์ ํธ์ package.json์ด๋ requirements.txt ๊ฐ์ ๊ฑฐ์์.
provider ๋ธ๋ก: AWS Provider๋ฅผ ์ค์ ํ๊ณ ์์ธ ๋ฆฌ์ (ap-northeast-2)์ ์ง์ ํ์ด์.
resource ๋ธ๋ก: ์ค์ ๋ก ์์ฑํ EC2 ์ธ์คํด์ค๋ฅผ ์ ์ํ์ด์. AMI ID, ์ธ์คํด์ค ํ์
, ํ๊ทธ ๋ฑ์ ์ค์ ํ์ฃ .
output ๋ธ๋ก: ์์ฑ๋ ์ธ์คํด์ค์ ํผ๋ธ๋ฆญ IP๋ฅผ ์ถ๋ ฅํ๋๋ก ํ์ด์. ๋์ค์ SSH ์ ์ํ ๋ ํ์ํ๋๊น์!
โก Terraform ์ํฌํ๋ก์ฐ ์คํํ๊ธฐ
Terraform์ ๊ธฐ๋ณธ ์ํฌํ๋ก์ฐ๋ ํฌ๊ฒ 4๋จ๊ณ๋ก ์ด๋ฃจ์ด์ ธ์. ํ๋์ฉ ์คํํด๋ณผ๊ฒ์!
Step 1: terraform init ๐ฌ
์์ ๋๋ ํ ๋ฆฌ๋ฅผ ์ด๊ธฐํํ๋ ๋ช ๋ น์ด์์. Provider ํ๋ฌ๊ทธ์ธ์ ๋ค์ด๋ก๋ํ๊ณ ๋ฐฑ์๋๋ฅผ ์ค์ ํ์ฃ .
terraform init
์คํํ๋ฉด .terraform ๋๋ ํ ๋ฆฌ๊ฐ ์์ฑ๋๊ณ ํ์ํ ํ๋ฌ๊ทธ์ธ๋ค์ด ๋ค์ด๋ก๋๋ผ์. ํ๋ก์ ํธ๋ฅผ ์ฒ์ ์์ํ ๋๋ Provider๋ฅผ ์ถ๊ฐ/๋ณ๊ฒฝํ์ ๋ ์คํํด์ผ ํด์!
Step 2: terraform plan ๐
์คํ ๊ณํ์ ํ์ธํ๋ ๋ช ๋ น์ด์์. ์ค์ ๋ก ๋ณ๊ฒฝํ์ง๋ ์๊ณ , "์ด๋ ๊ฒ ๋ฐ๋ ๊ฑฐ์์"๋ผ๊ณ ๋ฏธ๋ฆฌ ๋ณด์ฌ์ค์.
terraform plan
์ถ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ์์ฑ(+), ๋ณ๊ฒฝ(~), ์ญ์ (-) ๋ ๋ฆฌ์์ค๋ค์ด ํ์๋ผ์. ์ด ๋จ๊ณ์์ ๊ผผ๊ผผํ ํ์ธํ๋ ๊ฒ ์ค์ํด์! ์ค์๋ก ์ด์ ์๋ฒ๋ฅผ ๋ ๋ ค๋ฒ๋ฆฌ๋ ์ผ์ ๋ฐฉ์งํ ์ ์๊ฑฐ๋ ์. ๐
Step 3: terraform apply ๐
์ค์ ๋ก ์ธํ๋ผ๋ฅผ ์์ฑ/๋ณ๊ฒฝํ๋ ๋ช ๋ น์ด์์. ์ง์ง ๋ง๋ฒ์ด ์ผ์ด๋๋ ์๊ฐ์ด์ฃ !
terraform apply
์คํํ๋ฉด ๋ค์ ํ ๋ฒ ์คํ ๊ณํ์ ๋ณด์ฌ์ฃผ๊ณ "์ ๋ง ์งํํ ๊น์?"๋ผ๊ณ ๋ฌผ์ด๋ด์. yes๋ฅผ ์
๋ ฅํ๋ฉด ์ค์ ๋ก ๋ฆฌ์์ค๊ฐ ์์ฑ๋ผ์.
์๋์ผ๋ก ์น์ธํ๊ณ ์ถ๋ค๋ฉด -auto-approve ์ต์
์ ์ฌ์ฉํ ์ ์์ด์. ํ์ง๋ง ์ด์ ํ๊ฒฝ์์๋ ๊ถ์ฅํ์ง ์์์!
terraform apply -auto-approve # ์ฃผ์ํด์ ์ฌ์ฉ!
Step 4: terraform destroy ๐ฅ
์์ฑํ ๋ชจ๋ ๋ฆฌ์์ค๋ฅผ ์ญ์ ํ๋ ๋ช ๋ น์ด์์. ํ ์คํธ๊ฐ ๋๋ฌ๊ฑฐ๋ ๋ ์ด์ ํ์ ์์ ๋ ์ฌ์ฉํ์ฃ .
terraform destroy
์ด๊ฒ๋ ํ์ธ ์ ์ฐจ๋ฅผ ๊ฑฐ์ณ์. ์ค์๋ก ์ด์ ํ๊ฒฝ์ ๋ ๋ ค๋ฒ๋ฆฌ์ง ์๋๋ก ์กฐ์ฌํ์ธ์! ๐ฅ
๐จ ๋ณ์์ ์ถ๋ ฅ์ ํ์ฉํ ์ ์ฐํ ์ฝ๋ ์์ฑ
ํ๋์ฝ๋ฉ๋ ๊ฐ๋ค์ ๋ณ์๋ก ๋ถ๋ฆฌํ๋ฉด ์ฝ๋๊ฐ ํจ์ฌ ์ ์ฐํด์ ธ์. ํ๊ฒฝ๋ณ๋ก ๋ค๋ฅธ ์ค์ ์ ์ฝ๊ฒ ์ ์ฉํ ์ ์์ฃ ! ๐ง
๐ฅ ๋ณ์ ์ ์ํ๊ธฐ (variables.tf)
variable "aws_region" {
description = "AWS ๋ฆฌ์ "
type = string
default = "ap-northeast-2"
}
variable "instance_type" {
description = "EC2 ์ธ์คํด์ค ํ์
"
type = string
default = "t2.micro"
}
variable "instance_name" {
description = "EC2 ์ธ์คํด์ค ์ด๋ฆ"
type = string
default = "MyTerraformServer"
}
variable "environment" {
description = "ํ๊ฒฝ (dev, staging, prod)"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "ํ๊ฒฝ์ dev, staging, prod ์ค ํ๋์ฌ์ผ ํฉ๋๋ค."
}
}
๋ณ์์๋ ํ์ , ๊ธฐ๋ณธ๊ฐ, ์ค๋ช , ์ฌ์ง์ด ์ ํจ์ฑ ๊ฒ์ฌ๊น์ง ์ถ๊ฐํ ์ ์์ด์! ์ ์์ ์์๋ environment ๋ณ์๊ฐ ์ ํด์ง ๊ฐ๋ง ๋ฐ๋๋ก ์ ํํ์ฃ . ๐
๐ ๋ณ์ ์ฌ์ฉํ๊ธฐ (main.tf ์์ )
provider "aws" {
region = var.aws_region
}
resource "aws_instance" "my_server" {
ami = "ami-0c9c942bd7bf113a2"
instance_type = var.instance_type
tags = {
Name = var.instance_name
Environment = var.environment
}
}
๐พ ๋ณ์ ๊ฐ ์ ๋ฌํ๊ธฐ
๋ณ์ ๊ฐ์ ์ ๋ฌํ๋ ๋ฐฉ๋ฒ์ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์์ด์:
1. ๋ช ๋ น์ค์์ ์ง์ ์ ๋ฌ
terraform apply -var="environment=prod" -var="instance_type=t2.small"
2. ๋ณ์ ํ์ผ ์ฌ์ฉ (terraform.tfvars)
aws_region = "us-west-2"
instance_type = "t2.small"
instance_name = "ProductionServer"
environment = "prod"
์ด ํ์ผ์ ๋ง๋ค์ด๋๋ฉด ์๋์ผ๋ก ์ฝ์ด์์!
3. ํ๊ฒฝ ๋ณ์ ์ฌ์ฉ
export TF_VAR_environment="dev"
export TF_VAR_instance_type="t2.micro"
terraform apply
4. ๋ํํ ์
๋ ฅ
๊ธฐ๋ณธ๊ฐ์ด ์๋ ๋ณ์๋ ์คํ ์ ์
๋ ฅ์ ์๊ตฌํด์.
๐ ๋ฏผ๊ฐํ ์ ๋ณด ๋ค๋ฃจ๊ธฐ
๋น๋ฐ๋ฒํธ๋ API ํค ๊ฐ์ ๋ฏผ๊ฐํ ์ ๋ณด๋ sensitive = true ์์ฑ์ ์ถ๊ฐํ์ธ์:
variable "db_password" {
description = "๋ฐ์ดํฐ๋ฒ ์ด์ค ๋น๋ฐ๋ฒํธ"
type = string
sensitive = true
}
์ด๋ ๊ฒ ํ๋ฉด Terraform ์ถ๋ ฅ์์ ๊ฐ์ด ๊ฐ๋ ค์ ธ์. ํ์ง๋ง State ํ์ผ์๋ ํ๋ฌธ์ผ๋ก ์ ์ฅ๋๋, State ํ์ผ ์์ฒด๋ฅผ ์ํธํํ๊ฑฐ๋ ์ ๊ทผ์ ์ ํํด์ผ ํด์! ๐
๐๏ธ ์ค์ ์์ : ์์ ํ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ธํ๋ผ ๊ตฌ์ถ
์ด์ ์ข ๋ ์ค์ฉ์ ์ธ ์์ ๋ฅผ ๋ง๋ค์ด๋ณผ๊ฒ์. VPC, ์๋ธ๋ท, ๋ณด์ ๊ทธ๋ฃน, EC2 ์ธ์คํด์ค, RDS ๋ฐ์ดํฐ๋ฒ ์ด์ค๊น์ง ํฌํจ๋ ์์ ํ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ธํ๋ผ๋ฅผ ๊ตฌ์ถํด๋ณด๊ฒ ์ต๋๋ค! ๐ฏ
# VPC ์์ฑ
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "main-vpc"
}
}
# ํผ๋ธ๋ฆญ ์๋ธ๋ท ์์ฑ
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-northeast-2a"
map_public_ip_on_launch = true
tags = {
Name = "public-subnet"
}
}
# ํ๋ผ์ด๋น ์๋ธ๋ท ์์ฑ
resource "aws_subnet" "private" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.2.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "private-subnet"
}
}
# ์ธํฐ๋ท ๊ฒ์ดํธ์จ์ด
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "main-igw"
}
}
# ๋ผ์ฐํ
ํ
์ด๋ธ
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
Name = "public-route-table"
}
}
# ๋ผ์ฐํ
ํ
์ด๋ธ ์ฐ๊ฒฐ
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
# ์น ์๋ฒ ๋ณด์ ๊ทธ๋ฃน
resource "aws_security_group" "web" {
name = "web-sg"
description = "Security group for web servers"
vpc_id = aws_vpc.main.id
ingress {
description = "HTTP from anywhere"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "HTTPS from anywhere"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "SSH from my IP"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["YOUR_IP/32"] # ๋ณธ์ธ IP๋ก ๋ณ๊ฒฝ
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "web-security-group"
}
}
# EC2 ์ธ์คํด์ค
resource "aws_instance" "web" {
ami = "ami-0c9c942bd7bf113a2"
instance_type = "t2.micro"
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.web.id]
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "<h1>Hello from Terraform!</h1>" > /var/www/html/index.html
EOF
tags = {
Name = "web-server"
}
}
# ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ณด์ ๊ทธ๋ฃน
resource "aws_security_group" "db" {
name = "db-sg"
description = "Security group for database"
vpc_id = aws_vpc.main.id
ingress {
description = "MySQL from web servers"
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [aws_security_group.web.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "db-security-group"
}
}
# RDS ์๋ธ๋ท ๊ทธ๋ฃน
resource "aws_db_subnet_group" "main" {
name = "main-db-subnet-group"
subnet_ids = [aws_subnet.private.id, aws_subnet.public.id]
tags = {
Name = "main-db-subnet-group"
}
}
# RDS ์ธ์คํด์ค
resource "aws_db_instance" "main" {
identifier = "main-db"
engine = "mysql"
engine_version = "8.0"
instance_class = "db.t3.micro"
allocated_storage = 20
storage_type = "gp2"
db_name = "myapp"
username = "admin"
password = var.db_password # ๋ณ์๋ก ๊ด๋ฆฌ
db_subnet_group_name = aws_db_subnet_group.main.name
vpc_security_group_ids = [aws_security_group.db.id]
skip_final_snapshot = true
tags = {
Name = "main-database"
}
}
# ์ถ๋ ฅ
output "web_server_public_ip" {
description = "์น ์๋ฒ์ ํผ๋ธ๋ฆญ IP"
value = aws_instance.web.public_ip
}
output "web_server_url" {
description = "์น ์๋ฒ URL"
value = "http://${aws_instance.web.public_ip}"
}
output "database_endpoint" {
description = "๋ฐ์ดํฐ๋ฒ ์ด์ค ์๋ํฌ์ธํธ"
value = aws_db_instance.main.endpoint
sensitive = true
}
์! ์ฝ๋๊ฐ ๊ธธ์ด ๋ณด์ด์ง๋ง ํ๋์ฉ ๋ฏ์ด๋ณด๋ฉด ์ดํดํ๊ธฐ ์ฌ์์. ๐
์ด ์ฝ๋๋ ์์ ํ ๊ฒฉ๋ฆฌ๋ ๋คํธ์ํฌ ํ๊ฒฝ(VPC)์ ๋ง๋ค๊ณ , ํผ๋ธ๋ฆญ ์๋ธ๋ท์ ์น ์๋ฒ๋ฅผ, ํ๋ผ์ด๋น ์๋ธ๋ท์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๋ฐฐ์นํด์. ๋ณด์ ๊ทธ๋ฃน์ผ๋ก ์ ๊ทผ์ ์ ์ดํ๊ณ , ์น ์๋ฒ๋ ์ธํฐ๋ท์์ ์ ๊ทผ ๊ฐ๋ฅํ์ง๋ง ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ์น ์๋ฒ์์๋ง ์ ๊ทผํ ์ ์๋๋ก ์ค์ ํ์ฃ .
์ด๋ฐ ๊ตฌ์กฐ๋ฅผ ์๋์ผ๋ก ๋ง๋ค๋ ค๋ฉด AWS ์ฝ์์์ ์์ญ ๋ฒ ํด๋ฆญํด์ผ ํ๋๋ฐ, Terraform์ผ๋ก๋ terraform apply ํ ๋ฒ์ด๋ฉด ๋์ด์์! ๐
๐ฆ ๋ชจ๋์ ํ์ฉํ ์ฝ๋ ์ฌ์ฌ์ฉ
๊ฐ์ ํจํด์ ์ธํ๋ผ๋ฅผ ์ฌ๋ฌ ๋ฒ ๋ง๋ค์ด์ผ ํ๋ค๋ฉด ๋ชจ๋์ ์ฌ์ฉํ์ธ์! ๋ชจ๋์ Terraform ์ฝ๋๋ฅผ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ๋ก ๋ง๋๋ ๋ฐฉ๋ฒ์ด์์. ๐งฉ
๐๏ธ ๋ชจ๋ ๊ตฌ์กฐ ๋ง๋ค๊ธฐ
ํ๋ก์ ํธ ๊ตฌ์กฐ๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๋ง๋ค์ด๋ณผ๊ฒ์:
project/
โโโ modules/
โ โโโ web-server/
โ โโโ main.tf
โ โโโ variables.tf
โ โโโ outputs.tf
โโโ main.tf
๐ ๋ชจ๋ ์ฝ๋ ์์ฑ (modules/web-server/main.tf)
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = var.instance_type
subnet_id = var.subnet_id
vpc_security_group_ids = [aws_security_group.web.id]
user_data = var.user_data
tags = {
Name = var.server_name
Environment = var.environment
}
}
resource "aws_security_group" "web" {
name = "${var.server_name}-sg"
description = "Security group for ${var.server_name}"
vpc_id = var.vpc_id
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
๐ฅ ๋ชจ๋ ๋ณ์ ์ ์ (modules/web-server/variables.tf)
variable "ami_id" {
description = "AMI ID"
type = string
}
variable "instance_type" {
description = "์ธ์คํด์ค ํ์
"
type = string
default = "t2.micro"
}
variable "subnet_id" {
description = "์๋ธ๋ท ID"
type = string
}
variable "vpc_id" {
description = "VPC ID"
type = string
}
variable "server_name" {
description = "์๋ฒ ์ด๋ฆ"
type = string
}
variable "environment" {
description = "ํ๊ฒฝ"
type = string
}
variable "user_data" {
description = "User data ์คํฌ๋ฆฝํธ"
type = string
default = ""
}
variable "ingress_rules" {
description = "์ธ๊ทธ๋ ์ค ๊ท์น"
type = list(object({
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
}))
}
๐ค ๋ชจ๋ ์ถ๋ ฅ ์ ์ (modules/web-server/outputs.tf)
output "instance_id" {
description = "์ธ์คํด์ค ID"
value = aws_instance.web.id
}
output "public_ip" {
description = "ํผ๋ธ๋ฆญ IP"
value = aws_instance.web.public_ip
}
output "security_group_id" {
description = "๋ณด์ ๊ทธ๋ฃน ID"
value = aws_security_group.web.id
}
๐ ๋ชจ๋ ์ฌ์ฉํ๊ธฐ (main.tf)
module "dev_web_server" {
source = "./modules/web-server"
ami_id = "ami-0c9c942bd7bf113a2"
instance_type = "t2.micro"
subnet_id = aws_subnet.public.id
vpc_id = aws_vpc.main.id
server_name = "dev-web-server"
environment = "development"
ingress_rules = [
{
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
{
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
]
}
module "prod_web_server" {
source = "./modules/web-server"
ami_id = "ami-0c9c942bd7bf113a2"
instance_type = "t2.small" # ์ด์์ ์ข ๋ ํฐ ์ธ์คํด์ค
subnet_id = aws_subnet.public.id
vpc_id = aws_vpc.main.id
server_name = "prod-web-server"
environment = "production"
ingress_rules = [
{
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
{
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
]
}
output "dev_server_ip" {
value = module.dev_web_server.public_ip
}
output "prod_server_ip" {
value = module.prod_web_server.public_ip
}
๋ชจ๋์ ์ฌ์ฉํ๋ฉด ๊ฐ์ ์ฝ๋๋ฅผ ๋ฐ๋ณตํ์ง ์๊ณ ๋ ์ฌ๋ฌ ํ๊ฒฝ์ ์ฝ๊ฒ ๊ด๋ฆฌํ ์ ์์ด์! ๊ฐ๋ฐ, ์คํ ์ด์ง, ์ด์ ํ๊ฒฝ์ ๊ฐ๊ฐ ๋ค๋ฅธ ์ค์ ์ผ๋ก ๋ฐฐํฌํ๋ ๊ฒ๋ ๊ฐ๋จํ์ฃ . ๐
๐ ๊ณต๊ฐ ๋ชจ๋ ๋ ์ง์คํธ๋ฆฌ ํ์ฉํ๊ธฐ
Terraform Registry(https://registry.terraform.io/)์๋ ์ปค๋ฎค๋ํฐ์์ ๋ง๋ ์์ฒ ๊ฐ์ ๋ชจ๋์ด ์์ด์. AWS VPC, EKS ํด๋ฌ์คํฐ, Azure AKS ๋ฑ ์์ฃผ ์ฌ์ฉํ๋ ํจํด๋ค์ด ์ด๋ฏธ ๋ชจ๋๋ก ๋ง๋ค์ด์ ธ ์์ฃ .
์๋ฅผ ๋ค์ด AWS VPC ๋ชจ๋์ ์ฌ์ฉํ๋ฉด:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.0.0"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["ap-northeast-2a", "ap-northeast-2c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
enable_nat_gateway = true
enable_vpn_gateway = false
tags = {
Environment = "dev"
}
}
์ด๋ ๊ฒ ๋ช ์ค๋ง์ผ๋ก ์์ ํ VPC๋ฅผ ๋ง๋ค ์ ์์ด์! ๋ฐํด๋ฅผ ๋ค์ ๋ฐ๋ช ํ ํ์๊ฐ ์์ฃ . ๐ก
๐ State ๊ด๋ฆฌ์ ํ์
Terraform State๋ ๋งค์ฐ ์ค์ํ ๊ฐ๋ ์ด์์. ํ์ฌ ์ธํ๋ผ์ ์ํ๋ฅผ ์ ์ฅํ๋ ํ์ผ์ธ๋ฐ, ์ด๊ฒ ์์ผ๋ฉด Terraform์ด ๋ญ ํด์ผ ํ ์ง ๋ชจ๋ฅด๊ฑฐ๋ ์. ๐ค
๐ ๋ก์ปฌ State์ ๋ฌธ์ ์
โ ๏ธ ๋ก์ปฌ State ํ์ผ์ ์ํ์ฑ
๊ธฐ๋ณธ์ ์ผ๋ก State๋ terraform.tfstate๋ผ๋ ํ์ผ๋ก ๋ก์ปฌ์ ์ ์ฅ๋ผ์. ํ์ง๋ง ์ด๊ฑด ์ฌ๋ฌ ๋ฌธ์ ๊ฐ ์์ด์:
1. ํ์
๋ถ๊ฐ ๐ฅ
ํ์๋ค์ด ๊ฐ์ ๋ค๋ฅธ State ํ์ผ์ ๊ฐ์ง๋ฉด ์ถฉ๋์ด ๋ฐ์ํด์.
2. ๋ฐฑ์
๋ถ์ฌ ๐พ
ํ์ผ์ ์์ด๋ฒ๋ฆฌ๋ฉด Terraform์ด ๊ธฐ์กด ์ธํ๋ผ๋ฅผ ์ธ์ํ์ง ๋ชปํด์.
3. ๋ณด์ ๋ฌธ์ ๐
State ํ์ผ์๋ ๋ฏผ๊ฐํ ์ ๋ณด๊ฐ ํ๋ฌธ์ผ๋ก ์ ์ฅ๋ผ์.
4. ๋์ ์คํ ๋ฌธ์ ๐
์ฌ๋ฌ ์ฌ๋์ด ๋์์ applyํ๋ฉด State๊ฐ ๊ผฌ์ผ ์ ์์ด์.
โ๏ธ ์๊ฒฉ State ๋ฐฑ์๋ ์ค์
์ด๋ฐ ๋ฌธ์ ๋ค์ ํด๊ฒฐํ๋ ค๋ฉด ์๊ฒฉ ๋ฐฑ์๋๋ฅผ ์ฌ์ฉํด์ผ ํด์. AWS S3๋ฅผ ๋ฐฑ์๋๋ก ์ฌ์ฉํ๋ ์์ ๋ฅผ ๋ณผ๊ฒ์!
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "prod/terraform.tfstate"
region = "ap-northeast-2"
encrypt = true
dynamodb_table = "terraform-lock"
}
}
# DynamoDB ํ
์ด๋ธ (State Lock์ฉ)
resource "aws_dynamodb_table" "terraform_lock" {
name = "terraform-lock"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
tags = {
Name = "Terraform State Lock Table"
}
}
# S3 ๋ฒํท (State ์ ์ฅ์ฉ)
resource "aws_s3_bucket" "terraform_state" {
bucket = "my-terraform-state-bucket"
tags = {
Name = "Terraform State Bucket"
}
}
# S3 ๋ฒํท ๋ฒ์ ๋ ํ์ฑํ
resource "aws_s3_bucket_versioning" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
# S3 ๋ฒํท ์ํธํ
resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
# S3 ๋ฒํท ํผ๋ธ๋ฆญ ์ก์ธ์ค ์ฐจ๋จ
resource "aws_s3_bucket_public_access_block" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
์ด๋ ๊ฒ ์ค์ ํ๋ฉด:
โ
State ํ์ผ์ด S3์ ์์ ํ๊ฒ ์ ์ฅ๋ผ์
โ
๋ฒ์ ๋์ผ๋ก ์ด์ ๋ฒ์ ๋ณต๊ตฌ๊ฐ ๊ฐ๋ฅํด์
โ
์ํธํ๋ก ๋ณด์์ด ๊ฐํ๋ผ์
โ
DynamoDB๋ก ๋์ ์คํ์ ๋ฐฉ์งํด์ (State Locking)
ํ์๋ค์ ๊ฐ์ S3 ๋ฒํท์ ๋ฐ๋ผ๋ณด๊ธฐ ๋๋ฌธ์ ํญ์ ์ต์ State๋ฅผ ๊ณต์ ํ ์ ์์ด์! ๐
๐ State Locking ์ดํดํ๊ธฐ
State Locking์ ์ฌ๋ฌ ์ฌ๋์ด ๋์์ Terraform์ ์คํํ๋ ๊ฑธ ๋ฐฉ์งํ๋ ๋ฉ์ปค๋์ฆ์ด์์. DynamoDB๋ฅผ ์ฌ์ฉํ๋ฉด ๋๊ตฐ๊ฐ terraform apply๋ฅผ ์คํํ๋ ๋์ ๋ค๋ฅธ ์ฌ๋์ ๋๊ธฐํ๊ฒ ๋ผ์.
๋ง์ฝ Lock์ด ๊ฑธ๋ ค์ ์งํ์ด ์ ๋๋ค๋ฉด:
# Lock ๊ฐ์ ํด์ (์ฃผ์ํด์ ์ฌ์ฉ!)
terraform force-unlock LOCK_ID
ํ์ง๋ง ์ด๊ฑด ์ ๋ง ํ์ํ ๋๋ง ์ฌ์ฉํ์ธ์! ๋ค๋ฅธ ์ฌ๋์ด ์์ ์ค์ผ ์๋ ์๊ฑฐ๋ ์. ๐
๐ฏ Terraform ๋ฒ ์คํธ ํ๋ํฐ์ค
์ค๋ฌด์์ Terraform์ ์ฌ์ฉํ ๋ ์ง์ผ์ผ ํ ๋ฒ ์คํธ ํ๋ํฐ์ค๋ค์ ์ ๋ฆฌํด๋ณผ๊ฒ์! ์ด๊ฑธ ๋ฐ๋ฅด๋ฉด ํจ์ฌ ์์ ํ๊ณ ํจ์จ์ ์ผ๋ก ์ธํ๋ผ๋ฅผ ๊ด๋ฆฌํ ์ ์์ด์. ๐ช
1๏ธโฃ ์ฝ๋ ๊ตฌ์กฐํ ๐
ํ๋ก์ ํธ๋ฅผ ๋ ผ๋ฆฌ์ ์ผ๋ก ๋ถ๋ฆฌํ์ธ์:
project/
โโโ environments/
โ โโโ dev/
โ โ โโโ main.tf
โ โ โโโ variables.tf
โ โ โโโ terraform.tfvars
โ โโโ staging/
โ โโโ prod/
โโโ modules/
โ โโโ networking/
โ โโโ compute/
โ โโโ database/
โโโ global/
โโโ s3/
ํ๊ฒฝ๋ณ๋ก ๋๋ ํ ๋ฆฌ๋ฅผ ๋ถ๋ฆฌํ๊ณ , ๊ณตํต ๋ชจ๋์ modules์, ์ ์ญ ๋ฆฌ์์ค๋ global์ ๋ฐฐ์นํ์ธ์!
2๏ธโฃ ๋ฒ์ ๊ณ ์ ๐
Provider์ ๋ชจ๋ ๋ฒ์ ์ ๋ช ์์ ์ผ๋ก ์ง์ ํ์ธ์:
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # 5.x ๋ฒ์ ์ฌ์ฉ
}
}
}
์ด๋ ๊ฒ ํ๋ฉด ์์์น ๋ชปํ ๋ฒ์ ์ ๋ฐ์ดํธ๋ก ์ธํ ๋ฌธ์ ๋ฅผ ๋ฐฉ์งํ ์ ์์ด์!
3๏ธโฃ ํญ์ Plan ๋จผ์ ํ์ธ ๐
Apply ์ ์ ๋ฐ๋์ Plan์ ํ์ธํ์ธ์:
# 1. Plan ์ ์ฅ
terraform plan -out=tfplan
# 2. Plan ๊ฒํ
terraform show tfplan
# 3. ์น์ธ ํ Apply
terraform apply tfplan
ํนํ ์ด์ ํ๊ฒฝ์์๋ ์ด ๊ณผ์ ์ ์๋ตํ๋ฉด ์ ๋ผ์! ๐จ
4๏ธโฃ ๋ฆฌ์์ค ํ๊น ์ ๋ต ๐ท๏ธ
๋ชจ๋ ๋ฆฌ์์ค์ ์ผ๊ด๋ ํ๊ทธ๋ฅผ ์ ์ฉํ์ธ์:
locals {
common_tags = {
Project = "MyApp"
Environment = var.environment
ManagedBy = "Terraform"
Owner = "DevOps Team"
CostCenter = "Engineering"
}
}
resource "aws_instance" "web" {
# ... ๊ธฐํ ์ค์ ...
tags = merge(
local.common_tags,
{
Name = "web-server"
Role = "WebServer"
}
)
}
ํ๊ทธ๋ฅผ ์ ํ์ฉํ๋ฉด ๋น์ฉ ์ถ์ , ๋ฆฌ์์ค ๊ด๋ฆฌ, ์๋ํ๊ฐ ํจ์ฌ ์ฌ์์ ธ์!
5๏ธโฃ ๋ฏผ๊ฐํ ์ ๋ณด ๊ด๋ฆฌ ๐
์ ๋๋ก ์ฝ๋์ ๋น๋ฐ๋ฒํธ๋ API ํค๋ฅผ ํ๋์ฝ๋ฉํ์ง ๋ง์ธ์! ๋์ :
# AWS Secrets Manager ์ฌ์ฉ
data "aws_secretsmanager_secret_version" "db_password" {
secret_id = "prod/db/password"
}
resource "aws_db_instance" "main" {
# ...
password = data.aws_secretsmanager_secret_version.db_password.secret_string
}
๋๋ ํ๊ฒฝ ๋ณ์๋ .tfvars ํ์ผ์ ์ฌ์ฉํ๋, .gitignore์ ์ถ๊ฐํ์ธ์!
6๏ธโฃ ๋ฆฌ์์ค ๋ช ๋ช ๊ท์น ๐
์ผ๊ด๋ ๋ช ๋ช ๊ท์น์ ์ฌ์ฉํ์ธ์:
# ์ข์ ์
resource "aws_instance" "web_server" {
tags = {
Name = "${var.project}-${var.environment}-web-server"
}
}
# ๋์ ์
resource "aws_instance" "server1" {
tags = {
Name = "myserver"
}
}
ํ๋ก์ ํธ๋ช -ํ๊ฒฝ-์ญํ ํ์์ ์ถ์ฒํด์!
7๏ธโฃ ๋ฐ์ดํฐ ์์ค ํ์ฉ ๐
๊ธฐ์กด ๋ฆฌ์์ค ์ ๋ณด๋ ๋ฐ์ดํฐ ์์ค๋ก ๊ฐ์ ธ์ค์ธ์:
# ์ต์ Amazon Linux 2 AMI ์๋ ์กฐํ
data "aws_ami" "amazon_linux_2" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux_2.id
# ...
}
AMI ID๋ฅผ ํ๋์ฝ๋ฉํ์ง ์์๋ ๋๋ ํธ๋ฆฌํด์!
8๏ธโฃ ์กฐ๊ฑด๋ฌธ๊ณผ ๋ฐ๋ณต๋ฌธ ํ์ฉ ๐
count์ for_each๋ก ๋์ ์ธ ๋ฆฌ์์ค ์์ฑ:
# count ์ฌ์ฉ
resource "aws_instance" "web" {
count = var.instance_count
ami = data.aws_ami.amazon_linux_2.id
instance_type = "t2.micro"
tags = {
Name = "web-server-${count.index + 1}"
}
}
# for_each ์ฌ์ฉ (๋ ๊ถ์ฅ)
variable "instances" {
type = map(object({
instance_type = string
ami = string
}))
}
resource "aws_instance" "servers" {
for_each = var.instances
ami = each.value.ami
instance_type = each.value.instance_type
tags = {
Name = each.key
}
}
๐ ๊ณ ๊ธ ๊ธฐ๋ฅ๊ณผ ํ ํฌ๋
์ด์ ์ข ๋ ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค์ ์ดํด๋ณผ๊ฒ์. ์ด๋ฐ ๊ธฐ๋ฅ๋ค์ ์๋ฉด Terraform ๋ง์คํฐ๊ฐ ๋ ์ ์์ด์! ๐
๐ญ Workspace๋ก ํ๊ฒฝ ๋ถ๋ฆฌ
Workspace๋ ๊ฐ์ ์ฝ๋๋ก ์ฌ๋ฌ ํ๊ฒฝ์ ๊ด๋ฆฌํ ์ ์๊ฒ ํด์ค์. ๊ฐ Workspace๋ ๋ ๋ฆฝ์ ์ธ State๋ฅผ ๊ฐ์ง์ฃ .
# Workspace ๋ชฉ๋ก ํ์ธ
terraform workspace list
# ์ Workspace ์์ฑ
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod
# Workspace ์ ํ
terraform workspace select dev
# ํ์ฌ Workspace ํ์ธ
terraform workspace show
์ฝ๋์์ ํ์ฌ Workspace๋ฅผ ์ฐธ์กฐํ ์ ์์ด์:
locals {
environment = terraform.workspace
instance_type = {
dev = "t2.micro"
staging = "t2.small"
prod = "t2.large"
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux_2.id
instance_type = local.instance_type[local.environment]
tags = {
Name = "web-server"
Environment = local.environment
}
}
๐ง Provisioner๋ก ์ด๊ธฐ ์ค์
Provisioner๋ ๋ฆฌ์์ค ์์ฑ ํ ์ถ๊ฐ ์์ ์ ์ํํ ์ ์๊ฒ ํด์ค์. ํ์ง๋ง ๊ฐ๋ฅํ๋ฉด ์ฌ์ฉ์ ํผํ๊ณ user_data๋ Configuration Management ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ ์ข์์.
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux_2.id
instance_type = "t2.micro"
key_name = aws_key_pair.deployer.key_name
# ํ์ผ ๋ณต์ฌ
provisioner "file" {
source = "scripts/setup.sh"
destination = "/tmp/setup.sh"
connection {
type = "ssh"
user = "ec2-user"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
}
# ์๊ฒฉ ๋ช
๋ น ์คํ
provisioner "remote-exec" {
inline = [
"chmod +x /tmp/setup.sh",
"sudo /tmp/setup.sh"
]
connection {
type = "ssh"
user = "ec2-user"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
}
# ๋ก์ปฌ ๋ช
๋ น ์คํ
provisioner "local-exec" {
command = "echo ${self.public_ip} >> ip_addresses.txt"
}
}
โ ๏ธ Provisioner ์ฌ์ฉ ์ ์ฃผ์์ฌํญ
Provisioner๋ "์ตํ์ ์๋จ"์ผ๋ก๋ง ์ฌ์ฉํ์ธ์. ์๋ํ๋ฉด:
โข Terraform์ ์ ์ธ์ ํน์ฑ๊ณผ ๋ง์ง ์์์
โข ์คํจ ์ ๋ฆฌ์์ค๊ฐ "tainted" ์ํ๊ฐ ๋ผ์
โข ์ฌํ์ฑ์ด ๋จ์ด์ ธ์
๋์ user_data, cloud-init, Ansible, Chef ๊ฐ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ ์ข์์!
๐จ Dynamic ๋ธ๋ก์ผ๋ก ๋์ ์ค์
Dynamic ๋ธ๋ก์ ์ฌ์ฉํ๋ฉด ๋ฐ๋ณต์ ์ธ ์ค์ฒฉ ๋ธ๋ก์ ๋์ ์ผ๋ก ์์ฑํ ์ ์์ด์.
variable "ingress_rules" {
type = list(object({
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
description = string
}))
default = [
{
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTP"
},
{
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTPS"
},
{
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"]
description = "SSH from internal"
}
]
}
resource "aws_security_group" "web" {
name = "web-sg"
description = "Security group for web servers"
vpc_id = aws_vpc.main.id
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
description = ingress.value.description
}
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
์ด๋ ๊ฒ ํ๋ฉด ๊ท์น์ ์ถ๊ฐํ๊ฑฐ๋ ์ ๊ฑฐํ ๋ ์ฝ๋๋ฅผ ๋ฐ๋ณตํ์ง ์์๋ ๋ผ์! ๐
๐ Import๋ก ๊ธฐ์กด ๋ฆฌ์์ค ๊ฐ์ ธ์ค๊ธฐ
์ด๋ฏธ ์๋์ผ๋ก ๋ง๋ ๋ฆฌ์์ค๋ฅผ Terraform์ผ๋ก ๊ด๋ฆฌํ๊ณ ์ถ๋ค๋ฉด import๋ฅผ ์ฌ์ฉํ์ธ์!
# 1. ๋จผ์ ๋ฆฌ์์ค ์ ์๋ฅผ ์์ฑ
resource "aws_instance" "existing_server" {
# ์ต์ํ์ ํ์ ์์ฑ๋ง ์์ฑ
ami = "ami-0c9c942bd7bf113a2"
instance_type = "t2.micro"
}
# 2. Import ์คํ
terraform import aws_instance.existing_server i-1234567890abcdef0
# 3. State ํ์ธ
terraform show
# 4. ์ค์ ์ค์ ๊ณผ ๋ง๊ฒ ์ฝ๋ ์์
terraform plan # ์ฐจ์ด์ ํ์ธ
Import ํ์๋ plan์ ์คํํด์ ์ฝ๋์ ์ค์ ๋ฆฌ์์ค์ ์ฐจ์ด๋ฅผ ํ์ธํ๊ณ ์ฝ๋๋ฅผ ์์ ํด์ผ ํด์!
๐ CI/CD ํ์ดํ๋ผ์ธ๊ณผ Terraform ํตํฉ
Terraform์ CI/CD ํ์ดํ๋ผ์ธ์ ํตํฉํ๋ฉด ์ธํ๋ผ ๋ณ๊ฒฝ๋ ์ฝ๋ ๋ฐฐํฌ์ฒ๋ผ ์๋ํํ ์ ์์ด์! ๐ค
๐ GitHub Actions ์์
name: 'Terraform'
on:
push:
branches:
- main
pull_request:
branches:
- main
env:
TF_VERSION: '1.5.0'
AWS_REGION: 'ap-northeast-2'
jobs:
terraform:
name: 'Terraform'
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./terraform
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Terraform Format
id: fmt
run: terraform fmt -check
continue-on-error: true
- name: Terraform Init
id: init
run: terraform init
- name: Terraform Validate
id: validate
run: terraform validate -no-color
- name: Terraform Plan
id: plan
if: github.event_name == 'pull_request'
run: terraform plan -no-color -input=false
continue-on-error: true
- name: Comment PR
uses: actions/github-script@v6
if: github.event_name == 'pull_request'
env:
PLAN: "terraform\n${{ steps.plan.outputs.stdout }}"
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const output = `#### Terraform Format and Style ๐\`${{ steps.fmt.outcome }}\`
#### Terraform Initialization โ๏ธ\`${{ steps.init.outcome }}\`
#### Terraform Validation ๐ค\`${{ steps.validate.outcome }}\`
#### Terraform Plan ๐\`${{ steps.plan.outcome }}\`
<details><summary>Show Plan</summary>
\`\`\`\n
${process.env.PLAN}
\`\`\`
</details>
*Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
- name: Terraform Plan Status
if: steps.plan.outcome == 'failure'
run: exit 1
- name: Terraform Apply
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: terraform apply -auto-approve -input=false
์ด ์ํฌํ๋ก์ฐ๋:
โ
PR ์์ฑ ์ ์๋์ผ๋ก plan์ ์คํํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ์ฝ๋ฉํธ๋ก ๋ฌ์์ค์
โ
main ๋ธ๋์น์ ๋จธ์ง๋๋ฉด ์๋์ผ๋ก apply๋ฅผ ์คํํด์
โ
์ฝ๋ ํฌ๋งทํ
๊ณผ ์ ํจ์ฑ ๊ฒ์ฌ๋ ์๋์ผ๋ก ํด์
์ด๋ ๊ฒ ํ๋ฉด ์ธํ๋ผ ๋ณ๊ฒฝ๋ ์ฝ๋ ๋ฆฌ๋ทฐ ํ๋ก์ธ์ค๋ฅผ ๊ฑฐ์น๊ฒ ๋์ด ์์ ํด์ ธ์! ๐ก๏ธ
๐ต GitLab CI/CD ์์
image:
name: hashicorp/terraform:1.5.0
entrypoint:
- '/usr/bin/env'
- 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
variables:
TF_ROOT: ${CI_PROJECT_DIR}/terraform
TF_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/production
cache:
paths:
- ${TF_ROOT}/.terraform
before_script:
- cd ${TF_ROOT}
- terraform --version
- terraform init
stages:
- validate
- plan
- apply
validate:
stage: validate
script:
- terraform validate
plan:
stage: plan
script:
- terraform plan -out=tfplan
artifacts:
paths:
- ${TF_ROOT}/tfplan
expire_in: 1 week
only:
- merge_requests
- main
apply:
stage: apply
script:
- terraform apply -auto-approve tfplan
dependencies:
- plan
only:
- main
when: manual
๐ ํธ๋ฌ๋ธ์ํ ๊ณผ ๋๋ฒ๊น
Terraform์ ์ฌ์ฉํ๋ค ๋ณด๋ฉด ๊ฐ๋ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์์ด์. ์์ฃผ ๋ฐ์ํ๋ ๋ฌธ์ ๋ค๊ณผ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์์๋ณผ๊ฒ์! ๐ง
๋ฌธ์ 1: State Lock ๊ฑธ๋ฆผ ๐
์ฆ์: "Error acquiring the state lock" ๋ฉ์์ง๊ฐ ๋์์.
์์ธ: ์ด์ ์คํ์ด ๋น์ ์ ์ข
๋ฃ๋์ด Lock์ด ๋จ์์์ด์.
ํด๊ฒฐ:
# Lock ID ํ์ธ ํ ๊ฐ์ ํด์
terraform force-unlock LOCK_ID
# ๋๋ DynamoDB์์ ์ง์ ์ญ์
aws dynamodb delete-item \
--table-name terraform-lock \
--key '{"LockID": {"S": "your-lock-id"}}'
๋ฌธ์ 2: State ํ์ผ ์์ ๐
์ฆ์: State ํ์ผ์ด ๊นจ์ ธ์ Terraform์ด ์๋ํ์ง ์์์.
์์ธ: ์๋ ํธ์ง, ๋์ ์คํ, ๋คํธ์ํฌ ์ค๋ฅ ๋ฑ
ํด๊ฒฐ:
# S3 ๋ฒ์ ๋์ ์ฌ์ฉํ๋ค๋ฉด ์ด์ ๋ฒ์ ๋ณต๊ตฌ
aws s3api list-object-versions \
--bucket my-terraform-state-bucket \
--prefix terraform.tfstate
# ๋ฐฑ์
์์ ๋ณต๊ตฌ
terraform state pull > backup.tfstate
# ์์ ํ
terraform state push backup.tfstate
๋ฌธ์ 3: ๋ฆฌ์์ค Drift ๋ฐ์ ๐
์ฆ์: ๋๊ตฐ๊ฐ ์ฝ์์์ ์๋์ผ๋ก ๋ฆฌ์์ค๋ฅผ ๋ณ๊ฒฝํ์ด์.
์์ธ: Terraform ์ธ๋ถ์์ ๋ฆฌ์์ค๋ฅผ ์์ ํ์ด์.
ํด๊ฒฐ:
# Drift ๊ฐ์ง
terraform plan -refresh-only
# State ์
๋ฐ์ดํธ (์ค์ ๋ฆฌ์์ค ์ํ๋ก)
terraform apply -refresh-only
# ๋๋ ์ฝ๋๋ฅผ ์ค์ ์ํ์ ๋ง๊ฒ ์์ ํ
terraform plan
terraform apply
๋ฌธ์ 4: ์ํ ์์กด์ฑ โป๏ธ
์ฆ์: "Cycle" ์๋ฌ๊ฐ ๋ฐ์ํด์.
์์ธ: ๋ฆฌ์์ค ๊ฐ ์ํ ์ฐธ์กฐ๊ฐ ์์ด์.
ํด๊ฒฐ:
# ์์กด์ฑ ๊ทธ๋ํ ํ์ธ
terraform graph | dot -Tpng > graph.png
# depends_on์ผ๋ก ๋ช
์์ ์์กด์ฑ ์ง์
resource "aws_instance" "web" {
# ...
depends_on = [aws_security_group.web]
}
๐ ๋๋ฒ๊น ํ
๋ก๊ทธ ๋ ๋ฒจ ์กฐ์
# ์์ธํ ๋ก๊ทธ ์ถ๋ ฅ
export TF_LOG=DEBUG
export TF_LOG_PATH=terraform.log
terraform apply
# ๋ก๊ทธ ๋ ๋ฒจ: TRACE, DEBUG, INFO, WARN, ERROR
ํน์ ๋ฆฌ์์ค๋ง ์ ์ฉ
# ํน์ ๋ฆฌ์์ค๋ง apply
terraform apply -target=aws_instance.web
# ํน์ ๋ฆฌ์์ค๋ง destroy
terraform destroy -target=aws_instance.web
State ์กฐ์ ๋ช ๋ น์ด
# State ๋ชฉ๋ก ํ์ธ
terraform state list
# ํน์ ๋ฆฌ์์ค ์์ธ ์ ๋ณด
terraform state show aws_instance.web
# ๋ฆฌ์์ค ์ด๋ฆ ๋ณ๊ฒฝ
terraform state mv aws_instance.old aws_instance.new
# State์์ ๋ฆฌ์์ค ์ ๊ฑฐ (์ค์ ๋ฆฌ์์ค๋ ์ ์ง)
terraform state rm aws_instance.web
๐ฐ ๋น์ฉ ์ต์ ํ์ ๊ด๋ฆฌ
ํด๋ผ์ฐ๋ ๋ฆฌ์์ค๋ ๋์ด ๋ค์ด์! Terraform์ผ๋ก ๋น์ฉ์ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์์๋ณผ๊ฒ์. ๐ธ
1๏ธโฃ Infracost๋ก ๋น์ฉ ์์ธก ๐
Infracost๋ Terraform ์ฝ๋๋ฅผ ๋ถ์ํด์ ์์ ๋น์ฉ์ ๊ณ์ฐํด์ฃผ๋ ๋๊ตฌ์์!
# Infracost ์ค์น
brew install infracost
# API ํค ๋ฑ๋ก
infracost auth login
# ๋น์ฉ ์์ธก
infracost breakdown --path .
# CI/CD์์ PR ์ฝ๋ฉํธ๋ก ๋น์ฉ ๋ณํ ํ์
infracost diff --path . --format json \
| infracost comment github --repo owner/repo --pull-request 123
์ด๋ ๊ฒ ํ๋ฉด PR์ ์ฌ๋ฆด ๋๋ง๋ค "์ด ๋ณ๊ฒฝ์ผ๋ก ์ ๋น์ฉ์ด $50 ์ฆ๊ฐํฉ๋๋ค"๋ผ๊ณ ์๋ ค์ค์! ๐ก
2๏ธโฃ ์๋ ์ข ๋ฃ ์ค์ผ์ค๋ง โฐ
๊ฐ๋ฐ ํ๊ฒฝ์ ์ ๋ฌด ์๊ฐ์๋ง ์ผ๋๊ณ ๋ฐค์๋ ๋๋ฉด ๋น์ฉ์ ์ ์ฝํ ์ ์์ด์!
# Lambda ํจ์๋ก EC2 ์๋ ์์/์ข
๋ฃ
resource "aws_lambda_function" "ec2_scheduler" {
filename = "ec2_scheduler.zip"
function_name = "ec2-scheduler"
role = aws_iam_role.lambda_role.arn
handler = "index.handler"
runtime = "python3.9"
environment {
variables = {
TAG_KEY = "AutoShutdown"
TAG_VALUE = "true"
}
}
}
# CloudWatch Events๋ก ์ค์ผ์ค๋ง
resource "aws_cloudwatch_event_rule" "stop_instances" {
name = "stop-dev-instances"
description = "Stop dev instances at 7 PM"
schedule_expression = "cron(0 10 * * ? *)" # UTC 10:00 = KST 19:00
}
resource "aws_cloudwatch_event_target" "stop_instances" {
rule = aws_cloudwatch_event_rule.stop_instances.name
target_id = "StopInstances"
arn = aws_lambda_function.ec2_scheduler.arn
input = jsonencode({ action = "stop" })
}
resource "aws_cloudwatch_event_rule" "start_instances" {
name = "start-dev-instances"
description = "Start dev instances at 9 AM"
schedule_expression = "cron(0 0 * * ? *)" # UTC 00:00 = KST 09:00
}
resource "aws_cloudwatch_event_target" "start_instances" {
rule = aws_cloudwatch_event_rule.start_instances.name
target_id = "StartInstances"
arn = aws_lambda_function.ec2_scheduler.arn
input = jsonencode({ action = "start" })
}
3๏ธโฃ Spot ์ธ์คํด์ค ํ์ฉ ๐
์ค๋จ๋์ด๋ ๊ด์ฐฎ์ ์ํฌ๋ก๋๋ Spot ์ธ์คํด์ค๋ฅผ ์ฌ์ฉํ๋ฉด ์ต๋ 90% ์ ๋ ดํด์!
resource "aws_spot_instance_request" "worker" {
ami = data.aws_ami.amazon_linux_2.id
instance_type = "t3.medium"
spot_price = "0.05" # ์ต๋ ์ง๋ถ ๊ฐ๊ฒฉ
wait_for_fulfillment = true
spot_type = "persistent" # ์ข
๋ฃ ์ ์๋ ์ฌ์์
tags = {
Name = "worker-spot-instance"
}
}
๐ Terraform Cloud์ Enterprise
HashiCorp์์ ์ ๊ณตํ๋ Terraform Cloud๋ ํ ํ์ ์ ์ํ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ๋ค์ ์ ๊ณตํด์! ๐
๐ Terraform Cloud ์ฃผ์ ๊ธฐ๋ฅ
1. ์๊ฒฉ State ๊ด๋ฆฌ
S3 ์ค์ ์์ด๋ ์์ ํ๊ฒ State๋ฅผ ์ ์ฅํ๊ณ ๊ณต์ ํ ์ ์์ด์.
2. ์๊ฒฉ ์คํ
๋ก์ปฌ์ด ์๋ ํด๋ผ์ฐ๋์์ Terraform์ ์คํํด์. ์ผ๊ด๋ ํ๊ฒฝ์ ๋ณด์ฅํ์ฃ !
3. VCS ํตํฉ
GitHub, GitLab, Bitbucket๊ณผ ์ฐ๋ํด์ ์๋์ผ๋ก plan/apply๋ฅผ ์คํํด์.
4. ์ ์ฑ
๊ด๋ฆฌ (Sentinel)
Enterprise ๋ฒ์ ์์๋ ์ ์ฑ
์ ์ฝ๋๋ก ์ ์ํด์ ๊ท์ ์ค์๋ฅผ ๊ฐ์ ํ ์ ์์ด์.
5. Private Registry
ํ ๋ด๋ถ ๋ชจ๋์ ์์ ํ๊ฒ ๊ณต์ ํ ์ ์์ด์.
๐ง Terraform Cloud ์ค์
terraform {
cloud {
organization = "my-company"
workspaces {
name = "production"
}
}
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# ๋ก๊ทธ์ธ
# terraform login
# ์ด๊ธฐํ
# terraform init
Terraform Cloud์ ๋ฌด๋ฃ ํฐ์ด๋ ์ต๋ 5๋ช ๊น์ง ์ฌ์ฉํ ์ ์์ด์. ์๊ท๋ชจ ํ์ด๋ผ๋ฉด ์ถฉ๋ถํ์ฃ ! ๐
๐ ํ์ต ๋ฆฌ์์ค์ ์ปค๋ฎค๋ํฐ
Terraform์ ๋ ๊น์ด ๋ฐฐ์ฐ๊ณ ์ถ๋ค๋ฉด ์ด๋ฐ ๋ฆฌ์์ค๋ค์ ํ์ฉํด๋ณด์ธ์! ๐
| ๋ฆฌ์์ค | ์ค๋ช | ๋งํฌ |
|---|---|---|
| ๊ณต์ ๋ฌธ์ | ๊ฐ์ฅ ์ ํํ๊ณ ์ต์ ์ ๋ณด | terraform.io/docs |
| HashiCorp Learn | ๋จ๊ณ๋ณ ํํ ๋ฆฌ์ผ | learn.hashicorp.com |
| Terraform Registry | ๊ณต๊ฐ ๋ชจ๋๊ณผ Provider | registry.terraform.io |
| GitHub | ์คํ์์ค ์์ ์ฝ๋ | github.com/hashicorp/terraform |
| ์ปค๋ฎค๋ํฐ Q&A | reddit.com/r/Terraform | |
| Stack Overflow | ๊ธฐ์ ์ง๋ฌธ๊ณผ ๋ต๋ณ | stackoverflow.com/questions/tagged/terraform |
| YouTube | ์์ ํํ ๋ฆฌ์ผ | HashiCorp ๊ณต์ ์ฑ๋ |
| ์ฌ๋ฅ๋ท | Terraform ์ ๋ฌธ๊ฐ ์ฐพ๊ธฐ | jaenung.net |
ํนํ ์ฌ๋ฅ๋ท์์๋ Terraform ์ ๋ฌธ๊ฐ๋ค์๊ฒ 1:1 ์ปจ์คํ ์ ๋ฐ์ ์ ์์ด์. ์ค๋ฌด์์ ๋งํ๋ ๋ถ๋ถ์ด ์๋ค๋ฉด ์ ๋ฌธ๊ฐ์ ๋์์ ๋ฐ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ฃ ! ๐ช
๐ ์๊ฒฉ์ฆ๊ณผ ์ปค๋ฆฌ์ด
Terraform ์ค๋ ฅ์ ๊ณต์์ ์ผ๋ก ์ธ์ ๋ฐ๊ณ ์ถ๋ค๋ฉด HashiCorp ์๊ฒฉ์ฆ์ ๋์ ํด๋ณด์ธ์! ๐
๐ HashiCorp Certified: Terraform Associate
๋์ด๋: ์ด์ค๊ธ
์ํ ์๊ฐ: 60๋ถ
๋ฌธ์ ์: 57๋ฌธ์
ํฉ๊ฒฉ ์ ์: 70%
๋น์ฉ: $70.50
์ ํจ ๊ธฐ๊ฐ: 2๋
์ฃผ์ ์ถ์ ๋ฒ์:
โข IaC ๊ฐ๋
์ดํด
โข Terraform ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
โข Terraform ์ํฌํ๋ก์ฐ
โข ๋ชจ๋ ์ฌ์ฉ
โข State ๊ด๋ฆฌ
โข Terraform Cloud ๊ธฐ๋ณธ
์ด ์๊ฒฉ์ฆ์ด ์์ผ๋ฉด ์ทจ์
์ด๋ ํ๋ฆฌ๋์ ํ๋์ ํฐ ๋์์ด ๋ผ์! ๐ผ
๐ฎ Terraform์ ๋ฏธ๋์ ํธ๋ ๋
Terraform๊ณผ IaC๋ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ด์. ์์ผ๋ก์ ํธ๋ ๋๋ฅผ ์ดํด๋ณผ๊ฒ์! ๐
1๏ธโฃ GitOps์์ ํตํฉ ๐
Git์ ๋จ์ผ ์ง์ค ๊ณต๊ธ์(Single Source of Truth)์ผ๋ก ์ฌ์ฉํ๋ GitOps๊ฐ ๋์ธ์์. Terraform๋ GitOps ์ํฌํ๋ก์ฐ์ ์๋ฒฝํ๊ฒ ํตํฉ๋๊ณ ์์ฃ .
ArgoCD, Flux ๊ฐ์ ๋๊ตฌ๋ค๊ณผ ํจ๊ป ์ฌ์ฉํ๋ฉด ์ ์ธ์ ์ธํ๋ผ ๊ด๋ฆฌ๊ฐ ๋์ฑ ๊ฐ๋ ฅํด์ ธ์!
2๏ธโฃ Policy as Code ๐
๋ณด์, ๊ท์ ์ค์, ๋น์ฉ ๊ด๋ฆฌ ์ ์ฑ
์ ์ฝ๋๋ก ์ ์ํ๋ ๊ฒ ํ์ค์ด ๋๊ณ ์์ด์.
Open Policy Agent(OPA), Sentinel ๊ฐ์ ๋๊ตฌ๋ก "์ด ๋ฆฌ์์ค๋ ๋ฐ๋์ ์ํธํ๋์ด์ผ ํ๋ค", "์ ๋น์ฉ์ด $1000์ ์ด๊ณผํ๋ฉด ์ ๋๋ค" ๊ฐ์ ๊ท์น์ ๊ฐ์ ํ ์ ์์ด์.
3๏ธโฃ ๋ฉํฐ ํด๋ผ์ฐ๋ ์ ๋ต โ๏ธโ๏ธโ๏ธ
ํ ํด๋ผ์ฐ๋์ ์ข
์๋์ง ์๊ณ ์ฌ๋ฌ ํด๋ผ์ฐ๋๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ ๋ฉํฐ ํด๋ผ์ฐ๋ ์ ๋ต์ด ์ธ๊ธฐ์์.
Terraform์ AWS, Azure, GCP๋ฅผ ํ๋์ ์ฝ๋๋ฒ ์ด์ค๋ก ๊ด๋ฆฌํ ์ ์์ด์ ๋ฉํฐ ํด๋ผ์ฐ๋ ํ๊ฒฝ์ ์ต์ ์ด์ฃ !
4๏ธโฃ ์๋ฒ๋ฆฌ์ค์ ์ปจํ ์ด๋ ๐ณ
Lambda, Cloud Functions ๊ฐ์ ์๋ฒ๋ฆฌ์ค์ Kubernetes ๊ฐ์ ์ปจํ
์ด๋ ์ค์ผ์คํธ๋ ์ด์
๋ Terraform์ผ๋ก ๊ด๋ฆฌํ๋ ๊ฒ ์ผ๋ฐํ๋๊ณ ์์ด์.
์ธํ๋ผ๋ฟ๋ง ์๋๋ผ ์ ํ๋ฆฌ์ผ์ด์
๋ฐฐํฌ๊น์ง Terraform์ผ๋ก ํตํฉ ๊ด๋ฆฌํ๋ ์ถ์ธ์์!
๐ฌ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์! ์ฌ๊ธฐ๊น์ง ์ ๋ง ๊ธด ์ฌ์ ์ด์๋ค์. ๐
Terraform๊ณผ Infrastructure as Code์ ๋ํด ์ ๋ง ๋ง์ ๊ฑธ ๋ฐฐ์ ์ด์!
์ฒ์์๋ ๋ณต์กํด ๋ณด์ผ ์ ์์ง๋ง, ํ๋์ฉ ๋ฐ๋ผ ํ๋ค ๋ณด๋ฉด ๊ธ๋ฐฉ ์ต์ํด์ง ๊ฑฐ์์. ์ ๋ ์ฒ์์๋ "์ด๊ฒ ๋ญ์ผ?" ํ๋๋ฐ, ์ง๊ธ์ Terraform ์์ด๋ ์ธํ๋ผ ๊ด๋ฆฌ๋ฅผ ์์ํ ์ ์์ ์ ๋์์! ๐
ํต์ฌ๋ง ์ ๋ฆฌํ๋ฉด:
โจ ํต์ฌ ์์ฝ
1. IaC๋ ํ์ ๐
ํ๋์ ์ธ ์ธํ๋ผ ๊ด๋ฆฌ์์ IaC๋ ์ ํ์ด ์๋๋ผ ํ์์์. ์ฌํ์ฑ, ๋ฒ์ ๊ด๋ฆฌ, ์๋ํ์ ์ด์ ์ด ๋๋ฌด ํฌ๊ฑฐ๋ ์!
2. Terraform์ ๊ฐ๋ ฅํ ๋๊ตฌ ๐ช
์ ์ธ์ ๊ตฌ๋ฌธ, ๋ฉํฐ ํด๋ผ์ฐ๋ ์ง์, ํ๋ถํ ์ํ๊ณ๋ก ๊ฐ์ฅ ์ธ๊ธฐ ์๋ IaC ๋๊ตฌ์์.
3. ๋ฒ ์คํธ ํ๋ํฐ์ค๋ฅผ ๋ฐ๋ฅด์ธ์ โญ
์ฝ๋ ๊ตฌ์กฐํ, ๋ฒ์ ๊ณ ์ , State ๊ด๋ฆฌ, ๋ชจ๋ํ ๋ฑ ๋ฒ ์คํธ ํ๋ํฐ์ค๋ฅผ ๋ฐ๋ฅด๋ฉด ์์ ํ๊ณ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์์ด์.
4. ํ ํ์
์ด ์ค์ ๐ค
์๊ฒฉ State, CI/CD ํตํฉ, ์ฝ๋ ๋ฆฌ๋ทฐ๋ก ํ ์ ์ฒด๊ฐ ์์ ํ๊ฒ ํ์
ํ ์ ์์ด์.
5. ๊ณ์ ํ์ตํ์ธ์ ๐
Terraform๊ณผ ํด๋ผ์ฐ๋๋ ๊ณ์ ๋ฐ์ ํด์. ๊ณต์ ๋ฌธ์, ์ปค๋ฎค๋ํฐ, ๊ทธ๋ฆฌ๊ณ ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์ ํ์ฉํด์ ๊ณ์ ๋ฐฐ์ฐ์ธ์!
ํน์ Terraform์ ๋ฐฐ์ฐ๋ค๊ฐ ๋งํ๋ ๋ถ๋ถ์ด ์๊ฑฐ๋, ์ค๋ฌด ํ๋ก์ ํธ์ ์ ์ฉํ๋ ๋ฐ ๋์์ด ํ์ํ๋ค๋ฉด ์ฌ๋ฅ๋ท์์ ์ ๋ฌธ๊ฐ๋ฅผ ์ฐพ์๋ณด์ธ์! ๊ฒฝํ ๋ง์ DevOps ์์ง๋์ด๋ค์ด ์ฌ๋ฌ๋ถ์ ์ฑ๊ณต์ ๋์์ค ๊ฑฐ์์. ๐
์ธํ๋ผ๋ฅผ ์ฝ๋๋ก ๊ด๋ฆฌํ๋ ๊ฑด ์ฒ์์๋ ๋ฏ์ค ์ ์์ง๋ง, ํ๋ฒ ์ต์ํด์ง๋ฉด ์ ๋ ๋์๊ฐ ์ ์์ ๋งํผ ํธ๋ฆฌํด์. ๋ง์น ์๋์ฐจ๋ฅผ ํ๋ค๊ฐ ๊ฑธ์ด ๋ค๋๋ผ๊ณ ํ๋ ๊ฒ๊ณผ ๋น์ทํ๋ฌ๊น์? ๐
์ฌ๋ฌ๋ถ๋ Terraform์ผ๋ก ๋ฉ์ง ์ธํ๋ผ๋ฅผ ๊ตฌ์ถํ์๊ธธ ๋ฐ๋๊ฒ์! ํ์ดํ
! ๐
๐ Happy Terraforming! ๐
์ฝ๋๋ก ์ธ์์ ๋ฐ๊พธ๋ ์ฌ๋ฌ๋ถ์ ์์ํฉ๋๋ค! ๐
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.