Jenkins와 Docker, nginx를 이용한 자동 무중단 배포 🔥

초록·2023년 11월 23일
0
post-thumbnail

요약

배포의 편의성과 서비스 무중단을 위해 Blue-Green 무중단 배포를 적용했습니다. 깃헙에 코드가 푸시되면 웹훅에 의해 젠킨스의 파이프라인이 실행됩니다. 이 파이프라인이 소스코드를 클론, 빌드하고 이미지로 만들어 DockerHub에 푸시하고, 앱서버에선 이미지를 Pull해서 Blue-Green 배포 전략에 맞게 컨테이너를 생성합니다. 사용자는 nginx를 통해 작동중인 컨테이너의 포트로 포트포워딩되어 서버를 이용합니다.

서비스를 배포하고 싶을 때 Naver Cloud Platform에서 서버 1개를 받아서 배포하는데, 배포할 때마다 DB 등의 요소들을 일일이 컨테이너를 만드는 게 비효율적이라 생각되어 Docker-compose를 이용해 한 번에 모든 컨테이너가 작동될 수 있도록 했습니다.

왜 무중단 배포?

깃헙에 코드를 푸시할 때마다 새 코드를 클라우드 서버에 전송하고 빌드하고 실행하는 게 번거로워 자동배포가 진행되도록 했으면 좋겠다 생각했습니다. 그래서 조사해봤을 때 무중단 배포를 하는 게 실서비스를 가정했을 때 필수적이라고 생각되어 blue-green 방식으로 무중단배포를 구현해보았습니다.

무중단 배포

Blue-Green 무중단 배포란?

비유하자면 blue, green이라는 2개의 달리기 선수가 바톤 터치를 하는 것과 비슷합니다. blue라는 이름의 컨테이너가 먼저 앱서버로써 작동하고 있는 상황에서, 새 코드가 배포되면 green이라는 이름의 컨테이너로 새 코드가 실행되고, 새 코드를 실행중인 green 컨테이너가 일정시간 문제없이 작동한다면 blue 컨테이너를 작동 중지 시키는 것입니다.

그냥 배포하면 왜 문제지?

이런 방법 없이 배포한다면, 기존 코드를 중지시킨 뒤 새 코드를 실행시키는 시간 사이에 서비스가 불가해지는데, 이는 사용자 입장에선 갑자기 서비스가 먹통이 되는 상황입니다. blue-green 방식으로 새 코드(green)가 정상적으로 작동함을 확인한 뒤 기존 코드(blue)를 중지시키는 방식으로 서비스 중단 없이 배포할 수 있게 됩니다.

로드밸런싱과 포트포워딩을 활용

모든 api 트래픽은 먼저 nginx를 거치게 되는데, nginx가 트래픽을 받으면 blue, green 컨테이너가 있는 2개의 포트에게로 로드밸런싱을 합니다. 각 컨테이너는 포트포워딩을 통해 동일한 포트로 트래픽을 돌립니다. 이로써 api 서버측에선 동일한 포트로 트래픽을 받게됩니다.

스크린샷 2023-06-19 오후 3 27 29

파이프라인 과정

image 180

  1. 코드를 깃헙에 푸시하면 젠킨스로 웹훅을 날려 젠킨스의 파이프라인이 진행됩니다.
  2. 먼저 깃헙에서 소스코드를 젠킨스 서버로 클론해옵니다.
  3. 소스코드를 gradle로 빌드합니다.
  4. 그리고 소스코드에 포함된 Dockerfile 을 이용해 Docker 이미지로 빌드하는데, 이 때 프로덕션 프로파일을 활성화시킵니다.
  5. 만들어진 이미지를 DockerHub에 푸시 합니다.
  6. 그리고 젠킨스 서버에서 앱서버로 쉘 스크립트 명령어를 원격 실행해 DockerHub의 이미지를 앱서버로 Pull해옵니다.
  7. blue-green 배포 방식으로 컨테이너가 생성되도록 하는 deploy.sh 파일을 실행해 무중단배포를 구현했습니다.

🔗 자세한 파이프라인 코드

사용자는 nginx에 의해 앱 서버로 연결됩니다. 클라우드 플랫폼은 Naver Cloud Platform을 사용했습니다.

스크린샷 2023-06-23 오전 11 54 09

왜 Dockerhub?

빌드파일을 젠킨스 서버에서 앱서버로 넘기는 걸 처음엔 scp를 이용해 직접 전송하는 방식을 적용했었는데, DockerHub을 매개로 푸시,풀하는 게 빌드파일을 이미지화해서 클라우드에 저장도 같이하기 때문에 서버를 이미지로 관리하기 쉽겠다고 생각해서 그렇게 진행했습니다.

Docker-compose로 컨테이너 한번에 구동

서비스를 배포하고 싶을 때 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
profile
몰입하고 성장하는 삶을 동경합니다

0개의 댓글