Ver3.0 ๐ Terraform์ผ๋ก AWS ์ธํ๋ผ ์๋ ์์ฑํ๊ธฐ

๐ Terraform์ผ๋ก AWS ์ธํ๋ผ ์๋ ์์ฑํ๊ธฐ
์์ฆ ๊ฐ๋ฐ์๋ค ์ฌ์ด์์ ํซํ ํค์๋๊ฐ ๋ญ์ง ์์? ๋ฐ๋ก IaC(Infrastructure as Code)์ผ ใ
ใ
ใ
์์ ์๋ AWS ์ฝ์ ๋ค์ด๊ฐ์ ํด๋ฆญํด๋ฆญ ํ๋ฉด์ ์๋ฒ ๋ง๋ค๊ณ , ๋คํธ์ํฌ ์ค์ ํ๊ณ ... ์ง์ง ์ ์ํ๊ณ ์๊ฐ๋ ์ค๋ ๊ฑธ๋ ธ๋๋ฐ, ์ด์ ๋ ์ฝ๋ ๋ช ์ค๋ก ๋๋ฑ ๋ง๋ค ์ ์๋ค๋๊น? ๐
ํนํ Terraform์ AWS๋ฟ๋ง ์๋๋ผ Azure, GCP ๋ฑ ์ฌ๋ฌ ํด๋ผ์ฐ๋๋ฅผ ํ ๋ฒ์ ๊ด๋ฆฌํ ์ ์์ด์ ์์ ๊ฟํ
์ด์ผ! ์ค๋์ ์ด Terraform์ผ๋ก AWS ์ธํ๋ผ๋ฅผ ์๋์ผ๋ก ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์ฒ์๋ถํฐ ๋๊น์ง ๋ค ์๋ ค์ค๊ฒ~
๐ค Terraform์ด ๋ญ๋ฐ ์ด๋ ๊ฒ ๋๋ฆฌ์ผ?
์ผ๋จ Terraform์ด ๋ญ์ง๋ถํฐ ์์์ผ๊ฒ ์ง? ๐ง
Terraform์ HashiCorp๋ผ๋ ํ์ฌ์์ ๋ง๋ ์คํ์์ค IaC ๋๊ตฌ์ผ. ์ฝ๊ฒ ๋งํ๋ฉด ์ธํ๋ผ๋ฅผ ์ฝ๋๋ก ๊ด๋ฆฌํ ์ ์๊ฒ ํด์ฃผ๋ ํด์ธ๋ฐ, ์ง์ง ํ๋ช
์ ์ด๋ผ๊ณ ํ ์ ์์ด ใ
ใ
ใ
์์ ์๋ AWS ์ฝ์์ ๋ก๊ทธ์ธํด์ EC2 ๋ง๋ค๊ณ , VPC ์ค์ ํ๊ณ , ๋ณด์๊ทธ๋ฃน ์ถ๊ฐํ๊ณ ... ์ด๋ฐ ์์
์ ์ผ์ผ์ด ์์ผ๋ก ํ์ด์ผ ํ๊ฑฐ๋ ? ๊ทผ๋ฐ ์ด์ ๋ .tf ํ์ผ์ ์ํ๋ ์ธํ๋ผ ๊ตฌ์ฑ์ ์ฝ๋๋ก ์์ฑํ๋ฉด, Terraform์ด ์์์ AWS API๋ฅผ ํธ์ถํด์ ๋๋ฑ ๋ง๋ค์ด์ค! ๐
๐ก Terraform์ ํต์ฌ ์ฅ์
1. ๋ฉํฐ ํด๋ผ์ฐ๋ ์ง์ โ๏ธ
AWS๋ง ์ฐ๋ ๊ฒ ์๋๋ผ Azure, GCP, ์ฌ์ง์ด ์จํ๋ ๋ฏธ์ค๊น์ง ํ๋์ ๋๊ตฌ๋ก ๊ด๋ฆฌ ๊ฐ๋ฅํด. ์ด๊ฒ ์ง์ง ๊ฐ๊ฟ์ธ ๊ฒ, ํ์ฌ์์ ์ฌ๋ฌ ํด๋ผ์ฐ๋๋ฅผ ๋์์ ์ธ ๋ ์์ฒญ ํธํ๊ฑฐ๋ ?
2. ์ ์ธ์ ๋ฌธ๋ฒ(Declarative) ๐
"์ด๋ ๊ฒ ํด๋ผ, ์ ๋ ๊ฒ ํด๋ผ" ํ๋ ๊ฒ ์๋๋ผ "์ต์ข
์ํ๋ ์ด๊ฑฐ์ผ"๋ผ๊ณ ์ ์ธ๋ง ํ๋ฉด ๋ผ. Terraform์ด ์์์ ํ์ฌ ์ํ์ ๋น๊ตํด์ ํ์ํ ์์
๋ง ์ํํด์ค ใ
ใ
ใ
3. ์ํ ๊ด๋ฆฌ(State Management) ๐พ
terraform.tfstate ํ์ผ๋ก ํ์ฌ ์ธํ๋ผ ์ํ๋ฅผ ์ถ์ ํด. ๊ทธ๋์ ๋ญ๊ฐ ๋ฐ๋์๋์ง, ๋ญ ์ถ๊ฐ/์ญ์ ํด์ผ ํ๋์ง ์ ํํ๊ฒ ์ ์ ์์ด!
4. ๋ฒ์ ๊ด๋ฆฌ ๊ฐ๋ฅ ๐
์ฝ๋๋๊น ๋น์ฐํ Git์ผ๋ก ๋ฒ์ ๊ด๋ฆฌ๊ฐ ๋์ง! ์ธํ๋ผ ๋ณ๊ฒฝ ์ด๋ ฅ์ ์ถ์ ํ๊ณ , ๋ฌธ์ ์๊ธฐ๋ฉด ๋กค๋ฐฑ๋ ์ฝ๊ฒ ํ ์ ์์ด.
๐ ๏ธ Terraform ์ค์น๋ถํฐ ์์ํด๋ณด์!
์, ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ์์ํด๋ณผ๊น? ์ผ๋จ Terraform์ ์ค์นํด์ผ๊ฒ ์ง? โ๏ธ
์ค์น ๋ฐฉ๋ฒ์ ์ด์์ฒด์ ๋ง๋ค ์กฐ๊ธ์ฉ ๋ค๋ฅธ๋ฐ, ํ๋์ฉ ์๋ ค์ค๊ฒ!
๐ macOS ์ค์น
๋งฅ ์ ์ ๋ผ๋ฉด Homebrew ์ฐ๋ฉด ๊ฐ๊ฟ์ด์ผ ใ ใ ใ
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
์ค์น ๋! ์ง์ง ๊ฐ๋จํ์ง? ๐
๐ช Windows ์ค์น
์๋์ฐ ์ ์ ๋ Chocolatey ์ฐ๊ฑฐ๋ ์ง์ ๋ค์ด๋ก๋ ๋ฐ์ผ๋ฉด ๋ผ.
choco install terraform
๋๋ HashiCorp ๊ณต์ ์ฌ์ดํธ์์ ๋ฐ์ด๋๋ฆฌ ๋ค์ด๋ฐ์์ PATH์ ์ถ๊ฐํด์ฃผ๋ฉด ๋!
๐ง Linux ์ค์น
๋ฆฌ๋ ์ค๋ ํจํค์ง ๋งค๋์ ์ฐ๋ฉด ๋ผ. Ubuntu ๊ธฐ์ค์ผ๋ก ์๋ ค์ค๊ฒ!
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
๋ฒ์ ์ ๋ณด๊ฐ ๋์ค๋ฉด ์ค์น ์ฑ๊ณต! ๐
๐ AWS ์๊ฒฉ์ฆ๋ช ์ค์ ํ๊ธฐ
Terraform์ด AWS ๋ฆฌ์์ค๋ฅผ ๋ง๋ค๋ ค๋ฉด ๋น์ฐํ AWS ๊ณ์ ๊ถํ์ด ํ์ํ๊ฒ ์ง? ๐
AWS ์๊ฒฉ์ฆ๋ช
์ ์ค์ ํ๋ ๋ฐฉ๋ฒ์ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์๋๋ฐ, ๊ฐ์ฅ ๋ง์ด ์ฐ๋ ๋ฐฉ๋ฒ๋ค์ ์๋ ค์ค๊ฒ!
๋ฐฉ๋ฒ 1: AWS CLI ์ฌ์ฉ (์ถ์ฒ! โญ)
AWS CLI๋ฅผ ์ค์นํ๊ณ configure ๋ช ๋ น์ด๋ก ์ค์ ํ๋ ๊ฒ ์ ์ผ ํธํด.
aws configure
๊ทธ๋ผ ์ด๋ฐ ๊ฑธ ๋ฌผ์ด๋ด:
AWS Access Key ID [None]: YOUR_ACCESS_KEY
AWS Secret Access Key [None]: YOUR_SECRET_KEY
Default region name [None]: ap-northeast-2
Default output format [None]: json
์ด๋ ๊ฒ ํ๋ฉด ~/.aws/credentials ํ์ผ์ ์๊ฒฉ์ฆ๋ช ์ด ์ ์ฅ๋ผ. Terraform์ด ์๋์ผ๋ก ์ด ํ์ผ์ ์ฝ์ด์ ์ฌ์ฉํด ใ ใ ใ
๋ฐฉ๋ฒ 2: ํ๊ฒฝ๋ณ์ ์ฌ์ฉ
ํ๊ฒฝ๋ณ์๋ก ์ค์ ํ ์๋ ์์ด. ์ด๊ฑด CI/CD ํ์ดํ๋ผ์ธ์์ ๋ง์ด ์จ!
export AWS_ACCESS_KEY_ID="your_access_key"
export AWS_SECRET_ACCESS_KEY="your_secret_key"
export AWS_DEFAULT_REGION="ap-northeast-2"
๋ฐฉ๋ฒ 3: Terraform ์ฝ๋์ ์ง์ ์์ฑ (๋น์ถ์ฒ! โ)
์ด๊ฑด ์ ๋ ํ์ง ๋ง! ๋ณด์์ ์ํํด ใ ใ ใ
provider "aws" {
access_key = "your_access_key" # ์ด๋ฌ์ง ๋ง!!!
secret_key = "your_secret_key" # ์ง์ง ์ํํด!!!
region = "ap-northeast-2"
}
์๊ฒฉ์ฆ๋ช ์ ์ฝ๋์ ํ๋์ฝ๋ฉํ๋ฉด Git์ ์ฌ๋ผ๊ฐ ์๋ ์๊ณ , ๋ณด์ ์ฌ๊ณ ๋ ์ ์์ด. ์ ๋ ๊ธ์ง! ๐ซ
- .gitignore์ *.tfvars, terraform.tfstate ์ถ๊ฐ ํ์!
- ๊ฐ๋ฅํ๋ฉด MFA(๋ค์ค ์ธ์ฆ) ํ์ฑํํ๊ธฐ!
- ์ต์ ๊ถํ ์์น(Least Privilege) ์ ์ฉํ๊ธฐ!
๐ ์ฒซ ๋ฒ์งธ Terraform ์ฝ๋ ์์ฑํ๊ธฐ
์, ์ด์ ์ง์ง ์ฝ๋๋ฅผ ์์ฑํด๋ณผ ์๊ฐ์ด์ผ! ๐จ
๊ฐ๋จํ EC2 ์ธ์คํด์ค ํ๋๋ฅผ ๋ง๋๋ ์ฝ๋๋ถํฐ ์์ํด๋ณด์. ์ผ๋จ ํ๋ก์ ํธ ๋๋ ํ ๋ฆฌ๋ฅผ ๋ง๋ค์ด์ค.
mkdir my-first-terraform
cd my-first-terraform
1๏ธโฃ main.tf ํ์ผ ์์ฑ
Terraform์ ๋ฉ์ธ ์ค์ ํ์ผ์ด์ผ. ์ฌ๊ธฐ์ ํ๋ก๋ฐ์ด๋ ์ค์ ๊ณผ ๋ฆฌ์์ค๋ฅผ ์ ์ํด.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
required_version = ">= 1.0"
}
provider "aws" {
region = "ap-northeast-2" # ์์ธ ๋ฆฌ์ !
}
resource "aws_instance" "my_first_server" {
ami = "ami-0c9c942bd7bf113a2" # Amazon Linux 2023
instance_type = "t2.micro" # ํ๋ฆฌํฐ์ด ๊ฐ๋ฅ!
tags = {
Name = "MyFirstTerraformServer"
Environment = "Learning"
}
}
์ด๊ฒ ๋์ด์ผ! ์ง์ง ๊ฐ๋จํ์ง? ใ ใ ใ ์ด ์ฝ๋๋ง์ผ๋ก EC2 ์ธ์คํด์ค๊ฐ ๋ง๋ค์ด์ ธ!
๐ ์ฝ๋ ํด๋ถํ๊ธฐ
terraform ๋ธ๋ก:
Terraform ์์ฒด ์ค์ ์ ํ๋ ๋ถ๋ถ์ด์ผ. ์ด๋ค ํ๋ก๋ฐ์ด๋๋ฅผ ์ธ ๊ฑด์ง, ์ต์ ๋ฒ์ ์ ๋ญ์ง ์ ์ํด.
provider ๋ธ๋ก:
AWS, Azure, GCP ๊ฐ์ ํด๋ผ์ฐ๋ ํ๋ก๋ฐ์ด๋ ์ค์ ์ด์ผ. ์ฌ๊ธฐ์๋ AWS ์์ธ ๋ฆฌ์ ์ ์ฌ์ฉํ๊ฒ ๋ค๊ณ ์ ์ธํ์ด.
resource ๋ธ๋ก:
์ค์ ๋ก ๋ง๋ค ๋ฆฌ์์ค๋ฅผ ์ ์ํ๋ ๋ถ๋ถ! aws_instance๋ EC2 ์ธ์คํด์ค๋ฅผ ์๋ฏธํ๊ณ , my_first_server๋ Terraform ๋ด์์ ์ฌ์ฉํ ์ด๋ฆ์ด์ผ.
ami: Amazon Machine Image ID์ผ. ์ด๊ฑด ๋ฆฌ์ ๋ง๋ค ๋ค๋ฅด๋๊น ์ฃผ์! ์์ธ ๋ฆฌ์ ์ Amazon Linux 2023 AMI๋ฅผ ์ฌ์ฉํ์ด.
instance_type: ์ธ์คํด์ค ํฌ๊ธฐ! t2.micro๋ ํ๋ฆฌํฐ์ด๋ก ๋ฌด๋ฃ๋ก ์ธ ์ ์์ด ใ
ใ
ใ
tags: ๋ฆฌ์์ค์ ํ๊ทธ๋ฅผ ๋ฌ์์ฃผ๋ ๊ฑฐ์ผ. ๋์ค์ ๊ด๋ฆฌํ ๋ ์์ฒญ ์ ์ฉํด!
๐ Terraform ์คํํ๊ธฐ (๋๋์ด!)
์ฝ๋๋ ์์ฑํ์ผ๋ ์ด์ ์คํํด๋ณผ ์ฐจ๋ก์ผ! ๐ฏ
Terraform ์คํ์ ํฌ๊ฒ 3๋จ๊ณ๋ก ์ด๋ฃจ์ด์ ธ ์์ด. ํ๋์ฉ ํด๋ณด์!
Step 1: terraform init ๐ฌ
์ด๊ธฐํ ๋ช ๋ น์ด์ผ! ํ๋ก๋ฐ์ด๋ ํ๋ฌ๊ทธ์ธ์ ๋ค์ด๋ก๋ํ๊ณ ์์ ๋๋ ํ ๋ฆฌ๋ฅผ ์ค๋นํด์ค.
terraform init
์คํํ๋ฉด ์ด๋ฐ ๋ฉ์์ง๊ฐ ๋์:
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.0"...
- Installing hashicorp/aws v5.31.0...
- Installed hashicorp/aws v5.31.0
Terraform has been successfully initialized!
.terraform ๋๋ ํ ๋ฆฌ๊ฐ ์์ฑ๋๊ณ ํ์ํ ํ๋ฌ๊ทธ์ธ๋ค์ด ๋ค์ด๋ก๋๋ผ. ์ด๊ฑด ํ๋ก์ ํธ๋น ํ ๋ฒ๋ง ํ๋ฉด ๋ผ! โ
Step 2: terraform plan ๐
์คํ ๊ณํ์ ๋ฏธ๋ฆฌ ๋ณด๋ ๋ช ๋ น์ด์ผ! ์ค์ ๋ก ๋ญ๊ฐ ๋ง๋ค์ด์ง์ง ํ์ธํ ์ ์์ด์ ์์ ๊ฟ์ด์ผ ใ ใ ใ
terraform plan
๊ทธ๋ผ ์ด๋ฐ ์์ผ๋ก ์ถ๋ ฅ๋ผ:
Terraform will perform the following actions:
# aws_instance.my_first_server will be created
+ resource "aws_instance" "my_first_server" {
+ ami = "ami-0c9c942bd7bf113a2"
+ instance_type = "t2.micro"
+ tags = {
+ "Environment" = "Learning"
+ "Name" = "MyFirstTerraformServer"
}
# ... (์๋ต)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Plan: 1 to add, 0 to change, 0 to destroy ์ด ๋ถ๋ถ์ด ํต์ฌ์ด์ผ!
1๊ฐ๋ฅผ ์ถ๊ฐํ๊ณ , 0๊ฐ๋ฅผ ๋ณ๊ฒฝํ๊ณ , 0๊ฐ๋ฅผ ์ญ์ ํ๋ค๋ ๋ป์ด์ง. ์ค์๋ก ๋ญ๊ฐ ์ง์์ง ์ผ์ด ์๋์ง ํ์ธํ ์ ์์ด์ ์์ ํด! ๐ก๏ธ
Step 3: terraform apply ๐
๋๋์ด ์ค์ ๋ก ์ธํ๋ผ๋ฅผ ๋ง๋๋ ๋ช ๋ น์ด์ผ! ๊ธด์ฅ๋์ง? ใ ใ ใ
terraform apply
์คํํ๋ฉด plan ๊ฒฐ๊ณผ๋ฅผ ๋ค์ ๋ณด์ฌ์ฃผ๊ณ ํ์ธ์ ์์ฒญํด:
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value:
์ฌ๊ธฐ์ yes๋ฅผ ์
๋ ฅํ๋ฉด ์ง์ง๋ก AWS์ ๋ฆฌ์์ค๊ฐ ์์ฑ๋ผ! ๐ฑ
๋ช ์ด ๊ธฐ๋ค๋ฆฌ๋ฉด...
aws_instance.my_first_server: Creating...
aws_instance.my_first_server: Still creating... [10s elapsed]
aws_instance.my_first_server: Still creating... [20s elapsed]
aws_instance.my_first_server: Creation complete after 25s [id=i-0123456789abcdef0]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
์์ฑ! ๐ AWS ์ฝ์ ๊ฐ์ ํ์ธํด๋ด. EC2 ์ธ์คํด์ค๊ฐ ์ง์ง๋ก ๋ง๋ค์ด์ ธ ์์ ๊ฑฐ์ผ!
๐๏ธ ์ค์ ์์ : ์์ ํ ์น ์๋ฒ ์ธํ๋ผ ๊ตฌ์ถํ๊ธฐ
์ด์ ์ข ๋ ์ค์ ์ ์ธ ์์ ๋ฅผ ํด๋ณผ๊น? ๐ช
๋จ์ํ EC2 ํ๋๋ง ๋ง๋๋ ๊ฒ ์๋๋ผ, VPC, ์๋ธ๋ท, ๋ณด์๊ทธ๋ฃน, ๊ทธ๋ฆฌ๊ณ ์น ์๋ฒ๊น์ง ํ ๋ฒ์ ๊ตฌ์ถํด๋ณด์! ์ด๊ฒ ์ง์ง ์ค๋ฌด์์ ์ฐ๋ ๋ฐฉ์์ด์ผ ใ
ใ
ใ
๐ ํ๋ก์ ํธ ๊ตฌ์กฐ ์ก๊ธฐ
์ผ๋จ ํ์ผ์ ์ฌ๋ฌ ๊ฐ๋ก ๋๋๋ ๊ฒ ์ข์. ๊ด๋ฆฌํ๊ธฐ ํธํ๊ฑฐ๋ !
web-server-infra/
โโโ main.tf # ๋ฉ์ธ ๋ฆฌ์์ค ์ ์
โโโ variables.tf # ๋ณ์ ์ ์
โโโ outputs.tf # ์ถ๋ ฅ๊ฐ ์ ์
โโโ terraform.tfvars # ๋ณ์ ๊ฐ ์ค์
โโโ .gitignore # Git ๋ฌด์ ํ์ผ
1๏ธโฃ variables.tf - ๋ณ์ ์ ์ํ๊ธฐ
ํ๋์ฝ๋ฉ ๋์ ๋ณ์๋ฅผ ์ฌ์ฉํ๋ฉด ์ฌ์ฌ์ฉ์ฑ์ด ์์ฒญ ์ข์์ ธ!
variable "aws_region" {
description = "AWS ๋ฆฌ์ "
type = string
default = "ap-northeast-2"
}
variable "project_name" {
description = "ํ๋ก์ ํธ ์ด๋ฆ"
type = string
default = "my-web-server"
}
variable "environment" {
description = "ํ๊ฒฝ (dev, staging, prod)"
type = string
default = "dev"
}
variable "instance_type" {
description = "EC2 ์ธ์คํด์ค ํ์
"
type = string
default = "t2.micro"
}
variable "allowed_cidr_blocks" {
description = "์ ๊ทผ ํ์ฉํ IP ๋์ญ"
type = list(string)
default = ["0.0.0.0/0"] # ์ค๋ฌด์์๋ ํน์ IP๋ง ํ์ฉํด์ผ ํด!
}
2๏ธโฃ main.tf - ๋ฉ์ธ ์ธํ๋ผ ์ฝ๋
์ด์ ์ง์ง ์ธํ๋ผ๋ฅผ ์ ์ํด๋ณด์! ์ข ๊ธธ์ง๋ง ํ๋์ฉ ๋ณด๋ฉด ์ด๋ ต์ง ์์ ๐
# Provider ์ค์
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
required_version = ">= 1.0"
}
provider "aws" {
region = var.aws_region
}
# VPC ์์ฑ
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project_name}-vpc"
Environment = var.environment
}
}
# ์ธํฐ๋ท ๊ฒ์ดํธ์จ์ด
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-igw"
Environment = var.environment
}
}
# ํผ๋ธ๋ฆญ ์๋ธ๋ท
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "${var.aws_region}a"
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-public-subnet"
Environment = var.environment
}
}
# ๋ผ์ฐํ
ํ
์ด๋ธ
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 = "${var.project_name}-public-rt"
Environment = var.environment
}
}
# ๋ผ์ฐํ
ํ
์ด๋ธ ์ฐ๊ฒฐ
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 = "${var.project_name}-web-sg"
description = "Security group for web server"
vpc_id = aws_vpc.main.id
# HTTP ์ธ๋ฐ์ด๋
ingress {
description = "HTTP from anywhere"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = var.allowed_cidr_blocks
}
# HTTPS ์ธ๋ฐ์ด๋
ingress {
description = "HTTPS from anywhere"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = var.allowed_cidr_blocks
}
# SSH ์ธ๋ฐ์ด๋
ingress {
description = "SSH from anywhere"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = var.allowed_cidr_blocks
}
# ๋ชจ๋ ์์๋ฐ์ด๋ ํ์ฉ
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-web-sg"
Environment = var.environment
}
}
# ์ต์ Amazon Linux 2023 AMI ์ฐพ๊ธฐ
data "aws_ami" "amazon_linux_2023" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
# EC2 ์ธ์คํด์ค (์น ์๋ฒ)
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux_2023.id
instance_type = var.instance_type
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
echo "<p>Server deployed at: $(date)</p>" >> /var/www/html/index.html
EOF
tags = {
Name = "${var.project_name}-web-server"
Environment = var.environment
}
}
์... ๊ธธ๋ค ใ ใ ใ ๊ทผ๋ฐ ์ด๊ฒ ์ ๋ถ์ผ! ์ด ์ฝ๋๋ก VPC๋ถํฐ ์น ์๋ฒ๊น์ง ์์ ํ ์ธํ๋ผ๊ฐ ๋ง๋ค์ด์ ธ! ๐
3๏ธโฃ outputs.tf - ์ถ๋ ฅ๊ฐ ์ ์
์ธํ๋ผ ๋ง๋ค๊ณ ๋์ ํ์ํ ์ ๋ณด๋ฅผ ์ถ๋ ฅํ๊ฒ ํด์ค. ์์ฒญ ์ ์ฉํด!
output "vpc_id" {
description = "VPC ID"
value = aws_vpc.main.id
}
output "public_subnet_id" {
description = "Public Subnet ID"
value = aws_subnet.public.id
}
output "web_server_public_ip" {
description = "Web Server Public IP"
value = aws_instance.web.public_ip
}
output "web_server_url" {
description = "Web Server URL"
value = "http://${aws_instance.web.public_ip}"
}
output "security_group_id" {
description = "Security Group ID"
value = aws_security_group.web.id
}
4๏ธโฃ terraform.tfvars - ๋ณ์ ๊ฐ ์ค์
ํ๊ฒฝ๋ณ๋ก ๋ค๋ฅธ ๊ฐ์ ์ค์ ํ ์ ์์ด!
aws_region = "ap-northeast-2"
project_name = "my-awesome-web"
environment = "dev"
instance_type = "t2.micro"
# ๋ณด์์ ์ํด ํน์ IP๋ง ํ์ฉํ๋ ๊ฒ ์ข์!
# allowed_cidr_blocks = ["123.456.789.0/24"]
์ด์ ๋๊ฐ์ด ์คํํ๋ฉด ๋ผ:
terraform init
terraform plan
terraform apply
๋ช ๋ถ ๊ธฐ๋ค๋ฆฌ๋ฉด ์์ ํ ์น ์๋ฒ ์ธํ๋ผ๊ฐ ๋๋ฑ! ๐์ถ๋ ฅ๋ URL๋ก ์ ์ํ๋ฉด "Hello from Terraform!" ํ์ด์ง๊ฐ ๋ณด์ผ ๊ฑฐ์ผ!
๐ Terraform State ๊ด๋ฆฌํ๊ธฐ
Terraform์ ํต์ฌ ๊ฐ๋
์ค ํ๋๊ฐ ๋ฐ๋ก State์ผ! ๐พ
State ํ์ผ์ Terraform์ด ๊ด๋ฆฌํ๋ ์ธํ๋ผ์ ํ์ฌ ์ํ๋ฅผ ์ ์ฅํ๋ ํ์ผ์ธ๋ฐ, ์ด๊ฒ ์์ฒญ ์ค์ํด ใ
ใ
ใ
๐ State ํ์ผ์ด ๋ญ์ผ?
terraform.tfstate ํ์ผ์ JSON ํ์์ผ๋ก ๋์ด ์๊ณ , ํ์ฌ ์ธํ๋ผ์ ๋ชจ๋ ์ ๋ณด๋ฅผ ๋ด๊ณ ์์ด.
Terraform์ ์ด ํ์ผ์ ๋ณด๊ณ :
- ํ์ฌ ์ด๋ค ๋ฆฌ์์ค๊ฐ ์๋์ง
- ์ฝ๋์ ์ค์ ์ธํ๋ผ์ ์ฐจ์ด๊ฐ ๋ญ์ง
- ์ด๋ค ์์
์ ํด์ผ ํ๋์ง
๋ฅผ ํ๋จํด!
1. ํ์ ๋ถ๊ฐ: ํ์๋ค์ด ๊ฐ์ State๋ฅผ ๊ณต์ ํ ์ ์์ด
2. ๋์ ์คํ ์ํ: ๋ ๋ช ์ด ๋์์ apply ํ๋ฉด ์ถฉ๋ ๋ฐ์
3. ๋ฐฑ์ ๋ฌธ์ : ๋ก์ปฌ ํ์ผ์ด ๋ ์๊ฐ๋ฉด ๋์ฅ
4. ๋ณด์ ๋ฌธ์ : State์๋ ๋ฏผ๊ฐํ ์ ๋ณด๊ฐ ๋ค์ด์์ ์ ์์ด
๐ Remote State ์ฌ์ฉํ๊ธฐ (S3 + DynamoDB)
์ค๋ฌด์์๋ ๋ฌด์กฐ๊ฑด Remote State๋ฅผ ์จ์ผ ํด! AWS S3์ State๋ฅผ ์ ์ฅํ๊ณ , DynamoDB๋ก Lock์ ๊ด๋ฆฌํ๋ ๊ฒ ๋ฒ ์คํธ์ผ ๐
Step 1: S3 ๋ฒํท๊ณผ DynamoDB ํ ์ด๋ธ ๋ง๋ค๊ธฐ
๋จผ์ State๋ฅผ ์ ์ฅํ S3 ๋ฒํท๊ณผ Lock์ฉ DynamoDB ํ ์ด๋ธ์ ๋ง๋ค์ด์ผ ํด. ์ด๊ฑด ์๋์ผ๋ก ํ๊ฑฐ๋ ๋ณ๋์ Terraform ์ฝ๋๋ก ๋ง๋ค์ด์ค.
# backend-setup.tf (๋ณ๋๋ก ๋จผ์ ์คํ)
resource "aws_s3_bucket" "terraform_state" {
bucket = "my-terraform-state-bucket-unique-name-12345"
tags = {
Name = "Terraform State Bucket"
}
}
resource "aws_s3_bucket_versioning" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
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"
}
}
}
resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-state-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
tags = {
Name = "Terraform State Lock Table"
}
}
Step 2: Backend ์ค์ ํ๊ธฐ
์ด์ ๋ฉ์ธ ํ๋ก์ ํธ์์ ์ด S3 ๋ฒํท์ backend๋ก ์ฌ์ฉํ๋๋ก ์ค์ ํด์ค.
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket-unique-name-12345"
key = "web-server/terraform.tfstate"
region = "ap-northeast-2"
dynamodb_table = "terraform-state-locks"
encrypt = true
}
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
Step 3: State ๋ง์ด๊ทธ๋ ์ด์
๊ธฐ์กด์ ๋ก์ปฌ State๋ฅผ ์ฐ๊ณ ์์๋ค๋ฉด ๋ง์ด๊ทธ๋ ์ด์ ํด์ค์ผ ํด.
terraform init -migrate-state
๊ทธ๋ผ Terraform์ด ์๋์ผ๋ก ๋ก์ปฌ State๋ฅผ S3๋ก ์ฎ๊ฒจ์ค! ์์ ํธํ์ง? ใ ใ ใ
- State Locking: DynamoDB๋ก ๋์ ์คํ ๋ฐฉ์ง
- ๋ฒ์ ๊ด๋ฆฌ: S3 ๋ฒ์ ๋์ผ๋ก ์ด์ ์ํ ๋ณต๊ตฌ ๊ฐ๋ฅ
- ์ํธํ: State ํ์ผ์ด ์ํธํ๋์ด ์์
- ๋ฐฑ์ : S3์ ๋์ ๋ด๊ตฌ์ฑ์ผ๋ก ์์ ํ๊ฒ ๋ณด๊ด
๐ฏ Terraform ๋ชจ๋ ๋ง๋ค๊ธฐ
์ฝ๋ ์ฌ์ฌ์ฉ์ฑ์ ๊ทน๋ํํ๋ ค๋ฉด ๋ชจ๋(Module)์ ๋ง๋ค์ด์ผ ํด! ๐ฆ
๋ชจ๋์ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ Terraform ์ฝ๋ ๋ฌถ์์ด์ผ. ํ ๋ฒ ๋ง๋ค์ด๋๋ฉด ์ฌ๋ฌ ํ๋ก์ ํธ์์ ๊ณ์ ์ธ ์ ์์ด์ ์์ ๊ฟ์ด์ง ใ
ใ
ใ
๐๏ธ ๋ชจ๋ ๊ตฌ์กฐ ์์
modules/
โโโ web-server/
โโโ main.tf
โโโ variables.tf
โโโ outputs.tf
โโโ README.md
root/
โโโ main.tf
โโโ variables.tf
โโโ terraform.tfvars
1๏ธโฃ ๋ชจ๋ ๋ง๋ค๊ธฐ (modules/web-server/)
modules/web-server/variables.tf
variable "vpc_id" {
description = "VPC ID"
type = string
}
variable "subnet_id" {
description = "Subnet ID"
type = string
}
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t2.micro"
}
variable "server_name" {
description = "Server name tag"
type = string
}
variable "environment" {
description = "Environment name"
type = string
}
modules/web-server/main.tf
data "aws_ami" "amazon_linux_2023" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
}
resource "aws_security_group" "web" {
name = "${var.server_name}-sg"
description = "Security group for ${var.server_name}"
vpc_id = var.vpc_id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.server_name}-sg"
Environment = var.environment
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux_2023.id
instance_type = var.instance_type
subnet_id = var.subnet_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>${var.server_name}</h1>" > /var/www/html/index.html
EOF
tags = {
Name = var.server_name
Environment = var.environment
}
}
modules/web-server/outputs.tf
output "instance_id" {
description = "EC2 Instance ID"
value = aws_instance.web.id
}
output "public_ip" {
description = "Public IP address"
value = aws_instance.web.public_ip
}
output "security_group_id" {
description = "Security Group ID"
value = aws_security_group.web.id
}
2๏ธโฃ ๋ชจ๋ ์ฌ์ฉํ๊ธฐ (root/main.tf)
์ด์ ๋ง๋ ๋ชจ๋์ ์ฌ์ฉํด๋ณด์! ์์ฒญ ๊ฐ๋จํด์ ธ ใ ใ ใ
module "web_server_dev" {
source = "./modules/web-server"
vpc_id = aws_vpc.main.id
subnet_id = aws_subnet.public.id
instance_type = "t2.micro"
server_name = "dev-web-server"
environment = "dev"
}
module "web_server_staging" {
source = "./modules/web-server"
vpc_id = aws_vpc.main.id
subnet_id = aws_subnet.public.id
instance_type = "t2.small"
server_name = "staging-web-server"
environment = "staging"
}
# ๋ชจ๋ ์ถ๋ ฅ๊ฐ ์ฌ์ฉํ๊ธฐ
output "dev_server_ip" {
value = module.web_server_dev.public_ip
}
output "staging_server_ip" {
value = module.web_server_staging.public_ip
}
๊ฐ์ ๋ชจ๋์ ์ฌ๋ฌ ๋ฒ ํธ์ถํด์ dev, staging ์๋ฒ๋ฅผ ๋์์ ๋ง๋ค ์ ์์ด! ์์ ํธํ์ง? ๐
Terraform Registry์๋ ์ด๋ฏธ ๋ง๋ค์ด์ง ์๋ง์ ๋ชจ๋์ด ์์ด!
registry.terraform.io์์ ๊ฒ์ํด๋ณด๋ฉด AWS VPC, EKS, RDS ๋ฑ ๊ฑฐ์ ๋ชจ๋ ๋ฆฌ์์ค์ ๋ชจ๋์ ์ฐพ์ ์ ์์ด ใ ใ ใ
์์:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.2"
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 = {
Terraform = "true"
Environment = "dev"
}
}
๐ ๋ณด์ ๋ฒ ์คํธ ํ๋ํฐ์ค
Terraform ์ธ ๋ ๋ณด์์ ์ง์ง ์ค์ํด! ๐ก๏ธ
์ค์๋ก ์๊ฒฉ์ฆ๋ช
์ด๋ ๋ฏผ๊ฐํ ์ ๋ณด๊ฐ ๋
ธ์ถ๋๋ฉด ํฐ์ผ๋๊ฑฐ๋ ใ
ใ
ใ
๊ผญ ์ง์ผ์ผ ํ ๋ณด์ ์์น๋ค์ ์๋ ค์ค๊ฒ!
โ ๏ธ ์ ๋ ํ์ง ๋ง์์ผ ํ ๊ฒ๋ค
1. ์๊ฒฉ์ฆ๋ช
ํ๋์ฝ๋ฉ ๊ธ์ง!
# โ ์ด๋ฌ์ง ๋ง!!!
provider "aws" {
access_key = "AKIAIOSFODNN7EXAMPLE"
secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
2. State ํ์ผ Git์ ์ปค๋ฐ ๊ธ์ง!
terraform.tfstate์๋ ๋ชจ๋ ๋ฆฌ์์ค ์ ๋ณด๊ฐ ๋ค์ด์์ด. ์ฌ์ง์ด ๋น๋ฐ๋ฒํธ ๊ฐ์ ๋ฏผ๊ฐํ ์ ๋ณด๋!
3. ๋ฏผ๊ฐํ ๋ณ์ ํ๋ฌธ ์ ์ฅ ๊ธ์ง!
terraform.tfvars์ DB ๋น๋ฐ๋ฒํธ ๊ฐ์ ๊ฑฐ ๊ทธ๋๋ก ์ฐ์ง ๋ง!
โ ์ฌ๋ฐ๋ฅธ ๋ณด์ ๋ฐฉ๋ฒ
1. ํ๊ฒฝ๋ณ์ ์ฌ์ฉ
export TF_VAR_db_password="super_secret_password"
๊ทธ๋ฆฌ๊ณ ์ฝ๋์์๋:
variable "db_password" {
description = "Database password"
type = string
sensitive = true # ์ถ๋ ฅ์์ ์จ๊น!
}
2. 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
}
3. .gitignore ์ค์
# .gitignore
*.tfstate
*.tfstate.*
.terraform/
*.tfvars
crash.log
override.tf
override.tf.json
*_override.tf
*_override.tf.json
.terraformrc
terraform.rc
4. State ์ํธํ
S3 backend ์ฌ์ฉํ ๋ ๋ฐ๋์ ์ํธํ ํ์ฑํ!
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "ap-northeast-2"
encrypt = true # ํ์!
# KMS ํค ์ฌ์ฉ (๋ ๊ฐ๋ ฅํ ์ํธํ)
kms_key_id = "arn:aws:kms:ap-northeast-2:123456789012:key/12345678-1234-1234-1234-123456789012"
}
}
5. ์ต์ ๊ถํ ์์น
Terraform ์คํ์ฉ IAM ์ฌ์ฉ์/์ญํ ์๋ ํ์ํ ์ต์ํ์ ๊ถํ๋ง ๋ถ์ฌํด!
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"ec2:CreateTags",
"ec2:RunInstances",
"ec2:TerminateInstances"
],
"Resource": "*"
}
]
}
tfsec: Terraform ์ฝ๋์ ๋ณด์ ์ทจ์ฝ์ ์ ์ฐพ์์ค
brew install tfsec
tfsec .
Checkov: ๋ ํฌ๊ด์ ์ธ ๋ณด์ ๋ฐ ์ปดํ๋ผ์ด์ธ์ค ์ฒดํฌ
pip install checkov
checkov -d .
์ด๋ฐ ๋๊ตฌ๋ค์ CI/CD ํ์ดํ๋ผ์ธ์ ๋ฃ์ด์ ์๋์ผ๋ก ์ฒดํฌํ๋ฉด ์์ ๊ฟ์ด์ผ! ๐ฏ
๐ CI/CD ํ์ดํ๋ผ์ธ์ Terraform ํตํฉํ๊ธฐ
์ค๋ฌด์์๋ Terraform์ CI/CD ํ์ดํ๋ผ์ธ์ ํตํฉํด์ ์๋ํํ๋ ๊ฒ ์ผ๋ฐ์ ์ด์ผ! โ๏ธ
GitHub Actions๋ฅผ ์์๋ก ์๋ ค์ค๊ฒ ใ
ใ
ใ
๐ GitHub Actions ์ํฌํ๋ก์ฐ
.github/workflows/terraform.yml ํ์ผ์ ๋ง๋ค์ด์ค.
name: 'Terraform'
on:
push:
branches:
- main
pull_request:
branches:
- main
env:
AWS_REGION: ap-northeast-2
jobs:
terraform:
name: 'Terraform'
runs-on: ubuntu-latest
# OIDC๋ฅผ ์ฌ์ฉํ AWS ์ธ์ฆ (๋ ์์ !)
permissions:
id-token: write
contents: read
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
aws-region: ${{ env.AWS_REGION }}
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.6.0
- 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: Update Pull Request
uses: actions/github-script@v7
if: github.event_name == 'pull_request'
env:
PLAN: "${{ 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>
\`\`\`terraform
${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
๐ฏ ์ํฌํ๋ก์ฐ ์ค๋ช
1. PR ์์ฑ ์:
- ์ฝ๋ ํฌ๋งท ์ฒดํฌ
- Terraform ์ด๊ธฐํ
- ์ ํจ์ฑ ๊ฒ์ฆ
- Plan ์คํ ํ PR์ ์ฝ๋ฉํธ๋ก ๊ฒฐ๊ณผ ํ์
2. main ๋ธ๋์น ๋จธ์ง ์:
- ์์ ๋ชจ๋ ์ฒดํฌ ์ํ
- ๋ฌธ์ ์์ผ๋ฉด ์๋์ผ๋ก apply ์คํ
์ด๋ ๊ฒ ํ๋ฉด ์ฝ๋ ๋ฆฌ๋ทฐ ๊ณผ์ ์์ ์ธํ๋ผ ๋ณ๊ฒฝ์ฌํญ์ ๋ฏธ๋ฆฌ ํ์ธํ ์ ์์ด์ ์์ ์์ ํด! ๐
- ์ผ๊ด์ฑ: ํญ์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ๋ฐฐํฌ
- ์์ ์ฑ: ๋ฆฌ๋ทฐ ํ๋ก์ธ์ค ๊ฐ์
- ์ถ์ ์ฑ: ๋ชจ๋ ๋ณ๊ฒฝ์ฌํญ์ด Git ํ์คํ ๋ฆฌ์ ๊ธฐ๋ก
- ๋กค๋ฐฑ: ๋ฌธ์ ๋ฐ์ ์ ์ด์ ์ปค๋ฐ์ผ๋ก ์ฝ๊ฒ ๋ณต๊ตฌ
๐ ํธ๋ฌ๋ธ์ํ & ์์ฃผ ํ๋ ์ค์๋ค
Terraform ์ฐ๋ค ๋ณด๋ฉด ๋๊ตฌ๋ ๊ฒช๋ ๋ฌธ์ ๋ค์ด ์์ด ใ
ใ
ใ
๐
๋ด๊ฐ ๊ฒช์๋ ์ฝ์ง๋ค๊ณผ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ๊ณต์ ํด์ค๊ฒ!
โ ๋ฌธ์ 1: State Lock ๊ฑธ๋ฆผ
์ฆ์:
Error: Error acquiring the state lock
Error message: ConditionalCheckFailedException: The conditional
request failed
Lock Info:
ID: 12345678-1234-1234-1234-123456789012
Path: my-bucket/terraform.tfstate
Operation: OperationTypeApply
Who: user@hostname
Version: 1.6.0
Created: 2024-01-15 10:30:00.000000 UTC
์์ธ: ์ด์ ์คํ์ด ๋น์ ์ ์ข
๋ฃ๋์ด Lock์ด ์ ํ๋ ธ๊ฑฐ๋, ๋ค๋ฅธ ์ฌ๋์ด ๋์์ ์คํ ์ค
ํด๊ฒฐ:
# Lock ๊ฐ์ ํด์ (์กฐ์ฌํด์ ์จ์ผ ํด!)
terraform force-unlock 12345678-1234-1234-1234-123456789012
# ๋๋ DynamoDB์์ ์ง์ ์ญ์
aws dynamodb delete-item \
--table-name terraform-state-locks \
--key '{"LockID": {"S": "my-bucket/terraform.tfstate"}}'
โ ๋ฌธ์ 2: Provider ๋ฒ์ ์ถฉ๋
์ฆ์:
Error: Failed to query available provider packages
Could not retrieve the list of available versions for provider
hashicorp/aws: locked provider registry.terraform.io/hashicorp/aws
4.67.0 does not match configured version constraint ~> 5.0
์์ธ: .terraform.lock.hcl ํ์ผ์ ๋ฒ์ ๊ณผ ์ฝ๋์ ๋ฒ์ ์ ์ฝ์ด ๋ง์ง ์์
ํด๊ฒฐ:
# Lock ํ์ผ ์
๋ฐ์ดํธ
terraform init -upgrade
# ๋๋ Lock ํ์ผ ์ญ์ ํ ์ฌ์ด๊ธฐํ
rm .terraform.lock.hcl
terraform init
โ ๋ฌธ์ 3: ๋ฆฌ์์ค ์ด๋ฏธ ์กด์ฌํจ
์ฆ์:
Error: Error creating EC2 Instance: InvalidParameterValue:
Value (my-server) for parameter name is invalid.
A resource with that name already exists.
์์ธ: AWS์ ์ด๋ฏธ ๊ฐ์ ์ด๋ฆ์ ๋ฆฌ์์ค๊ฐ ์๋๋ฐ Terraform State์๋ ์์
ํด๊ฒฐ 1: Import ์ฌ์ฉ
# ๊ธฐ์กด ๋ฆฌ์์ค๋ฅผ Terraform State๋ก ๊ฐ์ ธ์ค๊ธฐ
terraform import aws_instance.my_server i-1234567890abcdef0
ํด๊ฒฐ 2: ๋ฆฌ์์ค ์ด๋ฆ ๋ณ๊ฒฝ
# ์ฝ๋์์ ๋ค๋ฅธ ์ด๋ฆ ์ฌ์ฉ
resource "aws_instance" "my_server_v2" {
# ...
}
โ ๋ฌธ์ 4: ์ํ ์์กด์ฑ
์ฆ์:
Error: Cycle: aws_security_group.web, aws_instance.app,
aws_security_group.app, aws_instance.web
์์ธ: ๋ฆฌ์์ค ๊ฐ ์์กด์ฑ์ด ์ํ ๊ตฌ์กฐ๋ฅผ ์ด๋ฃธ
ํด๊ฒฐ: ์์กด์ฑ ๊ตฌ์กฐ๋ฅผ ์ฌ์ค๊ณํ๊ฑฐ๋ depends_on์ ๋ช
์์ ์ผ๋ก ์ฌ์ฉ
resource "aws_instance" "app" {
# ...
depends_on = [
aws_security_group.web
]
}
โ ๋ฌธ์ 5: State Drift (์ํ ๋ถ์ผ์น)
์ฆ์: ๋๊ตฐ๊ฐ AWS ์ฝ์์์ ์ง์ ๋ฆฌ์์ค๋ฅผ ์์ ํด์ Terraform State์ ์ค์ ์ธํ๋ผ๊ฐ ๋ฌ๋ผ์ง
ํ์ธ:
terraform plan -refresh-only
ํด๊ฒฐ:
# State๋ฅผ ์ค์ ์ธํ๋ผ์ ๋ง์ถฐ ์
๋ฐ์ดํธ
terraform apply -refresh-only
# ๋๋ ์ค์ ์ธํ๋ผ๋ฅผ ์ฝ๋์ ๋ง์ถฐ ์์
terraform apply
์๋ฐฉ: AWS Config๋ CloudTrail๋ก ์๋ ๋ณ๊ฒฝ์ ๋ชจ๋ํฐ๋งํ๊ณ , ํ์๋ค์๊ฒ "์ ๋ ์ฝ์์์ ์ง์ ์์ ํ์ง ๋ง!" ๊ต์กํ๊ธฐ ใ ใ ใ
1. ๋ก๊ทธ ๋ ๋ฒจ ์ฌ๋ฆฌ๊ธฐ:
export TF_LOG=DEBUG
terraform apply
2. ํน์ ๋ฆฌ์์ค๋ง ํ๊ฒํ :
terraform apply -target=aws_instance.web
3. Plan ํ์ผ ์ ์ฅํด์ ํ์ธ:
terraform plan -out=tfplan
terraform show tfplan
๐ฐ ๋น์ฉ ์ต์ ํ ํ
AWS ์ฐ๋ค ๋ณด๋ฉด ๋น์ฉ์ด ๋ฌด์ญ๊ฒ ๋์ฌ ์ ์์ด ใ
ใ
ใ
๐ธ
Terraform์ผ๋ก ์ธํ๋ผ ๊ด๋ฆฌํ ๋ ๋น์ฉ์ ์ ์ฝํ๋ ๋ฐฉ๋ฒ๋ค์ ์๋ ค์ค๊ฒ!
1๏ธโฃ ๊ฐ๋ฐ ํ๊ฒฝ ์๋ ์ข ๋ฃ
๊ฐ๋ฐ ์๋ฒ๋ ์ ๋ฌด ์๊ฐ์๋ง ์ผ๋๊ณ ๋ฐค์๋ ๊บผ๋ฒ๋ฆฌ์!
# Lambda + EventBridge๋ก ์ค์ผ์ค๋ง
resource "aws_lambda_function" "stop_instances" {
filename = "stop_instances.zip"
function_name = "stop-dev-instances"
role = aws_iam_role.lambda_role.arn
handler = "index.handler"
runtime = "python3.11"
environment {
variables = {
INSTANCE_IDS = join(",", [aws_instance.dev.id])
}
}
}
resource "aws_cloudwatch_event_rule" "stop_instances" {
name = "stop-instances-evening"
schedule_expression = "cron(0 19 * * ? *)" # ๋งค์ผ ์ ๋
7์
}
resource "aws_cloudwatch_event_target" "stop_instances" {
rule = aws_cloudwatch_event_rule.stop_instances.name
target_id = "StopInstances"
arn = aws_lambda_function.stop_instances.arn
}
์ด๋ ๊ฒ ํ๋ฉด ํ ๋ฌ์ ์์ญ๋ง ์ ์ ์ฝํ ์ ์์ด! ๐
2๏ธโฃ Spot ์ธ์คํด์ค ํ์ฉ
๊ฐ๋ฐ/ํ ์คํธ ํ๊ฒฝ์์๋ Spot ์ธ์คํด์ค ์ฐ๋ฉด ์ต๋ 90% ํ ์ธ!
resource "aws_spot_instance_request" "dev" {
ami = data.aws_ami.amazon_linux_2023.id
instance_type = "t3.medium"
spot_price = "0.05" # ์๊ฐ๋น ์ต๋ ๊ฐ๊ฒฉ
wait_for_fulfillment = true
spot_type = "persistent"
tags = {
Name = "dev-spot-instance"
}
}
3๏ธโฃ ๋ฆฌ์์ค ํ๊น ์ผ๋ก ๋น์ฉ ์ถ์
๋ชจ๋ ๋ฆฌ์์ค์ ์ผ๊ด๋ ํ๊ทธ๋ฅผ ๋ฌ์์ ๋น์ฉ์ ์ถ์ ํ์!
locals {
common_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "Terraform"
CostCenter = "Engineering"
Owner = "devops-team"
}
}
resource "aws_instance" "web" {
# ...
tags = merge(
local.common_tags,
{
Name = "web-server"
Role = "webserver"
}
)
}
AWS Cost Explorer์์ ํ๊ทธ๋ณ๋ก ๋น์ฉ์ ํ์ธํ ์ ์์ด!
4๏ธโฃ ์ฌ์ฉํ์ง ์๋ ๋ฆฌ์์ค ์๋ ์ ๋ฆฌ
ํ ์คํธ ๋๋๋ฉด ๊ผญ destroy ํ์!
# ๋ฆฌ์์ค์ TTL ํ๊ทธ ์ถ๊ฐ
resource "aws_instance" "test" {
# ...
tags = {
Name = "test-server"
TTL = "2024-12-31" # ์ด ๋ ์ง ์ดํ ์๋ ์ญ์
}
}
Lambda๋ก TTL ์ง๋ ๋ฆฌ์์ค๋ฅผ ์๋์ผ๋ก ์ ๋ฆฌํ๋ ์คํฌ๋ฆฝํธ๋ฅผ ๋ง๋ค๋ฉด ์์ ๊ฟ! ๐ฏ
Infracost: Terraform ์ฝ๋์ ์์ ๋น์ฉ์ ๋ฏธ๋ฆฌ ๊ณ์ฐํด์ค!
brew install infracost
infracost breakdown --path .
# CI/CD์ ํตํฉํ๋ฉด PR์ ๋น์ฉ ๋ณํ๋ฅผ ์๋์ผ๋ก ์ฝ๋ฉํธ!
์ด๊ฑฐ ์ง์ง ์ ์ฉํด ใ ใ ใ apply ํ๊ธฐ ์ ์ "์ด? ์ด๊ฑฐ ํ ๋ฌ์ 100๋ง ์ ๋์ค๋ค?" ํ๊ณ ๋ฏธ๋ฆฌ ์ ์ ์์ด!
๐ ๋ ๊ณต๋ถํ๊ณ ์ถ๋ค๋ฉด?
Terraform ๊ธฐ๋ณธ์ ๋ค ๋ฐฐ์ ์ด! ์ด์ ๋ ๊น์ด ๊ณต๋ถํ๊ณ ์ถ๋ค๋ฉด? ๐
๐ ์ถ์ฒ ํ์ต ๋ฆฌ์์ค
๊ณต์ ๋ฌธ์ & ํํ ๋ฆฌ์ผ
- Terraform ๊ณต์ ๋ฌธ์: developer.hashicorp.com/terraform
- AWS Provider ๋ฌธ์: registry.terraform.io/providers/hashicorp/aws
- Learn Terraform: HashiCorp์ ๋ฌด๋ฃ ํํ ๋ฆฌ์ผ
์ฑ
์ถ์ฒ
- "Terraform: Up & Running" by Yevgeniy Brikman (์์ ๋ช
์!)
- "Infrastructure as Code" by Kief Morris
์จ๋ผ์ธ ๊ฐ์
- Udemy: "Learn DevOps: Infrastructure Automation With Terraform"
- A Cloud Guru: "HashiCorp Certified: Terraform Associate"
์ปค๋ฎค๋ํฐ
- Terraform ๊ณต์ ํฌ๋ผ
- Reddit r/Terraform
- HashiCorp User Groups
- ํ๊ตญ DevOps ์ปค๋ฎค๋ํฐ
๐ฏ ๋ค์ ๋จ๊ณ ํ์ต ์ฃผ์
1. Terraform Cloud / Enterprise
ํ ํ์
์ ์ํ SaaS ํ๋ซํผ. Remote State ๊ด๋ฆฌ, Policy as Code, Private Registry ๋ฑ ์ํฐํ๋ผ์ด์ฆ ๊ธฐ๋ฅ ์ ๊ณต
2. Terragrunt
Terraform์ DRY(Don't Repeat Yourself) ์์น์ ์งํค๊ธฐ ์ํ ๋ํผ ๋๊ตฌ. ์ฝ๋ ์ค๋ณต์ ์ค์ด๊ณ ์ฌ๋ฌ ํ๊ฒฝ ๊ด๋ฆฌ๋ฅผ ์ฝ๊ฒ!
3. Terraform CDK
TypeScript, Python, Java ๊ฐ์ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ก Terraform ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ด. HCL๋ณด๋ค ์ต์ํ ์ธ์ด๋ก ์ธ ์ ์์ง!
4. Policy as Code
Sentinel์ด๋ OPA(Open Policy Agent)๋ก ์ธํ๋ผ ์ ์ฑ
์ ์ฝ๋๋ก ๊ด๋ฆฌ. "ํ๋ก๋์
์์๋ ๋ฐ๋์ ์ํธํ ํ์ฑํ" ๊ฐ์ ๊ท์น์ ๊ฐ์ ํ ์ ์์ด
5. Multi-Cloud ์ ๋ต
AWS๋ฟ๋ง ์๋๋ผ Azure, GCP๋ฅผ ํจ๊ป ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ. ๊ฐ ํด๋ผ์ฐ๋์ ์ฅ์ ์ ํ์ฉํ ํ์ด๋ธ๋ฆฌ๋ ์ํคํ
์ฒ!
๊ฐ๋จํ ๊ฐ์ธ ํ๋ก์ ํธ๋ถํฐ ์์ํด์ ์ ์ ๋ณต์กํ ์ธํ๋ผ๋ฅผ ๊ตฌ์ถํด๋ณด์!
๋งํ๋ ๋ถ๋ถ์ด ์์ผ๋ฉด ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ๋ฉํ ๋ฅผ ์ฐพ์๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ. ๊ฒฝํ ๋ง์ DevOps ์์ง๋์ดํํ ์ง์ ๋ฐฐ์ฐ๋ฉด ์ฝ์ง ์๊ฐ์ ์์ฒญ ์ค์ผ ์ ์๊ฑฐ๋ ! ๐
๐ฌ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์... ์ฌ๊ธฐ๊น์ง ์ฝ์๋ค๋ฉด ์ง์ง ๋๋จํด! ๐๐๐
Terraform์ ์ฒ์์๋ ์ข ์ด๋ ต๊ฒ ๋๊ปด์ง ์ ์์ง๋ง, ํ๋ฒ ์ต์ํด์ง๋ฉด ์ง์ง ์์์ ๋์ ์ ์๋ ๋๊ตฌ๊ฐ ๋ผ ใ
ใ
ใ
์์ ์๋ AWS ์ฝ์์์ ํด๋ฆญํด๋ฆญํ๋ฉด์ ๋ช ์๊ฐ์ฉ ๊ฑธ๋ฆฌ๋ ์์
์ด, ์ด์ ๋ ์ฝ๋ ๋ช ์ค๋ก ๋ช ๋ถ ๋ง์ ๋๋์์? ์ด๊ฒ ๋ฐ๋ก IaC์ ๋ง๋ฒ์ด์ผ! โจ
๐ฏ ํต์ฌ ์ ๋ฆฌ
1. Terraform์ ์ธํ๋ผ๋ฅผ ์ฝ๋๋ก ๊ด๋ฆฌํ๋ ๋๊ตฌ
์ ์ธ์ ๋ฌธ๋ฒ์ผ๋ก ์ํ๋ ์ต์ข
์ํ๋ง ์ ์ํ๋ฉด, Terraform์ด ์์์ ๋ง๋ค์ด์ค!
2. State ๊ด๋ฆฌ๊ฐ ํต์ฌ
Remote State๋ฅผ ์ฌ์ฉํด์ ํ์๋ค๊ณผ ์์ ํ๊ฒ ํ์
ํ์. S3 + DynamoDB ์กฐํฉ ์ถ์ฒ!
3. ๋ชจ๋๋ก ์ฌ์ฌ์ฉ์ฑ ๊ทน๋ํ
ํ ๋ฒ ๋ง๋ ์ฝ๋๋ฅผ ์ฌ๋ฌ ํ๋ก์ ํธ์์ ์ฌ์ฌ์ฉ. DRY ์์น ์งํค๊ธฐ!
4. ๋ณด์์ ํ์
์๊ฒฉ์ฆ๋ช
ํ๋์ฝ๋ฉ ๊ธ์ง, State ํ์ผ ์ํธํ, ์ต์ ๊ถํ ์์น ์ ์ฉ!
5. CI/CD ํตํฉ์ผ๋ก ์๋ํ
GitHub Actions ๊ฐ์ ๋๊ตฌ๋ก ์ธํ๋ผ ๋ฐฐํฌ๋ฅผ ์๋ํํ๋ฉด ์ค์๋ ์ค๊ณ ํจ์จ๋ ์ฌ๋ผ๊ฐ!
6. ๋น์ฉ ๊ด๋ฆฌ ์์ง ๋ง๊ธฐ
ํ๊น
, ์ค์ผ์ค๋ง, Spot ์ธ์คํด์ค ํ์ฉ์ผ๋ก ํด๋ผ์ฐ๋ ๋น์ฉ ์ ์ฝ!
1. ๊ฐ๋จํ EC2 ์ธ์คํด์ค๋ถํฐ ์์
2. VPC, ์๋ธ๋ท ์ถ๊ฐํด์ ๋คํธ์ํฌ ๊ตฌ์ฑ
3. ๋ชจ๋๋ก ์ฝ๋ ์ ๋ฆฌ
4. Remote State ์ค์
5. CI/CD ํ์ดํ๋ผ์ธ ๊ตฌ์ถ
ํ๋์ฉ ์ฐจ๊ทผ์ฐจ๊ทผ ํด๋๊ฐ๋ค ๋ณด๋ฉด ์ด๋์ Terraform ๊ณ ์๊ฐ ๋์ด ์์ ๊ฑฐ์ผ! ๐ช
ํน์ ๋งํ๋ ๋ถ๋ถ์ด ์๊ฑฐ๋ ๋ ๊น์ด ๋ฐฐ์ฐ๊ณ ์ถ๋ค๋ฉด, ์ฌ๋ฅ๋ท์์ DevOps ์ ๋ฌธ๊ฐ๋ค์ ์ฐพ์๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ. ์ค๋ฌด ๊ฒฝํ์ด ํ๋ถํ ๋ฉํ ์๊ฒ ๋ฐฐ์ฐ๋ฉด ํจ์ฌ ๋น ๋ฅด๊ฒ ์ฑ์ฅํ ์ ์๊ฑฐ๋ ! ๐
์, ์ด์ ํฐ๋ฏธ๋ ์ด๊ณ terraform init ์ณ๋ณด์!
๋น์ ์ ์ธํ๋ผ ์๋ํ ์ฌ์ ์ ์์ํ ๊ฒ! ํ์ดํ
! ๐๐๐
๐ ์ด ๊ธ์ด ๋์์ด ๋์๋ค๋ฉด? ๐
์ฌ๋ฅ๋ท์์ ๋ ๋ง์ DevOps & ํด๋ผ์ฐ๋ ์ง์์ ๋ง๋๋ณด์ธ์!
์ ๋ฌธ๊ฐ๋ค์ ๋
ธํ์ฐ์ ์ค์ ๊ฒฝํ์ ๊ณต์ ๋ฐ์ ์ ์์ต๋๋ค ๐
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.