본문 바로가기

DevOps

[DevOps 구축하기 3] Blue/Green 웹 서버 + Nginx 리버스 프록시 설정

728x90
반응형

https://tomatohj.tistory.com/81 에서 이어집니다.


이제는 실제로 배포할 웹 서버 2대를 구성할 것이다.
Jenkins를 이용해서 Blue/Green 형태의 무중단 배포를 구현할 것이기에, 동일한 환경의 컨테이너 2개를 생성한다.
'docker run -it --name [이름] ubuntu' 명령어로 이름만 다른 2개의 컨테이너를 생성한 뒤, 2개 컨테이너 모두 'apt update && apt install -y vim net-tools openjdk-17-jdk ssh psmisc'를 수행한다.
이 과정을 통해 웹 서버에서 필요한 모든 패키지를 다운로드 받게 된다.

2개 서버 모두 설치가 끝나면. 'service ssh start' 명령어를 실행시켜 ssh server를 동작시킨다.
(Jenkins가 실제로 배포를 수행할 때 scp를 이용할 것이기에 웹 서버의 ssh 가동은 필수이다.)

이번엔 Jenkins 컨테이너로 돌아와서 ssh를 위한 public key를 생성할 것이다.
Jenkins가 scp를 이용하여 웹 서버에게 배포를 수행하기 위해선 ID/PW를 물어보는 과정이 있어서는 안된다. 그래서 Jenkins 컨테이너의 public key를 각 웹 서버들에 등록해두어, 별도의 추가 인증과정 없이 접근이 가능하도록 설정할 것이다.

'su - jenkins' 명령어를 통해 Jenkins를 구동시키는 전용 계정으로 전환한다.
그 이후 'ssh-keygen' 명령어를 통해 public key를 생성할 것이다. 이때 이어서 나오는 모든 질문은 엔터를 눌러 스킵하면 된다.

생성이 완료되었다면 'cat ~/.ssh/id_rsa.pub' 명령어를 통해 생성된 public key 내용을 확인한다.

이제 웹 서버 역할을 할 2개의 컨테이너로 돌아온다.
두 컨테이너 모두 Jenkins 컨테이너의 public key를 등록하는 것이기에, 동일한 명령어를 수행해주면 된다.
먼저 'mkdir ~/.ssh && chmod 700 ~/.ssh' 명령어를 통해 .ssh 디렉토리를 생성하고 권한을 부여한다.
그 뒤, 'echo [public key] > ~/.ssh/authorized_keys && chmod 644 ~/.ssh/authorized_keys' 명령어를 통해 authorized_keys라는 파일에 Jenkins 컨테이너의 public key를 등록하고 적절한 권한을 부여한다.

설정을 잘 마쳤다면, 웹 서버 역하을 하는 두 컨테이너에서 'ifconfig eth0' 명령어를 실행시켜 각각의 IP주소를 확인한다.

IP주소를 확인했다면, 의도한 대로 Jenkins 컨테이너에서 웹 서버용 컨테이너로 추가 인증과정 없이 ssh 접근이 잘 되는지 확인해보자.
'ssh root@[웹서버 IP]' 형태의 명령어를 실행시키면, 추가 인증과정 없이 원격접속에 성공하는 것을 확인할 수 있다.
다만 맨 처음 접속할때는 fingerprint관련하여 물어보는 부분이 있는데, 이 역시 scp를 통한 배포과정에 있어 문제를 야기하기에, 이는 배포 스크립트에서 처리할 것이다.

여기까지가 Blue/Green 웹 서버 구축 방법이었다.

이제는 Blue/Green 두 웹 서버 중 어떤 웹 서버로 서비스를 제공할지 통신 경로를 제어하는 리버스 프록시 역할의 Nginx를 구축할 것이다.
새로운 터미널에서 'docker run -it --name nginx -p 80:80 ubuntu' 명령어를 통해 Nginx용 컨테이너를 생성한다.
그 뒤 'apt update && apt install -y vim ssh nginx' 명령어를 통해 필요한 패키지를 설치한다.

여기까지 진행이 되었다면, 이전에 웹 서버들에 진행한 ssh public key 등록과정을 Nginx 서버에도 동일하게 진행한다.

등록을 마무리 했다면, Nginx를 리버스 프록시로서의 역할로 사용하기 위해 설정파일을 수정해줄 것이다.
'vi /etc/nginx/sites-available/default' 명령어를 통해 파일을 열어 아래와 같이 수정한다.
(location / 를 기준으로 위에 1줄을 추가하고, 아래 1줄을 주석처리, 2줄을 추가해주면 된다.)

상단의 include 구문은 /etc/nginx/sites-available/deploy_server라는 파일을 포함시킨다는 뜻으로, 이 파일 안에는 아래에서 사용될 $deploy_server의 값이 설정될 것이다.
proxy_pass 는 Nginx로 들어오는 웹 통신을 $deploy_server에 해당하는 대상에게 전달하는 역할을 하며, proxy_redirect는 웹 애플리케이션에서 redirect 발생 시 리버스 프록시 기능이 정상동작 하지 않을 수 있어, http://localhost로 리다이렉트 되는 경우 $deploy_server로 강제로 리다이렉트 시키는 역할을 한다.
(WebGoat가 localhost로의 리다이렉트가 잦아 추가함)

수정을 마쳤다면 저장하고 나간다.

include /etc/nginx/sites-available/deploy_server;

location / {
	# First attempt to serve request as file, then
	# as directory, then fall back to displaying a 404.
	#try_files $uri $uri/ =404;
	proxy_pass $deploy_server;
	proxy_redirect $deploy_server http://localhost;
}

뒤 이어 $deploy_server의 값을 설정하기 위해 'vi /etc/nginx/sites-available/deploy_server' 명령어를 통해 파일을 열어 아래와 같이 작성한다. 
이는 $deploy_server의 초기값을 webserver-blue에 해당하는 172.17.0.3으로 설정하겠다는 뜻이 되며, Jenkins에서 배포를 할 때 마다 이 값은 배포한 서버에 맞게 변경될 것이다.

set $deploy_server http://172.17.0.3:8080;

여기까지 마무리 되었다면, 'service nginx start'를 통해 nginx를 실행시킨다.

nginx역시 Jenkins에서 접근하여 파일을 수정해야하기 때문에 IP주소 정보가 필요하니 'apt install -y net-tools > /dev/null && ifconfig eth0' 명령어를 통해 확인해두도록 하자. 

자 이제 웹 서버 2대와, Nginx 리버스 프록시 세팅이 끝이났다.
본격적으로 Jenkins를 통해 웹 애플리케이션 빌드(CI), 배포(CD)를 수행해보도록 하자.

Jenkins 메인 화면으로 와서 왼쪽 메뉴 중 Jenkins 관리를 클릭한다.

Jenkins 관리 메뉴에서 System 메뉴를 클릭한다.

클릭하고 쭉 내려가다보면 Global properties라는 항목이 있다.
여기서 3번째 체크박스를 보면 Environment variables라는 것이 있는데, 환경변수를 설정하는 곳으로, 매번 IP주소를 치기 번거롭기 때문에 여기다가 2개의 웹 서버와 Nginx의 IP를 환경변수 형태로 저장하여 이용할 것이다.

총 3개의 환경변수에 대하여 아래 그림과 같이 설정해주도록 한다.
이때 IP주소는 이전에 ifconfig 명령어로 확인한 값으로 입력해주어야 한다.
다 되었다면 저장하면 된다.

저장했다면, 이젠 만들어둔 프로젝트로 돌아와, 왼쪽 구성 버튼을 클릭한다.

쭉 내려가다보면 Build Steps라는것을 확인할 수 있는데, 빌드를 진행할때 어떤 행동을 취할지를 지정하는것이다.
지금까지는 이걸 설정하지 않아, Github에서 코드를 다운로드만 받고 아무 행위도 취하지 않았었는데, 이 과정을 통해 진짜 빌드를 수행하게 될 것이다.

Shell Script를 이용하여 2개 Step으로 나누어 진행할 것인데, 첫번째는 사용할 WebGoat 애플리케이션을 빌드하는 과정이며, 두번째는 빌드된 WebGoat를 웹 서버로 배포하고, 실행시킨 뒤, Nginx 리버스 프록시 설정을 변경하는 역할을 할 것이다.

아래 그림과 같이 Add build step에서 Execute shell을 클릭하여 2개의 Build Step을 생성한다.

첫번째 Step에는 WebGoat를 빌드하기 위한 명령어를 입력할 것이다.
'./mvnw spotless:apply && ./mvnw clean install -D skipTests'를 입력해 준다.
maven 프로젝트인 WebGoat를 빌드하는 명령어이다.

2번째 Step에서는 빌드된 웹 애플리케이션을 웹 서버로 전달하고, 이를 실행시킨 뒤 Nginx 리버스 프록시 설정을 변경하는 과정을 작성할 것이다. 

코드를 간단히 해석하자면 blue 웹 서버에서 웹 애플리케이션을 동작중이라면 green 웹 서버에, green 웹 서버에서 웹 애플리케이션이 동작중이라면 blue 웹 서버에 배포를 수행할 것인데, 이때 fingerprint관련 이슈가 없도록 ssh-keyscan을 이용했고, scp를 이용해 빌드돤 jar파일을 /root/webgoat.jar 경로에 저장한다. 저장이 완료되면 ssh를 이용하여 webgoat.jar를 실행시키고 1분간 sleep 한다. 이는 WebGoat 애플리케이션이 정상동작하기까지 걸리는 시간이며, 1분이 끝나면 웹 애플리케이션이 정상적으로 배포되었는지 curl을 이용하여 확인한 뒤, 문제가 없다면 Nginx 컨테이너 내 /etc/nginx/sites-available/deploy_server에 설정된 IP주소를 배포서버 IP주소로 수정한다. 모든 작업이 다 끝나면, 이전에 사용하던 서버의 8080포트에서 동작중인 웹 애플리케이션을 kill한다.

이 작업을 통해 서비스가 새롭게 배포되면 배포되는 동안에는 기존 서버에 올라가 있는 서비스를 사용하고, 배포가 완료되면 리버스 프록시를 통해 자연스럽게 새롭게 배포된 서비스로 접근할 수 있게 되기때문에 서비스 사용을 중단할 필요가 없다. 즉 무중단 배포가 가능해진다.

다 입력했다면 저장을 눌러 설정을 마무리한다.

#!/bin/bash

if curl -s "http://${blue_ip}:8080" > /dev/null
then
	deploy_ip=$green_ip
else
	deploy_ip=$blue_ip
fi
echo "Deploy IP choosed : ${deploy_ip}"

ssh-keyscan ${deploy_ip} > ~/.ssh/known_hosts
scp ./target/webgoat-*-SNAPSHOT.jar root@${deploy_ip}:/root/webgoat.jar
ssh root@${deploy_ip} "nohup java -jar /root/webgoat.jar > /dev/null &" &

sleep 60
if curl -s "http://${deploy_ip}:8080" > /dev/null
then
	echo "Deploy Success"
else
	echo "Deploy Error"
	exit 1
fi

ssh-keyscan ${nginx_ip} > ~/.ssh/known_hosts
ssh root@${nginx_ip} "echo 'set \$deploy_server http://${deploy_ip}:8080;' > /etc/nginx/sites-available/deploy_server && service nginx reload"
echo "Nginx reverse proxy now pass the packet to ${deploy_ip}"

if [ "${deploy_ip}" == "${blue_ip}" ]
then
	ssh-keyscan ${green_ip} > ~/.ssh/known_hosts
	ssh root@${green_ip} "fuser -k 8080/tcp"
else
	ssh-keyscan ${blue_ip} > ~/.ssh/known_hosts
	ssh root@${blue_ip} "fuser -k 8080/tcp"
fi
echo "Kill other server process"

 

이제 얼마 남지않았다. 다음 글이 마지막이 될 것이다.


728x90
반응형