์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ Terraform์œผ๋กœ AWS ์ธํ”„๋ผ ์ž๋™ ์ƒ์„ฑํ•˜๊ธฐ

๐Ÿš€ Terraform์œผ๋กœ AWS ์ธํ”„๋ผ ์ž๋™ ์ƒ์„ฑํ•˜๊ธฐ

ํด๋ฆญ ๋ช‡ ๋ฒˆ์œผ๋กœ ์ธํ”„๋ผ ๊ตฌ์ถ• ๋๋‚ด๋Š” ์‹œ๋Œ€๊ฐ€ ์™”๋‹ค!

์š”์ฆ˜ ๊ฐœ๋ฐœ์ž๋“ค ์‚ฌ์ด์—์„œ ํ•ซํ•œ ํ‚ค์›Œ๋“œ๊ฐ€ ๋ญ”์ง€ ์•Œ์•„? ๋ฐ”๋กœ IaC(Infrastructure as Code)์•ผ ใ…‹ใ…‹ใ…‹ ์˜ˆ์ „์—๋Š” AWS ์ฝ˜์†” ๋“ค์–ด๊ฐ€์„œ ํด๋ฆญํด๋ฆญ ํ•˜๋ฉด์„œ ์„œ๋ฒ„ ๋งŒ๋“ค๊ณ , ๋„คํŠธ์›Œํฌ ์„ค์ •ํ•˜๊ณ ... ์ง„์งœ ์† ์•„ํ”„๊ณ  ์‹œ๊ฐ„๋„ ์˜ค๋ž˜ ๊ฑธ๋ ธ๋Š”๋ฐ, ์ด์ œ๋Š” ์ฝ”๋“œ ๋ช‡ ์ค„๋กœ ๋š๋”ฑ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๋‹ˆ๊นŒ? ๐Ÿ˜Ž

ํŠนํžˆ Terraform์€ AWS๋ฟ๋งŒ ์•„๋‹ˆ๋ผ Azure, GCP ๋“ฑ ์—ฌ๋Ÿฌ ํด๋ผ์šฐ๋“œ๋ฅผ ํ•œ ๋ฒˆ์— ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์„œ ์™„์ „ ๊ฟ€ํ…œ์ด์•ผ! ์˜ค๋Š˜์€ ์ด Terraform์œผ๋กœ AWS ์ธํ”„๋ผ๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋๊นŒ์ง€ ๋‹ค ์•Œ๋ ค์ค„๊ฒŒ~

Terraform ์ฝ”๋“œ main.tf variables.tf outputs.tf ๐Ÿ“ terraform apply AWS ํด๋ผ์šฐ๋“œ EC2 ์ธ์Šคํ„ด์Šค ์„œ๋ฒ„ ์ž๋™ ์ƒ์„ฑ VPC / ์„œ๋ธŒ๋„ท ๋„คํŠธ์›Œํฌ ๊ตฌ์„ฑ S3 / RDS ์Šคํ† ๋ฆฌ์ง€ & DB ๋ณด์•ˆ๊ทธ๋ฃน / IAM ๋ณด์•ˆ ์„ค์ • ์ž๋™ ๋ฐฐํฌ โœ… ์ธํ”„๋ผ ์™„์„ฑ! ๋ช‡ ๋ถ„ ๋งŒ์— ๊ตฌ์ถ•

๐Ÿค” 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 ์„ค์น˜๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด๋ณด์ž!

์ž, ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ์‹œ์ž‘ํ•ด๋ณผ๊นŒ? ์ผ๋‹จ 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
๋ฒ„์ „ ์ •๋ณด๊ฐ€ ๋‚˜์˜ค๋ฉด ์„ค์น˜ ์„ฑ๊ณต! ๐ŸŽ‰
โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ: Terraform ๋ฒ„์ „์ด ์ค‘์š”ํ•ด! ํ”„๋กœ์ ํŠธ๋งˆ๋‹ค ํ˜ธํ™˜๋˜๋Š” ๋ฒ„์ „์ด ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์œผ๋‹ˆ๊นŒ, ์‹ค๋ฌด์—์„œ๋Š” tfenv ๊ฐ™์€ ๋ฒ„์ „ ๊ด€๋ฆฌ ๋„๊ตฌ๋ฅผ ์“ฐ๋Š” ๊ฑธ ์ถ”์ฒœํ•ด ใ…‹ใ…‹ใ…‹

๐Ÿ”‘ 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์— ์˜ฌ๋ผ๊ฐˆ ์ˆ˜๋„ ์žˆ๊ณ , ๋ณด์•ˆ ์‚ฌ๊ณ  ๋‚  ์ˆ˜ ์žˆ์–ด. ์ ˆ๋Œ€ ๊ธˆ์ง€! ๐Ÿšซ

๐Ÿ’ก ํ”„๋กœ ํŒ: ์‹ค๋ฌด์—์„œ๋Š” IAM Role์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ๋ฒ ์ŠคํŠธ์•ผ! EC2๋‚˜ ECS์—์„œ Terraform ์‹คํ–‰ํ•  ๋•Œ๋Š” ์ธ์Šคํ„ด์Šค์— IAM Role์„ ๋ถ™์—ฌ์„œ ์‚ฌ์šฉํ•˜๋ฉด ์ž๊ฒฉ์ฆ๋ช… ๊ด€๋ฆฌ๊ฐ€ ํ›จ์”ฌ ์•ˆ์ „ํ•˜๊ณ  ํŽธํ•ด์ ธ ๐Ÿ˜Ž
โš ๏ธ ๋ณด์•ˆ ์ฃผ์˜์‚ฌํ•ญ: - Access Key๋Š” ์ ˆ๋Œ€ 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: ๋ฆฌ์†Œ์Šค์— ํƒœ๊ทธ๋ฅผ ๋‹ฌ์•„์ฃผ๋Š” ๊ฑฐ์•ผ. ๋‚˜์ค‘์— ๊ด€๋ฆฌํ•  ๋•Œ ์—„์ฒญ ์œ ์šฉํ•ด!

๐Ÿ’ฌ AMI ID ์ฐพ๋Š” ๊ฟ€ํŒ: AWS ์ฝ˜์†”์—์„œ EC2 > AMI ์นดํƒˆ๋กœ๊ทธ๋กœ ๊ฐ€๋ฉด ์ตœ์‹  AMI ID๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์–ด. ๋˜๋Š” aws_ami ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋™์ ์œผ๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜๋„ ์žˆ์–ด!

๐Ÿš€ 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 ์ธ์Šคํ„ด์Šค๊ฐ€ ์ง„์งœ๋กœ ๋งŒ๋“ค์–ด์ ธ ์žˆ์„ ๊ฑฐ์•ผ!

๐ŸŽฏ ์ž๋™ ์Šน์ธ ์˜ต์…˜: ๋งค๋ฒˆ yes ์ž…๋ ฅํ•˜๊ธฐ ๊ท€์ฐฎ์œผ๋ฉด terraform apply -auto-approve ์˜ต์…˜์„ ์“ธ ์ˆ˜ ์žˆ์–ด. ๊ทผ๋ฐ ์‹ค์ˆ˜ํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ ์กฐ์‹ฌํ•ด์„œ ์จ์•ผ ํ•ด! โš ๏ธ
๐Ÿ’ธ ๋น„์šฉ ์ฃผ์˜: t2.micro๋Š” ํ”„๋ฆฌํ‹ฐ์–ด์ง€๋งŒ, ๋‹ค๋ฅธ ์ธ์Šคํ„ด์Šค ํƒ€์ž…์ด๋‚˜ ์ถ”๊ฐ€ ๋ฆฌ์†Œ์Šค๋Š” ๋น„์šฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด! ํ…Œ์ŠคํŠธ ๋๋‚˜๋ฉด ๊ผญ terraform destroy๋กœ ์ •๋ฆฌํ•ด์ค˜์•ผ ํ•ด ใ…‹ใ…‹ใ…‹
Terraform ์‹คํ–‰ ์›Œํฌํ”Œ๋กœ์šฐ terraform init ์ดˆ๊ธฐํ™” ๋ฐ ํ”Œ๋Ÿฌ๊ทธ์ธ ๋‹ค์šด๋กœ๋“œ terraform plan ์‹คํ–‰ ๊ณ„ํš ํ™•์ธ (Dry Run) terraform apply ์‹ค์ œ ์ธํ”„๋ผ ์ƒ์„ฑ/๋ณ€๊ฒฝ terraform destroy ๋ฆฌ์†Œ์Šค ์‚ญ์ œ (์ •๋ฆฌ) ๐Ÿ“ฆ ๐Ÿ“ ๐Ÿš€ ๐Ÿ—‘๏ธ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰ ๋ณ€๊ฒฝ ์ „ ํ™•์ธ ์‹ค์ œ ๋ฐฐํฌ ํ…Œ์ŠคํŠธ ํ›„ ์ •๋ฆฌ

๐Ÿ—๏ธ ์‹ค์ „ ์˜ˆ์ œ: ์™„์ „ํ•œ ์›น ์„œ๋ฒ„ ์ธํ”„๋ผ ๊ตฌ์ถ•ํ•˜๊ธฐ

์ด์ œ ์ข€ ๋” ์‹ค์ „์ ์ธ ์˜ˆ์ œ๋ฅผ ํ•ด๋ณผ๊นŒ? ๐Ÿ’ช

๋‹จ์ˆœํžˆ 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!" ํŽ˜์ด์ง€๊ฐ€ ๋ณด์ผ ๊ฑฐ์•ผ!
๐Ÿ’ก ์‹ค๋ฌด ํŒ: user_data์— ๋ณต์žกํ•œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋„ฃ๊ธฐ๋ณด๋‹ค๋Š”, Ansible์ด๋‚˜ Chef ๊ฐ™์€ Configuration Management ๋„๊ตฌ๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹์•„. Terraform์€ ์ธํ”„๋ผ ํ”„๋กœ๋น„์ €๋‹์— ์ง‘์ค‘ํ•˜๊ณ , ์„œ๋ฒ„ ์„ค์ •์€ ๋‹ค๋ฅธ ๋„๊ตฌ์— ๋งก๊ธฐ๋Š” ๊ฑฐ์ง€!

๐Ÿ”„ Terraform State ๊ด€๋ฆฌํ•˜๊ธฐ

Terraform์˜ ํ•ต์‹ฌ ๊ฐœ๋… ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋ฐ”๋กœ State์•ผ! ๐Ÿ’พ

State ํŒŒ์ผ์€ Terraform์ด ๊ด€๋ฆฌํ•˜๋Š” ์ธํ”„๋ผ์˜ ํ˜„์žฌ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋Š” ํŒŒ์ผ์ธ๋ฐ, ์ด๊ฒŒ ์—„์ฒญ ์ค‘์š”ํ•ด ใ…‹ใ…‹ใ…‹

๐Ÿ“ State ํŒŒ์ผ์ด ๋ญ์•ผ?

terraform.tfstate ํŒŒ์ผ์€ JSON ํ˜•์‹์œผ๋กœ ๋˜์–ด ์žˆ๊ณ , ํ˜„์žฌ ์ธํ”„๋ผ์˜ ๋ชจ๋“  ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ์–ด.

Terraform์€ ์ด ํŒŒ์ผ์„ ๋ณด๊ณ :
- ํ˜„์žฌ ์–ด๋–ค ๋ฆฌ์†Œ์Šค๊ฐ€ ์žˆ๋Š”์ง€
- ์ฝ”๋“œ์™€ ์‹ค์ œ ์ธํ”„๋ผ์˜ ์ฐจ์ด๊ฐ€ ๋ญ”์ง€
- ์–ด๋–ค ์ž‘์—…์„ ํ•ด์•ผ ํ•˜๋Š”์ง€
๋ฅผ ํŒ๋‹จํ•ด!

โš ๏ธ ๋กœ์ปฌ State์˜ ๋ฌธ์ œ์  ๊ธฐ๋ณธ์ ์œผ๋กœ State ํŒŒ์ผ์€ ๋กœ์ปฌ์— ์ €์žฅ๋˜๋Š”๋ฐ, ์ด๊ฒŒ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์–ด:

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๋กœ ์˜ฎ๊ฒจ์ค˜! ์™„์ „ ํŽธํ•˜์ง€? ใ…‹ใ…‹ใ…‹

โœ… Remote State์˜ ์žฅ์  - ํŒ€ ํ˜‘์—…: ๋ชจ๋“  ํŒ€์›์ด ๊ฐ™์€ State ๊ณต์œ 
- State Locking: DynamoDB๋กœ ๋™์‹œ ์‹คํ–‰ ๋ฐฉ์ง€
- ๋ฒ„์ „ ๊ด€๋ฆฌ: S3 ๋ฒ„์ €๋‹์œผ๋กœ ์ด์ „ ์ƒํƒœ ๋ณต๊ตฌ ๊ฐ€๋Šฅ
- ์•”ํ˜ธํ™”: State ํŒŒ์ผ์ด ์•”ํ˜ธํ™”๋˜์–ด ์•ˆ์ „
- ๋ฐฑ์—…: S3์˜ ๋†’์€ ๋‚ด๊ตฌ์„ฑ์œผ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ๋ณด๊ด€
๐Ÿ’ฌ ์žฌ๋Šฅ๋„ท ํ™œ์šฉ ํŒ: Terraform State ๊ด€๋ฆฌ๋‚˜ Backend ์„ค์ •์ด ๋ณต์žกํ•˜๊ฒŒ ๋А๊ปด์ง„๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท์—์„œ DevOps ์ „๋ฌธ๊ฐ€์—๊ฒŒ ์ปจ์„คํŒ…์„ ๋ฐ›์•„๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ! ์ดˆ๊ธฐ ์„ค์ •์„ ์ œ๋Œ€๋กœ ํ•ด๋‘๋ฉด ๋‚˜์ค‘์— ์—„์ฒญ ํŽธํ•ด์ ธ ๐Ÿ˜Š

๐ŸŽฏ 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 ์‹คํ–‰

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ฝ”๋“œ ๋ฆฌ๋ทฐ ๊ณผ์ •์—์„œ ์ธํ”„๋ผ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๋ฏธ๋ฆฌ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์„œ ์™„์ „ ์•ˆ์ „ํ•ด! ๐Ÿ˜Ž

โœ… CI/CD ํ†ตํ•ฉ์˜ ์žฅ์  - ์ž๋™ํ™”: ์ˆ˜๋™ ์ž‘์—… ์ตœ์†Œํ™”
- ์ผ๊ด€์„ฑ: ํ•ญ์ƒ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋ฐฐํฌ
- ์•ˆ์ „์„ฑ: ๋ฆฌ๋ทฐ ํ”„๋กœ์„ธ์Šค ๊ฐ•์ œ
- ์ถ”์ ์„ฑ: ๋ชจ๋“  ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด 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 ์ณ๋ณด์ž!
๋‹น์‹ ์˜ ์ธํ”„๋ผ ์ž๋™ํ™” ์—ฌ์ •์„ ์‘์›ํ• ๊ฒŒ! ํ™”์ดํŒ…! ๐ŸŽ‰๐ŸŽ‰๐ŸŽ‰

Happy Terraforming! ๐Ÿš€ ์ธํ”„๋ผ๋ฅผ ์ฝ”๋“œ๋กœ, ๋ฏธ๋ž˜๋ฅผ ์ž๋™ํ™”๋กœ! โ˜๏ธ โšก ๐ŸŽฏ

๐ŸŒŸ ์ด ๊ธ€์ด ๋„์›€์ด ๋˜์—ˆ๋‹ค๋ฉด? ๐ŸŒŸ

์žฌ๋Šฅ๋„ท์—์„œ ๋” ๋งŽ์€ DevOps & ํด๋ผ์šฐ๋“œ ์ง€์‹์„ ๋งŒ๋‚˜๋ณด์„ธ์š”!
์ „๋ฌธ๊ฐ€๋“ค์˜ ๋…ธํ•˜์šฐ์™€ ์‹ค์ „ ๊ฒฝํ—˜์„ ๊ณต์œ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค ๐Ÿ˜Š

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.