Nginx, 로드밸런싱

표인수·2021년 5월 24일
0
post-thumbnail

Nginx

웹 서버 소프트웨어로 아래 기능들을 지원한다.

  • 정적 파일과 인덱스 파일 표현, 자동 인덱싱 기능.
  • 캐싱을 통한 리버스 프록시
  • 로드 밸런싱
  • 고장 진단
  • SSL 지원
  • 캐싱을 통한 FastCGI 지원
  • Name-, IP-기반 가상서버
  • FLV 스트리밍
  • MP4 스트리밍 모듈을 이용한 MP4 스트리밍
  • 웹페이지 접근 인증
  • gzip 압축
  • 10000개의 동시 접속을 처리할 수 있는 능력
  • URL 다시쓰기 (URL rewriting)
  • 맞춤 로깅
  • 서버 사이드 기능 포함
  • WebDAV

Nginx를 이용한 로드밸런싱

평소에도 부하 분산처리에 관심이 있어서, 마침 사용 할 기회가 생긴 Azure 서버를 이용하여 로드밸런서를 구축해 봤다. 전문 지식이 얕고 로드밸런서가 작동하는걸 보기 위해 만들어서 많은 처리를 하지는 않았다.

Azure 구성

둘 다 같은 구성(2cpu, 4gb ram) 으로 os의 차이만 있다.
로드 밸런싱을 위해, 가상 네트워크 기능을 이용하여 로컬 네트워크에 있는 것처럼 구성해주었다.

  • 우분투 서버 x 1
  • 윈도우 서버 x 1

Nginx.conf

로드 밸런싱 스케쥴러는 기본 Round Robin 방식을 사용했다.

...

        upstream TPS {
            server localhost:3000;
            server localhost:3001;
            server 10.0.0.4:3000;
            server 10.0.0.4:3001;
        }

        server {
            listen 80;
            server_name SERVER_ADDR;

            location / {
                proxy_pass http://TPS;
            }
        }
...

서버 코드 (Typescript)

Hello World + 포트번호 를 반환하는 서버코드이다.

import express from 'express';
const server = express();
const port = process.argv[process.argv.length-1];

server.get("/", (req, res) => {
    return res.send("Hello World"+port);
});

server.listen(port, () => {
    console.log("Server on", port, process.pid);
})

TPS 테스트 코드 (Typescript)

http Request를 빠르게 반복적으로 보내고, 결과를 측정해야 하기에 속도 저하가 걱정되어 외부 httpRequest 라이브러리를 쓰지 않고 Node.js 내장 http 라이브러리를 사용했다.

사용자가 종료하기 전까지 계속 요청을 보내며 동기로 실행된다.
비동기로 하려 했지만 socket hang up 문제와 너무 빠른 반복속도로 인해 멈추는 현상이 있었다.

import http, {RequestOptions} from 'http';

let avgDelay = 0;
let Loop = 0;
let MakeReq = true;
let Timer = 0;

const options = {
	hostname: 'SERVER_ADDRESS',
	path: '/',
	port: '3000',
	method: "GET"
};

process.on('SIGINT', () => {
    MakeReq = false;
    console.log("\nResult")
    CalculateTPS();
    process.exit();
});

function Req(options: RequestOptions): Promise<void> {
    return new Promise(resolve => {
        const Time = Date.now();
        http.request(options, () => {
            avgDelay += Date.now() - Time;
            return resolve();
        }).end();
    })
}

function CalculateTPS() {
    return console.log("TPS:", Math.round(Loop/((Date.now()-Timer)/1000)), '|', Math.round((Date.now() - Timer)/1000), 'Sec', '|', "Avarage ResponseTime:", parseFloat((avgDelay/Loop).toFixed(2)), "ms");
}

(async () => {
	console.log("Testing", options.hostname, options.port, options.path, options.method);
    Timer = Date.now();

    setInterval(CalculateTPS, 1000);

	while (MakeReq) {
        await Req(options);
        Loop++;
	}
})();

LoopTest (Typescript)

미리 지정된 수만큼 요청을 보내고, 평균 요청 지연시간을 구한다.

import http, {IncomingMessage, RequestOptions} from 'http';

let avgDelay = 0;
// let errorCount = 0;
let finishCount = 0;
let Loop = 500;

const options = {
	hostname: 'SERVER_ADDRESS',
	path: '/',
	port: '3000',
	method: "GET"
};

(() => {
	console.log("Testing", options.hostname, options.port, options.path, options.method);
	for (let i = 0; i <= Loop; i++) {
		const Timer = Date.now();
		function ResponseHandler(response: IncomingMessage) {
			avgDelay += Date.now() - Timer;
			finishCount++;
			if (i == Loop) {
				return console.log(avgDelay / Loop)
			}
		}
		http.request(options, ResponseHandler).end();
	}
})();

결과

이상하게도 TPS 테스트 결과에서는 로드밸런싱을 하지 않은 쪽이 더 좋은 결과를 보여줬다.

그러나 Loop 테스트 결과는 확실한 차이가 보였다.
로드밸런싱이 적용된 쪽의 평균 응답 지연시간이 짧게 나왔다.

요청 수를 크게 잡으면 socket hang up 오류가 나면서 테스트 코드가 멈춰버리기 때문에 매우 큰 규모의 테스트는 아직 진행하지 못했다.

결론

아마 TPS 테스트는 동기로 진행되어 연속적인 요청을 처리하는 능력이 중요시 된것 같고, Loop 테스트는 동시에 여러 작업을 처리하는 속도가 돋보인 것 같다.

추가로 같은 자원을 사용하는 서버에서 CPU 쓰레드 수만큼 서버 프로세스를 실행시키고 로드 밸런싱을 진행해 보았는데 결과는 똑같이 나왔다. 같은 자원을 공유하기 때문에 로드 밸런싱의 의미가 없는것 같다.

나중에 다시 확인해볼 거

  • 로드 밸런서용 서버를 따로 구축하고 측정했을때의 성능 수치가 더 높게 나오는지
  • 로드 밸런싱 스케쥴러간의 성능 차이
  • 더 복잡한 서버 코드에서의 성능 차이
  • TPS 측정 코드, 요청을 보내는 부분의 동기/비동기에 따른 결과 차이
  • Loop 테스트 코드의 socket hang up 문제 해결

Nginx 정보 출처

0개의 댓글