์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ CodeBuild + CodeDeploy ์ž๋™ ๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ ์™„์ „ ์ •๋ณต ๊ฐ€์ด๋“œ
โ˜๏ธ ํด๋ผ์šฐ๋“œ / DevOps

๐Ÿš€ CodeBuild + CodeDeploy ์ž๋™ ๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ ์™„์ „ ์ •๋ณต ๊ฐ€์ด๋“œ

AWS ๋„ค์ดํ‹ฐ๋ธŒ ๋„๊ตฌ๋กœ CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ๋š๋”ฑ ๋งŒ๋“ค์–ด๋ณด์ž! ๋นŒ๋“œ๋ถ€ํ„ฐ ๋ฐฐํฌ๊นŒ์ง€, ์† ํ•˜๋‚˜ ์•ˆ ๋Œ€๊ณ  ์ž๋™ํ™”ํ•˜๋Š” ๋ฒ• ๐ŸŽฏ

์•ผ, ์†”์งํžˆ ๋งํ•ด๋ด. ๐Ÿ˜… ์ฝ”๋“œ ์ˆ˜์ •ํ•˜๊ณ  ๋‚˜์„œ ์„œ๋ฒ„์— ์ง์ ‘ SSH ์ ‘์†ํ•ด์„œ git pull ํ•˜๊ณ , npm run build ๋Œ๋ฆฌ๊ณ , ์„œ๋น„์Šค ์žฌ์‹œ์ž‘ํ•˜๊ณ ... ์ด ๋ฐ˜๋ณต ์ž‘์—… ์ง„์งœ ์ง€์น˜์ง€ ์•Š์•„? ๊ฐœ๋ฐœ์ž๊ฐ€ ์ด๋Ÿฐ ๋‹จ์ˆœ ๋ฐ˜๋ณต ์ž‘์—…์— ์‹œ๊ฐ„์„ ์Ÿ๋Š” ๊ฑด ์†”์งํžˆ ๋„ˆ๋ฌด ์•„๊น์ž–์•„.


๊ทธ๋ž˜์„œ ์˜ค๋Š˜์€ AWS CodeBuild์™€ AWS CodeDeploy๋ฅผ ์กฐํ•ฉํ•ด์„œ ์™„์ „ ์ž๋™ํ™”๋œ CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‚ฑ๋‚ฑ์ด ํŒŒํ—ค์ณ๋ณผ ๊ฑฐ์•ผ. ์ฝ”๋“œ ํ‘ธ์‹œ ํ•œ ๋ฒˆ์œผ๋กœ ๋นŒ๋“œ โ†’ ํ…Œ์ŠคํŠธ โ†’ ๋ฐฐํฌ๊นŒ์ง€ ์ž๋™์œผ๋กœ ๋Œ์•„๊ฐ€๋Š” ๊ทธ ์พŒ๊ฐ, ํ•œ ๋ฒˆ ๋ง›๋ณด๋ฉด ์ ˆ๋Œ€ ์˜ˆ์ „์œผ๋กœ ๋ชป ๋Œ์•„๊ฐ€๊ฑฐ๋“ . ๐Ÿ”ฅ


์ด ๊ธ€์—์„œ๋Š” ๋‹จ์ˆœํžˆ "์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ผ์š”~" ์ˆ˜์ค€์ด ์•„๋‹ˆ๋ผ, ๊ฐ ์„œ๋น„์Šค๊ฐ€ ์™œ ๊ทธ๋ ‡๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€, ์–ด๋–ค ์„ค์ •์ด ์–ด๋–ค ์—ญํ• ์„ ํ•˜๋Š”์ง€ ํŒฉํŠธ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ผผ๊ผผํ•˜๊ฒŒ ์„ค๋ช…ํ•  ๊ฑฐ์•ผ. ์žฌ๋Šฅ๋„ท์—์„œ DevOps ๊ด€๋ จ ์žฌ๋Šฅ์„ ์ฐพ์•„๋ณด๋ฉด ์ด๋Ÿฐ ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์„ฑ ๋Šฅ๋ ฅ์ด ์–ผ๋งˆ๋‚˜ ๊ฐ€์น˜ ์žˆ๋Š”์ง€ ๋ฐ”๋กœ ์•Œ ์ˆ˜ ์žˆ์„ ์ •๋„๋กœ, ์‹ค๋ฌด์—์„œ ์—„์ฒญ ์ค‘์š”ํ•œ ์Šคํ‚ฌ์ด๊ฑฐ๋“ !

AWS CI/CD ์ž๋™ ๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ ์ „์ฒด ํ๋ฆ„ ๐Ÿ‘จโ€๐Ÿ’ป ๊ฐœ๋ฐœ์ž ์ฝ”๋“œ ํ‘ธ์‹œ git push ๐Ÿ“ฆ ์†Œ์Šค ์ €์žฅ์†Œ CodeCommit/GitHub ํŠธ๋ฆฌ๊ฑฐ โš™๏ธ CodePipeline ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ ์ „์ฒด ํ๋ฆ„ ๊ด€๋ฆฌ ๋‹จ๊ณ„๋ณ„ ์‹คํ–‰ ์ œ์–ด ๐Ÿ”จ CodeBuild ๋นŒ๋“œ & ํ…Œ์ŠคํŠธ ๐Ÿš€ CodeDeploy ์„œ๋ฒ„ ๋ฐฐํฌ ์‹คํ–‰ ๋ฐฐํฌ์™„๋ฃŒ ๐Ÿ–ฅ๏ธ EC2 / ECS ์šด์˜ ์„œ๋ฒ„ ๐Ÿชฃ S3 ์•„ํ‹ฐํŒฉํŠธ ์ €์žฅ ์ฝ”๋“œ ํ•œ ๋ฒˆ ํ‘ธ์‹œํ•˜๋ฉด โ†’ ๋นŒ๋“œ โ†’ ํ…Œ์ŠคํŠธ โ†’ ์•„ํ‹ฐํŒฉํŠธ ์ €์žฅ โ†’ ์ž๋™ ๋ฐฐํฌ๊นŒ์ง€ ์™„์ „ ์ž๋™ํ™”! ๐ŸŽ‰

๐Ÿง  1. ํ•ต์‹ฌ ๊ฐœ๋…๋ถ€ํ„ฐ ์žก๊ณ  ๊ฐ€์ž

ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์„ฑํ•˜๊ธฐ ์ „์—, ๊ฐ ์„œ๋น„์Šค๊ฐ€ ๋ญ˜ ํ•˜๋Š” ๋…€์„์ธ์ง€ ์ •ํ™•ํžˆ ์•Œ๊ณ  ๊ฐ€์•ผ ํ•ด. ๊ฐœ๋… ์—†์ด ๋”ฐ๋ผ ํ•˜๋ฉด ๋‚˜์ค‘์— ๋ญ”๊ฐ€ ์•ˆ ๋  ๋•Œ ์™œ ์•ˆ ๋˜๋Š”์ง€ ์ „ํ˜€ ๋ชจ๋ฅด๊ฑฐ๋“ . ๐Ÿ˜ค


๐Ÿ”จ AWS CodeBuild๋ž€?

CodeBuild๋Š” AWS์—์„œ ์ œ๊ณตํ•˜๋Š” ์™„์ „ ๊ด€๋ฆฌํ˜• ๋นŒ๋“œ ์„œ๋น„์Šค์•ผ. ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด, ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ๋ฐ›์•„์„œ ์ปดํŒŒ์ผํ•˜๊ณ , ํ…Œ์ŠคํŠธ ๋Œ๋ฆฌ๊ณ , ๋ฐฐํฌ ๊ฐ€๋Šฅํ•œ ์•„ํ‹ฐํŒฉํŠธ(๊ฒฐ๊ณผ๋ฌผ)๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ์„œ๋น„์Šค์•ผ.


์ค‘์š”ํ•œ ํŠน์ง•๋“ค์„ ์ •๋ฆฌํ•˜๋ฉด:


๐Ÿ“Œ CodeBuild ํ•ต์‹ฌ ํŠน์ง• โ‘  ์„œ๋ฒ„๋ฆฌ์Šค ๋นŒ๋“œ ํ™˜๊ฒฝ
๋นŒ๋“œ ์„œ๋ฒ„๋ฅผ ์ง์ ‘ ๊ด€๋ฆฌํ•  ํ•„์š” ์—†์–ด. AWS๊ฐ€ ์•Œ์•„์„œ ๋นŒ๋“œ ํ™˜๊ฒฝ์„ ํ”„๋กœ๋น„์ €๋‹ํ•˜๊ณ , ๋นŒ๋“œ ๋๋‚˜๋ฉด ์ž๋™์œผ๋กœ ์ •๋ฆฌํ•ด์ค˜. Jenkins์ฒ˜๋Ÿผ ๋นŒ๋“œ ์„œ๋ฒ„ ์œ ์ง€๋ณด์ˆ˜์— ์‹ ๊ฒฝ ์“ธ ํ•„์š” ์—†๋‹ค๋Š” ๊ฑฐ์ง€.

โ‘ก buildspec.yml ๊ธฐ๋ฐ˜ ๋™์ž‘
๋นŒ๋“œ ๋ช…๋ น์–ด๋“ค์„ buildspec.yml ํŒŒ์ผ์— ์ •์˜ํ•ด. ์ด ํŒŒ์ผ์ด CodeBuild์˜ "๋ ˆ์‹œํ”ผ"์•ผ. ์–ด๋–ค ํ™˜๊ฒฝ์—์„œ, ์–ด๋–ค ๋ช…๋ น์–ด๋ฅผ, ์–ด๋–ค ์ˆœ์„œ๋กœ ์‹คํ–‰ํ• ์ง€ ๋‹ค ์—ฌ๊ธฐ์— ์ ์–ด.

โ‘ข ๋‹ค์–‘ํ•œ ๋Ÿฐํƒ€์ž„ ์ง€์›
Java, Python, Node.js, Ruby, Go, Android, Docker ๋“ฑ ๊ฑฐ์˜ ๋ชจ๋“  ์–ธ์–ด/ํ™˜๊ฒฝ์„ ์ง€์›ํ•ด. ์ปค์Šคํ…€ Docker ์ด๋ฏธ์ง€๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ.

โ‘ฃ ๋ณ‘๋ ฌ ๋นŒ๋“œ ์ง€์›
์—ฌ๋Ÿฌ ๋นŒ๋“œ๋ฅผ ๋™์‹œ์— ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์–ด. ๋นŒ๋“œ ํ์—์„œ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์‹œ๊ฐ„์ด ์ค„์–ด๋“ค์ง€.

๐Ÿš€ AWS CodeDeploy๋ž€?

CodeDeploy๋Š” AWS์—์„œ ์ œ๊ณตํ•˜๋Š” ์™„์ „ ๊ด€๋ฆฌํ˜• ๋ฐฐํฌ ์„œ๋น„์Šค์•ผ. EC2 ์ธ์Šคํ„ด์Šค, Lambda ํ•จ์ˆ˜, ECS ์ปจํ…Œ์ด๋„ˆ ๋“ฑ ๋‹ค์–‘ํ•œ ์ปดํ“จํŒ… ํ™˜๊ฒฝ์— ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ž๋™์œผ๋กœ ๋ฐฐํฌํ•ด์ค˜.


๐Ÿ“Œ CodeDeploy ํ•ต์‹ฌ ํŠน์ง• โ‘  ๋‹ค์–‘ํ•œ ๋ฐฐํฌ ์ „๋žต ์ง€์›
- In-Place ๋ฐฐํฌ: ๊ธฐ์กด ์„œ๋ฒ„์—์„œ ์ง์ ‘ ์—…๋ฐ์ดํŠธ (๋‹ค์šดํƒ€์ž„ ๋ฐœ์ƒ ๊ฐ€๋Šฅ)
- Blue/Green ๋ฐฐํฌ: ์ƒˆ ํ™˜๊ฒฝ์„ ๋งŒ๋“ค๊ณ  ํŠธ๋ž˜ํ”ฝ์„ ์ „ํ™˜ (๋ฌด์ค‘๋‹จ ๋ฐฐํฌ)
- Canary ๋ฐฐํฌ: ์ผ๋ถ€ ํŠธ๋ž˜ํ”ฝ๋งŒ ์ƒˆ ๋ฒ„์ „์œผ๋กœ ๋ณด๋‚ด์„œ ๊ฒ€์ฆ
- Linear ๋ฐฐํฌ: ์ผ์ • ๋น„์œจ์”ฉ ์ ์ง„์ ์œผ๋กœ ํŠธ๋ž˜ํ”ฝ ์ „ํ™˜

โ‘ก appspec.yml ๊ธฐ๋ฐ˜ ๋™์ž‘
๋ฐฐํฌ ๊ณผ์ •์„ appspec.yml ํŒŒ์ผ์— ์ •์˜ํ•ด. ์–ด๋–ค ํŒŒ์ผ์„ ์–ด๋””์— ๋ณต์‚ฌํ• ์ง€, ๋ฐฐํฌ ์ „ํ›„์— ์–ด๋–ค ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ• ์ง€ ์—ฌ๊ธฐ์— ์ ์–ด.

โ‘ข ๋กค๋ฐฑ ์ง€์›
๋ฐฐํฌ ์‹คํŒจ ์‹œ ์ž๋™์œผ๋กœ ์ด์ „ ๋ฒ„์ „์œผ๋กœ ๋กค๋ฐฑํ•  ์ˆ˜ ์žˆ์–ด. ์‹ค์ˆ˜ํ•ด๋„ ๋น ๋ฅด๊ฒŒ ๋ณต๊ตฌ ๊ฐ€๋Šฅ.

โ‘ฃ CodeDeploy Agent
EC2 ๋ฐฐํฌ์˜ ๊ฒฝ์šฐ, ๋Œ€์ƒ ์„œ๋ฒ„์— CodeDeploy Agent๊ฐ€ ์„ค์น˜๋˜์–ด ์žˆ์–ด์•ผ ํ•ด. ์ด ์—์ด์ „ํŠธ๊ฐ€ ์‹ค์ œ ๋ฐฐํฌ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด.

โš™๏ธ AWS CodePipeline์ด๋ž€?

CodeBuild์™€ CodeDeploy๋ฅผ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ์•ผ. ์†Œ์Šค ๋ณ€๊ฒฝ ๊ฐ์ง€ โ†’ ๋นŒ๋“œ โ†’ ๋ฐฐํฌ ๋‹จ๊ณ„๋ฅผ ํ•˜๋‚˜์˜ ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ ๋ฌถ์–ด์„œ ์ž๋™์œผ๋กœ ์‹คํ–‰ํ•ด์ค˜. ์˜ค๋Š˜ ๊ตฌ์„ฑํ•  ํŒŒ์ดํ”„๋ผ์ธ์˜ "๋ผˆ๋Œ€"๋ผ๊ณ  ๋ณด๋ฉด ๋ผ.


๐Ÿ’ก ํ•œ ์ค„ ์š”์•ฝ CodePipeline = ์ง€ํœ˜์ž ๐ŸŽผ | CodeBuild = ๋นŒ๋“œ ๋‹ด๋‹น ๐Ÿ”จ | CodeDeploy = ๋ฐฐํฌ ๋‹ด๋‹น ๐Ÿš€
์„ธ ๊ฐœ๊ฐ€ ํ•ฉ์ณ์ ธ์•ผ ์™„์ „ํ•œ CI/CD ํŒŒ์ดํ”„๋ผ์ธ์ด ์™„์„ฑ๋ผ!

๐Ÿ› ๏ธ 2. ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์„ฑ ์ „ ์‚ฌ์ „ ์ค€๋น„

๋ณธ๊ฒฉ์ ์œผ๋กœ ํŒŒ์ดํ”„๋ผ์ธ ๋งŒ๋“ค๊ธฐ ์ „์— ์ค€๋น„ํ•ด์•ผ ํ•  ๊ฒƒ๋“ค์ด ์žˆ์–ด. ์ด๊ฑธ ๋น ๋œจ๋ฆฌ๋ฉด ๋‚˜์ค‘์— "์™œ ์•ˆ ๋˜์ง€?" ํ•˜๋ฉด์„œ ์‚ฝ์งˆํ•˜๊ฒŒ ๋˜๋‹ˆ๊นŒ ๊ผผ๊ผผํ•˜๊ฒŒ ์ฒดํฌํ•˜์ž. ๐Ÿ“‹


โœ… ํ•„์š”ํ•œ AWS ๋ฆฌ์†Œ์Šค ๋ชฉ๋ก

๋ฆฌ์†Œ์Šค ์šฉ๋„ ๋น„๊ณ 
IAM Role (CodeBuild์šฉ) CodeBuild๊ฐ€ S3, ECR, CloudWatch ๋“ฑ์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•œ ๊ถŒํ•œ ํ•„์ˆ˜
IAM Role (CodeDeploy์šฉ) CodeDeploy๊ฐ€ EC2, Auto Scaling ๋“ฑ์„ ์ œ์–ดํ•˜๊ธฐ ์œ„ํ•œ ๊ถŒํ•œ ํ•„์ˆ˜
IAM Role (EC2 ์ธ์Šคํ„ด์Šค ํ”„๋กœํŒŒ์ผ) EC2๊ฐ€ S3์—์„œ ์•„ํ‹ฐํŒฉํŠธ๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜๊ธฐ ์œ„ํ•œ ๊ถŒํ•œ ํ•„์ˆ˜
IAM Role (CodePipeline์šฉ) CodePipeline์ด CodeBuild, CodeDeploy๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•œ ๊ถŒํ•œ ํ•„์ˆ˜
S3 ๋ฒ„ํ‚ท ๋นŒ๋“œ ์•„ํ‹ฐํŒฉํŠธ ์ €์žฅ์†Œ ํ•„์ˆ˜
EC2 ์ธ์Šคํ„ด์Šค ๋ฐฐํฌ ๋Œ€์ƒ ์„œ๋ฒ„ (CodeDeploy Agent ์„ค์น˜ ํ•„์š”) ํ•„์ˆ˜
์†Œ์Šค ์ €์žฅ์†Œ CodeCommit, GitHub, Bitbucket ์ค‘ ์„ ํƒ ํ•„์ˆ˜

๐Ÿ” IAM ์—ญํ•  ์„ค์ • (๊ฐ€์žฅ ์ค‘์š”!)

IAM ์„ค์ •์ด ์ž˜๋ชป๋˜๋ฉด ํŒŒ์ดํ”„๋ผ์ธ์ด ์•„์˜ˆ ๋™์ž‘์„ ์•ˆ ํ•ด. ๊ฐ ์„œ๋น„์Šค๋ณ„๋กœ ํ•„์š”ํ•œ ๊ถŒํ•œ์„ ์ •ํ™•ํžˆ ๋ถ€์—ฌํ•ด์•ผ ํ•ด.


โ‘  CodeBuild ์„œ๋น„์Šค ์—ญํ• ์— ํ•„์š”ํ•œ ์ •์ฑ…:


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:GetObjectVersion",
        "s3:PutObject",
        "s3:GetBucketAcl",
        "s3:GetBucketLocation"
      ],
      "Resource": [
        "arn:aws:s3:::your-artifact-bucket",
        "arn:aws:s3:::your-artifact-bucket/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "codebuild:CreateReportGroup",
        "codebuild:CreateReport",
        "codebuild:UpdateReport",
        "codebuild:BatchPutTestCases"
      ],
      "Resource": "arn:aws:codebuild:*:*:report-group/*"
    }
  ]
}

โ‘ก CodeDeploy ์„œ๋น„์Šค ์—ญํ• ์— ํ•„์š”ํ•œ ์ •์ฑ…:


CodeDeploy ์„œ๋น„์Šค ์—ญํ• ์€ AWS ๊ด€๋ฆฌํ˜• ์ •์ฑ…์ธ AWSCodeDeployRole์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ผ. ์ด ์ •์ฑ…์—๋Š” EC2, Auto Scaling, ELB, CloudWatch ๋“ฑ์— ๋Œ€ํ•œ ํ•„์š”ํ•œ ๊ถŒํ•œ์ด ๋ชจ๋‘ ํฌํ•จ๋˜์–ด ์žˆ์–ด.


โ‘ข EC2 ์ธ์Šคํ„ด์Šค ํ”„๋กœํŒŒ์ผ์— ํ•„์š”ํ•œ ์ •์ฑ…:


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:GetObjectVersion",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::your-artifact-bucket",
        "arn:aws:s3:::your-artifact-bucket/*",
        "arn:aws:s3:::aws-codedeploy-*"
      ]
    }
  ]
}

๐Ÿค– EC2์— CodeDeploy Agent ์„ค์น˜

EC2 ๋ฐฐํฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋Œ€์ƒ ์„œ๋ฒ„์— ๋ฐ˜๋“œ์‹œ CodeDeploy Agent๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•ด. ์ด ์—์ด์ „ํŠธ๊ฐ€ CodeDeploy ์„œ๋น„์Šค๋กœ๋ถ€ํ„ฐ ๋ฐฐํฌ ๋ช…๋ น์„ ๋ฐ›์•„์„œ ์‹ค์ œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ฑฐ๋“ .


# Amazon Linux 2 / Amazon Linux 2023 ๊ธฐ์ค€
sudo yum update -y
sudo yum install -y ruby wget

# CodeDeploy Agent ์„ค์น˜ ์Šคํฌ๋ฆฝํŠธ ๋‹ค์šด๋กœ๋“œ
cd /home/ec2-user
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install

# ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ ๋ฐ ์„ค์น˜
chmod +x ./install
sudo ./install auto

# ์„œ๋น„์Šค ์‹œ์ž‘ ๋ฐ ์ƒํƒœ ํ™•์ธ
sudo service codedeploy-agent start
sudo service codedeploy-agent status
โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ CodeDeploy Agent ์„ค์น˜ ์Šคํฌ๋ฆฝํŠธ URL์—์„œ ๋ฆฌ์ „ ์ฝ”๋“œ(ap-northeast-2)๋Š” ์‹ค์ œ ์‚ฌ์šฉํ•˜๋Š” AWS ๋ฆฌ์ „์— ๋งž๊ฒŒ ๋ณ€๊ฒฝํ•ด์•ผ ํ•ด. ์„œ์šธ ๋ฆฌ์ „์€ ap-northeast-2์•ผ.

Ubuntu ์„œ๋ฒ„๋ผ๋ฉด ์ด๋ ‡๊ฒŒ ์„ค์น˜ํ•ด:


# Ubuntu ๊ธฐ์ค€
sudo apt-get update
sudo apt-get install -y ruby-full wget

cd /home/ubuntu
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install

chmod +x ./install
sudo ./install auto

# ์„œ๋น„์Šค ์ƒํƒœ ํ™•์ธ
sudo systemctl status codedeploy-agent
IAM ์—ญํ•  ๊ตฌ์„ฑ ๊ด€๊ณ„๋„ CodePipeline ์„œ๋น„์Šค ์—ญํ•  CodeBuild/CodeDeploy ํ˜ธ์ถœ ๊ถŒํ•œ CodeBuild ์„œ๋น„์Šค ์—ญํ•  S3 ์ฝ๊ธฐ/์“ฐ๊ธฐ CloudWatch Logs ์“ฐ๊ธฐ CodeDeploy ์„œ๋น„์Šค ์—ญํ•  EC2/Auto Scaling ์ œ์–ด ELB ๋Œ€์ƒ ๊ทธ๋ฃน ๊ด€๋ฆฌ EC2 ์ธ์Šคํ„ด์Šค ํ”„๋กœํŒŒ์ผ S3 ์•„ํ‹ฐํŒฉํŠธ ๋‹ค์šด๋กœ๋“œ ๊ถŒํ•œ ๊ฐ ์„œ๋น„์Šค๋งˆ๋‹ค ๋…๋ฆฝ์ ์ธ IAM ์—ญํ• ์ด ํ•„์š”ํ•ด. ๊ถŒํ•œ ์ตœ์†Œํ™” ์›์น™์„ ์ง€ํ‚ค์ž! ๐Ÿ”

๐Ÿ“ 3. buildspec.yml ์™„์ „ ์ •๋ณต

buildspec.yml์€ CodeBuild์˜ ํ•ต์‹ฌ ์„ค์ • ํŒŒ์ผ์ด์•ผ. ์ด ํŒŒ์ผ ํ•˜๋‚˜๋กœ ๋นŒ๋“œ ํ™˜๊ฒฝ ์„ค์ •๋ถ€ํ„ฐ ์‹ค์ œ ๋นŒ๋“œ ๋ช…๋ น์–ด, ๊ฒฐ๊ณผ๋ฌผ ์ €์žฅ๊นŒ์ง€ ๋ชจ๋“  ๊ฑธ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์–ด. ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ์— ์œ„์น˜ํ•ด์•ผ ํ•ด.


๐Ÿ“ buildspec.yml ๊ธฐ๋ณธ ๊ตฌ์กฐ

version: 0.2

env:
  variables:
    ENVIRONMENT: "production"
  parameter-store:
    DB_PASSWORD: "/myapp/db/password"
  secrets-manager:
    API_KEY: "myapp/api-key:API_KEY"

phases:
  install:
    runtime-versions:
      nodejs: 18
    commands:
      - echo "์˜์กด์„ฑ ์„ค์น˜ ์‹œ์ž‘"
      - npm ci

  pre_build:
    commands:
      - echo "๋นŒ๋“œ ์ „ ์ค€๋น„ ์ž‘์—…"
      - npm run lint
      - npm run test

  build:
    commands:
      - echo "๋นŒ๋“œ ์‹œ์ž‘"
      - npm run build
      - echo "๋นŒ๋“œ ์™„๋ฃŒ"

  post_build:
    commands:
      - echo "๋นŒ๋“œ ํ›„ ์ž‘์—…"
      - cp appspec.yml dist/
      - cp -r scripts/ dist/

artifacts:
  files:
    - dist/**/*
    - appspec.yml
    - scripts/**/*
  discard-paths: no
  base-directory: .

cache:
  paths:
    - node_modules/**/*

๐Ÿ” ๊ฐ ์„น์…˜ ์ƒ์„ธ ์„ค๋ช…

โ‘  version buildspec ํ˜•์‹์˜ ๋ฒ„์ „์ด์•ผ. ํ˜„์žฌ 0.2๊ฐ€ ์ตœ์‹ ์ด๊ณ , ์ด๊ฑธ ์‚ฌ์šฉํ•ด์•ผ ํ•ด. 0.1์€ ๊ตฌ๋ฒ„์ „์ด๋ผ ์ผ๋ถ€ ๊ธฐ๋Šฅ์ด ์—†์–ด.

โ‘ก env
ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•˜๋Š” ์„น์…˜์ด์•ผ. ์„ธ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ์–ด:
- variables: ํ‰๋ฌธ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ (๋ฏผ๊ฐ ์ •๋ณด ๊ธˆ์ง€!)
- parameter-store: AWS Systems Manager Parameter Store์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ
- secrets-manager: AWS Secrets Manager์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ (๊ฐ€์žฅ ์•ˆ์ „)

โ‘ข phases
๋นŒ๋“œ ๋‹จ๊ณ„๋ฅผ ์ •์˜ํ•ด. ๊ฐ ๋‹จ๊ณ„๋Š” ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰๋ผ:
- install: ๋Ÿฐํƒ€์ž„ ์„ค์น˜, ํŒจํ‚ค์ง€ ์„ค์น˜
- pre_build: ๋นŒ๋“œ ์ „ ์ค€๋น„ (๋ฆฐํŠธ, ํ…Œ์ŠคํŠธ ๋“ฑ)
- build: ์‹ค์ œ ๋นŒ๋“œ ๋ช…๋ น์–ด
- post_build: ๋นŒ๋“œ ํ›„ ์ž‘์—… (Docker ์ด๋ฏธ์ง€ ํ‘ธ์‹œ ๋“ฑ)

โ‘ฃ artifacts
๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ์„ S3์— ์—…๋กœ๋“œํ•  ํŒŒ์ผ๋“ค์„ ์ง€์ •ํ•ด. files์— ํฌํ•จํ•  ํŒŒ์ผ ํŒจํ„ด์„ ์ ์–ด.

โ‘ค cache
์บ์‹œํ•  ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•ด. node_modules๋ฅผ ์บ์‹œํ•˜๋ฉด ๋‹ค์Œ ๋นŒ๋“œ ๋•Œ ์˜์กด์„ฑ ์„ค์น˜ ์‹œ๊ฐ„์„ ํฌ๊ฒŒ ์ค„์ผ ์ˆ˜ ์žˆ์–ด.

๐Ÿณ Docker ์ด๋ฏธ์ง€ ๋นŒ๋“œ & ECR ํ‘ธ์‹œ ์˜ˆ์‹œ

Docker ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•ด์„œ ECR์— ํ‘ธ์‹œํ•˜๋Š” ๊ฒฝ์šฐ์˜ buildspec.yml์ด์•ผ. ์‹ค๋ฌด์—์„œ ๋งŽ์ด ์“ฐ๋Š” ํŒจํ„ด์ด๋‹ˆ๊นŒ ์ž˜ ๋ด๋‘ฌ:


version: 0.2

env:
  variables:
    AWS_DEFAULT_REGION: ap-northeast-2
    IMAGE_REPO_NAME: my-app
    IMAGE_TAG: latest

phases:
  pre_build:
    commands:
      - echo "ECR ๋กœ๊ทธ์ธ ์ค‘..."
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
      - REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - IMAGE_TAG=${COMMIT_HASH:=latest}

  build:
    commands:
      - echo "Docker ์ด๋ฏธ์ง€ ๋นŒ๋“œ ์ค‘..."
      - docker build -t $REPOSITORY_URI:latest .
      - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG

  post_build:
    commands:
      - echo "ECR์— ์ด๋ฏธ์ง€ ํ‘ธ์‹œ ์ค‘..."
      - docker push $REPOSITORY_URI:latest
      - docker push $REPOSITORY_URI:$IMAGE_TAG
      - echo "imagedefinitions.json ์ƒ์„ฑ ์ค‘..."
      - printf '[{"name":"my-container","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json

artifacts:
  files:
    - imagedefinitions.json
๐Ÿ’ก ํŒ: CODEBUILD_RESOLVED_SOURCE_VERSION CodeBuild๋Š” ๋นŒ๋“œ ์‹คํ–‰ ์‹œ ์—ฌ๋Ÿฌ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์ž๋™์œผ๋กœ ์ฃผ์ž…ํ•ด์ค˜. CODEBUILD_RESOLVED_SOURCE_VERSION์€ ํ˜„์žฌ ๋นŒ๋“œ ์ค‘์ธ ์†Œ์Šค์˜ ์ปค๋ฐ‹ ํ•ด์‹œ์•ผ. ์ด๊ฑธ ์ด๋ฏธ์ง€ ํƒœ๊ทธ๋กœ ์‚ฌ์šฉํ•˜๋ฉด ์–ด๋–ค ์ปค๋ฐ‹์ด ์–ด๋–ค ์ด๋ฏธ์ง€์ธ์ง€ ์ถ”์ ํ•˜๊ธฐ ์‰ฌ์›Œ!

โ˜• Java/Maven ํ”„๋กœ์ ํŠธ ์˜ˆ์‹œ

version: 0.2

phases:
  install:
    runtime-versions:
      java: corretto17

  pre_build:
    commands:
      - echo "Maven ๋ฒ„์ „ ํ™•์ธ"
      - mvn --version

  build:
    commands:
      - echo "Maven ๋นŒ๋“œ ์‹œ์ž‘"
      - mvn clean package -DskipTests=false
      - echo "๋นŒ๋“œ ์™„๋ฃŒ"

  post_build:
    commands:
      - echo "JAR ํŒŒ์ผ ํ™•์ธ"
      - ls -la target/*.jar

artifacts:
  files:
    - target/my-app-*.jar
    - appspec.yml
    - scripts/**/*
  discard-paths: no

cache:
  paths:
    - /root/.m2/**/*

๐Ÿ“‹ 4. appspec.yml ์™„์ „ ์ •๋ณต

appspec.yml์€ CodeDeploy์˜ ํ•ต์‹ฌ ์„ค์ • ํŒŒ์ผ์ด์•ผ. ๋นŒ๋“œ๋œ ์•„ํ‹ฐํŒฉํŠธ๋ฅผ ์„œ๋ฒ„์˜ ์–ด๋””์— ์–ด๋–ป๊ฒŒ ๋ฐฐํฌํ• ์ง€, ๋ฐฐํฌ ์ „ํ›„์— ์–ด๋–ค ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ• ์ง€ ์ •์˜ํ•ด. ์ด ํŒŒ์ผ์€ ๋ฐ˜๋“œ์‹œ ์•„ํ‹ฐํŒฉํŠธ์˜ ๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ์— ์žˆ์–ด์•ผ ํ•ด!


๐Ÿ“ EC2 ๋ฐฐํฌ์šฉ appspec.yml ๊ธฐ๋ณธ ๊ตฌ์กฐ

version: 0.0
os: linux

files:
  - source: /
    destination: /var/www/my-app
  - source: /config/nginx.conf
    destination: /etc/nginx/conf.d/

file_exists_behavior: OVERWRITE

permissions:
  - object: /var/www/my-app
    pattern: "**"
    owner: ec2-user
    group: ec2-user
    mode: 755
    type:
      - directory
  - object: /var/www/my-app
    pattern: "*.sh"
    owner: ec2-user
    group: ec2-user
    mode: 755
    type:
      - file

hooks:
  BeforeInstall:
    - location: scripts/before_install.sh
      timeout: 300
      runas: root

  AfterInstall:
    - location: scripts/after_install.sh
      timeout: 300
      runas: ec2-user

  ApplicationStart:
    - location: scripts/start_server.sh
      timeout: 300
      runas: ec2-user

  ApplicationStop:
    - location: scripts/stop_server.sh
      timeout: 300
      runas: ec2-user

  ValidateService:
    - location: scripts/validate_service.sh
      timeout: 300
      runas: ec2-user

๐ŸŽฃ ๋ฐฐํฌ ํ›…(Hooks) ์‹คํ–‰ ์ˆœ์„œ

CodeDeploy์˜ ๋ฐฐํฌ ํ›…์€ ์ •ํ•ด์ง„ ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰๋ผ. ์ด ์ˆœ์„œ๋ฅผ ์ดํ•ดํ•ด์•ผ ๊ฐ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์–ด๋””์— ๋ฐฐ์น˜ํ• ์ง€ ์•Œ ์ˆ˜ ์žˆ์–ด:


CodeDeploy In-Place ๋ฐฐํฌ ํ›… ์‹คํ–‰ ์ˆœ์„œ โ‘  ApplicationStop ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ์•ฑ ์ข…๋ฃŒ โ‘ก BeforeInstall ์„ค์น˜ ์ „ ์ค€๋น„ (๋ฐฑ์—…, ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ ๋“ฑ) โ‘ข Install ํŒŒ์ผ ๋ณต์‚ฌ (CodeDeploy๊ฐ€ ์ž๋™ ์ฒ˜๋ฆฌ) โ‘ฃ AfterInstall ์„ค์น˜ ํ›„ ์„ค์ • (ํ™˜๊ฒฝ ๋ณ€์ˆ˜, ๊ถŒํ•œ ์„ค์ • ๋“ฑ) โ‘ค ApplicationStart ์ƒˆ ๋ฒ„์ „ ์•ฑ ์‹œ์ž‘ โ‘ฅ ValidateService ์„œ๋น„์Šค ์ •์ƒ ๋™์ž‘ ๊ฒ€์ฆ ์‚ฌ์šฉ์ž ์ •์˜ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ CodeDeploy ์ž๋™ ์ฒ˜๋ฆฌ

๐Ÿ“œ ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ ์˜ˆ์‹œ

์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ๋“ค์„ ์‚ดํŽด๋ณด์ž. Node.js ์•ฑ ๊ธฐ์ค€์ด์•ผ:


scripts/before_install.sh

#!/bin/bash
# ๊ธฐ์กด ์•ฑ ๋””๋ ‰ํ† ๋ฆฌ ์ •๋ฆฌ
echo "๋ฐฐํฌ ์ „ ์ค€๋น„ ์ž‘์—… ์‹œ์ž‘..."

# ์•ฑ ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ์—†์œผ๋ฉด ์ƒ์„ฑ
if [ ! -d /var/www/my-app ]; then
    mkdir -p /var/www/my-app
fi

# ๋กœ๊ทธ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ
mkdir -p /var/log/my-app

echo "๋ฐฐํฌ ์ „ ์ค€๋น„ ์™„๋ฃŒ!"

scripts/after_install.sh

#!/bin/bash
echo "์„ค์น˜ ํ›„ ์„ค์ • ์ž‘์—… ์‹œ์ž‘..."

# ์•ฑ ๋””๋ ‰ํ† ๋ฆฌ๋กœ ์ด๋™
cd /var/www/my-app

# ํ”„๋กœ๋•์…˜ ์˜์กด์„ฑ ์„ค์น˜
npm ci --only=production

# ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ํŒŒ์ผ ๋ณต์‚ฌ (Parameter Store์—์„œ ๊ฐ€์ ธ์˜จ ๊ฐ’ ์‚ฌ์šฉ)
echo "NODE_ENV=production" > .env
echo "PORT=3000" >> .env

echo "์„ค์น˜ ํ›„ ์„ค์ • ์™„๋ฃŒ!"

scripts/start_server.sh

#!/bin/bash
echo "์„œ๋ฒ„ ์‹œ์ž‘ ์ค‘..."

cd /var/www/my-app

# PM2๋กœ ์•ฑ ์‹œ์ž‘ (์ด๋ฏธ ์‹คํ–‰ ์ค‘์ด๋ฉด ์žฌ์‹œ์ž‘)
if pm2 list | grep -q "my-app"; then
    pm2 restart my-app
else
    pm2 start app.js --name my-app
fi

# PM2 ์„ค์ • ์ €์žฅ (์„œ๋ฒ„ ์žฌ๋ถ€ํŒ… ์‹œ ์ž๋™ ์‹œ์ž‘)
pm2 save

echo "์„œ๋ฒ„ ์‹œ์ž‘ ์™„๋ฃŒ!"

scripts/stop_server.sh

#!/bin/bash
echo "์„œ๋ฒ„ ์ข…๋ฃŒ ์ค‘..."

# PM2๋กœ ์‹คํ–‰ ์ค‘์ธ ์•ฑ ์ข…๋ฃŒ
if pm2 list | grep -q "my-app"; then
    pm2 stop my-app
    echo "์„œ๋ฒ„ ์ข…๋ฃŒ ์™„๋ฃŒ!"
else
    echo "์‹คํ–‰ ์ค‘์ธ ์„œ๋ฒ„ ์—†์Œ. ๊ณ„์† ์ง„ํ–‰..."
fi

scripts/validate_service.sh

#!/bin/bash
echo "์„œ๋น„์Šค ๊ฒ€์ฆ ์ค‘..."

# ํ—ฌ์Šค์ฒดํฌ ์—”๋“œํฌ์ธํŠธ ํ˜ธ์ถœ
HEALTH_CHECK_URL="http://localhost:3000/health"
MAX_RETRIES=10
RETRY_INTERVAL=5

for i in $(seq 1 $MAX_RETRIES); do
    HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" $HEALTH_CHECK_URL)
    
    if [ "$HTTP_STATUS" = "200" ]; then
        echo "์„œ๋น„์Šค ์ •์ƒ ๋™์ž‘ ํ™•์ธ! (HTTP $HTTP_STATUS)"
        exit 0
    fi
    
    echo "ํ—ฌ์Šค์ฒดํฌ ์‹คํŒจ ($i/$MAX_RETRIES). ${RETRY_INTERVAL}์ดˆ ํ›„ ์žฌ์‹œ๋„..."
    sleep $RETRY_INTERVAL
done

echo "์„œ๋น„์Šค ๊ฒ€์ฆ ์‹คํŒจ! ๋ฐฐํฌ๋ฅผ ๋กค๋ฐฑํ•ฉ๋‹ˆ๋‹ค."
exit 1
๐Ÿ’ก validate_service.sh์˜ ์ค‘์š”์„ฑ ์ด ์Šคํฌ๋ฆฝํŠธ๊ฐ€ exit 1์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด CodeDeploy๊ฐ€ ์ž๋™์œผ๋กœ ๋ฐฐํฌ๋ฅผ ์‹คํŒจ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ๋กค๋ฐฑ์„ ์‹œ์ž‘ํ•ด. ํ—ฌ์Šค์ฒดํฌ ๋กœ์ง์„ ์ž˜ ์ž‘์„ฑํ•ด๋‘๋ฉด ์ž˜๋ชป๋œ ๋ฐฐํฌ๊ฐ€ ํ”„๋กœ๋•์…˜์— ๋ฐ˜์˜๋˜๋Š” ๊ฑธ ๋ง‰์„ ์ˆ˜ ์žˆ์–ด!

๐Ÿ”จ 5. CodeBuild ํ”„๋กœ์ ํŠธ ์ƒ์„ฑํ•˜๊ธฐ

์ด์ œ ์‹ค์ œ๋กœ CodeBuild ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž. AWS ์ฝ˜์†”์—์„œ ํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ AWS CLI๋กœ ํ•˜๋Š” ๋ฐฉ๋ฒ• ๋‘ ๊ฐ€์ง€๋ฅผ ๋ชจ๋‘ ์•Œ์•„๋ณผ ๊ฑฐ์•ผ.


๐Ÿ–ฅ๏ธ AWS ์ฝ˜์†”์—์„œ CodeBuild ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

1
AWS ์ฝ˜์†” โ†’ CodeBuild โ†’ ๋นŒ๋“œ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
ํ”„๋กœ์ ํŠธ ์ด๋ฆ„์„ ์ž…๋ ฅํ•ด. ๋‚˜์ค‘์— CodePipeline์—์„œ ์ฐธ์กฐํ•  ์ด๋ฆ„์ด๋‹ˆ๊นŒ ์˜๋ฏธ ์žˆ๊ฒŒ ์ง€์–ด.
2
์†Œ์Šค ์„ค์ •
์†Œ์Šค ๊ณต๊ธ‰์ž๋ฅผ ์„ ํƒํ•ด. CodeCommit, GitHub, GitHub Enterprise, Bitbucket, S3 ์ค‘ ์„ ํƒ ๊ฐ€๋Šฅ. GitHub ์—ฐ๊ฒฐ ์‹œ OAuth ๋˜๋Š” ๊ฐœ์ธ ์•ก์„ธ์Šค ํ† ํฐ์œผ๋กœ ์ธ์ฆํ•ด์•ผ ํ•ด.
3
ํ™˜๊ฒฝ ์„ค์ •
- ํ™˜๊ฒฝ ์ด๋ฏธ์ง€: ๊ด€๋ฆฌํ˜• ์ด๋ฏธ์ง€ (AWS ์ œ๊ณต) ๋˜๋Š” ์ปค์Šคํ…€ ์ด๋ฏธ์ง€
- ์šด์˜ ์ฒด์ œ: Amazon Linux 2, Ubuntu, Windows Server
- ๋Ÿฐํƒ€์ž„: Standard
- ์ด๋ฏธ์ง€: aws/codebuild/standard:7.0 (์ตœ์‹  ๋ฒ„์ „ ์‚ฌ์šฉ ๊ถŒ์žฅ)
- ํ™˜๊ฒฝ ์œ ํ˜•: Linux (์ผ๋ฐ˜์ ์œผ๋กœ ์ด๊ฑธ ์„ ํƒ)
- ๊ถŒํ•œ ์žˆ์Œ: Docker ๋นŒ๋“œ๊ฐ€ ํ•„์š”ํ•˜๋ฉด ์ฒดํฌ โœ…
4
์„œ๋น„์Šค ์—ญํ•  ์„ค์ •
์•ž์„œ ๋งŒ๋“  CodeBuild ์„œ๋น„์Šค ์—ญํ• ์„ ์„ ํƒํ•˜๊ฑฐ๋‚˜, ์ƒˆ ์—ญํ• ์„ ์ž๋™ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์–ด. ์ž๋™ ์ƒ์„ฑ ์‹œ ๊ธฐ๋ณธ ๊ถŒํ•œ๋งŒ ๋ถ€์—ฌ๋˜๋‹ˆ๊นŒ S3 ์ ‘๊ทผ ๋“ฑ ์ถ”๊ฐ€ ๊ถŒํ•œ์€ ๋‚˜์ค‘์— ์ง์ ‘ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ด.
5
Buildspec ์„ค์ •
- buildspec ํŒŒ์ผ ์‚ฌ์šฉ: ์†Œ์Šค ์ฝ”๋“œ ๋ฃจํŠธ์˜ buildspec.yml ์ž๋™ ์‚ฌ์šฉ (๊ถŒ์žฅ)
- ๋นŒ๋“œ ๋ช…๋ น ์‚ฝ์ž…: ์ฝ˜์†”์—์„œ ์ง์ ‘ ๋ช…๋ น์–ด ์ž…๋ ฅ (๊ฐ„๋‹จํ•œ ๊ฒฝ์šฐ)
6
์•„ํ‹ฐํŒฉํŠธ ์„ค์ •
- ์œ ํ˜•: Amazon S3
- ๋ฒ„ํ‚ท ์ด๋ฆ„: ์•„ํ‹ฐํŒฉํŠธ ์ €์žฅ์šฉ S3 ๋ฒ„ํ‚ท ์„ ํƒ
- ์ด๋ฆ„: ์•„ํ‹ฐํŒฉํŠธ ํŒŒ์ผ/ํด๋” ์ด๋ฆ„
- ํŒจํ‚ค์ง•: ZIP (CodeDeploy์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ ์‹œ ZIP ๊ถŒ์žฅ)

โŒจ๏ธ AWS CLI๋กœ CodeBuild ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

CLI๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด IaC(Infrastructure as Code) ๋ฐฉ์‹์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์„œ ๋” ์ข‹์•„. ์•„๋ž˜ JSON ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ  CLI ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ด:


# codebuild-project.json ํŒŒ์ผ ์ƒ์„ฑ
{
  "name": "my-app-build",
  "description": "My App ๋นŒ๋“œ ํ”„๋กœ์ ํŠธ",
  "source": {
    "type": "GITHUB",
    "location": "https://github.com/your-org/your-repo.git",
    "buildspec": "buildspec.yml",
    "auth": {
      "type": "OAUTH"
    }
  },
  "artifacts": {
    "type": "S3",
    "location": "your-artifact-bucket",
    "name": "my-app-artifact",
    "packaging": "ZIP",
    "namespaceType": "BUILD_ID"
  },
  "environment": {
    "type": "LINUX_CONTAINER",
    "image": "aws/codebuild/standard:7.0",
    "computeType": "BUILD_GENERAL1_SMALL",
    "environmentVariables": [
      {
        "name": "ENVIRONMENT",
        "value": "production",
        "type": "PLAINTEXT"
      }
    ],
    "privilegedMode": false
  },
  "serviceRole": "arn:aws:iam::123456789012:role/CodeBuildServiceRole",
  "cache": {
    "type": "S3",
    "location": "your-artifact-bucket/cache"
  },
  "logsConfig": {
    "cloudWatchLogs": {
      "status": "ENABLED",
      "groupName": "/aws/codebuild/my-app-build"
    }
  }
}
# CodeBuild ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
aws codebuild create-project --cli-input-json file://codebuild-project.json

# ํ”„๋กœ์ ํŠธ ๋ชฉ๋ก ํ™•์ธ
aws codebuild list-projects

# ๋นŒ๋“œ ์ˆ˜๋™ ์‹œ์ž‘ (ํ…Œ์ŠคํŠธ์šฉ)
aws codebuild start-build --project-name my-app-build
โš ๏ธ ์ปดํ“จํŒ… ์œ ํ˜•๋ณ„ ์ŠคํŽ™ ์ฐธ๊ณ  - BUILD_GENERAL1_SMALL: vCPU 3๊ฐœ, ๋ฉ”๋ชจ๋ฆฌ 4GB (๊ธฐ๋ณธ, ์ €๋ ด)
- BUILD_GENERAL1_MEDIUM: vCPU 7๊ฐœ, ๋ฉ”๋ชจ๋ฆฌ 16GB
- BUILD_GENERAL1_LARGE: vCPU 15๊ฐœ, ๋ฉ”๋ชจ๋ฆฌ 32GB
- BUILD_GENERAL1_2XLARGE: vCPU 72๊ฐœ, ๋ฉ”๋ชจ๋ฆฌ 144GB (๋Œ€ํ˜• ํ”„๋กœ์ ํŠธ)
๋นŒ๋“œ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋ฉด ์ปดํ“จํŒ… ์œ ํ˜•์„ ์˜ฌ๋ ค๋ด. ๋น„์šฉ์€ ๋นŒ๋“œ ์‹œ๊ฐ„ ร— ์ปดํ“จํŒ… ์œ ํ˜•์œผ๋กœ ๊ณ„์‚ฐ๋ผ.

๐Ÿš€ 6. CodeDeploy ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ & ๋ฐฐํฌ ๊ทธ๋ฃน ์„ค์ •

CodeDeploy๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๋ฐฐํฌ ๊ทธ๋ฃน ๋‘ ๊ฐ€์ง€ ๊ฐœ๋…์œผ๋กœ ๊ตฌ์„ฑ๋ผ. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋ฐฐํฌํ•  ์•ฑ์˜ ๋…ผ๋ฆฌ์  ๋‹จ์œ„๊ณ , ๋ฐฐํฌ ๊ทธ๋ฃน์€ ์‹ค์ œ ๋ฐฐํฌ ๋Œ€์ƒ ์„œ๋ฒ„์™€ ๋ฐฐํฌ ์„ค์ •์„ ๋‹ด๊ณ  ์žˆ์–ด.


๐Ÿ“ฑ CodeDeploy ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒ์„ฑ

# CodeDeploy ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒ์„ฑ
aws deploy create-application \
  --application-name my-app \
  --compute-platform Server

# ์ƒ์„ฑ๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ™•์ธ
aws deploy get-application --application-name my-app

๐Ÿ‘ฅ ๋ฐฐํฌ ๊ทธ๋ฃน ์ƒ์„ฑ

๋ฐฐํฌ ๊ทธ๋ฃน ์ƒ์„ฑ ์‹œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฑด ๋ฐฐํฌ ๋Œ€์ƒ ์ง€์ •๊ณผ ๋ฐฐํฌ ์„ค์ •์ด์•ผ. EC2 ์ธ์Šคํ„ด์Šค๋ฅผ ํƒœ๊ทธ๋กœ ์ง€์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ด์•ผ:


# ๋ฐฐํฌ ๊ทธ๋ฃน ์ƒ์„ฑ (EC2 ํƒœ๊ทธ ๊ธฐ๋ฐ˜)
aws deploy create-deployment-group \
  --application-name my-app \
  --deployment-group-name my-app-production \
  --deployment-config-name CodeDeployDefault.OneAtATime \
  --ec2-tag-filters Key=Environment,Value=production,Type=KEY_AND_VALUE \
  --service-role-arn arn:aws:iam::123456789012:role/CodeDeployServiceRole \
  --auto-rollback-configuration enabled=true,events=DEPLOYMENT_FAILURE,DEPLOYMENT_STOP_ON_ALARM \
  --deployment-style deploymentType=IN_PLACE,deploymentOption=WITHOUT_TRAFFIC_CONTROL

โš™๏ธ ๋ฐฐํฌ ์„ค์ •(Deployment Config) ์ข…๋ฅ˜

๋ฐฐํฌ ์„ค์ • ์„ค๋ช… ์ ํ•ฉํ•œ ์ƒํ™ฉ
CodeDeployDefault.OneAtATime ํ•œ ๋ฒˆ์— ์ธ์Šคํ„ด์Šค 1๊ฐœ์”ฉ ๋ฐฐํฌ. ํ•˜๋‚˜ ์‹คํŒจํ•˜๋ฉด ์ค‘๋‹จ. ์•ˆ์ „ ์šฐ์„ , ์†Œ๊ทœ๋ชจ ์„œ๋ฒ„
CodeDeployDefault.HalfAtATime ์ „์ฒด์˜ ์ ˆ๋ฐ˜์”ฉ ๋ฐฐํฌ. ๋‚˜๋จธ์ง€ ์ ˆ๋ฐ˜์€ ๊ณ„์† ์„œ๋น„์Šค. ์ค‘๊ฐ„ ๊ทœ๋ชจ, ๊ฐ€์šฉ์„ฑ ์œ ์ง€
CodeDeployDefault.AllAtOnce ๋ชจ๋“  ์ธ์Šคํ„ด์Šค์— ๋™์‹œ ๋ฐฐํฌ. ๊ฐ€์žฅ ๋น ๋ฅด์ง€๋งŒ ์œ„ํ—˜. ๊ฐœ๋ฐœ/์Šคํ…Œ์ด์ง• ํ™˜๊ฒฝ
์ปค์Šคํ…€ ์„ค์ • ์ตœ์†Œ ์ •์ƒ ํ˜ธ์ŠคํŠธ ์ˆ˜/๋น„์œจ์„ ์ง์ ‘ ์ง€์ • ์„ธ๋ฐ€ํ•œ ์ œ์–ด๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ

๐Ÿ”ต๐ŸŸข Blue/Green ๋ฐฐํฌ ์„ค์ •

๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ์›ํ•œ๋‹ค๋ฉด Blue/Green ๋ฐฐํฌ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ด. ALB(Application Load Balancer)์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ผ๋ฐ˜์ ์ด์•ผ:


# Blue/Green ๋ฐฐํฌ ๊ทธ๋ฃน ์ƒ์„ฑ
aws deploy create-deployment-group \
  --application-name my-app \
  --deployment-group-name my-app-bluegreen \
  --deployment-config-name CodeDeployDefault.AllAtOnce \
  --service-role-arn arn:aws:iam::123456789012:role/CodeDeployServiceRole \
  --deployment-style deploymentType=BLUE_GREEN,deploymentOption=WITH_TRAFFIC_CONTROL \
  --load-balancer-info targetGroupInfoList=[{name=my-app-tg}] \
  --blue-green-deployment-configuration '{
    "terminateBlueInstancesOnDeploymentSuccess": {
      "action": "TERMINATE",
      "terminationWaitTimeInMinutes": 5
    },
    "deploymentReadyOption": {
      "actionOnTimeout": "CONTINUE_DEPLOYMENT",
      "waitTimeInMinutes": 0
    },
    "greenFleetProvisioningOption": {
      "action": "COPY_AUTO_SCALING_GROUP"
    }
  }' \
  --auto-scaling-groups my-app-asg
๐Ÿ’ก Blue/Green ๋ฐฐํฌ์˜ ๋™์ž‘ ์›๋ฆฌ 1. ํ˜„์žฌ ์šด์˜ ์ค‘์ธ ํ™˜๊ฒฝ(Blue)๊ณผ ๋™์ผํ•œ ์ƒˆ ํ™˜๊ฒฝ(Green)์„ ์ƒ์„ฑ
2. Green ํ™˜๊ฒฝ์— ์ƒˆ ๋ฒ„์ „ ๋ฐฐํฌ
3. ํ—ฌ์Šค์ฒดํฌ ํ†ต๊ณผ ํ›„ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ ํŠธ๋ž˜ํ”ฝ์„ Green์œผ๋กœ ์ „ํ™˜
4. ์ผ์ • ์‹œ๊ฐ„ ํ›„ Blue ํ™˜๊ฒฝ ์ข…๋ฃŒ
์ด ๋ฐฉ์‹์€ ๋ฐฐํฌ ์ค‘ ๋‹ค์šดํƒ€์ž„์ด ์—†๊ณ , ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ์ฆ‰์‹œ Blue๋กœ ๋กค๋ฐฑ ๊ฐ€๋Šฅํ•ด!

โš™๏ธ 7. CodePipeline์œผ๋กœ ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ ์—ฐ๊ฒฐํ•˜๊ธฐ

๋“œ๋””์–ด ๋ชจ๋“  ์กฐ๊ฐ์„ ํ•˜๋‚˜๋กœ ์—ฐ๊ฒฐํ•  ์‹œ๊ฐ„์ด์•ผ! ๐ŸŽ‰ CodePipeline์ด ์†Œ์Šค โ†’ ๋นŒ๋“œ โ†’ ๋ฐฐํฌ ๋‹จ๊ณ„๋ฅผ ์ž๋™์œผ๋กœ ์‹คํ–‰ํ•˜๋„๋ก ์„ค์ •ํ•ด๋ณด์ž.


๐Ÿ“„ ํŒŒ์ดํ”„๋ผ์ธ JSON ์ •์˜

# pipeline.json
{
  "pipeline": {
    "name": "my-app-pipeline",
    "roleArn": "arn:aws:iam::123456789012:role/CodePipelineServiceRole",
    "artifactStore": {
      "type": "S3",
      "location": "your-artifact-bucket"
    },
    "stages": [
      {
        "name": "Source",
        "actions": [
          {
            "name": "SourceAction",
            "actionTypeId": {
              "category": "Source",
              "owner": "ThirdParty",
              "provider": "GitHub",
              "version": "1"
            },
            "configuration": {
              "Owner": "your-github-org",
              "Repo": "your-repo",
              "Branch": "main",
              "OAuthToken": "{{resolve:secretsmanager:github-token:SecretString:token}}",
              "PollForSourceChanges": "false"
            },
            "outputArtifacts": [
              {"name": "SourceOutput"}
            ]
          }
        ]
      },
      {
        "name": "Build",
        "actions": [
          {
            "name": "BuildAction",
            "actionTypeId": {
              "category": "Build",
              "owner": "AWS",
              "provider": "CodeBuild",
              "version": "1"
            },
            "configuration": {
              "ProjectName": "my-app-build"
            },
            "inputArtifacts": [
              {"name": "SourceOutput"}
            ],
            "outputArtifacts": [
              {"name": "BuildOutput"}
            ]
          }
        ]
      },
      {
        "name": "Deploy",
        "actions": [
          {
            "name": "DeployAction",
            "actionTypeId": {
              "category": "Deploy",
              "owner": "AWS",
              "provider": "CodeDeploy",
              "version": "1"
            },
            "configuration": {
              "ApplicationName": "my-app",
              "DeploymentGroupName": "my-app-production"
            },
            "inputArtifacts": [
              {"name": "BuildOutput"}
            ]
          }
        ]
      }
    ]
  }
}
# ํŒŒ์ดํ”„๋ผ์ธ ์ƒ์„ฑ
aws codepipeline create-pipeline --cli-input-json file://pipeline.json

# ํŒŒ์ดํ”„๋ผ์ธ ์ƒํƒœ ํ™•์ธ
aws codepipeline get-pipeline-state --name my-app-pipeline

# ํŒŒ์ดํ”„๋ผ์ธ ์ˆ˜๋™ ์‹คํ–‰ (ํ…Œ์ŠคํŠธ์šฉ)
aws codepipeline start-pipeline-execution --name my-app-pipeline

๐Ÿ”” GitHub Webhook ์„ค์ • (์ž๋™ ํŠธ๋ฆฌ๊ฑฐ)

PollForSourceChanges: false๋กœ ์„ค์ •ํ•˜๋ฉด CodePipeline์ด GitHub๋ฅผ ์ฃผ๊ธฐ์ ์œผ๋กœ ํด๋งํ•˜์ง€ ์•Š์•„. ๋Œ€์‹  Webhook์„ ์„ค์ •ํ•ด์„œ ์ฝ”๋“œ ํ‘ธ์‹œ ์‹œ ์ฆ‰์‹œ ํŒŒ์ดํ”„๋ผ์ธ์ด ํŠธ๋ฆฌ๊ฑฐ๋˜๋„๋ก ํ•ด์•ผ ํ•ด. ์ด๊ฒŒ ๋” ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์ด์•ผ:


# Webhook ์ƒ์„ฑ
aws codepipeline put-webhook \
  --webhook '{
    "name": "my-app-webhook",
    "targetPipeline": "my-app-pipeline",
    "targetAction": "SourceAction",
    "filters": [
      {
        "jsonPath": "$.ref",
        "matchEquals": "refs/heads/main"
      }
    ],
    "authentication": "GITHUB_HMAC",
    "authenticationConfiguration": {
      "SecretToken": "your-webhook-secret"
    }
  }'

# Webhook ๋“ฑ๋ก (GitHub์— ์‹ค์ œ๋กœ ๋“ฑ๋ก)
aws codepipeline register-webhook-with-third-party \
  --webhook-name my-app-webhook

โœ… ์Šน์ธ ๋‹จ๊ณ„ ์ถ”๊ฐ€ (์„ ํƒ์‚ฌํ•ญ)

ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ ์ „์— ์ˆ˜๋™ ์Šน์ธ ๋‹จ๊ณ„๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์–ด. ์Šคํ…Œ์ด์ง• ๋ฐฐํฌ ํ›„ QA ํŒ€์ด ํ™•์ธํ•˜๊ณ  ์Šน์ธํ•˜๋ฉด ํ”„๋กœ๋•์…˜์— ๋ฐฐํฌ๋˜๋Š” ๋ฐฉ์‹์ด์•ผ:


# ํŒŒ์ดํ”„๋ผ์ธ์— ์Šน์ธ ๋‹จ๊ณ„ ์ถ”๊ฐ€ (Deploy ๋‹จ๊ณ„ ์•ž์— ์‚ฝ์ž…)
{
  "name": "Approval",
  "actions": [
    {
      "name": "ManualApproval",
      "actionTypeId": {
        "category": "Approval",
        "owner": "AWS",
        "provider": "Manual",
        "version": "1"
      },
      "configuration": {
        "NotificationArn": "arn:aws:sns:ap-northeast-2:123456789012:deployment-approval",
        "CustomData": "ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ ์Šน์ธ ์š”์ฒญ. ์Šคํ…Œ์ด์ง• ํ™˜๊ฒฝ์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”.",
        "ExternalEntityLink": "https://staging.your-app.com"
      }
    }
  ]
}
๋ฉ€ํ‹ฐ ์Šคํ…Œ์ด์ง€ ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์„ฑ ์˜ˆ์‹œ ์†Œ์Šค ๋‹จ๊ณ„ ๐Ÿ“ฆ GitHub main ๋ธŒ๋žœ์น˜ ํ‘ธ์‹œ ๊ฐ์ง€ Webhook ํŠธ๋ฆฌ๊ฑฐ ๋นŒ๋“œ ๋‹จ๊ณ„ ๐Ÿ”จ CodeBuild ์˜์กด์„ฑ ์„ค์น˜ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๋นŒ๋“œ ์‹คํ–‰ S3์— ์•„ํ‹ฐํŒฉํŠธ ์ €์žฅ ์Šคํ…Œ์ด์ง• ๋ฐฐํฌ ๐Ÿงช CodeDeploy ์Šคํ…Œ์ด์ง• ์„œ๋ฒ„ ์ž๋™ ๋ฐฐํฌ QA ๊ฒ€์ฆ ํ™˜๊ฒฝ ์ˆ˜๋™ ์Šน์ธ โœ… Manual Approval SNS ์•Œ๋ฆผ ๋ฐœ์†ก ๋‹ด๋‹น์ž ์Šน์ธ ๋Œ€๊ธฐ ์ตœ๋Œ€ 7์ผ ๋Œ€๊ธฐ ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ ๐Ÿš€ CodeDeploy Blue/Green ๋ฐฐํฌ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ์ž๋™ ๋กค๋ฐฑ ์šด์˜ ์„œ๋ฒ„ ๋ฐ˜์˜ ๐ŸŽ‰ ๋ฐฐํฌ ์™„๋ฃŒ! ์†Œ์Šค ํ‘ธ์‹œ โ†’ ์ž๋™ ๋นŒ๋“œ โ†’ ์Šคํ…Œ์ด์ง• ๋ฐฐํฌ โ†’ ์Šน์ธ โ†’ ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ ๐Ÿ”„

๐Ÿ“Š 8. ํŒŒ์ดํ”„๋ผ์ธ ๋ชจ๋‹ˆํ„ฐ๋ง & ์•Œ๋ฆผ ์„ค์ •

ํŒŒ์ดํ”„๋ผ์ธ ๋งŒ๋“ค์—ˆ๋‹ค๊ณ  ๋์ด ์•„๋‹ˆ์•ผ! ๋ฐฐํฌ๊ฐ€ ์‹คํŒจํ–ˆ์„ ๋•Œ ๋น ๋ฅด๊ฒŒ ์•Œ์•„์ฑ„๊ณ  ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ์•Œ๋ฆผ ์„ค์ •๋„ ๊ผญ ํ•ด์ค˜์•ผ ํ•ด. ๐Ÿ””


๐Ÿ“ง SNS + Lambda๋กœ Slack ์•Œ๋ฆผ ์„ค์ •

CodePipeline์€ ์ƒํƒœ ๋ณ€๊ฒฝ ์‹œ EventBridge(CloudWatch Events)๋กœ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ. ์ด๊ฑธ SNS์™€ Lambda๋กœ ์—ฐ๊ฒฐํ•ด์„œ Slack ์•Œ๋ฆผ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์–ด:


# EventBridge ๊ทœ์น™ ์ƒ์„ฑ (ํŒŒ์ดํ”„๋ผ์ธ ์‹คํŒจ ๊ฐ์ง€)
aws events put-rule \
  --name "codepipeline-failure-notification" \
  --event-pattern '{
    "source": ["aws.codepipeline"],
    "detail-type": ["CodePipeline Pipeline Execution State Change"],
    "detail": {
      "state": ["FAILED", "SUCCEEDED"],
      "pipeline": ["my-app-pipeline"]
    }
  }' \
  --state ENABLED
# Lambda ํ•จ์ˆ˜ (Slack ์•Œ๋ฆผ ๋ฐœ์†ก) - Python
import json
import urllib.request
import os

def lambda_handler(event, context):
    detail = event['detail']
    pipeline = detail['pipeline']
    state = detail['state']
    
    # ์ด๋ชจ์ง€ ์„ค์ •
    emoji = "โœ…" if state == "SUCCEEDED" else "โŒ"
    color = "#36a64f" if state == "SUCCEEDED" else "#ff0000"
    
    message = {
        "attachments": [
            {
                "color": color,
                "title": f"{emoji} CodePipeline ๋ฐฐํฌ ์•Œ๋ฆผ",
                "fields": [
                    {"title": "ํŒŒ์ดํ”„๋ผ์ธ", "value": pipeline, "short": True},
                    {"title": "์ƒํƒœ", "value": state, "short": True},
                    {"title": "์‹คํ–‰ ID", "value": detail.get('execution-id', 'N/A'), "short": False}
                ],
                "footer": "AWS CodePipeline",
                "ts": int(event['time'][:10])
            }
        ]
    }
    
    webhook_url = os.environ['SLACK_WEBHOOK_URL']
    data = json.dumps(message).encode('utf-8')
    req = urllib.request.Request(webhook_url, data=data, headers={'Content-Type': 'application/json'})
    urllib.request.urlopen(req)
    
    return {'statusCode': 200}

๐Ÿ“ˆ CloudWatch ๋ฉ”ํŠธ๋ฆญ & ๋Œ€์‹œ๋ณด๋“œ

CodeBuild์™€ CodeDeploy๋Š” ์ž๋™์œผ๋กœ CloudWatch ๋ฉ”ํŠธ๋ฆญ์„ ๋ฐœํ–‰ํ•ด. ์ฃผ์š” ๋ฉ”ํŠธ๋ฆญ๋“ค์„ ์•Œ์•„๋‘์ž:


์„œ๋น„์Šค ๋ฉ”ํŠธ๋ฆญ ์„ค๋ช…
CodeBuild SucceededBuilds ์„ฑ๊ณตํ•œ ๋นŒ๋“œ ์ˆ˜
CodeBuild FailedBuilds ์‹คํŒจํ•œ ๋นŒ๋“œ ์ˆ˜
CodeBuild Duration ๋นŒ๋“œ ์†Œ์š” ์‹œ๊ฐ„ (์ดˆ)
CodeDeploy DeploymentAttempts ๋ฐฐํฌ ์‹œ๋„ ํšŸ์ˆ˜
CodeDeploy DeploymentErrors ๋ฐฐํฌ ์˜ค๋ฅ˜ ์ˆ˜
CodeDeploy DeploymentDuration ๋ฐฐํฌ ์†Œ์š” ์‹œ๊ฐ„

๐Ÿ” ๋นŒ๋“œ ๋กœ๊ทธ ํ™•์ธ

๋นŒ๋“œ๊ฐ€ ์‹คํŒจํ–ˆ์„ ๋•Œ ์›์ธ์„ ํŒŒ์•…ํ•˜๋ ค๋ฉด CloudWatch Logs๋ฅผ ํ™•์ธํ•ด์•ผ ํ•ด:


# ์ตœ๊ทผ ๋นŒ๋“œ ๋กœ๊ทธ ๊ทธ๋ฃน ํ™•์ธ
aws logs describe-log-groups \
  --log-group-name-prefix /aws/codebuild/my-app-build

# ํŠน์ • ๋นŒ๋“œ์˜ ๋กœ๊ทธ ์ŠคํŠธ๋ฆผ ํ™•์ธ
aws logs describe-log-streams \
  --log-group-name /aws/codebuild/my-app-build \
  --order-by LastEventTime \
  --descending \
  --max-items 5

# ๋กœ๊ทธ ๋‚ด์šฉ ํ™•์ธ
aws logs get-log-events \
  --log-group-name /aws/codebuild/my-app-build \
  --log-stream-name "build-id/phase-context"

๐ŸŽฏ 9. ์‹ค๋ฌด์—์„œ ๊ผญ ์•Œ์•„์•ผ ํ•  ๊ณ ๊ธ‰ ์„ค์ • & ํŒ

๊ธฐ๋ณธ ํŒŒ์ดํ”„๋ผ์ธ์€ ๋งŒ๋“ค์—ˆ์–ด. ์ด์ œ ์‹ค๋ฌด์—์„œ ์ง„์งœ ์“ธ ์ˆ˜ ์žˆ๋Š” ์ˆ˜์ค€์œผ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•  ์ฐจ๋ก€์•ผ. ์žฌ๋Šฅ๋„ท์—์„œ DevOps ์ „๋ฌธ๊ฐ€๋“ค์ด ๊ณต์œ ํ•˜๋Š” ๋…ธํ•˜์šฐ ์ˆ˜์ค€์˜ ํŒ๋“ค์„ ๋ชจ์•„๋ดค์–ด! ๐Ÿ’ช


๐ŸŒฟ ๋ธŒ๋žœ์น˜๋ณ„ ํŒŒ์ดํ”„๋ผ์ธ ์ „๋žต

๐Ÿ“Œ GitFlow ๊ธฐ๋ฐ˜ ํŒŒ์ดํ”„๋ผ์ธ ์ „๋žต develop ๋ธŒ๋žœ์น˜ โ†’ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ํŒŒ์ดํ”„๋ผ์ธ
- ๋นŒ๋“œ + ๋‹จ์œ„ ํ…Œ์ŠคํŠธ + ๊ฐœ๋ฐœ ์„œ๋ฒ„ ์ž๋™ ๋ฐฐํฌ
- ๋น ๋ฅธ ํ”ผ๋“œ๋ฐฑ ๋ฃจํ”„ (์Šน์ธ ๋‹จ๊ณ„ ์—†์Œ)

release ๋ธŒ๋žœ์น˜ โ†’ ์Šคํ…Œ์ด์ง• ํ™˜๊ฒฝ ํŒŒ์ดํ”„๋ผ์ธ
- ๋นŒ๋“œ + ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ + ์Šคํ…Œ์ด์ง• ์„œ๋ฒ„ ์ž๋™ ๋ฐฐํฌ
- QA ํŒ€ ๊ฒ€์ฆ ํ›„ ์ˆ˜๋™ ์Šน์ธ

main ๋ธŒ๋žœ์น˜ โ†’ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ ํŒŒ์ดํ”„๋ผ์ธ
- ๋นŒ๋“œ + ์ „์ฒด ํ…Œ์ŠคํŠธ + Blue/Green ๋ฐฐํฌ
- ์ž๋™ ๋กค๋ฐฑ ์„ค์ • ํ•„์ˆ˜

โšก ๋นŒ๋“œ ์†๋„ ์ตœ์ ํ™”

1
S3 ์บ์‹œ ํ™œ์šฉ
node_modules, Maven .m2, Gradle ์บ์‹œ ๋“ฑ์„ S3์— ์ €์žฅํ•ด๋‘๋ฉด ๋‹ค์Œ ๋นŒ๋“œ ๋•Œ ์˜์กด์„ฑ ์„ค์น˜ ์‹œ๊ฐ„์„ ํฌ๊ฒŒ ์ค„์ผ ์ˆ˜ ์žˆ์–ด. buildspec.yml์˜ cache ์„น์…˜์„ ํ™œ์šฉํ•ด.
2
Docker ๋ ˆ์ด์–ด ์บ์‹œ
Docker ๋นŒ๋“œ ์‹œ --cache-from ์˜ต์…˜์„ ์‚ฌ์šฉํ•ด์„œ ์ด์ „ ๋นŒ๋“œ์˜ ๋ ˆ์ด์–ด๋ฅผ ์žฌ์‚ฌ์šฉํ•ด. ECR์— ์บ์‹œ ์ด๋ฏธ์ง€๋ฅผ ์ €์žฅํ•ด๋‘๋Š” ๋ฐฉ์‹์ด์•ผ.
3
๋ณ‘๋ ฌ ๋นŒ๋“œ ํ™œ์šฉ
๋…๋ฆฝ์ ์ธ ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ๋Š” ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•ด. CodeBuild์˜ ๋ฐฐ์น˜ ๋นŒ๋“œ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ ๋นŒ๋“œ๋ฅผ ๋™์‹œ์— ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์–ด.
4
์ปค์Šคํ…€ Docker ์ด๋ฏธ์ง€ ์‚ฌ์šฉ
์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๋„๊ตฌ๋“ค์„ ๋ฏธ๋ฆฌ ์„ค์น˜ํ•œ ์ปค์Šคํ…€ ๋นŒ๋“œ ์ด๋ฏธ์ง€๋ฅผ ๋งŒ๋“ค์–ด ECR์— ์ €์žฅํ•ด๋‘๋ฉด, install ๋‹จ๊ณ„์—์„œ ๋„๊ตฌ ์„ค์น˜ ์‹œ๊ฐ„์„ ์ ˆ์•ฝํ•  ์ˆ˜ ์žˆ์–ด.

๐Ÿ”’ ๋ณด์•ˆ ๊ฐ•ํ™” ์„ค์ •

# buildspec.yml์—์„œ ๋ฏผ๊ฐ ์ •๋ณด ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌ
version: 0.2

env:
  secrets-manager:
    # Secrets Manager์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ๊ฐ€์ ธ์˜ค๊ธฐ
    DB_HOST: "prod/myapp/db:host"
    DB_PASSWORD: "prod/myapp/db:password"
    API_SECRET: "prod/myapp/api:secret_key"
  parameter-store:
    # SSM Parameter Store์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ (SecureString ํƒ€์ž…)
    JWT_SECRET: "/prod/myapp/jwt_secret"

phases:
  build:
    commands:
      # ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋Š” ๋กœ๊ทธ์— ์ถœ๋ ฅํ•˜์ง€ ์•Š๋„๋ก ์ฃผ์˜!
      - echo "๋นŒ๋“œ ์‹œ์ž‘ (๋ฏผ๊ฐ ์ •๋ณด๋Š” ๋กœ๊ทธ์— ์ถœ๋ ฅ ์•ˆ ํ•จ)"
      - npm run build
      
      # ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ์— ๋ฏผ๊ฐ ์ •๋ณด๊ฐ€ ํฌํ•จ๋˜์ง€ ์•Š๋„๋ก ํ™•์ธ
      - grep -r "password\|secret\|key" dist/ || echo "๋ฏผ๊ฐ ์ •๋ณด ์—†์Œ ํ™•์ธ"
โš ๏ธ ๋ณด์•ˆ ์ฃผ์˜์‚ฌํ•ญ - echo $DB_PASSWORD ๊ฐ™์€ ๋ช…๋ น์–ด๋Š” ์ ˆ๋Œ€ buildspec.yml์— ๋„ฃ์ง€ ๋งˆ. CloudWatch Logs์— ๊ทธ๋Œ€๋กœ ๋‚จ์•„.
- buildspec.yml์˜ variables ์„น์…˜์—๋Š” ์ ˆ๋Œ€ ๋น„๋ฐ€๋ฒˆํ˜ธ, API ํ‚ค ๊ฐ™์€ ๋ฏผ๊ฐ ์ •๋ณด๋ฅผ ๋„ฃ์ง€ ๋งˆ.
- ํ•ญ์ƒ Secrets Manager ๋˜๋Š” SSM Parameter Store๋ฅผ ์‚ฌ์šฉํ•ด!

๐Ÿ”„ ์ž๋™ ๋กค๋ฐฑ ์„ค์ •

# CodeDeploy ๋ฐฐํฌ ๊ทธ๋ฃน์— ์ž๋™ ๋กค๋ฐฑ ์„ค์ •
aws deploy update-deployment-group \
  --application-name my-app \
  --current-deployment-group-name my-app-production \
  --auto-rollback-configuration '{
    "enabled": true,
    "events": [
      "DEPLOYMENT_FAILURE",
      "DEPLOYMENT_STOP_ON_ALARM"
    ]
  }' \
  --alarm-configuration '{
    "enabled": true,
    "alarms": [
      {"name": "my-app-error-rate-alarm"},
      {"name": "my-app-cpu-alarm"}
    ]
  }'

CloudWatch ์•Œ๋žŒ๊ณผ ์—ฐ๋™ํ•˜๋ฉด ๋ฐฐํฌ ํ›„ ์—๋Ÿฌ์œจ์ด ๊ธ‰์ฆํ•˜๊ฑฐ๋‚˜ CPU๊ฐ€ ์น˜์†Ÿ์„ ๋•Œ ์ž๋™์œผ๋กœ ๋กค๋ฐฑ์ด ํŠธ๋ฆฌ๊ฑฐ๋ผ. ์ง„์งœ ๊ฐ•๋ ฅํ•œ ์•ˆ์ „๋ง์ด์•ผ!


๐Ÿ“ฆ Terraform์œผ๋กœ ํŒŒ์ดํ”„๋ผ์ธ IaC ๊ด€๋ฆฌ

ํŒŒ์ดํ”„๋ผ์ธ ์„ค์ •์„ Terraform์œผ๋กœ ์ฝ”๋“œํ™”ํ•˜๋ฉด ๋ฒ„์ „ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ณ , ๋™์ผํ•œ ํŒŒ์ดํ”„๋ผ์ธ์„ ์—ฌ๋Ÿฌ ํ™˜๊ฒฝ์— ์‰ฝ๊ฒŒ ๋ณต์ œํ•  ์ˆ˜ ์žˆ์–ด:


# main.tf - CodePipeline ๋ฆฌ์†Œ์Šค ์ •์˜
resource "aws_codepipeline" "my_app_pipeline" {
  name     = "my-app-pipeline"
  role_arn = aws_iam_role.codepipeline_role.arn

  artifact_store {
    location = aws_s3_bucket.artifact_bucket.bucket
    type     = "S3"
  }

  stage {
    name = "Source"
    action {
      name             = "Source"
      category         = "Source"
      owner            = "ThirdParty"
      provider         = "GitHub"
      version          = "1"
      output_artifacts = ["source_output"]

      configuration = {
        Owner      = var.github_owner
        Repo       = var.github_repo
        Branch     = var.github_branch
        OAuthToken = var.github_token
      }
    }
  }

  stage {
    name = "Build"
    action {
      name             = "Build"
      category         = "Build"
      owner            = "AWS"
      provider         = "CodeBuild"
      input_artifacts  = ["source_output"]
      output_artifacts = ["build_output"]
      version          = "1"

      configuration = {
        ProjectName = aws_codebuild_project.my_app_build.name
      }
    }
  }

  stage {
    name = "Deploy"
    action {
      name            = "Deploy"
      category        = "Deploy"
      owner           = "AWS"
      provider        = "CodeDeploy"
      input_artifacts = ["build_output"]
      version         = "1"

      configuration = {
        ApplicationName     = aws_codedeploy_app.my_app.name
        DeploymentGroupName = aws_codedeploy_deployment_group.production.deployment_group_name
      }
    }
  }
}

๐Ÿ”ง 10. ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ & ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์„ฑํ•˜๋‹ค ๋ณด๋ฉด ๋ฐ˜๋“œ์‹œ ๋ง‰ํžˆ๋Š” ๊ตฌ๊ฐ„๋“ค์ด ์žˆ์–ด. ๋ฏธ๋ฆฌ ์•Œ์•„๋‘๋ฉด ์‚ฝ์งˆ ์‹œ๊ฐ„์„ ํ™• ์ค„์ผ ์ˆ˜ ์žˆ์–ด! ๐Ÿ˜ค


โŒ ๋ฌธ์ œ 1: CodeBuild ๋นŒ๋“œ ์‹คํŒจ - "Access Denied"

์ฆ์ƒ: ๋นŒ๋“œ ๋กœ๊ทธ์— AccessDeniedException ๋˜๋Š” Access Denied ์—๋Ÿฌ

์›์ธ: CodeBuild ์„œ๋น„์Šค ์—ญํ• ์— ํ•„์š”ํ•œ ๊ถŒํ•œ์ด ์—†์Œ

ํ•ด๊ฒฐ:
1. CloudWatch Logs์—์„œ ์ •ํ™•ํ•œ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ํ™•์ธ
2. ์—๋Ÿฌ ๋ฉ”์‹œ์ง€์—์„œ ์–ด๋–ค ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ์–ด๋–ค ์•ก์…˜์ด ๊ฑฐ๋ถ€๋๋Š”์ง€ ํ™•์ธ
3. CodeBuild ์„œ๋น„์Šค ์—ญํ• ์— ํ•ด๋‹น ๊ถŒํ•œ ์ถ”๊ฐ€
4. IAM Policy Simulator๋กœ ๊ถŒํ•œ ๊ฒ€์ฆ

โŒ ๋ฌธ์ œ 2: CodeDeploy ๋ฐฐํฌ ์‹คํŒจ - "No such file or directory"

์ฆ์ƒ: ๋ฐฐํฌ ๋กœ๊ทธ์— ์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†๋‹ค๋Š” ์—๋Ÿฌ

์›์ธ: appspec.yml์— ์ง€์ •ํ•œ ์Šคํฌ๋ฆฝํŠธ ๊ฒฝ๋กœ๊ฐ€ ์•„ํ‹ฐํŒฉํŠธ์— ํฌํ•จ๋˜์ง€ ์•Š์Œ

ํ•ด๊ฒฐ:
1. buildspec.yml์˜ artifacts.files์— scripts ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธ
2. appspec.yml์ด ์•„ํ‹ฐํŒฉํŠธ ๋ฃจํŠธ์— ์žˆ๋Š”์ง€ ํ™•์ธ
3. ์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ์— ์‹คํ–‰ ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€ ํ™•์ธ (chmod +x)
4. ์•„ํ‹ฐํŒฉํŠธ ZIP ํŒŒ์ผ์„ ์ง์ ‘ ๋‹ค์šด๋กœ๋“œํ•ด์„œ ๋‚ด์šฉ ํ™•์ธ

โŒ ๋ฌธ์ œ 3: CodeDeploy Agent๊ฐ€ ์‘๋‹ตํ•˜์ง€ ์•Š์Œ

์ฆ์ƒ: ๋ฐฐํฌ๊ฐ€ ์‹œ์ž‘๋˜์ง€ ์•Š๊ณ  ํƒ€์ž„์•„์›ƒ ๋ฐœ์ƒ

์›์ธ: EC2 ์ธ์Šคํ„ด์Šค์˜ CodeDeploy Agent๊ฐ€ ์‹คํ–‰ ์ค‘์ด์ง€ ์•Š๊ฑฐ๋‚˜, ์ธ์Šคํ„ด์Šค ํ”„๋กœํŒŒ์ผ ๊ถŒํ•œ ๋ฌธ์ œ

ํ•ด๊ฒฐ:
# EC2 ์ธ์Šคํ„ด์Šค์—์„œ ์‹คํ–‰
# Agent ์ƒํƒœ ํ™•์ธ
sudo service codedeploy-agent status

# Agent ์žฌ์‹œ์ž‘
sudo service codedeploy-agent restart

# Agent ๋กœ๊ทธ ํ™•์ธ
tail -f /var/log/aws/codedeploy-agent/codedeploy-agent.log

# ์ธ์Šคํ„ด์Šค ํ”„๋กœํŒŒ์ผ ํ™•์ธ
curl http://169.254.169.254/latest/meta-data/iam/info

โŒ ๋ฌธ์ œ 4: ํŒŒ์ดํ”„๋ผ์ธ์ด ํŠธ๋ฆฌ๊ฑฐ๋˜์ง€ ์•Š์Œ

์ฆ์ƒ: GitHub์— ์ฝ”๋“œ๋ฅผ ํ‘ธ์‹œํ•ด๋„ ํŒŒ์ดํ”„๋ผ์ธ์ด ์‹œ์ž‘๋˜์ง€ ์•Š์Œ

์›์ธ: Webhook ์„ค์ • ๋ฌธ์ œ ๋˜๋Š” ๋ธŒ๋žœ์น˜ ํ•„ํ„ฐ ์„ค์ • ์˜ค๋ฅ˜

ํ•ด๊ฒฐ:
1. GitHub ์ €์žฅ์†Œ Settings โ†’ Webhooks์—์„œ Webhook ์ƒํƒœ ํ™•์ธ
2. ์ตœ๊ทผ Webhook ์ „์†ก ๋‚ด์—ญ์—์„œ ์‘๋‹ต ์ฝ”๋“œ ํ™•์ธ (200์ด์–ด์•ผ ์ •์ƒ)
3. CodePipeline ์†Œ์Šค ๋‹จ๊ณ„์˜ ๋ธŒ๋žœ์น˜ ์„ค์ •์ด ์‹ค์ œ ํ‘ธ์‹œํ•œ ๋ธŒ๋žœ์น˜์™€ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธ
4. Webhook ์‹œํฌ๋ฆฟ ํ† ํฐ์ด ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธ

โŒ ๋ฌธ์ œ 5: ๋นŒ๋“œ๋Š” ์„ฑ๊ณตํ–ˆ๋Š”๋ฐ ๋ฐฐํฌ ํ›„ ์•ฑ์ด ์•ˆ ๋œธ

์ฆ์ƒ: ValidateService ๋‹จ๊ณ„์—์„œ ์‹คํŒจํ•˜๊ฑฐ๋‚˜, ๋ฐฐํฌ ํ›„ ์•ฑ์— ์ ‘๊ทผ์ด ์•ˆ ๋จ

์›์ธ: ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋ฏธ์„ค์ •, ํฌํŠธ ์ถฉ๋Œ, ์˜์กด์„ฑ ๋ฌธ์ œ ๋“ฑ ๋‹ค์–‘

ํ•ด๊ฒฐ:
1. EC2 ์ธ์Šคํ„ด์Šค์— ์ง์ ‘ SSH ์ ‘์†ํ•ด์„œ ์•ฑ ๋กœ๊ทธ ํ™•์ธ
2. /opt/codedeploy-agent/deployment-root/ ๊ฒฝ๋กœ์—์„œ ๋ฐฐํฌ ๋กœ๊ทธ ํ™•์ธ
3. ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์„ค์ •๋˜์—ˆ๋Š”์ง€ ํ™•์ธ
4. ์•ฑ ์‹œ์ž‘ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์‹คํ–‰ํ•ด์„œ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ํ™•์ธ
๐Ÿ’ก ๋””๋ฒ„๊น… ๊ฟ€ํŒ CodeDeploy ๋ฐฐํฌ ๋กœ๊ทธ๋Š” EC2 ์ธ์Šคํ„ด์Šค์˜ /opt/codedeploy-agent/deployment-root/ ๋””๋ ‰ํ† ๋ฆฌ์— ์ €์žฅ๋ผ. ๊ฐ ๋ฐฐํฌ ID๋ณ„๋กœ ํด๋”๊ฐ€ ์ƒ์„ฑ๋˜๊ณ , deployment-logs/codedeploy-agent-deployments.log ํŒŒ์ผ์— ์ƒ์„ธ ๋กœ๊ทธ๊ฐ€ ์žˆ์–ด. ๋ฐฐํฌ ์‹คํŒจ ์‹œ ์—ฌ๊ธฐ๋ฅผ ๋จผ์ € ํ™•์ธํ•ด!

๐Ÿ’ฐ 11. ๋น„์šฉ ์ตœ์ ํ™” ์ „๋žต

AWS ์„œ๋น„์Šค ์“ฐ๋‹ค ๋ณด๋ฉด ๋น„์šฉ์ด ์Šฌ๊ธˆ์Šฌ๊ธˆ ์˜ฌ๋ผ๊ฐ€๋Š” ๊ฒŒ ๋ฌด์„ญ์ž–์•„. CodeBuild์™€ CodeDeploy์˜ ๋น„์šฉ ๊ตฌ์กฐ๋ฅผ ์ดํ•ดํ•˜๊ณ  ์ตœ์ ํ™”ํ•ด๋ณด์ž.


๐Ÿ’ต CodeBuild ๋น„์šฉ ๊ตฌ์กฐ

์ปดํ“จํŒ… ์œ ํ˜• ๋ถ„๋‹น ๋น„์šฉ (์„œ์šธ ๋ฆฌ์ „) ๊ถŒ์žฅ ์‚ฌ์šฉ ์ผ€์ด์Šค
BUILD_GENERAL1_SMALL ์•ฝ $0.005/๋ถ„ ์†Œ๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ, ๊ฐ„๋‹จํ•œ ๋นŒ๋“œ
BUILD_GENERAL1_MEDIUM ์•ฝ $0.01/๋ถ„ ์ค‘๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ, Docker ๋นŒ๋“œ
BUILD_GENERAL1_LARGE ์•ฝ $0.02/๋ถ„ ๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ, ๋ณ‘๋ ฌ ํ…Œ์ŠคํŠธ

CodeDeploy๋Š” EC2/์˜จํ”„๋ ˆ๋ฏธ์Šค ๋ฐฐํฌ๋Š” ๋ฌด๋ฃŒ์•ผ! Lambda์™€ ECS ๋ฐฐํฌ๋Š” API ํ˜ธ์ถœ ์ˆ˜์— ๋”ฐ๋ผ ๊ณผ๊ธˆ๋ผ. CodePipeline์€ ํ™œ์„ฑ ํŒŒ์ดํ”„๋ผ์ธ๋‹น ์›” $1์ด์•ผ (์ฒซ ๋ฒˆ์งธ ํŒŒ์ดํ”„๋ผ์ธ์€ ๋ฌด๋ฃŒ).


๐Ÿ’ก ๋น„์šฉ ์ ˆ๊ฐ ํŒ โ‘  ๋นŒ๋“œ ์บ์‹œ ์ ๊ทน ํ™œ์šฉ
S3 ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋นŒ๋“œ ์‹œ๊ฐ„์„ 30~70% ์ค„์ผ ์ˆ˜ ์žˆ์–ด. ๋นŒ๋“œ ์‹œ๊ฐ„์ด ์ค„๋ฉด ๋น„์šฉ๋„ ์ค„์–ด๋“ค์ง€.

โ‘ก ๋ถˆํ•„์š”ํ•œ ๋นŒ๋“œ ๋ฐฉ์ง€
๋ฌธ์„œ ํŒŒ์ผ(*.md, *.txt)๋งŒ ๋ณ€๊ฒฝ๋์„ ๋•Œ๋Š” ๋นŒ๋“œ๋ฅผ ๊ฑด๋„ˆ๋›ฐ๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ์–ด. CodePipeline์˜ ์†Œ์Šค ๋‹จ๊ณ„์—์„œ ํŒŒ์ผ ํ•„ํ„ฐ๋ฅผ ์„ค์ •ํ•ด.

โ‘ข ์ ์ ˆํ•œ ์ปดํ“จํŒ… ์œ ํ˜• ์„ ํƒ
๋นŒ๋“œ ์‹œ๊ฐ„์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•ด์„œ SMALL๋กœ๋„ ์ถฉ๋ถ„ํ•˜๋‹ค๋ฉด MEDIUM์ด๋‚˜ LARGE๋ฅผ ์“ธ ํ•„์š” ์—†์–ด.

โ‘ฃ ๋นŒ๋“œ ํƒ€์ž„์•„์›ƒ ์„ค์ •
๋ฌดํ•œ ๋ฃจํ”„๋‚˜ ํ–‰(hang) ์ƒํƒœ์˜ ๋นŒ๋“œ๊ฐ€ ๊ณ„์† ์‹คํ–‰๋˜์ง€ ์•Š๋„๋ก ํƒ€์ž„์•„์›ƒ์„ ์„ค์ •ํ•ด. ๊ธฐ๋ณธ๊ฐ’์€ 60๋ถ„์ธ๋ฐ, ์‹ค์ œ ๋นŒ๋“œ ์‹œ๊ฐ„์˜ 2๋ฐฐ ์ •๋„๋กœ ์„ค์ •ํ•˜๋Š” ๊ฒŒ ์ข‹์•„.

๐Ÿ—๏ธ 12. ์‹ค์ „ ์˜ˆ์ œ: Node.js ์•ฑ ์™„์ „ ์ž๋™ํ™” ํŒŒ์ดํ”„๋ผ์ธ

์ง€๊ธˆ๊นŒ์ง€ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ข…ํ•ฉํ•ด์„œ ์‹ค์ œ Node.js Express ์•ฑ์„ ์œ„ํ•œ ์™„์ „ํ•œ ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์„ฑํ•ด๋ณด์ž. ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋ถ€ํ„ฐ ์‹œ์ž‘ํ• ๊ฒŒ:


๐Ÿ“ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

my-express-app/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ app.js
โ”‚   โ”œโ”€โ”€ routes/
โ”‚   โ””โ”€โ”€ models/
โ”œโ”€โ”€ tests/
โ”‚   โ”œโ”€โ”€ unit/
โ”‚   โ””โ”€โ”€ integration/
โ”œโ”€โ”€ scripts/
โ”‚   โ”œโ”€โ”€ before_install.sh
โ”‚   โ”œโ”€โ”€ after_install.sh
โ”‚   โ”œโ”€โ”€ start_server.sh
โ”‚   โ”œโ”€โ”€ stop_server.sh
โ”‚   โ””โ”€โ”€ validate_service.sh
โ”œโ”€โ”€ buildspec.yml          โ† CodeBuild ์„ค์ •
โ”œโ”€โ”€ appspec.yml            โ† CodeDeploy ์„ค์ •
โ”œโ”€โ”€ package.json
โ””โ”€โ”€ README.md

๐Ÿ“„ ์™„์„ฑ๋œ buildspec.yml

version: 0.2

env:
  variables:
    NODE_ENV: production
  secrets-manager:
    DB_CONNECTION_STRING: "prod/myapp:db_connection"

phases:
  install:
    runtime-versions:
      nodejs: 18
    commands:
      - echo "Node.js ๋ฒ„์ „ ํ™•์ธ"
      - node --version
      - npm --version

  pre_build:
    commands:
      - echo "์˜์กด์„ฑ ์„ค์น˜ ์ค‘..."
      - npm ci
      - echo "๋ฆฐํŠธ ๊ฒ€์‚ฌ ์ค‘..."
      - npm run lint
      - echo "๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ค‘..."
      - npm run test:unit
      - echo "ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ค‘..."
      - npm run test:integration

  build:
    commands:
      - echo "๋นŒ๋“œ ์‹œ์ž‘: $(date)"
      - npm run build
      - echo "๋นŒ๋“œ ์™„๋ฃŒ: $(date)"

  post_build:
    commands:
      - echo "๋ฐฐํฌ ํŒจํ‚ค์ง€ ์ค€๋น„ ์ค‘..."
      - mkdir -p deploy-package
      - cp -r dist/ deploy-package/
      - cp package.json deploy-package/
      - cp package-lock.json deploy-package/
      - cp appspec.yml deploy-package/
      - cp -r scripts/ deploy-package/
      - echo "๋ฐฐํฌ ํŒจํ‚ค์ง€ ์ค€๋น„ ์™„๋ฃŒ!"

artifacts:
  files:
    - "**/*"
  base-directory: deploy-package
  discard-paths: no

reports:
  test-reports:
    files:
      - "coverage/test-results.xml"
    file-format: JUNITXML

cache:
  paths:
    - node_modules/**/*

๐Ÿ“„ ์™„์„ฑ๋œ appspec.yml

version: 0.0
os: linux

files:
  - source: /
    destination: /var/www/my-express-app

file_exists_behavior: OVERWRITE

permissions:
  - object: /var/www/my-express-app
    pattern: "**"
    owner: nodejs
    group: nodejs
    mode: 755
    type:
      - directory
  - object: /var/www/my-express-app/scripts
    pattern: "*.sh"
    mode: 755
    type:
      - file

hooks:
  ApplicationStop:
    - location: scripts/stop_server.sh
      timeout: 60
      runas: root

  BeforeInstall:
    - location: scripts/before_install.sh
      timeout: 120
      runas: root

  AfterInstall:
    - location: scripts/after_install.sh
      timeout: 300
      runas: root

  ApplicationStart:
    - location: scripts/start_server.sh
      timeout: 120
      runas: nodejs

  ValidateService:
    - location: scripts/validate_service.sh
      timeout: 120
      runas: nodejs

๐ŸŽฌ ํŒŒ์ดํ”„๋ผ์ธ ์‹คํ–‰ ํ๋ฆ„ ์ตœ์ข… ํ™•์ธ

๐Ÿ”„ ์ „์ฒด ์ž๋™ํ™” ํ๋ฆ„ ์š”์•ฝ 1๋‹จ๊ณ„ (ํŠธ๋ฆฌ๊ฑฐ): ๊ฐœ๋ฐœ์ž๊ฐ€ main ๋ธŒ๋žœ์น˜์— ์ฝ”๋“œ ํ‘ธ์‹œ
โ†“
2๋‹จ๊ณ„ (์†Œ์Šค): GitHub Webhook โ†’ CodePipeline ํŠธ๋ฆฌ๊ฑฐ โ†’ ์†Œ์Šค ์ฝ”๋“œ S3์— ์ €์žฅ
โ†“
3๋‹จ๊ณ„ (๋นŒ๋“œ): CodeBuild ์‹คํ–‰ โ†’ ์˜์กด์„ฑ ์„ค์น˜ โ†’ ๋ฆฐํŠธ/ํ…Œ์ŠคํŠธ โ†’ ๋นŒ๋“œ โ†’ ์•„ํ‹ฐํŒฉํŠธ S3 ์—…๋กœ๋“œ
โ†“
4๋‹จ๊ณ„ (๋ฐฐํฌ): CodeDeploy ์‹คํ–‰ โ†’ EC2 Agent๊ฐ€ S3์—์„œ ์•„ํ‹ฐํŒฉํŠธ ๋‹ค์šด๋กœ๋“œ โ†’ ํ›… ์Šคํฌ๋ฆฝํŠธ ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰
โ†“
5๋‹จ๊ณ„ (๊ฒ€์ฆ): ValidateService ์Šคํฌ๋ฆฝํŠธ๋กœ ํ—ฌ์Šค์ฒดํฌ โ†’ ์„ฑ๊ณต ์‹œ ๋ฐฐํฌ ์™„๋ฃŒ, ์‹คํŒจ ์‹œ ์ž๋™ ๋กค๋ฐฑ
โ†“
6๋‹จ๊ณ„ (์•Œ๋ฆผ): Slack์œผ๋กœ ๋ฐฐํฌ ์„ฑ๊ณต/์‹คํŒจ ์•Œ๋ฆผ ๋ฐœ์†ก ๐Ÿ””

๐ŸŽ‰ ๋งˆ๋ฌด๋ฆฌ: ์ž๋™ํ™”์˜ ์ง„์งœ ๊ฐ€์น˜

์ž, ์—ฌ๊ธฐ๊นŒ์ง€ ์™”์œผ๋ฉด ์ด์ œ CodeBuild + CodeDeploy ํŒŒ์ดํ”„๋ผ์ธ์„ ์ง์ ‘ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ์‹ค๋ ฅ์ด ๋์–ด! ๐ŸŽŠ


์ฒ˜์Œ์— ์„ค์ •ํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์ด ์ข€ ๊ฑธ๋ฆฌ๋”๋ผ๋„, ํ•œ ๋ฒˆ ์ œ๋Œ€๋กœ ๋งŒ๋“ค์–ด๋‘๋ฉด ๊ทธ ์ดํ›„๋กœ๋Š” ์ฝ”๋“œ ํ‘ธ์‹œ ํ•œ ๋ฒˆ์œผ๋กœ ๋ชจ๋“  ๊ฒŒ ์ž๋™์œผ๋กœ ๋Œ์•„๊ฐ€. ๋ฐฐํฌ ๋•Œ๋งˆ๋‹ค ์„œ๋ฒ„์— ์ ‘์†ํ•ด์„œ ์ˆ˜๋™์œผ๋กœ ์ž‘์—…ํ•˜๋˜ ์‹œ๊ฐ„์„ ์•„๊ปด์„œ ๋” ์ค‘์š”ํ•œ ๊ฐœ๋ฐœ์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋Š” ๊ฑฐ์ง€.


ํ•ต์‹ฌ ํฌ์ธํŠธ๋ฅผ ๋‹ค์‹œ ํ•œ๋ฒˆ ์ •๋ฆฌํ•˜๋ฉด:


โœ… ์˜ค๋Š˜ ๋ฐฐ์šด ํ•ต์‹ฌ ๋‚ด์šฉ ์ฒดํฌ๋ฆฌ์ŠคํŠธ
  • โ˜‘๏ธ CodeBuild, CodeDeploy, CodePipeline์˜ ์—ญํ• ๊ณผ ์ฐจ์ด์  ์ดํ•ด
  • โ˜‘๏ธ IAM ์—ญํ•  ๋ฐ ๊ถŒํ•œ ์„ค์ • (๊ฐ€์žฅ ์ค‘์š”!)
  • โ˜‘๏ธ EC2์— CodeDeploy Agent ์„ค์น˜
  • โ˜‘๏ธ buildspec.yml ์ž‘์„ฑ (๋นŒ๋“œ ๋ ˆ์‹œํ”ผ)
  • โ˜‘๏ธ appspec.yml ์ž‘์„ฑ (๋ฐฐํฌ ๋ ˆ์‹œํ”ผ)
  • โ˜‘๏ธ ๋ฐฐํฌ ํ›… ์Šคํฌ๋ฆฝํŠธ ์ž‘์„ฑ (stop/install/start/validate)
  • โ˜‘๏ธ CodePipeline์œผ๋กœ ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ ์—ฐ๊ฒฐ
  • โ˜‘๏ธ Webhook์œผ๋กœ ์ž๋™ ํŠธ๋ฆฌ๊ฑฐ ์„ค์ •
  • โ˜‘๏ธ CloudWatch + Slack์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋ง & ์•Œ๋ฆผ ์„ค์ •
  • โ˜‘๏ธ ์ž๋™ ๋กค๋ฐฑ ์„ค์ •์œผ๋กœ ์•ˆ์ „๋ง ๊ตฌ์ถ•
  • โ˜‘๏ธ ๋น„์šฉ ์ตœ์ ํ™” ์ „๋žต ์ ์šฉ

DevOps๋Š” ํ•œ ๋ฒˆ์— ์™„๋ฒฝํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์—†์–ด. ์ฒ˜์Œ์—” ๊ฐ„๋‹จํ•˜๊ฒŒ ์‹œ์ž‘ํ•ด์„œ ์ ์  ๊ฐœ์„ ํ•ด๋‚˜๊ฐ€๋Š” ๊ฒŒ ๋งž์•„. ์ผ๋‹จ ๊ธฐ๋ณธ ํŒŒ์ดํ”„๋ผ์ธ๋ถ€ํ„ฐ ๋งŒ๋“ค๊ณ , ์šด์˜ํ•˜๋ฉด์„œ ํ•„์š”ํ•œ ๊ธฐ๋Šฅ๋“ค์„ ํ•˜๋‚˜์”ฉ ์ถ”๊ฐ€ํ•ด๋ด. ๊ทธ๋Ÿฌ๋‹ค ๋ณด๋ฉด ์–ด๋А์ƒˆ ์—„์ฒญ ํƒ„ํƒ„ํ•œ CI/CD ์‹œ์Šคํ…œ์ด ์™„์„ฑ๋˜์–ด ์žˆ์„ ๊ฑฐ์•ผ! ๐Ÿ’ช


์ด๋Ÿฐ DevOps ์Šคํ‚ฌ์€ ์‹ค๋ฌด์—์„œ ์ •๋ง ๊ฐ€์น˜ ์žˆ๋Š” ๋Šฅ๋ ฅ์ด์•ผ. ์žฌ๋Šฅ๋„ท์—์„œ๋„ AWS ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์„ฑ์ด๋‚˜ DevOps ์ปจ์„คํŒ… ๊ด€๋ จ ์žฌ๋Šฅ๋“ค์ด ๊พธ์ค€ํžˆ ์ธ๊ธฐ๋ฅผ ๋Œ๊ณ  ์žˆ์„ ์ •๋„๋‹ˆ๊นŒ. ์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ์œผ๋กœ ๋ฉ‹์ง„ ์ž๋™ํ™” ํŒŒ์ดํ”„๋ผ์ธ ๋งŒ๋“ค์–ด๋ด! ๐Ÿš€

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

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

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

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