AWS CodeDeploy를 사용해 Blue/Green 배포하는 방법을 소개하는 글입니다.
다른 국내, 해외 블로그 등에 많이 나와있지만, 처음부터 끝까지 알려주는 글이 없어 직접 작성하게 되었습니다.
이 글과 함께
이 영상을 보면서 구현하시는 것을 추천합니다.
영상에는 VPC 및 Subnet, IAM 설정의 일부가 없습니다.
VPC에 대한 설명을 제외하고 구현하려는 방향은 위의 다이어그램과 같습니다.
AutoScailingGroup 및 로드 밸런서에서 사용하기 위해 VPC 내 서브넷 설정이 필요합니다.
AWS 시 가입시 생성되는 기본 VPC를 사용해도 될 것 같습니다.
하지만, 헷갈릴 만한 요소를 제거하기 위해 Blue/Green 배포를 위한 VPC와 서브넷을 직접 생성하겠습니다.
AWS VPC 대시보드에 들어갑니다.
상단에 Create VPC
를 클릭합니다.
Subnet, Route Table, IG 등을 직접 설정하기 위해 VPC Only
를 선택합니다.
이름은 자유롭게 작성해도 됩니다. 간단히BlueGreen
이라고 하겠습니다.
IPv4 CIDR manual input
를 선택하고 IPv4 CIDR
을 10.0.0.0/16
으로 지정합니다.
위와 같이 설정하고 Create VPC
를 클릭합니다.
생성되고 나면 Resource map
에 VPC
와 Route tables
가 생긴 것을 확인할 수 있습니다.
( 라우팅테이블은 새로 생성하고 자동으로 생성된 것은 삭제할 예정입니다. )
왼쪽 메뉴에 Subnets
로 들어가 Create Subnet
을 클릭해 서브넷을 생성합니다.
방금 생성한 VPC를 선택하고 아래 Subnet settings
을 다음과 같이 생성합니다.
그리고, 서브넷을 하나 더 생성해야 합니다.
( 추후, Application Load Balancer를 생성할 때, 2개이상의 서브넷을 필요로 합니다. )
두번째 서브넷을 아래와 같이 생성합니다.
두 서브넷 설정은 아래와 같습니다.
Subnet_name | AZ | Subnet_CIDR_block | |
---|---|---|---|
1 | BlueGreen_Public_Subnet_1 | ap-northeast-2a | 10.0.0.0/24 |
2 | BlueGreen_Public_Subnet_2 | ap-northeast-2c | 10.0.1.0/24 |
Internet gateways
에서 Create internet gateway
를 클릭해 새 인터넷 게이트웨이를 생성합니다.
이름은 BlueGreen_IG
로 설정하겠습니다.
생성 직후 다음과 같은 화면에서 Actions
메뉴에서 Attach to VPC
를 선택해 앞서 생성한 BlueGreen
VPC 를 선택해 Attach internet gateway
를 진행합니다.
앞서 언급했듯이 자동으로 생성된 Route table이 있으나 새로 생성해서 연결하겠습니다.
Route tables
메뉴에서 Create route table
을 클릭해 새 라우팅 테이블을 생성합니다.
이름과 VPC를 각각 BlueGreen_RT
, 기존에 생성한 BlueGreen
VPC 로 설정합니다.
생성된 라우팅 테이블에서 아래 메뉴중 Routes에서 Edit routes를 통해 앞서 생성한 인터넷 게이트웨이와 연결하겠습니다.
아래와 같이 설정한후 Save changes
를 눌러 저장합니다.
이후, Subnet associations
탭으로 이동하면 Subnets without explicit associations
에 기존에 생성한 두 개의 서브넷이 보입니다. 하지만 이를 Explicit subnet associations
로 바꾸겠습니다. Edit subnet associations
를 클릭합니다.
두 서브넷을 체크표시로 바꾸고 Save associations
를 클릭합니다.
이렇게 설정한 라우팅 테이블을 Actions
에 Set main route table
을 클릭해 기본 라우팅 테이블로 설정한 후 기존에 자동으로 생성된 것을 지워줍니다.
그러면 Your VPCs 메뉴에서 생성한 VPC의 Resource map에 다음과 같이 설정되어 있는 것을 보실 수 있습니다.
앞으로 사용하게 될 두개의 보안그룹을 생성해야 합니다.
하나는 로드밸런서가 TCP 80번 포트로 오는 요청 (HTTP 요청)을 받기 위함이고,
다른 하나는 EC2 인스턴스가 로드밸런서로 부터 들어온 80 번 요청만 받아서 처리하기 위함입니다.
Security Groups
에서 Create security group
을 눌러 새 보안그룹을 생성합니다.
로드밸런서를 위한 보안그룹 이름을 BlueGreen LB SG
라고 하겠습니다.
VPC는 BlueGreen
을 선택해줍니다.
또 하나의 보안그룹을 생성해줍니다.
인스턴스를 위한 보안그룹으로 이름은 BlueGreen APP SG
라고 하고
인바운드 규칙을 아래와 같이 설정합니다.
HTTP 요청을 방금 위에서 생성한 BlueGreen LB SG
를 선택하면 됩니다.
위 SSH 연결을 열어놓은 것은 잠시 후 EC2 AMI를 생성하기 위함입니다.
모든 설정이 끝나면 해당 인바운드 규칙을 삭제해야 합니다.
여기까지 해서 VPC 및 서브넷 설정이 완료되었습니다.
IAM
으로 이동해 EC2 역할을 생성합니다.
Create role
을 클릭하고 AWS service
선택 및
Use case
에서 EC2 를 선택하고 다음으로 넘어갑니다.
Add permissions
에 AmazonEC2RoleforAWSCodeDeploy
를 검색해 추가해줍니다.
다음으로 넘어가 이름을 BlueGreenEC2Role
로 설정하겠습니다.
설정한 후 역할을 생성하면 위와 같이 생성됩니다.
CodeDeploy Application 을 생성할 때 필요한 Service role을 생성합니다.
Create role
을 클릭하고 AWS service
선택한 후 아래와 같이 설정하고 넘어갑니다.
이름은 BlueGreenCodeDeployServiceRole
로 한 후 생성해줍니다.
CodeDeploy 의 블루/그린 배포 방식은 정책을 더 추가해야 합니다.
새로 생성된 역할로 들어가서 Add permissions
를 클릭하고 인라인 편집기를 이용해 다음과 같이 작성합니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"iam:PassRole",
"ec2:CreateTags",
"ec2:RunInstances"
],
"Resource": "*"
}
]
}
새 정책이름은 간단히 BlueGreenPolicy
라고 하겠습니다.
완료 후 정책이 위와 같이 추가된 것을 확인할 수 있습니다.
로드밸런서 생성에 앞서 대상 그룹을 생성해야 합니다.
EC2
- Target groups
에서 새 대상 그룹을 생성합니다.
이 때, Health checks
에서 path
는 자신의 어플리케이션에 health check이 가능한 경로로 설정하시면 됩니다.
EC2
- Load Balancing
탭 - Load balancers
에서 새 로드밸런서를 생성할 수 있습니다.
아래와 같은 화면이 나오면 우리가 사용할 것이 ALB 이므로 가장 좌측의 ALB를 선택합니다.
그 이후 설정들은 아래와 같이 진행합니다.
이 때, 서브넷을 두개 이상 선택해야 생성할 수 있습니다.
따로 인증서 등 설정은 하지 않아 HTTP 만 설정하도록 하겠습니다.
이렇게 로드밸런서 생성을 완료했습니다.
시작 템플릿(Launch Template)을 만들기 위해 AMI
가 필요합니다. AMI
를 만들어보도록 하겠습니다.
우선 EC2 대시보드로 이동해 인스턴스를 하나 생성하겠습니다.
이 글에서는Ubuntu 20.04
로 t2.micro
로 시작하겠습니다.
Network settings
에 기존에 생성한 VPC, 서브넷, 보안 그룹 등을 아래와 같이 설정합니다.
그 아래 Advanced network configuration
도 아래와 같이 설정되어 있는지 확인해 줍니다.
아직 끝이 아닙니다!
아래로 내려가서, Advanced details
에 IAM instance profile
에 IAM
설정 단계에서 만든 BlueGreenEC2Role
을 지정합니다.
그 후 Launch instance
를 클릭해 인스턴스를 생성합니다.
이제 이 인스턴스에 접속해서 필요한 요소들을 여러분의 상황에 맞게 설치하면 됩니다.
하지만, 반드시 CodeDeploy Agent
는 설치해야 합니다.
Ubuntu 인스턴스에 CodeDeploy Agent 설치하기
EC2
- Instances
로 들어가 현재 인스턴스 목록 중 ForBlueGreenAMI
인스턴스를 선택하고 우측 위 Actions
- Image and template
- Create image
를 선택합니다.
이름은 BlueGreenAMI
으로 설정하고 이미지를 생성합니다.
EC2
- Images
- AMIs
에 들어가보면 새로 생긴 이미지를 확인할 수 있습니다.
위 버튼을 눌러 생성을 시작합니다.
이름은 또 BlueGreen
이라고 설정하겠습니다.
Image
를 앞서 생성한 BlueGreenAMI
로 지정합니다.
인스턴스 타입은 상황에 맞게 설정하시면 됩니다.
전과 마찬가지로 t2.micro
로 진행하겠습니다.
다음 Network settings
는 서브넷은 설정하지 않고
아래 보안그룹만 설정하겠습니다.
Advanced network configuration
에서는 보안그룹을 확인하고,
Public IP 자동할당을 켜줍니다.
Auto-assign public IP
를Enable
설정하지 않으면,
CodeDeploy 배포과정에서 instance에 접근하지 못하는 에러가 발생합니다.
[ERROR] The overall deployment failed because too many individual instances failed deployment, too few healthy instances are available for deployment, or some instances in your deployment group are experiencing problems.
그 외 나머지 설정은 변경하지 않고
Advanced details
에 IAM instance profile
만
앞서 만든 역할인 BlueGreenEC2Role
로 설정합니다.
EC2
- Auto Scailing gorups
로 이동합니다.
위 버튼을 클릭해 생성을 시작합니다.
Step 1 에서는
이름은 BlueGreenASG
,
시작 템플릿은 방금 위에서 만든 BlueGreen
으로 작성합니다.
Step 2 에서는
위는 그대로 두고,
아래 Network
설정에서 VPC와 AZ, 서브넷을 앞서 만든 VPC, 서브넷으로 설정합니다.
Step 3는 아래와 같이 설정합니다.
Step 4에서는 평소 유지하고 싶은 인스턴스 수를 지정할 수 있습니다.
위의 Desired Capacity를 2로 설정하면, 항상 두개의 인스턴스를 유지할 수 있습니다.
하지만 간단히 1로 설정하겠습니다.
그 이후 설정은 하지않고 생성해주겠습니다.
생성된 ASG를 확인할 수 있습니다.
ASG가 생성되고 나면 Desired Capacity를 맞추기 위해 새 인스턴스를 생성하기 시작합니다.
ASG 설정에 태그를 추가하면 새 인스턴스들의 이름을 지정할 수 있습니다.
Code Deploy
메뉴로 이동해 새 어플리케이션을 생성합니다.
이름은 한결같이 BlueGreen
으로 설정했습니다.
아래 Compute Platform
은 EC2 로 설정합니다.
위에서 어플리케이션을 생성하고 나면 위와 같은 화면이 나오는데 바로 Create deployment group
을 클릭해 배포그룹을 생성합니다.
이름과 Service role
을 설정해야 하는데
Service role
은 IAM 단계에서 생성한 BlueGreenCodeDeployServiceRole
을 선택합니다.
배포 타입은 이 글의 핵심인 Blue/green
을 선택합니다.
오토스케일링 그룹도 이전에 만든 오토스케일링 그룹으로 설정해줍니다.
새 배포가 생성되면 바로 트래픽을 리라우팅 하도록 했고,
기존 인스턴스들은 15분 후 바로 종료되도록 했습니다.
로드 밸런서는 기존에 생성한 로드밸런서를 연결합니다.
appspec.yml
을 포함한 코드 전체를 저장할 버킷을 생성합니다.
이름은 BlueGreen-[any]
로 설정합니다.
버킷이름은 중복이 허용되지 않아 제 이름 이니셜을 덧붙혔습니다.
이 글에서 배포하게 될 서버는 express.js
로 구성된 간단한 서버입니다.
/
와 /health
두가지 요청만 받아서 처리하게 됩니다.
전체 코드는 아래와 같습니다.
// index.js
const express = require("express");
const app = express();
const port = 8080;
app.get("/", (req, res) => {
res.send("Hello World!");
});
app.get("/health", (req, res) => {
res.send("OK");
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
이를 CodeDeploy
를 통해 배포하기 위해 appspec.yml
파일을 설정해 보겠습니다.
appspec
파일은 codeDeploy
의 수명 주기 이벤트 후크로 각 배포를 관리하는 데 사용되는 사양 파일입니다.
version: 0.0
os: linux
# Windows Server 인스턴스의 경우 os: windows 로 변경해야 합니다.
files:
- source: /
destination: /app
overwrite: yes
hooks:
# hooks 아래의 location은
# 위의 files - destination 을 기준으로 한 상대경로 입니다.
BeforeInstall:
- location: scripts/before_install.sh
timeout: 300
AfterInstall:
- location: scripts/after_install.sh
timeout: 300
ApplicationStart:
- location: scripts/application_start.sh
timeout: 300
각 수명 주기 이벤트 후크에 스크립트 파일을 실행시키도록 설정합니다.
이 수명 주기 이벤트에 사전 설치 작업 등과 같은 작업을 수행할 수 있습니다.
여기서는 EC2 인스턴스 내부에 nodejs
, npm
, forever
등을 설치하도록 설정 했습니다.
before_install.sh
는 아래와 같습니다.
#!/bin/bash
# install node 20.x
sudo apt-get update
sudo apt-get install build-essential
sudo apt-get install curl -y
curl -sL https://deb.nodesource.com/setup_20.x | sudo -E bash --
sudo apt-get install nodejs -y
# Install npm
sudo apt install npm -y
sudo npm install npm@latest -g
# Install forever
sudo npm install forever -g
어플리케이션 구성 또는 파일 권한 변경과 같은 작업에 사용하는 배포 수명 주기 이벤트입니다.
express
앱에 필요한 패키지를 설치하는 작업을 수행합니다.
#!/bin/bash
# move to the server directory and install the node modules
cd /app
sudo npm install
서비스를 시작하거나, ApplicationStop
으로 중지된 서비스를 다시 시작할 때 사용하는 배포 수명 주기 이벤트입니다.
express
앱을 이곳에서 forever
를 이용해 실행합니다.
#!/bin/bash
# Mapport 80 to 3000
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080
# Stop all servers and start the server
forever start /app/index.js
실제로 인스턴스로 들어오는 HTTP 요청을 8080으로 맵핑시켜 실행된 서버가 해당 요청에 대응할 수 있도록 합니다.
나중에는 nginx
의 리버스-프록시
등을 이용해야 할 것 같습니다.
Install
: CodeDeploy
에이전트가 임시 위치의 수정 버전의 파일을 최종 대상 위치로 복사합니다.ValidateService
: 실제 서비스가 시작되어서, 배포가 성공적으로 이루어졌는지 확인하는데 사용합니다.Github-Actions 까지 연결하기 전에 .zip 파일을 통해 배포해보는 과정입니다.
바로 Github-Actions 설정으로 넘어가도 무방합니다.
Github-Actions
으로 코드를 압축해 S3에 업로드하고, CodeDeploy
에서 이를 가지고 배포하는 과정을 진행하기 전에, 필요한 요소들을 압축해 수동으로 S3에 업로드 하고 AWS 콘솔 내에서 배포를 해보도록 하겠습니다.
먼저 위에서 언급했던 파일들의 구조는 아래와 같습니다.
appspec.yml은 반드시 최상위 경로에 위치해야 합니다.
(rootDir)
├── .github/workflows
│ └── ci-cd.yml
├── index.js
├── appspec.yml
├── package.json
└── scripts
├── before_install.sh
├── after_install.sh
└── application_start.sh
이 중 index.js, appspec.yml, package.json, scripts/
,
3개의 파일과 하나의 폴더를 압축해 deployment-test.zip
을 생성하겠습니다.
앞서 만든 S3 버킷에 이를 업로드합니다.
우측에 있는 S3 URI를 복사합니다.
이제 CodeDeploy
로 넘어갑니다.
위와 같이 앞서 만든 어플리케이션에 Deployment
탭으로 넘어옵니다.
여기서 Create deployment
를 클릭합니다.
아래와 같이 설정한 후 배포 생성을 클릭합니다.
배포를 생성하게 되면 아래와 같은 화면이 자동으로 켜지게 됩니다.
Step 1 :
새 인스턴스를 시작합니다.
Step 2 :
새 인스턴스에 새 리비젼의 어플리케이션을 배포합니다.
Step 3 :
기존 인스턴스로 가던 트래픽을 새로운 트래픽으로 연결하기 위해
새 인스턴스에 트래픽을 연결하고 그 이후 기존 인스턴스를 종료하는 작업을 진행합니다.
Step 4 :
새 인스턴스로 모든 트래픽을 보내고 나면,
기존 인스턴스를 설정한 시간 이후에 종료하게 됩니다.
(우측상단에 Terminate
버튼으로 조기 종료할 수 있습니다.)
이 모든 동작 이후
Load Balancer
로 들어가서 DNS name
으로 접속해보면,
위 서버가 배포가 되어있음을 알 수 있다.
그리고 EC2
내 인스턴스가 하나 실행중이고, 하나가 종료되어 있음도 확인할 수 있습니다.
앞에서 코드와 스크립트, appspec
파일 들을 .zip
으로 압축해서 S3 올리고,
CodeDeploy
에서 업로드 하는 과정을 Github-Actions
을 이용해 자동화 하겠습니다.
.github/workflows
내 ci-cd.yml
파일을 생성하고 아래와 같이 작성합니다.
on:
push:
branches: [main]
pull_request:
branches: [main]
name: CI/CD (Blue-Green Deployment)
jobs:
ci-cd:
name: CI/CD
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Compress files
env:
IMAGE_TAG: ${{ github.sha }}
run: zip -r deploy-$IMAGE_TAG.zip ./appspec.yml ./scripts ./index.js ./package.json
- name: Upload S3 bucket
env:
IMAGE_TAG: ${{ github.sha }}
run: |
aws s3 cp ./deploy-$IMAGE_TAG.zip s3://${{ secrets.AWS_S3_BUCKET_NAME }}/deploy-$IMAGE_TAG.zip
- name: Trigger CodeDeploy
env:
IMAGE_TAG: ${{ github.sha }}
run: |
aws deploy create-deployment \
--application-name ${{ secrets.AWS_CODEDEPLOY_APPLICATION_NAME }} \
--deployment-config-name CodeDeployDefault.AllAtOnce \
--deployment-group-name ${{ secrets.AWS_CODEDEPLOY_DEPLOYMENT_GROUP_NAME }} \
--s3-location bucket=${{ secrets.AWS_S3_BUCKET_NAME }},bundleType=zip,key=deploy-$IMAGE_TAG.zip
위 secrets
에 포함되는 변수들은 레포지토리 설정에서 작성해주어야 합니다.
위 yml
파일을 간단히 설명하면 아래와 같습니다.
role-to-assume
과 aws-region
으로 AWS
서비스에 로그인합니다.CodeDeploy
의 새 배포를 생성합니다.( IMAGE_TAG: ${{ github.sha }}
는 압축파일 각각을 식별하기 위해 추가했습니다. )
위와 같이 Github-Actions
에서 정상적으로 업로드 및 트리거를 했음을 확인할 수 있고,
AWS S3 버킷과, CodeDeploy, EC2 대시보드에서 결과를 확인할 수 있습니다.
모든 과정이 끝났습니다.
SSH 접근을 위해 APP_SG (보안그룹)에 설정한 인바운드 규칙(SSH, TCP port:22, from Anywhere)을 삭제해야 합니다.
EC2와 CodeDeploy와 Github-Actions를 이용해 이렇게 Blue/Green 및 CI/CD를 구현을 마쳤습니다.
저와 같은 방식으로 Blue/Green 배포 자동화를 구현하고자 하시는 분들에게 조금이나마 도움이 되면 좋겠습니다.
2주가 넘도록 시도한 끝에 성공했습니다. 많은 국내 기술 블로그, 해외 기술 블로그를 찾아봤지만, 그동안은 성공하지 못했습니다. 제 생각에 가장 큰 도움이 된 것은 참고목록 중 How to Create Blue/Green Deployments to ... 라는 AWS의 공식 유투브입니다. 위 글도 해당 영상의 구성 순으로 작성되어 있습니다. 하지만, VPC 설정, Role 에 대한 설명이 부족해 직접 찾아보고 추가했습니다. 이 글과 해당 영상을 함께 보면서 설정하시면 더 쉽게 구성할 수 있을 것 같습니다.
저처럼 AWS에 대한 이해도가 낮다고 생각하신다면, IAM Role, Policy, AWS VPC, 보안그룹에 대해 조금 더 공부하고 나서 진행하시면 훨씬 수월하게 진행하실 수 있습니다!
이렇게 자세하고 친절한 블로그 글을 발견하게 되어 도움 많이 받았습니다! 좋은 글 감사합니다 :)