์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ 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) ๐Ÿค
ํŒ€์›๋“ค๊ณผ ์ฝ”๋“œ ๋ฆฌ๋ทฐํ•˜๋“ฏ์ด ์ธํ”„๋ผ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๊ฒ€ํ† ํ•˜๊ณ  ์Šน์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

์ „ํ†ต์  ๋ฐฉ์‹ vs IaC ๋ฐฉ์‹ ์ „ํ†ต์  ๋ฐฉ์‹ ๐Ÿ˜ฐ ๐Ÿ‘จโ€๐Ÿ’ป ์ˆ˜๋™ ํด๋ฆญ ์ž‘์—… ์‹œ๊ฐ„ ์†Œ์š” ๋งŽ์Œ โฐ ์‹ค์ˆ˜ ๋ฐœ์ƒ ๊ฐ€๋Šฅ โŒ ์žฌํ˜„์„ฑ ๋‚ฎ์Œ ๐Ÿ“‰ ํ˜‘์—… ์–ด๋ ค์›€ ๐Ÿคท IaC ๋ฐฉ์‹ ๐Ÿš€ ๐Ÿ’ป ์ฝ”๋“œ ๊ธฐ๋ฐ˜ ์ž๋™ํ™” ๋น ๋ฅธ ๋ฐฐํฌ โšก ์ผ๊ด€์„ฑ ๋ณด์žฅ โœ… ์™„๋ฒฝํ•œ ์žฌํ˜„์„ฑ ๐Ÿ“ˆ ํŒ€ ํ˜‘์—… ์ตœ์ ํ™” ๐Ÿ‘ฅ ์ง„ํ™”!

๐Ÿ”ง 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

์ด๊ฒƒ๋„ ํ™•์ธ ์ ˆ์ฐจ๋ฅผ ๊ฑฐ์ณ์š”. ์‹ค์ˆ˜๋กœ ์šด์˜ ํ™˜๊ฒฝ์„ ๋‚ ๋ ค๋ฒ„๋ฆฌ์ง€ ์•Š๋„๋ก ์กฐ์‹ฌํ•˜์„ธ์š”! ๐Ÿ”ฅ

Terraform ์›Œํฌํ”Œ๋กœ์šฐ terraform init ์ดˆ๊ธฐํ™” ๐ŸŽฌ terraform plan ๊ณ„ํš ํ™•์ธ ๐Ÿ“‹ terraform apply ์‹คํ–‰ ๐Ÿš€ terraform destroy ์‚ญ์ œ ๐Ÿ’ฅ ๐Ÿ’ก Tip: plan์œผ๋กœ ํ™•์ธ โ†’ apply๋กœ ์‹คํ–‰ โ†’ ํ•„์š”์‹œ 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
Reddit ์ปค๋ฎค๋‹ˆํ‹ฐ 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 ์ƒํƒœ๊ณ„ Terraform Infrastructure as Code AWS Provider Azure Provider GCP Provider Kubernetes Provider GitHub Provider Datadog Provider 3,000๊ฐœ ์ด์ƒ์˜ Provider๋กœ ๊ฑฐ์˜ ๋ชจ๋“  ํ”Œ๋žซํผ ์ง€์›! ๐ŸŒ

๐ŸŽฌ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์™€! ์—ฌ๊ธฐ๊นŒ์ง€ ์ •๋ง ๊ธด ์—ฌ์ •์ด์—ˆ๋„ค์š”. ๐Ÿ˜… 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
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

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