무중단 배포를 한번 해보자!
들어가며
다운 타임
다운타임이란 스템, 서버, 네트워크, 또는 애플리케이션이 정상적으로 작동하지 않고, 사용자가 접근할 수 없는 시간을 의미합니다. 다운타임은 계획된 경우와 계획되지 않은 경우로 나뉩니다.
- 계획된 다운 타임 :
- 시스템 유지 보수 : 시스템의 성능을 올리거나 유지보수를 하는 동안 사용할 수 없는 시간
- 배포 및 릴리스 : 새로운 기능이느 버그가 발생해 새로운 버전을 배포 시 발생 시간
- 계획되지 않은 다운 타임:
- 시스템 오류 : 시스템이 다운됐거나, 오류가 발생해 사용할 수 없을 때 발생하는 시간
- 보안 : 블랙해커의 의해 시스템이 해킹되어 사용 할 수 없는 시간
만약 새로운 기능일 개발해 CI/CD를 진행한다면 위에서 설명한 계횐된 다운타임-배포 및 릴리스의 해당되어 새 버가 새롭게 배포되기 전까지의 과정에서 다운타임이 발생하게 됩니다. 그래서 이를 해결하기 위해서는 무중단 배포(Zero Downtime Deployment) 기술을 활용하면 서비스 중단 없이 사용할 수 있습니다.
무중단 배포 방식
무중단 배포방식의 여러가지 방식이 있습니다, 그중 이번 포스팅에서는 블루 그린 방식에 대해서만 설명하겠습니다.
블루-그린 무중단 배포란?
블루-그린 배포(Blue-Green Deployment)는 두 개의 서버를 활용한 배포 방식입니다.
동작 과정
![](https://blog.kakaocdn.net/dn/EjDI9/btsIV6QKl45/ZWbKsad4240MK9WcyTrRHK/img.png)
현재 서버는 GREEN 서버가 동작 중입니다.
새로운 버전을 개발하여 새로운 배포 서버를 띄우려고 합니다.
먼저, 현재 사용 중인 서버가 BLUE 서버인지 GREEN 서버인지 확인합니다.
확인 결과, 현재 GREEN 서버가 동작 중임을 확인했습니다.
![](https://blog.kakaocdn.net/dn/cY51Nx/btsIVLFZC8R/RSTN3PUfr0E1lr8SXnikqK/img.png)
그럼 BLUE서버 포트를 사용해 새롭게 서버를 실행합니다.
아직 그대로 데이터 전송은 GREEN 서버로 전달합니다.
기존에 GREEN서버를 DOWN 시키고 BLUE 서버로 전달이 되도록 변경합니다.
이런식의 과정으로 GREEN-BLUE 방식이 동작합니다.
단점
1. 비용 증가: 두 개의 환경을 동시에 운영하기 때문에 인프라 비용이 증가할 수 있습니다.
2. 복잡성 증가: 환경 관리와 전환 작업이 추가되므로 설정과 운영의 복잡성이 높아질 수 있습니다.
사전 준비
🚨이번 구현 방법에서는 Nginx, Docker, AWS, Jenkins, Spring를 기본적으로 숙지한 상태라고 가정하며, 자세한 설치 과정 등은 생략하도록 하겠습니다. 🚨
사전 준비
Spring-boot : actuator, web 준비
AWS-EC2 : docker, docker-compose, nginx, jenkins 준비
Docker : docker-hub 에 빌드된 image
Nginx 설정
Docker-compose를통해 nginx를 실행할 수 있게 작성해 줍니다.
version: '3.9'
services:
nginx:
image: nginx:latest
volumes:
- ./conf/nginx.conf:/etc/nginx/nginx.conf
restart: always
ports:
- 80:80
networks:
- backend_network
depends_on:
- api
networks:
backend_network:
external: true
- 간단하게 작성해 줍니다. 서버는 다른 docker-compose 작성해 실행해 줄 예정이니 depends_on을 통해 의존해 줍니다.
또한 사용할 수 있게 네트워크도 연결해줘야 합니다.
docker network create backend_network
명령어를 통해 네트워크를 만들어줍니다.
이제 두 개의 blue, green의 yml파일도 작성해 줍니다.
docker-compose.blue.yml
version: "3.9"
networks:
backend_network:
external: true
services:
api:
image: [이미지명]
container_name: [컨테이너명]
restart: always
ports:
- "8443:8443"
networks:
- backend_network
docker-compose.green.yml
version: "3.9"
networks:
backend_network:
external: true
services:
api:
image: [이미지명]
container_name: [컨테이너명]
restart: always
ports:
- "8444:8443"
networks:
- backend_network
nginx.conf
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name 52.78.225.238;
location /.well-known/acme-challenge/ {
allow all;
}
location /actuator {
add_header X-Served-By $host;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Scheme $scheme;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
docker-compose -f [docker-compose.[색].yml] -p [별명] -f docker-compose.yml up -d
위 명령어를 입력하여 실행해 줍니다.
실행 중인 Niginx의 접속하여 VIM을 설치해 줍니다.
vim /etc/nginx/include/service_url.conf
아래 내용을 작성해 줍니다.
set $service_url :8081;
현재 실행 중인 Green 서버의 포트번호입니다.
이제 shell작성하면 현재 실행중인 포트번호로 자동으로 바뀌게 할 예정입니다.
deploy.sh
EXIST_BLUE=$(sudo docker-compose -p blog-blue -f /blog/docker-compose.blue.yml ps | grep blog-blue)
echo "${EXIST_BLUE}"
#1
if [ -z "$EXIST_BLUE" ]; then
echo "blue 컨테이너는 실행 중이지 않습니다."
sudo docker-compose -p blog-blue -f /blog/docker-compose.blue.yml up -d
BEFORE_COLOR="green"
AFTER_COLOR="blue"
BEFORE_PORT=8081
AFTER_PORT=8080
else
echo "blue 컨테이너는 실행 중입니다."
sudo docker-compose -p blog-green -f /blog/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://52.78.225.238/:${AFTER_PORT}/actuator/health)
if [ -z "$UP" ]; then
sleep 10
continue
else
break
fi
done
if [ $cnt -eq 10 ]; then
echo "서버가 정상적으로 구동되지 않았습니다."
exit 1
fi
#3
# 설정 파일을 호스트에서 수정
echo "Modifying Nginx Configuration on Host..."
sudo docker exec blog-green_nginx_1 sed -i "s/:${BEFORE_PORT}/:${AFTER_PORT}/" /etc/nginx/include/service_url.conf
# Nginx 서버 재로드
echo "Reloading Nginx..."
sudo docker exec blog-green_nginx_1 nginx -s reload
echo "Deploy Completed!!"
#4
echo "$BEFORE_COLOR server down(port:${BEFORE_PORT})"
pwd
sudo docker stop blog-${BEFORE_COLOR}
sudo docker rm blog-${BEFORE_COLOR}
디렉터리 구조
blog
├── deploy.sh
├── docker-compose.blue.yml
├── docker-compose.green.yml
├── docker-compose.yml
├── nginx.conf
└── service_url.conf
젠킨스 설정
원격으로 ssh를 접속하기 위해서 SSH 플러그인을 다운로드하여줍니다.
그 후 EC2에 접속할 때 사용한. pem키의 내용을 복사해 줍니다.
credenials를 추가해줍니다.
ID : 사용할 커스텀 아이디(마음대로 지정가능합니다.)
Description : 부가설명을 작성합니다.
Username : EC2의 접속할 때 사용한 명을 사용합니다.(우분투 : ubuntu, 아마존리눅스면 : ec2-user)
Key : ec2 접속 시 사용했던 키의 내용을 복사
파이프라인 작성
pipeline{
agent any
stages{
stage('Deploy') {
steps {
sshagent (credentials: ['ec2_blog']) {
sh """
ssh -o StrictHostKeyChecking=no ubuntu@52.78.225.238 '
cd /blog
ls -a
sudo ./deploy.sh
'
"""
}
}
}
}
}
추가적인 필요한 것이 있으면 stage를 통해 추가하시면 됩니다.
현재는 ip주소가 그대로 노출되지만 블로그 작성 시에만 사용했지만 꼭 환경변수로 사용해야 합니다!!
이제 쉘스크립트를 실행하게 되면 이전에 작성했던 service_url.conf 이 동적으로 바뀌어 새롭게 연결해 주는 것을 기대할 수 있다.
쉘 스크립트에 작성한 내용이 정상적으로 나오는 것을 볼 수 있다.
결과 학인
위 로그에서 blue가 실행 중이지 않아서 blue가 실행되고 green서버가 다운된 것을 확인할 수 있다.
마무리
이렇게 우여곡절 끝에 green-blue 무중단배포를 구현하게 됐습니다. 어려운 점이 많이 있었지만.. 역시.. 삽질하면서 하다 보면 언젠가는 구현하는 것 같습니다.!! 궁금하거나 틀린 부분이 있으면 말씀해 주세요!! 감사합니다
Reference
Jenkins, Docker를 이용한 무중단 배포 시작하기
시작하기 예전에는 배포하는 날이면 접속하는 사용자가 적은 새벽시간에 했다고 본적이 있습니다. 배포를 할 때에는 새로운 버전의 jar 파일을 배포할 서버로 복사시키고, 직접 SSH로 접속하여 ja
iseunghan.tistory.com
'Devops > 도커' 카테고리의 다른 글
[Docker] 도커 허브(Docker hub)에 리파지토리(Repository)에 저장하기 및 다운받기 (0) | 2024.02.03 |
---|---|
[Dokcer] docker-compose로 도커컨테이너 실행하기 (0) | 2024.02.02 |
[Docker] 도커로 만든 MySQL을 MySQL Workbench로 연동하는 방법 및 볼륨 사용하기 (1) | 2024.01.31 |
[Docker] nginx 설정파일 커스텀 해보기 (1) | 2024.01.31 |
[Docker] Docker파일 작성방법 및 실행해보기 (1) | 2024.01.31 |