0. 무중단배포? 블루그린(blue/green)?
기존에 Spring Boot → Github Actions → Docker image Build → image push to docker hub → image pull to ec2 → image run 이었던 방식에는 push 가 발생하면 서버가 업데이트 되는 동안 잠깐 서버가 내려가는 downtime이 발생한다. 따라서 서비스가 중단되지않고 유지되기 위해서 무중단배포를 진행하였다.
블루 그린 배포방법은 무중단 배포 기법의 하나다. 동작 방식은 다음과 같다.
8080포트를 blue, 8081포트를 green이라 가정한다.
blue 포트로 서비스를 실행중일때 업데이트가 일어나면 green 포트로 호스팅을 옮겨 서비스를 말그대로 번갈아가며 배포하는 방법이다. 이때 blue로 향하던 호스팅을 green으로 옮길때 두 포트를 모두 실행하고 green으로 호스팅 연결을 마치면 blue 컨테이너를 종료하여 서비스의 중단없이 배포가 이루어질 수 있다.
1. 왜 Nginx를 사용했는가? 🤷♂️
우선, EC2 프리티어로 서버를 운영할 계획이었기 때문에 nginx를 통해 한대의 서버를 이용해 배포가 가능했고, 구현하기 쉽고 추가 비용이 없었기에 사용하였다.
Nginx는 외부의 요청을 받아 서버로 요청을 전달하는 “리버스 프록시” 기능이 있다. 즉, 클라이언트는 nginx의 주소로 접속하고 nginx는 웹서버의 클라이언트 요청을 전달하는“클라이언트 → nginx → 웹서버 “
구조가 된다.
리버스프록시 : 도메인에 대한 요청을 도메인(IP)에 보내게 되면 중간에서 Nginx 리버스 프록시 서버가 해당 요청을 확인하고 알맞는 내부 서버로 데이터를 전달해주는 역할
동작 방식을 살펴보면 다음과 같다.
Github Actions, Docker, Nginx, AWS EC2를 이용하여 블루/그린 방식으로 무중단배포 CI-CD 파이프라인 구성
- Nginx가 리버스 프록시 서버 역할로, 서버로 들어오는 모든 요청을 받는다.
- 매 배포마다 8080, 8081 포트에 톰캣 서버를 번갈아가면서 하나씩 띄우고,
리버스 프록시 서버로 사용중인 nginx 웹 서버가 포트 포워딩 해주는 포트를 변경하는 식으로 ‘무중단’을 구현한다.
하나의 EC2 서버에 Nginx 1대와 스프링부트 jar 2개를 사용하는 방식이다.
클라이언트는 nginx 서비스 주소로 접속 (80 포트 or 443 포트) → nginx는 클라이언트 요청을 받아 현재 연결된 스프링 부트 1로 요청을 전달한다. 이때 연결되지 않은 스프링 부트2는 전달받지 못한다. 1.1 버전으로 신규 배포가 필요하면 nginx와 연결되지 않은 스프링 부트2로 배포가 된다. 이때, nginx는 스프링부트 1을 바라보고 있으므로 배포하는 동안 서비스가 중단되지 않는다. 배포 이후 스프링 부트2가 정상적으로 구동되는지 체크하고, nginx reload 명령어(1초 이내)를 통해 스프링 부트 2를 바라보도록 한다.
이 처럼, nginx는 서버 내부에서 트래픽을 어디로 라우팅할 것인지를 정해 요청을 전달하므로 한대의 서버에서 2개의 에플리케이션을 무중단 배포할 수 있게된다.
2. 배포의 흐름, github-actions.yml
🫠
기존 포스팅에서 했던 방법대로 github-actions.yml에서 push, pull 까지 진행해준다.
그 다음 그전까지의 과정이 성공적으로 수행했을 때, deploy.sh 스크립트를 실행한다.
github-actions.yml 파일 내용을 직접 한번 보자.
be/dev 브랜치로만 push를 통하여 배포하도록 세팅해놓은 yml파일이다.
# github repository actions 페이지에 나타날 이름
name: CI/CD using github actions & docker
# event trigger
# be/dev 브랜치에 push가 되었을 때 실행
on:
push:
branches: [ "be/dev" ]
permissions:
contents: read
jobs:
CI-CD:
runs-on: ubuntu-latest
steps:
# JDK setting - github actions에서 사용할 JDK 설정 (프로젝트나 AWS의 java 버전과 달라도 무방)
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
# gradle caching - 빌드 시간 향상
- name: Gradle Caching
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
# 환경별 yml 파일 생성(1) - application.yml
- name: make application.yml
if: |
contains(github.ref, 'main') ||
contains(github.ref, 'be/dev')
run: |
cd ./src/main/resources
touch ./application.yml
echo "${{ secrets.YML }}" > ./application.yml
shell: bash
# 환경별 yml 파일 생성(2) - dev
- name: make application-dev.yml
if: contains(github.ref, 'be/dev')
run: |
cd ./src/main/resources
touch ./application-dev.yml
echo "${{ secrets.YML_DEV }}" > ./application-dev.yml
shell: bash
# gradle build - name: Build with Gradle
run: ./gradlew build -x test
# docker build & push to develop
- name: Docker build & push to dev
if: contains(github.ref, 'be/dev')
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker build -t ${{ secrets.DOCKER_USERNAME }}/deliveryrepository_dev .
docker push ${{ secrets.DOCKER_USERNAME }}/deliveryrepository_dev
## deploy to dev
- name: Deploy to dev
uses: appleboy/ssh-action@master
id: deploy-dev
if: contains(github.ref, 'be/dev')
with:
host: ${{ secrets.HOST_DEV }} # EC2 퍼블릭 IPv4 DNS username: ${{ secrets.USERNAME }} # ubuntu
port: 22
key: ${{ secrets.PRIVATE_KEY }}
envs: GITHUB_SHA
script: |
sudo docker ps
docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)
sudo docker pull ${{ secrets.DOCKER_USERNAME }}/deliveryrepository_dev
docker-pull-and-run:
runs-on: [self-hosted, dev]
if: ${{ needs.CI-CD.result == 'success' }}
needs: [ CI-CD ]
steps:
- name : 배포 스크립트 실행
run: |
sh /deploy.sh
sudo docker image prune -f
앞선 포스팅들과 달라지는 부분은 밑의 두 단이다.
## deploy to dev
- name: Deploy to dev
uses: appleboy/ssh-action@master
id: deploy-dev
if: contains(github.ref, 'be/dev')
with:
host: ${{ secrets.HOST_DEV }} # EC2 퍼블릭 IPv4 DNS username: ${{ secrets.USERNAME }} # ubuntu
port: 22
key: ${{ secrets.PRIVATE_KEY }}
envs: GITHUB_SHA
script: |
sudo docker ps
docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)
sudo docker pull ${{ secrets.DOCKER_USERNAME }}/deliveryrepository_dev
기존의 deploy to dev 에서 run ~ 부분을 빼고 docker image를 pull 받아오는것 까지만 수행한다.
왜냐면 다음부분에서 deploy.sh 스크립트 실행으로 컨테이너를 실행하기 때문.
docker-pull-and-run:
runs-on: [self-hosted, dev]
if: ${{ needs.CI-CD.result == 'success' }}
needs: [ CI-CD ]
steps:
- name : 배포 스크립트 실행
run: |
sh /deploy.sh
sudo docker image prune -f
부분부분 살펴보고 종합해보면 다음과 같이 동작한다.
CI-CD 파트의 수행결과가 성공이면 deploy.sh 스크립트를 수행한다.
docker image prune -f 는 혹시모를 불필요한 이미지 리소스를 제거하기 위해 넣었다.
3. 배포 스크립트 세팅 🌟
3-1) deploy.sh
ubuntu ec2 서버에 deploy.sh 를 넣어놔야한다.
터미널로 ubuntu ec2 서버에 접속하고
cd /
를 입력하면 root 디렉토리로 이동한다.
sudo vim deploy.sh
를 입력해서 root 위치에 deploy.sh 파일을 세팅한다.
파일의 내용은 아래와 같다.I
를 입력한 후, 아래 내용을 입력하고 esc
-> :wq
로 저장하고 나오면된다.
#1
EXIST_BLUE=$(sudo docker-compose -p test-blue -f /docker-compose.blue.yml ps | grep Up)
if [ -z "$EXIST_BLUE" ];
then
echo "BLUE 컨테이너 실행"
sudo docker-compose -p test-blue -f /docker-compose.blue.yml up -d
BEFORE_COLOR="green"
AFTER_COLOR="blue"
BEFORE_PORT=8081
AFTER_PORT=8080
else
echo "GREEN 컨테이너 실행"
sudo docker-compose -p test-green -f /docker-compose.green.yml up -d
BEFORE_COLOR="blue"
AFTER_COLOR="green"
BEFORE_PORT=8080
AFTER_PORT=8081
fi
echo "${AFTER_COLOR} server up(port:${AFTER_PORT})"
# 2
for cnt in {1..10}
do
echo "서버 응답 확인중(${cnt}/10)";
UP=$(curl -s http://127.0.0.1:${AFTER_PORT}/health-check)
if [ "${UP}" != "up" ]
then
sleep 10
continue
else
break
fi
done
if [ $cnt -eq 10 ]
then
echo "서버가 정상적으로 구동되지 않았습니다."
exit 1
fi
# 3
sudo sed -i "s/${BEFORE_PORT}/${AFTER_PORT}/" /etc/nginx/conf.d/service-url.inc
sudo nginx -s reload
echo "Deploy Completed!!"
# 4
echo "$BEFORE_COLOR server down(port:${BEFORE_PORT})"
sudo docker-compose -p test-${BEFORE_COLOR} -f /docker-compose.${BEFORE_COLOR}.yml down
ls
명령어를 입력해 root위치에 deploy.sh 파일이 잘 들어갔는지 확인하자.
다음으로 deploy.sh 의 코드들을 파트별로 나눠보면 다음과 같다.
현재 활성화된 컨테이너 확인 -> 새로운 컨테이너 실행
#1
EXIST_BLUE=$(sudo docker-compose -p test-blue -f /docker-compose.blue.yml ps | grep Up)
if [ -z "$EXIST_BLUE" ];
then
echo "BLUE 컨테이너 실행"
sudo docker-compose -p test-blue -f /docker-compose.blue.yml up -d
BEFORE_COLOR="green"
AFTER_COLOR="blue"
BEFORE_PORT=8081
AFTER_PORT=8080
else
echo "GREEN 컨테이너 실행"
sudo docker-compose -p test-green -f /docker-compose.green.yml up -d
BEFORE_COLOR="blue"
AFTER_COLOR="green"
BEFORE_PORT=8080
AFTER_PORT=8081
fi
echo "${AFTER_COLOR} server up(port:${AFTER_PORT})"
이 부분은 동작이 어떻게 되는지 좀 더 자세히 뜯어서 살펴보자.
- EXIST_BLUE 확인
EXIST_BLUE=$(sudo docker-compose -p test-blue -f /docker-compose.blue.yml ps | grep Up)
“/docker-compose.blue.yml” 파일에서 정의된 "test-blue"라는 이름의 Docker 컨테이너가 현재 실행 중인지 확인한다. 결과는 $EXIST_BLUE
변수에 저장된다.
- Blue 컨테이너 설정
if [ -z "$EXIST_BLUE" ];
then
echo "BLUE 컨테이너 실행"
sudo docker-compose -p test-blue -f /docker-compose.blue.yml up -d
BEFORE_COLOR="green"
AFTER_COLOR="blue"
BEFORE_PORT=8081
AFTER_PORT=8080
만약 $EXIST_BLUE
변수가 비어있다면, 이는 Blue 컨테이너가 실행 중이 아니라는 것을 의미한다.
이 경우 스크립트는 "BLUE 컨테이너 실행"을 출력하고 Blue 환경을 설정한다.
BEFORE_COLOR
: 업데이트 전 환경의 색상 (이 경우 “green”).AFTER_COLOR
: 업데이트 후 환경의 색상 (이 경우 “blue”).BEFORE_PORT
: 업데이트 전 환경이 사용하는 포트 (이 경우 8081).AFTER_PORT
: 업데이트 후 환경이 사용하는 포트 (이 경우 8080).sudo docker-compose -p test-blue -f /docker-compose.blue.yml up -d
명령은 Blue 컨테이너를 백그라운드 모드로 실행한다.- Green 컨테이너 설정
else
echo "GREEN 컨테이너 실행"
sudo docker-compose -p test-green -f /docker-compose.green.yml up -d
BEFORE_COLOR="blue"
AFTER_COLOR="green"
BEFORE_PORT=8080
AFTER_PORT=8081
fi
만약 $EXIST_BLUE
변수가 비어있지 않다면, 이는 Blue 컨테이너가 실행 중이라는 것을 의미한다.
이 경우 스크립트는 "GREEN 컨테이너 실행"을 출력하고 Green 환경을 설정하며, 변수 할당 및 Green 컨테이너를 시작한다.
- 컨테이너 상태 출력
echo "${AFTER_COLOR} server up(port:${AFTER_PORT})"
선택된 컨테이너 환경의 색상과 해당 포트가 실행 중인지를 나타내는 메시지를 출력한다.
서버 응답 확인
10번의 시도를 통해 서버가 올바르게 실행되고 있는지 확인한다.
# 2
for cnt in {1..10}
do
echo "서버 응답 확인중(${cnt}/10)";
UP=$(curl -s http://127.0.0.1:${AFTER_PORT}/health-check)
if [ "${UP}" != "up" ]
then
sleep 10
continue
else
break
fi
done
if [ $cnt -eq 10 ]
then
echo "서버가 정상적으로 구동되지 않았습니다."
exit 1
fi
새로운 컨테이너
Nginx 구성 업데이트 및 reload
Nginx 구성 파일을 업데이트하고 Nginx를 다시 로드하여 변경 사항을 적용한다.
# 3
sudo sed -i "s/${BEFORE_PORT}/${AFTER_PORT}/" /etc/nginx/conf.d/service-url.inc
sudo nginx -s reload
echo "Deploy Completed!!"
이전 컨테이너 정리
새 컨테이너가 실행되면 이전 환경을 종료한다.
# 4
echo "$BEFORE_COLOR server down(port:${BEFORE_PORT})"
sudo docker-compose -p test-${BEFORE_COLOR} -f /docker-compose.${BEFORE_COLOR}.yml down
이제 다음으로 docker-compose.blue, green.yml 파일들을 만들어줘야한다.
3-2) docker-compose.xxx.yml
우선 해당 파일들을 만들고 실행하기 위해서는 docker-compose 설치가 필요하다.
sudo apt install docker-compose
위 명령어로 간단하게 설치를 끝낼 수 있다.
설치 한 후에 이어서 docker-compose.{원하는파일명}.yml
을 만들어주면 된다.
만드는 방법은 똑같이 root 경로에서 sudo vim {파일명}
을 입력하여 만들어주면 된다.
- docker-compose.blue.yml
version: '3.1'
services:
api:
image: 95hyun/deliveryrepository_dev:latest
container_name: test-blue
environment:
- LANG=ko_KR.UTF-8
- HTTP_PORT=8080
ports:
- '8080:8080'
- docker-compose.green.yml
version: '3.1'
services:
api:
image: 95hyun/deliveryrepository_dev:latest
container_name: test-green
environment:
- LANG=ko_KR.UTF-8
- HTTP_PORT=8081
ports:
- '8081:8080'
코드의 내용은 같기 때문에 docker-compose.blue.yml 을 기준으로 살펴보면 다음과 같다.
version: '3.1'
: 사용 중인 Docker Compose 파일 구문의 버전을 지정. 이 경우 버전 3.1services:
: 이는 애플리케이션을 구성하는 다양한 서비스(컨테이너)를 정의하는 키image: 95hyun/deliveryrepository_dev:latest
: “api” 서비스에 사용할 Docker 이미지를 지정한다. 이 경우 Docker Hub에서 "95현/deliveryrepository_dev"라는 이미지의 최신 버전을 가져온다.container_name: test-blue
: 컨테이너 이름을 "test-blue"로 설정.environment:
: 실행 중인 컨테이너에 사용할 수 있는 환경 변수를 정의.- LANG=ko_KR.UTF-8
:LANG
환경 변수를 "ko_KR.UTF-8"로 설정. 이는 컨테이너의 로케일을 정의한다.- HTTP_PORT=8080
:HTTP_PORT
환경 변수를 "8080"으로 설정한다. 이는 컨테이너 내부의 애플리케이션이 수신 대기하는 포트를 적어준다.- '8080:8080'
: 호스트의 포트 8080을 컨테이너 내부의 포트 8080에 매핑한다. 즉, 호스트 시스템에서 포트 8080에 액세스하면 “api” 컨테이너 내부의 포트 8080으로 전달된다.
4. Github Actions self-hosted runner
깃허브 액션이 원격으로 조종할 수 있도록 Github Actions self-hosted runner가 필요하다.
github actions를 통한 CI/CD가 진행되기 원하는 저장소에서Settings - Actions - Runners - Add runner
메뉴로 접근한다.
해당 프로젝트에서는 ubuntu ec2를 사용하므로 Linux로 선택한다.
그리고 Download 파트에 순서대로 적혀있는 명령어들을 ubuntu ec2 터미널에 복사, 붙여넣기 한다.
이어서 Configure 파트에 ubuntu ec2 터미널에서 순서대로 명령어를 복사, 붙여넣기 하면 토큰등록을 통해 설정, 실행할 수 있다.
name of runner
설정은 Settings - Actions - Runners
메뉴에서 runner를 식별하기 위한 설정이다. 간단하게 Runner의 이름이라고 생각하면 된다.
additional lables
설정은 Runner의 추가 라벨이다. 잠시 후 CI/CD 작업을 위한 yml 파일작성시 runner를 식별하기 위해 이름이 아닌 라벨을 사용하므로, 본 프로젝트에서는 ‘dev’ 라벨을 추가해줬다. 이 과정에서 라벨을 혹시 추가하지 못했더라도 Settings > Actions > Runners
에서 설정할 수 있다.
연결이 성공적으로 완료되었다면 ./run.sh
명령어 입력시 actions runner가 실행되면서 CI/CD 작업 요청을 기다리는 대기상태로 진입된 것을 확인할 수 있다.
추가적으로, nohup ./run.sh &
명령어를 통하여 터미널을 종료해도 백그라운드에서 runner가 돌아가도록 하자.
5. Nginx
5-1) Nginx 설치, 실행
sudo apt update
sudo apt install nginx
설치 끝.
잘 설치되었다면 cd /etc
경로로 이동하여 nginx 디렉토리가 잘 생성되었는지 확인까지하자.
sudo service nginx start // nginx 실행 시작
sudo service nginx status // nginx 상태 확인
start
하면 실행, status
로 확인하면 아래처럼 active
되어있는지 확인이 가능하다.
종료 명령어는 다음과 같다.
sudo service nginx stop // nginx 실행 종료
설정 파일은 /etc/nginx/sites-available
에 있는 default
이다.
cd /etc/nginx/sites-available
로 해당 경로로 이동하여 sudo vim default
통해서 수정할 수 있다. insert 모드 아닌 일반모드에서 ggdG
입력하면 전체내용을 한번에 삭제할 수 있다.
삭제 후, 아래 코드를 입력하자.
server {
include /etc/nginx/conf.d/service-url.inc;
listen 80 default_server;
listen [::]:80 default_server;
gzip on;
gzip_types application/javascript application/octet-stream;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
gzip_static on;
root /usr/share/nginx/dist;
index index.html index.htm;
try_files $uri $uri/ $uri /index.html;
}
location /api {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, PATCH, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
add_header 'Access-Control-Max-Age' 86400;
return 204;
}
add_header 'Access-Control-Allow-Origin' '*' always;
proxy_pass $service_url;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
}
}
이 코드에서 중요한 부분들 위주로 뽑아서 살펴보자.
기본 설정
server {
include /etc/nginx/conf.d/service-url.inc;
listen 80 default_server;
listen [::]:80 default_server;
gzip on;
gzip_types application/javascript application/octet-stream;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
include /etc/nginx/conf.d/service-url.inc;
: 외부에서 설정된 서비스 URL을 포함하여 동적으로 서비스를 지원한다.
📌 여기에 쓰인 service-url.inc
는 cd /etc/nginx/conf.d
로 경로이동하여sudo vim service-url.inc
로 set $service_url http://127.0.0.1:8080;
를 입력해주면 된다.
Container location 설정
location / {
gzip_static on;
root /usr/share/nginx/dist;
index index.html index.htm;
try_files $uri $uri/ $uri /index.html;
}
location / {
: 정적 파일 서비스를 위한 설정으로, Blue Container에 대한 설정gzip_static on;
: 정적 파일에 대해 Gzip 압축 사용을 활성화하여 성능을 향상시킨다.root /usr/share/nginx/dist;
: 정적 파일의 기본 경로를 설정한다.try_files $uri $uri/ $uri /index.html;
: 파일이나 디렉터리가 없을 경우, 기본적으로/index.html
을 제공하여 SPA (Single Page Application)의 경우 무중단 배포를 지원한다. SPA란 말 그대로, 한 개의 페이지로 이루어진 application을 말한다. SPA에 대해서는 정말 잘 정리해둔 글이 있어 첨부한다. 궁금한 사람들은 보면 좋을 것 같다. SPA
Container API 프록시 설정
location /api {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, PATCH, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
add_header 'Access-Control-Max-Age' 86400;
return 204;
}
add_header 'Access-Control-Allow-Origin' '*' always;
proxy_pass $service_url;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
}
location /api {
: Blue Container에 대한 API 프록시 설정.if ($request_method = 'OPTIONS') {
: HTTP OPTIONS 메서드로 요청이 오는 경우 CORS 헤더를 추가하고 204 No Content 응답을 반환하여 무중단 배포에서 발생할 수 있는 문제를 해결한다.proxy_pass $service_url;
: 백엔드 서비스의 URL을 동적으로 변경하여 Blue Container로의 트래픽을 보낸다.프록시 관련 설정: 버퍼 크기, 버퍼 수 등을 조절하여 무중단 배포 과정에서 발생할 수 있는 지연을 최소화한다.
…여기까지 세팅이 끝났다면 무중단배포에 대한 세팅이 끝났다!!! 🤪
하지만…
아직 한발남았다.
7. 로드밸런서 리스너 설정
오토스케일링이나 인스턴스가 한개가 아니라면 7번 로드밸런서 부분은 보지 않아도된다.
이 부분은 완벽하게 해결된 부분이 아니다.
트러블 슈팅으로도 남겨놨지만, 하나의 인스턴스로 블루 그린 무중단 배포를 했을때의 한계인건지…
내가 방법을 못찾은건지 모르겠지만 우선 적용한 방법을 살펴보면… 다음과 같다.
먼저 https 설정할때 만들어뒀던 AWS EC2의 로드밸런서로 들어간다.
)
그리고 배포에 이용중인 해당 로드밸런서로 들어가 HTTPS:443 포트로 들어가 리스너 규칙을 수정해줘야한다.
hh99-delivery-abl 가 8080포트로 인스턴스를 연결하는 대상그룹이고, 아래 8081이 8081포트 대상그룹이다.
둘다 추가해놔야 블루그린배포로 8080, 8081 포트를 번갈아가면서 하나의 인스턴스로 배포를 해도 서비스를 이용할 수 있다.
단, 이대로 적용하면 문제가 있는데 가중치를 둘로 나누어서 페이지에서 새로고침 될 때마다 50%확률로 502 Bad Gateway
를 겪게된다.
일단 실제 서비스를 목적이 아니라 연습용 프로젝트였기 때문에 8080(green)일때는 8080 대상그룹만 추가해주고, 8081(blue)일때는 8081대상그룹만 추가하는 임시방편으로 100% 가중치를 유지해 502 Bad Gateway
오류를 피했다.
이 부분은 추후 해결되면 수정을… 해야한다. 기술매니저님께 여쭤본바로는 오토스케일링이던, 인스턴스의 개수를 늘려주는 방법을 써야한다고 하셨다. 예상은 했지만 혹시 다른 방법이 있다면 다시 적용해보려고한다.
여기까지하면 진짜 무중단배포 끝! 🤪
💫 Trouble shooting
1) set-url.inc 파일 만들기
원인추론 :/etc/nginx/conf.d/
경로로 옮겨서 vi 편집기로 직접 작성을 해야하는지 모름
해결방안 :
이와 같이 경로를 옮겨서 vi 편집기로 url 지정을 해줌.
2) 배포 작업을 기다리는 중에서 무한 대기 중 문제
원인추론 :
github actions runner 가 실행 종료되었기 때문.
해결방안 :
github actions runner를 백그라운드에서도 계속 실행중으로 바꾸기 위해서 ec2에서 actions-runner 경로로 옮겨 nohub 명령어로 재실행
./run.sh
명령어를 실행했을때 위 처럼 이미 존재한다고 나온다면 nohup 옵션이 잘 적용되어 백그라운드에서도 잘 돌아가는 상태!
3) yaml.scanner.ScannerError: while scanning for the next token found character '\\t' that cannot start any token in "/docker-compose.blue.yml", line 5, column 1 /deploy.sh: 11: Syntax error: "else" unexpected (expecting "then") Error: Process completed with exit code 2.
원인추론 :
deploy.sh와 docker-compose.blue.yml 파일의 들여쓰기 및 오타
해결방안 :
- 스크립트에 서로 다른 따옴표(
"
및”
)가 혼합되어 있고BEFORE_COLOR="blue"
줄에 닫는 따옴표가 누락된 것 같습니다. 또한 일반 따옴표를 사용하고 있는지 확인하세요. 둥근 따옴표(“”
) 대신 ASCII 큰따옴표("
)를 사용하세요. - yml 파일의 코드마다 들여쓰기 주의해서 수정
4) 8081 포트에서 컨테이너를 실행하면 502 Bad Gateway
원인추론 : ec2 쪽 8081포트가 안열려있음
해결방안 : ec2 보안규칙, 로드밸런서, 대상그룹에 전부 8081관련 규칙 추가
4-1) 8081포트에서 503 Service Temporarily Unavailable 에러
원인추론 : 로드밸런서 리스너 규칙에 연결해줬던 8081 관련 대상그룹에 등록된 대상(인스턴스)가 없었음
해결방안 : ec2인스턴스를 등록… 해결…
'Experience > 항해99 18기' 카테고리의 다른 글
WIL 8주차 0204 (1) | 2024.02.04 |
---|---|
WIL 7주차 0128 (1) | 2024.01.28 |
[챌린지 프로젝트 사전주차] CI-CD : Github Actions, Docker, AWS EC2로 자동배포를 해보자 (1) | 2024.01.26 |
[챌린지 프로젝트 사전주차] CI-CD : Github actions을 사용해보자 (2) | 2024.01.24 |
Week I Learned #2 (1) | 2023.12.25 |