NGINX를 통한 Blue-Green Deployment와 WebSocket Reverse Proxy

생선스프·2020년 5월 6일
0
post-thumbnail

개발을 하기 전 무중단 배포시 기존의 서버 컨테이너가 교체될 때 소켓의 트래픽도 체크되는지 궁금해졌고 확인이 필요하여 테스트를 진행해보려고 합니다.

Blue-Green Deployment

이 글을 참고했습니다.

이게 뭔가요?

Blue-Green 배포가 무엇인지 우선 알아봅시다.

  1. 아래와 같이 운영중인 서비스가 있습니다.

  2. 새로운 버전의 서버를 실행하고 Health Check를 통해 서버가 제대로 시작된 것이 확인되면 로드밸런서에 Server2를 연결합니다. 이때 클라이언트의 새로운 요청은 모두 Server2로 연결됩니다.

  3. 기존 서버인 Server1로 가는 트래픽이 더 이상 없다면 Server1을 로드밸런서에서 제거합니다.

  4. Server1을 제거합니다.

이런식으로 간단하게 무중단 배포를 할 수 있습니다.

문제점

일반적으로 서버는 아래와 같이 같은 역할을 하는 여러개의 서버가 묶여서 운영됩니다.

그런데 여기서 새로운 버전의 서버를 배포한다면 아래와 같이 되는데..

서버의 자원이 두 배로 필요하기 때문에 부담되는 방법일 수 있습니다.

NGINX로 어떻게 할 수 있을까

NGINX의 Graceful한 Reload

NGINX의 공식 문서에 따르면 NGINX가 Reload 신호를 받았을 때

  1. 새로운 설정 파일의 문법을 확인하고, 적용시킵니다.

  2. 성공했다면 master process는 새로운 woker process를 실행시키고 old worker process에 shutdown 신호를 보냅니다. 새로운 요청은 새롭게 만들어진 worker process로 전달됩니다.

  3. 실패했다면 old worker process를 유지합니다

NGINX의 동작 방식이 Blue-Green Deployment를 손쉽게 할 수 있겠네요!

해보자!

테스트는 간단하게 해보려고 합니다.

소켓에 연결하면 1초마다 자신의 서버 번호를 보내는 앱을 작성하고,
NGINX에 서버 1을 연결하고 클라이언트에서 소켓에 접속한 뒤,
NGINX에 서버 2를 연결하고 클라이언트에서 소켓 연결을 끊으면
그 때부터 서버 2로 정상적으로 연결 되는지 확인.

여기서 제가 초점을 맞추는 것은 서버2로 reload 했을 때 서버1과 소켓 연결이 끊어지지 않는가 입니다.

Node.js 앱 실행

Node.js 앱 두 개를 포트만 바꿔서 올리겠습니다.

우선 아래와 같이 코드를 작성해줍니다.

const http = require('http');
const express = require('express');
const socketIO = require('socket.io');

const app = express();
const io = socketIO();

const port = process.argv[2];
const serverNo = process.argv[3];

const httpServer = http.createServer(app);

httpServer.listen(port, () => {
  console.log(`Http server is running at ${port}`);
  console.log(`ServerNo : ${serverNo}`);
});
io.listen(httpServer);

app.get('/', (req, res) => {
  res.send(`
  <html>
    <script src="/socket.io/socket.io.js"></script>
    <script>
      const socket = io.connect();

      let cnt = 0;
      socket.on('test', data => {
        console.log(data, cnt++);
      });
    </script>
    ${serverNo}
  </html>
  `);
});

io.on('connection', socket => {
  const interval = setInterval(() => {
    socket.emit('test', { serverNo });
  }, 1000);

  socket.on('disconnect', () => {
    clearInterval(interval);
  });
});

그리고 앱을 실행시켜 주도록 하겠습니다.

sudo apt install nodejs
sudo apt install npm

npm init
npm install express socket.io

screen -S s1
node index 3000 1
(Ctrl + A + D)

screen -S s2
node index 3001 2
(Ctrl + A + D)

Nginx Config

Nginx에서 WebSocket Reverse Proxy를 사용하기 위해서는 아래의 설정이 중요합니다

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";

전체 설정은 아래와 같습니다.

server {
    listen 80;
    
    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header X-forwarded-For $proxy_add_x_forwarded_for;
    }
}

준비 끝!

이제 생각대로 되는지 확인해보죠!

브라우저로 서버에 접속하니 콘솔에 1초 간격으로 로그가 잘 찍히고 있습니다.

그럼 이제 Nginx Config 파일에서 두 번째 서버로 포트를 바꾸고 reload를 해줍니다.

sudo service nginx reload

그리고 처음에 열어두었던 브라우저의 콘솔을 확인하면 아직 1번 서버와 연결이 끊어지지 않은 것을 확인할 수 있습니다.

이때 새 탭을 열어서 확인하면 2번으로 로그가 찍히고 있습니다.

둘을 같이 보면 이렇습니다.

마지막으로 첫 번째 탭을 새로고침하면 2번이 잘 뜹니다.

문제점

1번으로 가던 트래픽이 완전히 제거된 것을 확인하고 서버를 내려야 하는데 트래픽 체크를 하지 않았습니다.
Nginx에서 할 수 있는 건지, 아니면 서버에서 모니터링을 따로 해야 하는지 알아봐야겠네요

또한 이 글에서는 Health Check를 하지 않았습니다.

끝!

reload시 1번 소켓이 끊어지는 것을 걱정했는데 다행히 잘 돼서 기분이 좋네요!
글로만 읽던 것을 직접 눈으로 확인하니 느낌이 새롭습니다.

읽어주셔서 감사합니다 :D

profile
뒹굴거리는거 좋아하는 학생

0개의 댓글