Nginx, Docker를 활용한 무중단 배포맛보기

Jeongkuk Seo
sjk5766
Published in
9 min readJun 21, 2020

제목 그대로 맛보기 입니다. 실무에선 AWS 환경에 CI/CD 도구도 고려하지만.. 저는 Docker container 위에서 Nginx를 로드 밸런서로 사용하고 뒷 단에 Express를 활용해 Rolling , Blue-Green 배포를 진행하도록 하겠습니다.

핵심은 무중단 배포에 있기 때문에 Nginx, docker-compose, Express에 대한 설명은 최대한 간단하게 언급했습니다.

Rolling

무 중단 배포 방법 중 하나인 Rolling Deploy 입니다.

  • step1-배포 전 상태입니다. 서버 2대가 로드 밸런서 뒷 단에서 동작합니다.
  • step2-서버 B를 로드 밸런서에 빼고 서버 B에 배포를 진행합니다.
  • step3-서버 B에 배포가 완료되면 로드 밸런서에 추가합니다. 서버 A를 로드 밸런서에 빼고, 서버 A에 배포를 진행합니다.
  • step4-서버 A에 배포가 완료되면 로드 밸런서에 다시 연결합니다.

장/단점

장점은 무중단 배포에 추가적인 서버가 필요하지 않습니다.

단점은 롤백이 어렵습니다. 그나마 배포 중간에 문제를 확인 할 경우 다행이지만 모든 서버에 배포 후 문제가 발견되면, 문제가 있는 서버를 운영하는 상태에서 이전 버전의 프로그램을 배포해야 합니다.

실습

아래와 같은 docker-compose.yml 파일을 준비합니다. 로드 밸런서 역할을 할 nginx와 서버 역할을 할 Express 2개로 구성됩니다. (아래 docker-compose 환경을 직접 구축하고 싶은 분들은 이 글을 읽어주세요)

뒷 단의 Express에게 Request를 전달할 nginx.conf 파일입니다.

docker-express란 upstream을 정의하고 여기에 express 2대의 정보를 적었습니다. 로드 밸런싱 메소드를 사용하지 않았기에 default로 Round Robin 방식으로 공평하게 패킷이 전달됩니다.

upstream docker-express {
server express1:3000;
server express2:3000;
}

docker-compose up -d 명령어로 container들을 실행한 후, chrome을 열어 localhost에 여러번 접속합니다. express 두 대의 로그를 확인하면 공평하게 3개씩 패킷이 들어왔습니다.

이 상태에서 express1을 로드 밸런서에서 제거하도록 하겠습니다. nginx.conf의 upstream 부분에서 express1에 down parameter를 추가합니다.

upstream docker-express {
server express1:3000 down;
server express2:3000;
}

그 후 nginx를 reload 합니다.

다시 localhost에 몇 번 접속하면 nginx가 로드 밸런싱을 express2에만 수행하는 것을 알 수 있습니다.

이 때 express1 에 대한 배포를 끝냈다고 가정합니다. git pull또는 빌드 또는 Jenkinstravis 같은 도구를 이용하든 배포를 했다고 가정하고 nginx.conf 파일을 아래와 같이 변경하고 nginx를 reload 합니다.

upstream docker-express {
server express1:3000;
server express2:3000 down;
}

다시 localhost에 접속해보면 nginx에 express1 에게만 로드 밸런싱을 수행합니다.

express2에도 배포가 끝났다고 가정하고 nginx.confdown 파라미터를 제거하고 reload 합니다.

upstream docker-express {
server express1:3000;
server express2:3000;
}

중단 없이 배포가 끝난 두 서버가 정상적으로 패킷을 받고 있습니다.

Blue-Green

  • step1-배포 전 상태입니다. 기존 서버 2대가 로드 밸런서 뒷 단에서 동작합니다. 배포가 완료된 신규 서버를 준비합니다.
  • step2-배포 할 신규 서버가 준비되면 신규 서버를 로드 밸런서에 추가하고 기존 서버를 제거합니다.

장/단점

장점은 배포 속도가 빠르며, 장애가 발생했을 때 로드 밸런서가 기존 서버를 가리키면 되기 때문에 롤백이 쉽습니다.

단점은 추가적인 서버로 인한 비용입니다.

실습

docker-compose.yml 파일과 nginx.conf 파일은 위에서 Rolling에서 소개한 내용과 동일하게 시작합니다. 아래 명령어로 container들을 시작합니다.

docker-compose up -d

chrome을 열고 localhost에 여러 번 접속합니다. 그 후 express1, express2 로그를 확인하면 균등하게 패킷을 수신한 것을 알 수 있습니다.

이 상태에서 docker-compose.yml 파일에 express3, express4 정보를 추가합니다. (express3, express4는 배포 할 최신버전의 서버라고 가정합니다.)

express3:
build:
context: ./server
container_name: express3
expose:
- "3000"
volumes:
- ./source/express3:/source
- /source/node_modules
restart: "unless-stopped"
express4:
build:
context: ./server
container_name: express4
expose:
- "3000"
volumes:
- ./source/express4:/source
- /source/node_modules
restart: "unless-stopped"

추가 후 다시 docker-compose up -d 명령어를 실행하면 express3, express4 container가 실행됩니다. (제 경우 cache 되어 아래와 같이 빌드 없이 실행되었습니다.)

express3, express4가 실행 중 이지만, 로드 밸런서 역할을 하는 nginx와 연결고리가 없습니다. 따라서 express 전체 로그는 아래와 같이 express1과 express2만 패킷을 받고 있습니다.

정리하면 기존 서버인 express1, express2가 아직 동작중이며 신규 버전의 express3, express4가 준비 된 상태입니다.

이제 nginx.conf 내용을 아래와 같이 변경합니다. express3, express4를 가리키는 docker-new-express 라는 upstream을 추가하고, proxy_pass에 지정합니다.

upstream docker-express {
server express1:3000;
server express2:3000;
}
upstream docker-new-express {
server express3:3000;
server express4:3000;
}

server {
listen 80;
server_name localhost;
location / {
proxy_http_version 1.1;
proxy_pass http://docker-new-express;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}

그리고 아래와 같이 nginx가 변경된 설정을 반영하도록 합니다

다시 chrome에서 localhost에 여러 번 접속합니다. 이제 nginx가 패치가 끝난 신규 서버인 express3, express4 에게 패킷을 전달하는 것을 확인 할 수 있습니다.

이렇게 Blue-Green Deploy를 맛 보았습니다.

마치며

AWS와 CI/CD 도구가 사용되는 실무 환경의 무 중단 배포와는 과정에 많은 차이가 있지만, 기본 원리는 정리할 수 있을 것 같아 이 글을 작성했습니다. 감사합니다.

--

--