blue-green 배포 적용하기
☑️ 왜 필요했나?
기존 배포방식은 다음과 같았다.
GitAction
을 사용했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
name: Deploy to EC2
on:
push:
branches:
- main
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
# JDK Setting
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
# Gradle Caching
- name: Cache Gradle packages
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: $-gradle-$
restore-keys: $-gradle-
- name: Grant execute permission for gradlew
run: chmod +x gradlew
# Create application-prod.properties
- name: Create application-prod.properties
run: |
cd ./src/main/resources
touch ./application-prod.properties
echo "$PROPERTIES_PROD" > ./application-prod.properties
env:
PROPERTIES_PROD: $
# Gradle Build
- name: Build with Gradle
run: ./gradlew build -x test
# Image Tagging with Docker Meta
- name: Docker meta
id: docker_meta
uses: crazy-max/ghaction-docker-meta@v1
with:
images: teambpm2023/dnd-bpm
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
# DockerHub Login
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: $
password: $
- name: Docker build & push
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
platforms: linux/amd64
push: true
tags: $
labels: $
- name: create remote directory
uses: appleboy/ssh-action@master
with:
host: $
username: ubuntu
key: $
script: mkdir -p /home/ubuntu/srv/ubuntu
- name: copy source via ssh key
uses: burnett01/rsync-deployments@4.1
with:
switches: -avzr --delete
remote_path: /home/ubuntu/srv/ubuntu/
remote_host: $
remote_user: ubuntu
remote_key: $
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@master
with:
host: $
username: ubuntu
key: $
script: |
cd /home/ubuntu/srv/ubuntu
sh ./config/scripts/docker-install.sh
sudo docker stop $(sudo docker ps -a -q)
sudo docker rm $(sudo docker ps -a -q)
sudo docker rmi $(sudo docker images -q)
sudo docker-compose -f docker-compose.yml pull
sudo docker-compose -f docker-compose.yml up --build -d
GitAction을 통해 빌드를한 파일을 DockerHub에 올리고, 운영서버에서 명령어로 DockerHub에 올린 빌드된 파일들을 받아서 서버를 띄우고 있었다.
근데, 결국 운영서버에서 빌드하고 다시 띄우는데 시간이 걸렸고 안드로이드 개발자들한테 수정사항이 생기면
“배포 지금 다 했고, 운영서버 뜰 때까지 좀 기다리셔야해요.” 라고 말하는 버릇이 생겼다.
그래서 무중단 배포에 관심이 생겼고 적용해야겠다고 생각했다.
😕 왜 블루-그린 배포를 선택했는가?
블루-그린 배포 방식 말고도 카나리 배포, 롤링 배포가 있다.
그 둘을 적용하지 않고 블루-그린을 배포한 이유는 다음과 같았다.
카나리 배포를 선택하지 않은 이유
- 카나리 배포 방식의 장점인 조금씩 배포해가며, 위험을 빠르게 감지할 필요가 없다고 생각했다.
- 카나리 배포 방식의 인프라 구성방식이 복잡하다고 하여, 이는 주어진 일정내에 빠르게 적용할 수 없다고 생각했다.
롤링 배포를 선택하지 않은 이유
- 롤링 배포 방식의 단점중 하나인 데이터 일관성 문제가 생길 수 있다는 것에 끌리지 않았다.
🔅 해결 과정
로드 밸런서를 두지 않았다.
앱 자체가 서비스를 안 했기에 로드밸런서로 트래픽 관리를 할 필요가 없다고 생각했다. 실제 앱이 출시되고, 트래픽을 감당할 수 없는 상황에 로드 밸런서를 놓을 생각이다.
Flow
내가 생각한 플로우는 기존 코드는 최대한 덜 건드리고 적용하고 싶었기에 다음과 같이 생각했다.
- port를 다르게 설정한 docker-compose파일을 blue용, green용으로 둔다.
green
1
2
3
4
5
6
7
8
9
10
version: '3'
services:
web-green:
container_name: web-green
image: teambpm2023/dnd-bpm:main
expose:
- "8082"
ports:
- "8082:8080"
blue
1
2
3
4
5
6
7
8
9
10
version: '3'
services:
web-blue:
container_name: web-blue
image: teambpm2023/dnd-bpm:main
expose:
- "8081"
ports:
- "8081:8080"
- 배포스크립트를 짜는데, 이 compose파일들에 저장된 정보들을 이용해서 DockerHub에서 이미지를 가져오고 빌드시킨다.
- 기존에 존재했던 서버(green이나 blue)와 반대 서버를 띄워서 2포트를 사용하고 있게 한다.(8081, 8082포트)
nginx
설정을 건드려서 green이나 blue의 설정파일을 가리키게 만든다.(포트 변경)- 서버가 가리키는 포트가 변경되었으면, 기존 떠있던 green이나 blue의 컨테이너를 종료시킨다.
이를 그림으로 잘 표현한것은 https://jay-ji.tistory.com/m/99 이 블로그이고, 실제로 여기있는 코드들을 잘 적용해서 사용하고 있다.
그러므로 잘 이해가 안된다면 참고하시길 바랍니다.
nginx blue버전의 설정은 다음과 같이 구성했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
events {}
http{
upstream web-blue {
server localhost:8081;
}
server {
listen 80;
location / {
proxy_pass http://web-blue;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
proxy_redirect off;
}
}
}
nginx green버전의 설정이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
events {}
http{
upstream web-green{
server localhost:8082;
}
server {
listen 80;
location / {
proxy_pass http://web-green;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
proxy_redirect off;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
}
}
}
그리고, 배포 스크립트는 다음과 같이 작성했다.
EC2환경에서 실행되는거라 sudo
코드를 이용해서 작성했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/bin/bash
# Blue 를 기준으로 현재 떠있는 컨테이너를 체크한다.
EXIST_BLUE=$(sudo docker-compose -p web-blue -f docker-compose-blue.yml ps | grep Up)
echo "EXIST_BLUE value is: $EXIST_BLUE"
if [ -z "$EXIST_BLUE" ]; then
echo "blue up"
sudo docker-compose -f docker-compose-blue.yml pull
sudo docker-compose -p web-blue -f docker-compose-blue.yml up --build -d
BEFORE_COMPOSE_COLOR="green"
AFTER_COMPOSE_COLOR="blue"
else
echo "green up"
sudo docker-compose -f docker-compose-green.yml pull
sudo docker-compose -p web-green -f docker-compose-green.yml up --build -d
BEFORE_COMPOSE_COLOR="blue"
AFTER_COMPOSE_COLOR="green"
fi
sleep 10
# 새로운 컨테이너가 제대로 떴는지 확인
EXIST_AFTER=$(sudo docker-compose -p web-${AFTER_COMPOSE_COLOR} -f docker-compose-${AFTER_COMPOSE_COLOR}.yml ps | grep Up)
if [ -n "$EXIST_AFTER" ]; then
# nginx.config를 컨테이너에 맞게 변경해주고 reload 한다 -> 필요없을듯?
sudo cp ./config/nginx/nginx.${AFTER_COMPOSE_COLOR}.conf /etc/nginx/nginx.conf
sudo nginx -s reload
# 이전 컨테이너 종료
sudo docker-compose -p web-${BEFORE_COMPOSE_COLOR} -f docker-compose-${BEFORE_COMPOSE_COLOR}.yml down
echo "$BEFORE_COMPOSE_COLOR down"
fi
그리고 기존 gitAction에서 배포방식의 스크립트도 변경해줘야했다. 배포되고 deploy.sh파일을 실행하게끔 말이다.
그러므로 chmod
를 통해 권한을 주고 실행시킨다.
1
2
3
4
5
6
7
8
9
10
11
12
~~~
script: |
cd /home/ubuntu/srv/ubuntu
sh ./config/scripts/docker-install.sh
- sudo docker stop $(sudo docker ps -a -q)
- sudo docker rm $(sudo docker ps -a -q)
- sudo docker rmi $(sudo docker images -q)
- sudo docker-compose -f docker-compose.yml pull
- sudo docker-compose -f docker-compose.yml up --build -d
+ sh chmod +x ./deploy.sh
+ sh ./deploy.sh
🤔 어려웠던 점, 아쉬운 점
어려웠던 점으로는 환경부분이 컸다.
내 노트북은 mac이고, 데스크탑은 window11 운영체제를 가지고 있어서 로컬에서 테스트하기 좀 어려웠다.
왜냐하면 Dockerhub에 올라간 파일은 arm/64운영체제에서 돌아가게끔 되어있었기 때문이다.. 운영환경인 ubuntu와 셋 다 운영체제가 달라서 도커허브에 이미지를 올리거나 받아오는 작업을 운영환경속에서 확인해야했다.
그 외에는 어려웠던건 없었다.
아쉬웠던 점으로는 nginx추가 설정에 있다.
일단 지금 운영환경에 nginx
가 무조건 떠 있는걸 가정하고 배포를 진행하였는데, 이에 대한 체크도 스크립트에서 작성이 필요하다고 느꼈다.
이는 추후 작업할 것이다.
😀 배운 점
확실히 직접 구성해보니, 왜 필요하고 왜 이런 배포방식을 구성했는지에 대해 깊게 생각하는 계기가 되었다.
그리고, 기존 배포방식에서 많은 코드를 건들지 않도록 바꾸니 어떻게 하면 배포방식들을 내 프로젝트에 자연스럽게 스며들게 할 지 고민할 수도 있었다.
blue-green 배포 쉬운데?