Ver3.5 ๐ CodeBuild + CodeDeploy ์๋ ๋ฐฐํฌ ํ์ดํ๋ผ์ธ ์์ ์ ๋ณต ๊ฐ์ด๋

๐ CodeBuild + CodeDeploy ์๋ ๋ฐฐํฌ ํ์ดํ๋ผ์ธ ์์ ์ ๋ณต ๊ฐ์ด๋
AWS ๋ค์ดํฐ๋ธ ๋๊ตฌ๋ก CI/CD ํ์ดํ๋ผ์ธ์ ๋๋ฑ ๋ง๋ค์ด๋ณด์! ๋น๋๋ถํฐ ๋ฐฐํฌ๊น์ง, ์ ํ๋ ์ ๋๊ณ ์๋ํํ๋ ๋ฒ ๐ฏ
์ผ, ์์งํ ๋งํด๋ด. ๐
์ฝ๋ ์์ ํ๊ณ ๋์ ์๋ฒ์ ์ง์ SSH ์ ์ํด์ git pull ํ๊ณ , npm run build ๋๋ฆฌ๊ณ , ์๋น์ค ์ฌ์์ํ๊ณ ... ์ด ๋ฐ๋ณต ์์
์ง์ง ์ง์น์ง ์์? ๊ฐ๋ฐ์๊ฐ ์ด๋ฐ ๋จ์ ๋ฐ๋ณต ์์
์ ์๊ฐ์ ์๋ ๊ฑด ์์งํ ๋๋ฌด ์๊น์์.
๊ทธ๋์ ์ค๋์ AWS CodeBuild์ AWS CodeDeploy๋ฅผ ์กฐํฉํด์ ์์ ์๋ํ๋ CI/CD ํ์ดํ๋ผ์ธ์ ๊ตฌ์ฑํ๋ ๋ฐฉ๋ฒ์ ๋ฑ๋ฑ์ด ํํค์ณ๋ณผ ๊ฑฐ์ผ. ์ฝ๋ ํธ์ ํ ๋ฒ์ผ๋ก ๋น๋ โ ํ ์คํธ โ ๋ฐฐํฌ๊น์ง ์๋์ผ๋ก ๋์๊ฐ๋ ๊ทธ ์พ๊ฐ, ํ ๋ฒ ๋ง๋ณด๋ฉด ์ ๋ ์์ ์ผ๋ก ๋ชป ๋์๊ฐ๊ฑฐ๋ . ๐ฅ
์ด ๊ธ์์๋ ๋จ์ํ "์ด๋ ๊ฒ ํ๋ฉด ๋ผ์~" ์์ค์ด ์๋๋ผ, ๊ฐ ์๋น์ค๊ฐ ์ ๊ทธ๋ ๊ฒ ๋์ํ๋์ง, ์ด๋ค ์ค์ ์ด ์ด๋ค ์ญํ ์ ํ๋์ง ํฉํธ ๊ธฐ๋ฐ์ผ๋ก ๊ผผ๊ผผํ๊ฒ ์ค๋ช ํ ๊ฑฐ์ผ. ์ฌ๋ฅ๋ท์์ DevOps ๊ด๋ จ ์ฌ๋ฅ์ ์ฐพ์๋ณด๋ฉด ์ด๋ฐ ํ์ดํ๋ผ์ธ ๊ตฌ์ฑ ๋ฅ๋ ฅ์ด ์ผ๋ง๋ ๊ฐ์น ์๋์ง ๋ฐ๋ก ์ ์ ์์ ์ ๋๋ก, ์ค๋ฌด์์ ์์ฒญ ์ค์ํ ์คํฌ์ด๊ฑฐ๋ !
๐ง 1. ํต์ฌ ๊ฐ๋ ๋ถํฐ ์ก๊ณ ๊ฐ์
ํ์ดํ๋ผ์ธ ๊ตฌ์ฑํ๊ธฐ ์ ์, ๊ฐ ์๋น์ค๊ฐ ๋ญ ํ๋ ๋ ์์ธ์ง ์ ํํ ์๊ณ ๊ฐ์ผ ํด. ๊ฐ๋ ์์ด ๋ฐ๋ผ ํ๋ฉด ๋์ค์ ๋ญ๊ฐ ์ ๋ ๋ ์ ์ ๋๋์ง ์ ํ ๋ชจ๋ฅด๊ฑฐ๋ . ๐ค
๐จ AWS CodeBuild๋?
CodeBuild๋ AWS์์ ์ ๊ณตํ๋ ์์ ๊ด๋ฆฌํ ๋น๋ ์๋น์ค์ผ. ์ฝ๊ฒ ๋งํ๋ฉด, ์์ค ์ฝ๋๋ฅผ ๋ฐ์์ ์ปดํ์ผํ๊ณ , ํ ์คํธ ๋๋ฆฌ๊ณ , ๋ฐฐํฌ ๊ฐ๋ฅํ ์ํฐํฉํธ(๊ฒฐ๊ณผ๋ฌผ)๋ฅผ ๋ง๋ค์ด์ฃผ๋ ์๋น์ค์ผ.
์ค์ํ ํน์ง๋ค์ ์ ๋ฆฌํ๋ฉด:
๋น๋ ์๋ฒ๋ฅผ ์ง์ ๊ด๋ฆฌํ ํ์ ์์ด. AWS๊ฐ ์์์ ๋น๋ ํ๊ฒฝ์ ํ๋ก๋น์ ๋ํ๊ณ , ๋น๋ ๋๋๋ฉด ์๋์ผ๋ก ์ ๋ฆฌํด์ค. Jenkins์ฒ๋ผ ๋น๋ ์๋ฒ ์ ์ง๋ณด์์ ์ ๊ฒฝ ์ธ ํ์ ์๋ค๋ ๊ฑฐ์ง.
โก buildspec.yml ๊ธฐ๋ฐ ๋์
๋น๋ ๋ช ๋ น์ด๋ค์
buildspec.yml ํ์ผ์ ์ ์ํด. ์ด ํ์ผ์ด CodeBuild์ "๋ ์ํผ"์ผ. ์ด๋ค ํ๊ฒฝ์์, ์ด๋ค ๋ช
๋ น์ด๋ฅผ, ์ด๋ค ์์๋ก ์คํํ ์ง ๋ค ์ฌ๊ธฐ์ ์ ์ด.โข ๋ค์ํ ๋ฐํ์ ์ง์
Java, Python, Node.js, Ruby, Go, Android, Docker ๋ฑ ๊ฑฐ์ ๋ชจ๋ ์ธ์ด/ํ๊ฒฝ์ ์ง์ํด. ์ปค์คํ Docker ์ด๋ฏธ์ง๋ ์ฌ์ฉ ๊ฐ๋ฅ.
โฃ ๋ณ๋ ฌ ๋น๋ ์ง์
์ฌ๋ฌ ๋น๋๋ฅผ ๋์์ ์คํํ ์ ์์ด. ๋น๋ ํ์์ ๊ธฐ๋ค๋ฆฌ๋ ์๊ฐ์ด ์ค์ด๋ค์ง.
๐ AWS CodeDeploy๋?
CodeDeploy๋ AWS์์ ์ ๊ณตํ๋ ์์ ๊ด๋ฆฌํ ๋ฐฐํฌ ์๋น์ค์ผ. EC2 ์ธ์คํด์ค, Lambda ํจ์, ECS ์ปจํ ์ด๋ ๋ฑ ๋ค์ํ ์ปดํจํ ํ๊ฒฝ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์๋์ผ๋ก ๋ฐฐํฌํด์ค.
- In-Place ๋ฐฐํฌ: ๊ธฐ์กด ์๋ฒ์์ ์ง์ ์ ๋ฐ์ดํธ (๋ค์ดํ์ ๋ฐ์ ๊ฐ๋ฅ)
- Blue/Green ๋ฐฐํฌ: ์ ํ๊ฒฝ์ ๋ง๋ค๊ณ ํธ๋ํฝ์ ์ ํ (๋ฌด์ค๋จ ๋ฐฐํฌ)
- Canary ๋ฐฐํฌ: ์ผ๋ถ ํธ๋ํฝ๋ง ์ ๋ฒ์ ์ผ๋ก ๋ณด๋ด์ ๊ฒ์ฆ
- Linear ๋ฐฐํฌ: ์ผ์ ๋น์จ์ฉ ์ ์ง์ ์ผ๋ก ํธ๋ํฝ ์ ํ
โก appspec.yml ๊ธฐ๋ฐ ๋์
๋ฐฐํฌ ๊ณผ์ ์
appspec.yml ํ์ผ์ ์ ์ํด. ์ด๋ค ํ์ผ์ ์ด๋์ ๋ณต์ฌํ ์ง, ๋ฐฐํฌ ์ ํ์ ์ด๋ค ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ ์ง ์ฌ๊ธฐ์ ์ ์ด.โข ๋กค๋ฐฑ ์ง์
๋ฐฐํฌ ์คํจ ์ ์๋์ผ๋ก ์ด์ ๋ฒ์ ์ผ๋ก ๋กค๋ฐฑํ ์ ์์ด. ์ค์ํด๋ ๋น ๋ฅด๊ฒ ๋ณต๊ตฌ ๊ฐ๋ฅ.
โฃ CodeDeploy Agent
EC2 ๋ฐฐํฌ์ ๊ฒฝ์ฐ, ๋์ ์๋ฒ์ CodeDeploy Agent๊ฐ ์ค์น๋์ด ์์ด์ผ ํด. ์ด ์์ด์ ํธ๊ฐ ์ค์ ๋ฐฐํฌ ์์ ์ ์ํํด.
โ๏ธ AWS 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
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
๐ 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/**/*
๐ ๊ฐ ์น์ ์์ธ ์ค๋ช
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์ ํ์ฌ ๋น๋ ์ค์ธ ์์ค์ ์ปค๋ฐ ํด์์ผ. ์ด๊ฑธ ์ด๋ฏธ์ง ํ๊ทธ๋ก ์ฌ์ฉํ๋ฉด ์ด๋ค ์ปค๋ฐ์ด ์ด๋ค ์ด๋ฏธ์ง์ธ์ง ์ถ์ ํ๊ธฐ ์ฌ์!
โ 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์ ๋ฐฐํฌ ํ ์ ์ ํด์ง ์์๋๋ก ์คํ๋ผ. ์ด ์์๋ฅผ ์ดํดํด์ผ ๊ฐ ์คํฌ๋ฆฝํธ๋ฅผ ์ด๋์ ๋ฐฐ์นํ ์ง ์ ์ ์์ด:
๐ ๋ฐฐํฌ ์คํฌ๋ฆฝํธ ์์
์ค์ ๋ก ์ฌ์ฉํ๋ ๋ฐฐํฌ ์คํฌ๋ฆฝํธ๋ค์ ์ดํด๋ณด์. 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
exit 1์ ๋ฐํํ๋ฉด CodeDeploy๊ฐ ์๋์ผ๋ก ๋ฐฐํฌ๋ฅผ ์คํจ๋ก ์ฒ๋ฆฌํ๊ณ ๋กค๋ฐฑ์ ์์ํด. ํฌ์ค์ฒดํฌ ๋ก์ง์ ์ ์์ฑํด๋๋ฉด ์๋ชป๋ ๋ฐฐํฌ๊ฐ ํ๋ก๋์
์ ๋ฐ์๋๋ ๊ฑธ ๋ง์ ์ ์์ด!
๐จ 5. CodeBuild ํ๋ก์ ํธ ์์ฑํ๊ธฐ
์ด์ ์ค์ ๋ก CodeBuild ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์ด๋ณด์. AWS ์ฝ์์์ ํ๋ ๋ฐฉ๋ฒ๊ณผ AWS CLI๋ก ํ๋ ๋ฐฉ๋ฒ ๋ ๊ฐ์ง๋ฅผ ๋ชจ๋ ์์๋ณผ ๊ฑฐ์ผ.
๐ฅ๏ธ AWS ์ฝ์์์ CodeBuild ํ๋ก์ ํธ ์์ฑ
ํ๋ก์ ํธ ์ด๋ฆ์ ์ ๋ ฅํด. ๋์ค์ CodePipeline์์ ์ฐธ์กฐํ ์ด๋ฆ์ด๋๊น ์๋ฏธ ์๊ฒ ์ง์ด.
์์ค ๊ณต๊ธ์๋ฅผ ์ ํํด. CodeCommit, GitHub, GitHub Enterprise, Bitbucket, S3 ์ค ์ ํ ๊ฐ๋ฅ. GitHub ์ฐ๊ฒฐ ์ OAuth ๋๋ ๊ฐ์ธ ์ก์ธ์ค ํ ํฐ์ผ๋ก ์ธ์ฆํด์ผ ํด.
- ํ๊ฒฝ ์ด๋ฏธ์ง: ๊ด๋ฆฌํ ์ด๋ฏธ์ง (AWS ์ ๊ณต) ๋๋ ์ปค์คํ ์ด๋ฏธ์ง
- ์ด์ ์ฒด์ : Amazon Linux 2, Ubuntu, Windows Server
- ๋ฐํ์: Standard
- ์ด๋ฏธ์ง:
aws/codebuild/standard:7.0 (์ต์ ๋ฒ์ ์ฌ์ฉ ๊ถ์ฅ)- ํ๊ฒฝ ์ ํ: Linux (์ผ๋ฐ์ ์ผ๋ก ์ด๊ฑธ ์ ํ)
- ๊ถํ ์์: Docker ๋น๋๊ฐ ํ์ํ๋ฉด ์ฒดํฌ โ
์์ ๋ง๋ CodeBuild ์๋น์ค ์ญํ ์ ์ ํํ๊ฑฐ๋, ์ ์ญํ ์ ์๋ ์์ฑํ ์ ์์ด. ์๋ ์์ฑ ์ ๊ธฐ๋ณธ ๊ถํ๋ง ๋ถ์ฌ๋๋๊น S3 ์ ๊ทผ ๋ฑ ์ถ๊ฐ ๊ถํ์ ๋์ค์ ์ง์ ์ถ๊ฐํด์ผ ํด.
- buildspec ํ์ผ ์ฌ์ฉ: ์์ค ์ฝ๋ ๋ฃจํธ์ buildspec.yml ์๋ ์ฌ์ฉ (๊ถ์ฅ)
- ๋น๋ ๋ช ๋ น ์ฝ์ : ์ฝ์์์ ์ง์ ๋ช ๋ น์ด ์ ๋ ฅ (๊ฐ๋จํ ๊ฒฝ์ฐ)
- ์ ํ: 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
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"
}
}
]
}
๐ 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 ์ ๋ฌธ๊ฐ๋ค์ด ๊ณต์ ํ๋ ๋ ธํ์ฐ ์์ค์ ํ๋ค์ ๋ชจ์๋ดค์ด! ๐ช
๐ฟ ๋ธ๋์น๋ณ ํ์ดํ๋ผ์ธ ์ ๋ต
- ๋น๋ + ๋จ์ ํ ์คํธ + ๊ฐ๋ฐ ์๋ฒ ์๋ ๋ฐฐํฌ
- ๋น ๋ฅธ ํผ๋๋ฐฑ ๋ฃจํ (์น์ธ ๋จ๊ณ ์์)
release ๋ธ๋์น โ ์คํ ์ด์ง ํ๊ฒฝ ํ์ดํ๋ผ์ธ
- ๋น๋ + ํตํฉ ํ ์คํธ + ์คํ ์ด์ง ์๋ฒ ์๋ ๋ฐฐํฌ
- QA ํ ๊ฒ์ฆ ํ ์๋ ์น์ธ
main ๋ธ๋์น โ ํ๋ก๋์ ํ๊ฒฝ ํ์ดํ๋ผ์ธ
- ๋น๋ + ์ ์ฒด ํ ์คํธ + Blue/Green ๋ฐฐํฌ
- ์๋ ๋กค๋ฐฑ ์ค์ ํ์
โก ๋น๋ ์๋ ์ต์ ํ
node_modules, Maven .m2, Gradle ์บ์ ๋ฑ์ S3์ ์ ์ฅํด๋๋ฉด ๋ค์ ๋น๋ ๋ ์์กด์ฑ ์ค์น ์๊ฐ์ ํฌ๊ฒ ์ค์ผ ์ ์์ด. buildspec.yml์ cache ์น์
์ ํ์ฉํด.
Docker ๋น๋ ์
--cache-from ์ต์
์ ์ฌ์ฉํด์ ์ด์ ๋น๋์ ๋ ์ด์ด๋ฅผ ์ฌ์ฌ์ฉํด. ECR์ ์บ์ ์ด๋ฏธ์ง๋ฅผ ์ ์ฅํด๋๋ ๋ฐฉ์์ด์ผ.
๋ ๋ฆฝ์ ์ธ ํ ์คํธ ์ค์ํธ๋ ๋ณ๋ ฌ๋ก ์คํํด. CodeBuild์ ๋ฐฐ์น ๋น๋ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ฉด ์ฌ๋ฌ ๋น๋๋ฅผ ๋์์ ์คํํ ์ ์์ด.
์์ฃผ ์ฌ์ฉํ๋ ๋๊ตฌ๋ค์ ๋ฏธ๋ฆฌ ์ค์นํ ์ปค์คํ ๋น๋ ์ด๋ฏธ์ง๋ฅผ ๋ง๋ค์ด 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: ํ์ดํ๋ผ์ธ์ด ํธ๋ฆฌ๊ฑฐ๋์ง ์์
์์ธ: Webhook ์ค์ ๋ฌธ์ ๋๋ ๋ธ๋์น ํํฐ ์ค์ ์ค๋ฅ
ํด๊ฒฐ:
1. GitHub ์ ์ฅ์ Settings โ Webhooks์์ Webhook ์ํ ํ์ธ
2. ์ต๊ทผ Webhook ์ ์ก ๋ด์ญ์์ ์๋ต ์ฝ๋ ํ์ธ (200์ด์ด์ผ ์ ์)
3. CodePipeline ์์ค ๋จ๊ณ์ ๋ธ๋์น ์ค์ ์ด ์ค์ ํธ์ํ ๋ธ๋์น์ ์ผ์นํ๋์ง ํ์ธ
4. Webhook ์ํฌ๋ฆฟ ํ ํฐ์ด ์ฌ๋ฐ๋ฅธ์ง ํ์ธ
โ ๋ฌธ์ 5: ๋น๋๋ ์ฑ๊ณตํ๋๋ฐ ๋ฐฐํฌ ํ ์ฑ์ด ์ ๋ธ
์์ธ: ํ๊ฒฝ ๋ณ์ ๋ฏธ์ค์ , ํฌํธ ์ถฉ๋, ์์กด์ฑ ๋ฌธ์ ๋ฑ ๋ค์
ํด๊ฒฐ:
1. EC2 ์ธ์คํด์ค์ ์ง์ SSH ์ ์ํด์ ์ฑ ๋ก๊ทธ ํ์ธ
2.
/opt/codedeploy-agent/deployment-root/ ๊ฒฝ๋ก์์ ๋ฐฐํฌ ๋ก๊ทธ ํ์ธ3. ํ๊ฒฝ ๋ณ์๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ์ค์ ๋์๋์ง ํ์ธ
4. ์ฑ ์์ ์คํฌ๋ฆฝํธ๋ฅผ ์๋์ผ๋ก ์คํํด์ ์๋ฌ ๋ฉ์์ง ํ์ธ
/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
๐ฌ ํ์ดํ๋ผ์ธ ์คํ ํ๋ฆ ์ต์ข ํ์ธ
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
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.