이 방식을 이용하게 된다면 기존에 유지하고 있는 인스턴스로만 진행하기 때문에 추가적으로 비용이 발생하지않는다. 하지만 새로운 버전으로 교체하는 과정에서 일부 인스턴스가 연결되어 있지 않기 때문에 트래픽을 감당할 수 있는 선에서 진행해야하며 이전 버전과 새로운 버전의 호환성 문제도 생각해야 한다.
블루그린 배포는 블루는 이전 버전, 그린은 새로운 버전으로 지칭하며 구버전과 동일한 인스턴스 환경을 가진 새로운 버전을 만들어 로드 밸런싱에 연결 시키고 이전 버전을 해제시키는 방식이다.
이 방식은 이전 버전과 새로운 버전의 호환성이나 트래픽 문제는 해결이 됐지만 자원을 두배로 소모한다는 단점을 가지고 있다.
카나리 배포는 이전 버전의 인스턴스를 새로운 버전의 인스턴스로 점진적으로 바꿔나가는 방식이다.
새로운 버전의 제공 범위를 늘려가는 과정 속에서 문제점을 발견하거나 사용자들의 피드백을 받을 수 있다. 하지만 롤링 배포와 마찬가지로 이전 버전과 새로운 버전이 함께 공존하기 때문에 호환성 문제가 생길 수 있다.
해당 실습은 EC2 Ubuntu 18.04 LTS 환경에서 Spring Boot + Nginx를 이용해 간단한 쉘 스크립트를 작성해 Blue-Green 배포를 해보려고 한다.
시나리오는 기존에 서비스가 8080 또는 8082 포트로 배포되고 있는데 새로운 버전 서비스를 다른 포트로 실행시켜 nginx를 이용해 경로만 새로운 버전으로 이어주고나서 기존 서비스를 해제하는 방식으로 실습을 해나가려고 한다.
@RestController
@RequiredArgsConstructor
public class TestController {
private final Environment env;
@GetMapping("/")
public String hello() {
return "현재 실행 중인 서비스의 포트는 : " + env.getProperty("server.port");
}
}
Spring Boot를 이용해 정상적으로 서비스가 변경된 것을 확인하기 위해서 포트를 반환하는 간단한 API 예제를 구현한다.
# /etc/nginx/sites-enabled/<>.conf
server {
listen 80;
listen [::]80;
server_name <호스트 네임>;
include /etc/nginx/conf.d/service-url.inc; # 추가된 부분
location / {
# proxy_pass http://node-app;
proxy_pass $service_url; # 변경된 부분
}
}
# /etc/nginx/conf.d/service-url.inc
set $service_url http://127.0.0.1:8080;
conf.d 폴더에 파일을 만들어 서비스 경로를 환경 변수로 셋팅하고 nginx 설정 파일에 include로 추가해준다. 이런 식으로 해주는 이유는 설정 파일과 같은 경우는 하드 코딩하는 방식으로 관리하기에는 유지보수 측면에서 좋지 않기 때문에 다음과 같은 방식으로 진행한다. 참고로 OS 전역변수로 설정해서 참조하는 방식으로 하면되지 않냐라고 할 수 있는데 nginx에서 그것을 지원하지 않기 때문에 다음과 같이 include해주어야 한다.
# git pull
# chmod 755 gradlew
# ./gradlew build
# cp ./build/libs/<프로젝트명>.jar .
serviceA=$(sudo netstat -nltp | grep 8080 | awk '{print $7}' | awk -F '/' '{print $1}')
serviceB=$(sudo netstat -nltp | grep 8082 | awk '{print $7}' | awk -F '/' '{print $1}')
if [ "$serviceA" = "" ]; then
echo "8082 포트가 존재하여 8080 포트 실행"
sudo nohup java -jar -Dserver.port=8080 <프로젝트명>.jar &
else
echo "8080 포트가 존재하여 8082 포트 실행"
sudo nohup java -jar -Dserver.port=8082 <프로젝트명>.jar &
fi
echo "서비스 구동 대기"
var=1
while [ 1 ]
do
if [ -n "$(sudo netstat -nltp | grep 8080)" ] && [ -n "$(sudo netstat -nltp | grep 8082)" ]; then
echo "서비스 구동 완료"
break
fi
echo wait $var ...
var=`expr $var + 1`
sleep 1s
done
## 이 부분에 nginx 설정 변경 소스 필요
if [ "$serviceA" = "" ]; then
echo "8082 포트 프로세스 죽이기"
sudo kill -9 $serviceB
else
echo "8080 포트 프로세스 죽이기"
sudo kill -9 $serviceA
fi
위 쉘 스크립트는 현재 실행중인 서비스의 포트 번호를 확인하여 둘중 하나를 실행시키고 삭제하는 스크립트이다.
echo "경로 설정"
if [ "$serviceA" = "" ]; then
echo "8082 -> 8080 경로 변경"
echo "set \$service_url http://127.0.0.1:8080;" | sudo tee /etc/nginx/conf.d/service-url.inc
else
echo "8080 -> 8082 경로 변경"
echo "set \$service_url http://127.0.0.1:8082;" | sudo tee /etc/nginx/conf.d/service-url.inc
fi
sudo service nginx reload
위 내용은 아까 nginx 설정을 동적으로 변경하기 위해서 필요한 소스이며 이전 소스에 nginx 설정 변경 소스 필요 부분에 들어가면 된다.
합치면 위와 같은 소스가 나오게 된다.
쉘 스크립트가 완성되었으니 실행시키게 되면 새로운 서비스를 생성하고 이전 서비스를 제거하고 라우팅 설정까지 진행이 되며 요청을 보내게 되면 올바르게 버전이 변경된 것을 확인할 수 있다.