Nginx, Docker를 활용한 무중단 배포맛보기
제목 그대로 맛보기
입니다. 실무에선 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
또는 빌드
또는 Jenkins
나 travis
같은 도구를 이용하든 배포를 했다고 가정하고 nginx.conf 파일을 아래와 같이 변경하고 nginx를 reload
합니다.
upstream docker-express {
server express1:3000;
server express2:3000 down;
}
다시 localhost에 접속해보면 nginx에 express1 에게만 로드 밸런싱을 수행합니다.
express2에도 배포가 끝났다고 가정하고 nginx.conf
의 down
파라미터를 제거하고 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 도구가 사용되는 실무 환경의 무 중단 배포와는 과정에 많은 차이가 있지만, 기본 원리는 정리할 수 있을 것 같아 이 글을 작성했습니다. 감사합니다.