요약
배포의 편의성과 서비스 무중단을 위해 Blue-Green 무중단 배포를 적용했습니다. 깃헙에 코드가 푸시되면 웹훅에 의해 젠킨스의 파이프라인이 실행됩니다. 이 파이프라인이 소스코드를 클론, 빌드하고 이미지로 만들어 DockerHub에 푸시하고, 앱서버에선 이미지를 Pull해서 Blue-Green 배포 전략에 맞게 컨테이너를 생성합니다. 사용자는 nginx를 통해 작동중인 컨테이너의 포트로 포트포워딩되어 서버를 이용합니다.
서비스를 배포하고 싶을 때 Naver Cloud Platform에서 서버 1개를 받아서 배포하는데, 배포할 때마다 DB 등의 요소들을 일일이 컨테이너를 만드는 게 비효율적이라 생각되어 Docker-compose를 이용해 한 번에 모든 컨테이너가 작동될 수 있도록 했습니다.
깃헙에 코드를 푸시할 때마다 새 코드를 클라우드 서버에 전송하고 빌드하고 실행하는 게 번거로워 자동배포가 진행되도록 했으면 좋겠다 생각했습니다. 그래서 조사해봤을 때 무중단 배포를 하는 게 실서비스를 가정했을 때 필수적이라고 생각되어 blue-green 방식으로 무중단배포를 구현해보았습니다.
비유하자면 blue, green이라는 2개의 달리기 선수가 바톤 터치를 하는 것과 비슷합니다. blue라는 이름의 컨테이너가 먼저 앱서버로써 작동하고 있는 상황에서, 새 코드가 배포되면 green이라는 이름의 컨테이너로 새 코드가 실행되고, 새 코드를 실행중인 green 컨테이너가 일정시간 문제없이 작동한다면 blue 컨테이너를 작동 중지 시키는 것입니다.
이런 방법 없이 배포한다면, 기존 코드를 중지시킨 뒤 새 코드를 실행시키는 시간 사이에 서비스가 불가해지는데, 이는 사용자 입장에선 갑자기 서비스가 먹통이 되는 상황입니다. blue-green 방식으로 새 코드(green)가 정상적으로 작동함을 확인한 뒤 기존 코드(blue)를 중지시키는 방식으로 서비스 중단 없이 배포할 수 있게 됩니다.
모든 api 트래픽은 먼저 nginx를 거치게 되는데, nginx가 트래픽을 받으면 blue, green 컨테이너가 있는 2개의 포트에게로 로드밸런싱을 합니다. 각 컨테이너는 포트포워딩을 통해 동일한 포트로 트래픽을 돌립니다. 이로써 api 서버측에선 동일한 포트로 트래픽을 받게됩니다.
사용자는 nginx에 의해 앱 서버로 연결됩니다. 클라우드 플랫폼은 Naver Cloud Platform을 사용했습니다.
빌드파일을 젠킨스 서버에서 앱서버로 넘기는 걸 처음엔 scp를 이용해 직접 전송하는 방식을 적용했었는데, DockerHub을 매개로 푸시,풀하는 게 빌드파일을 이미지화해서 클라우드에 저장도 같이하기 때문에 서버를 이미지로 관리하기 쉽겠다고 생각해서 그렇게 진행했습니다.
서비스를 배포하고 싶을 때 Naver Cloud Platform에서 서버 1개를 받아서 배포하는데, 배포할 때마다 DB 등의 요소들을 일일이 컨테이너를 만드는 게 비효율적이라 생각되어 Docker-compose를 이용해 한 번에 모든 컨테이너가 작동될 수 있도록 했습니다.
Naver Cloud Platform으로부터 서버 1개를 받아오면, 컨테이너에 필요한 데이터는 /home
디렉토리에 넣어놓고, 미리 작성한 deploy.sh
를 실행하면 dockerhub에서 이미지를 받아와 DB, nginx 등의 컨테이너를 한번에 실행합니다.
# /home/deploy.sh
docker rm -f $(docker ps -qa) # 기존 컨테이너 삭제
docker image rm -f $(docker image ls -q) # 기존 이미지 삭제
docker-compose -f /home/dambae200-docker-compose.yml up # docker-compose 시작
# /home/dambae200-docker-compose.yml
version: '3.9'
services:
dambae200-mysql-master:
image: leehyeonmin34/dambae200-mysql-master:1.1.1
environment:
- MYSQL_ROOT_PASSWORD=XXXX
ports:
- "3306:3306"
volumes:
- /home/mysql-master/mysql:/var/lib/mysql
dambae200-mysql-slave:
image: leehyeonmin34/dambae200-mysql-slave:1.1.1
environment:
- MYSQL_ROOT_PASSWORD=XXXX
ports:
- "3307:3306"
volumes:
- /home/mysql-slave/mysql:/var/lib/mysql
depends_on:
- dambae200-mysql-master
dambae200-redis-cache:
image: leehyeonmin34/dambae200-redis-cache:1.1.1
ports:
- "6379:6379"
dambae200-nginx:
image: leehyeonmin34/dambae200-nginx:1.1.1
volumes:
- /home/dambae200/dambae200-front/www:/home/dambae200/dambae200-front/www
ports:
- "80:80"
dambae200-jenkins:
image: leehyeonmin34/dambae200-jenkins:1.1.1
environment:
- JENKINS_OPTS=--httpPort=8010
- TZ=Asia/Seoul
ports:
- "8010:8010"
volumes:
- /home/jenkins:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker
user: root
restart: unless-stopped
extra_hosts:
- host.docker.internal:host-gateway