V-logue

[Nginx] 프로젝트 속 Nginx와 proxy 본문

발자취

[Nginx] 프로젝트 속 Nginx와 proxy

보그 2022. 7. 31. 18:50

Nginx와 proxy에 대해서 좀 정리를 해보려고 한다.

 

일단 기본적으로

Proxy란 

프록시 서버는 클라이언트가 자신을 통해서 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해 주는 컴퓨터 시스템이나 응용 프로그램을 가리킨다. 서버와 클라이언트 사이에 중계기로서 대리로 통신을 수행하는 것을 가리켜 '프록시', 그 중계 기능을 하는 것을 프록시 서버라고 부른다.

그러니까 Proxy = 대리

즉, 남을 대신하여 일을 처리하는 것을 의미한다.

일반적으로 Proxy라고 하면, 범용적인 의미로 사용될 확률이 높기 때문에 여기서 내가 말하고자

하는 Proxy는 Network Proxy이다.

결국 Proxy Server란 대신 일을 처리해주는 서버를 말한다.
일반적으로 Proxy Server는 캐시/보안/트래픽 분산 등 여러 장점을 가진다.

Proxy의 종류에는 3가지 종류가 있다.

 

1. Forward Proxy

일단 일반적으로 사람들이 말하는 Proxy는 Forward Proxy를 의미한다.

Forward Proxy는 Client와 Server의 사이에 위치해 있다.

                          Client <ㅡㅡ> Forward Proxy <ㅡㅡ> Internet <ㅡㅡ> Server

 

1. Forward Proxy의 첫번째 특징은 캐싱에 있다.

불필요하게 Server나 DB에 접근할 필요가 없기 때문에 외부 요청이 감소하고

이를 통해서 병목 현상을 방지한다.

 

2. 두번째 특징은 익명성에 있다.

 클라이언트가 요청을 보냈지만 마치 Forward Proxy가 요청을 보낸 것처럼 가장해

서버에 정보를 전달하고 요청할 수 있다. Server가 응답 받은 요청을 누가 받았는지 알지 못하기 때문에

Server가 받은 요청은 Proxy IP가 된다.

 

2. Reverse Proxy

Reverse Proxy도 Client와 Server의 사이에 위치해 있지만, 조금 다르다 Reverse Proxy는

서버와 더 가깝다.

                          Client <ㅡㅡ> Internet <ㅡㅡ> Reverse Proxy <ㅡㅡ> Server

1. Reverse Proxy의 첫번째 특징은 캐싱에 있는데, 이는 Forward Proxy와 같다.

 

2. 두번째 특징은 바로 보안에 있다. 바로 Server 정보를 Client부터 숨길 수 있다.

Client는 서버 정보를 요청할 때, 서버들에 대해서 Direct하게 접근할 수 없고 Reverse Proxy에 요청을 전달하게 된다.

Reverse Proxy가 이 요청을 다시 서버에 전달하고 실제의 서버들의 IP 등이 클라이언트로부터 보호된다.

 

3. 세번째 특징은 Load Balancing이다.

Load Balancing이란 부하분산으로, 해야할 작업을 나누어 서버의 부하를 분산시키는 것을 말한다.

서버가 처리해야 하는 데이터가 총 9개 있다고 치면,

다음과 같이 부하분산 작업을 Load balancer가 수행한다.

 

Load balancing에는 여러가지 종류가 있겠지만, 그중에서 백엔드 개발자는 L4와 L7정도만 알면 된다.

L4는 Transport layer(IP & Port) Level에서 Load balancing (TCP/UDP)을 한다.

예를들어, vlogue.tistory.com으로 접속을 한다고 하면 서버 A와 서버 B로 로드밸런싱 하게 되는데

80port로 접근을 한다고 치면 서버A와 B로 요청을 고르게 분배하는 것이 L4이다.

L7는 Application Layer(User Request) Level에서 Load balancing(HTTPS/HTTP/FTP)을 한다.

예를들어, vlogue.tistory.com으로 접속을 한다고 하면 서버 A와 서버 B로 로드밸런싱 하게 되는데

L7은 url경로에 따라, /api or /category 등 요청의 방법에 따라서 어떤 서버에 로드 밸런싱을

수행하게 될지 결정하는 것이 L7이다.

 


그리고, Nginx에 대해서 설명해야 하는데

Nginx란

nginx는 웹서버이자, Reverse Proxy, Load balancer 그리고 http 캐시로도 쓰일 수 있는 소프트웨어를 말한다.

요청에 응답하기 위해서 이벤트 기반 구조를 채택했고,

웹서버 분야에서 높은 지분을 가지고 있다.

Nginx가 벤치마크 테스트를 했을 때 가볍고 성능이 좋아서 사용했다.

그리고 최신기술이다(참고로 Apache는 1995, Nginx는 2004)

 

Nginx와 쌍벽을 이루는 소프트웨어인 Apache는 버그가 많던 어떤 프로그램을 수정하는 과정에서

만들어진 프로그램이다.

Apache서버는 요청(Request)가 들어오면, 커넥션을 형성하기 위해 프로세스를 생성한다.

그래서 새로운 클라이언트의 요청이 들어올 때마다 새로운 프로세스를 생성한다.

그런데 이런 프로세스를 만드는 과정이 오래 걸리다보니

미리 프로세스를 만들어 놓고 사용하는 Prefork방식을 사용하게 된다.

만들어 놓은 프로세스가 모두 사용된다면 추가로 프로세스를 생성하고 사용했다.

 

하지만, 이전과 달리 1999년부터 요청이 많이 생기면서 서버에 동시에 연결되는 커넥션이 많아졌을 때

더이상 커넥션을 형성하지 못하는 문제가 발생했다.(C10K문제)

Nginx는 C10K문제와 깊은 연관이 있다.

 

여기서 동시에 연결되는 커넥션 수는 초당 요청 처리 개수와는 다르다.

초당 요청 처리 수는 그냥 단순히 초(second)마다 얼마나 많은 처리를 하는지를 나타내는 지표일 뿐이고,

동시에 연결되는 커넥션 수는 서버가 한 시점에 얼마나 많은 클라이언트와 커넥션을 맺고

있는지를 알려주는 숫자다.

하나의 클라이언트는 하나의 커넥션을 통해 여러 요청을 보낼 수 있었는데, 커넥션은 긴 시간동안 유지될 수 있었다.

 

하지만, 이런 순간에 커넥션 숫자가 10000단위가 넘어가면 더이상 서버는 커넥션을 형성하지 못하게 된다.

하드웨어는 문제가 없었지만, Apache서버는 문제가 있었다.

커넥션 숫자가 늘어난다는 것은 프로세스 생성 숫자가 많아지고 곧 메모리부족 현상과 이어진다.

CPU 부하도 지나치게 많은 커넥션을 감당하게되 발생했다.

 

Nginx는 바로 이런 Apache서버를 보완하는 과정에서 등장하게 됐다.

Nginx는 초반에 등장했을 때만 해도 Apache서버 앞에서 동시 커넥션을 유지할 수 있도록 Apache서버의

부하를 줄이도록 앞단에서 요청을 받아줬다.

 

Nginx의 구조를 살펴보면, 가장 먼저 Master Process가 있다. 설정 파일을 읽고, 설정 파일에 맞게

Worker Process를 생성한다. Woker Process가 만들어 질때 각자 지정된 Listen 소켓을 지정받는다.

새로운 클라이언트의 요청이 소켓에 들어오게되면, 커넥션을 형성하고 그 요청을 처리한다.

물론 생성된 프로세스가 하나의 커넥션만을 형성하고 처리하는 것은 아니고, 커넥션으로부터의 요청이 없을 때

다른 커넥션으로부터 들어온 요청을 처리하기도 한다. 그리고 이런 새로운 요청을 형성, 제거, 새로운 요청을

처리하는 것을 이벤트라고 부르고 이는 비동기적으로 형성된다..

 

Apache서버와 비교해 봤을 때, 요청이 없을 때 방치되던 Apache서버에 비해서 서버자원을 효율적으로

쓰는 샘이다. 한편, 하나의 작업이 시간이 오래걸리는 요청일 때 Nginx는 이런 긴 시간을 필요로 하는 작업들을 

위한 스레드 풀을 만들어 놓고 이벤트를 위임한다. 이런 Worker process는 CPU의 코어만큼 생성된다.

이게바로 이벤트 기반 구조이다.

 

한편, 단점도 있는데 개발자가 기능 추가를 시도하다 Worker Process를 종료하게 되는 일이 발생할 수 있다.

관련된 커넥션과 요청을 처리할 수 없게 된다.

 

여기까지가 기본적인 설명이고,


이번 renDev 프로젝트에서 진행한 Nginx 사용 방식은

Node.js + elb + nginx인데, 이 방식은 elb를 통해 전달된 트래픽이 서버 앞에 위치한 Nginx로 들어오고

Nginx가 뒤쪽에 위치한 서버로 전달시키고 이런 과정을 통해 http 요청이 들어 왔을 때,

http 요청을 https로 Redirect로 EC2에게 전달하는 역할을 위해 사용됐다.

 

물론 elb 만으로도 https로 redirect하는 동작을 수행할 수 있지만, 굳이 Nginx까지 사용하는 이유는

Nginx가 서버 앞단에서 먼저 데이터를 받아주고 걸러주기 때문에 보안상 우수하다는 이유도 있고,

https로 리디렉션 설정을 하게 된다면, $http_x_forwarded_proto를 통해 https인지 아닌지

알 수 있기 때문에 만약 https가 아니라면 https로 리디렉션 작업까지 대신 수행해서 elb로 보내주는

이런 일련의 과정 덕분에 조금 더 보안적으로 우수하고, ssl인증서를 통해 서버를 점검하는 과정에서

생길 수 있는 불필요한 에로사항을 막을 수 있다는 장점이 있다.

 

궁극적으로 nginx를 이런식으로 사용하는 것은 Nginx를 앞단에 둠으로써 Express가 사용하는 실제 포트를 숨기고 Nginx의 80포트를 통해 Reverse Proxying 을 함으로써 저런 보안 이슈를 방지할수도 있고 Nginx의 우수한 기능을 활용할 수 있는 장점이 있다.

 

 server {
        listen       80;
        server_name  rendev.link;
        # redirect https setting
        if ($http_x_forwarded_proto != 'https') {
        return 301 https://$host$request_uri;
        }
        location /api {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header HOST $http_host;
        proxy_set_header X-NginX-Proxy true;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # port setting , 서버의 port와 동일한 port로 pass 시켜야 합니다.
        proxy_pass http://127.0.0.1:3000;
        proxy_redirect off;
        proxy_connect_timeout 300s;
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
        proxy_buffers 8 16k;
        }

80번 포트에 server_name rendev.link;로 들어온 요청을 nginx가 받아, 로컬에서 오픈되어 있는
"proxy_pass http://127.0.0.1:3000/;"로 포트포워딩을 수행한다.

 

+++++

Nginx는 로드밸런싱 기능도 대신 수행할 수 있다.

Load Balancing에 대해선 위에서 설명했으니, 설명은 생략하겠다.

로드 밸런싱을 통해 물리적이고 논리적인 방법인 Scale out을 통해서 트래픽을 나눔으로서

서버에 전해지는 부담을 줄일 수 있다.

 

기본적으로 nignx에서 로드밸런싱은 Reverse proxy기반으로 돌아가게 된다.

http {
	include /etc/nginx/sites-enabled/example.com
}

로드밸런싱을 위한 Vritual host file을 nginx 폴더에 만들고,

백엔드 서버의 IP 주소를 upstream Module에 추가 한다.

upstream rendev {  // backend자리에 이름
    least_conn;     // algorithm (default : Round Robin)
    server 127.0.0.1:3000; 
    server 127.0.0.1:3001; 
    server 127.0.0.1:3002; // Nginx로 요청시 우회할 server
}

server {
  listen 80; // 80번 port로 들어온 요청을 nginx가 받아서 proxy_pass로 전달
  
  location / {
    proxy_set_header Host $host; // Client host set
    proxy_set_header Connection ""; //upstream서버를 Using declare(반드시 필요한 코드)
    proxy_pass http://rendev; 
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-NginX-Proxy true;
    proxy_redirect off;
    proxy_connect_timeout 300s;
    proxy_read_timeout 600s;
    proxy_send_timeout 600s;
    proxy_buffers 8 16k;
  }
}

 

80번 포트로 요청이 들어오면, [ 3000, 3001, 3002 ]과 같이 열려 있는 3개의 서버로 로드밸런싱이 진행된다.

 

++++ 

 

하나 배웠다. rendev 블록에 있는 여러개의 값들은 한대의 가상 서버에 포트 여러개 열면된다.

이 사실을 알기까지 얼마나 많은 시간이 걸렸는지..... (22.08.09)

 

근데 이렇게 포트를 여러개 열어서 사용하는 것이 EC2 가상 서버에 어떤 부담을 줄일 수 있는지는

나도 잘 모르겠다. 추가로 찾아봐야할 문제

 

+++++

 

하나의 가상 서버에 포트를 여러개 열어서 사용하는 것이 어떤 장점이 있는가? 

==>>> 멀티 프로세싱 작업으로 동시 다발적인 프로세싱이 이루어져 서버의 부하를 줄여준다

라는 답변이 있었다. 먼가 납득가는 느낌

그니까 일종의 비동기적으로 처리해 동시다발적으로 기능별로 처리해주는 느낌적인 느낌이다.

 

+++++

 

한대의 머신에서 멀티프로세싱 하는 경우는 노드가 있는데, 노드에서 자체적으로 지원하기 때문에 엔진엑스로

멀티 프로세싱 로드밸런싱을 하려고하면 오버헤드만 생긴다고 한다. 즉 우리 프로젝트에는 쓸모없다는 소리

 

+++++++

 

이렇게 사용하는건 무슨의미인가 싶고, 이걸 제대로 사용하고 싶다면 2대의 머신을 nginx로 이어줘야

한다는 소리같은데 정확히 어떻게 이어서 사용한다는지 모르겠다.

 

AWS 도큐먼트 실습가이드 참고,

 

알고리즘의 upstream 방식에는 총 4개의 값이 존재한다.

Round-robin 서버로 들어온 요청을 순서대로 Upstream 서버에 나눠주는 방식, 여러 대의 서버가 스펙이 비슷할 때 유용하게 사용할 수 있다.
Weighted Round Robin 각 서버에 가중치를 매기고, 가중치가 높은 서버에 우선적으로 배분하는 방식, 여러 서버의 스펙이 상이할 때 사용할 수 있다.
IP Hash 사용자의 IP주소를 해싱(hashing)해서 특정 서버로 보내주는 방식이다. 사용자가 서버를 사용할 때 동일성을 보장해주는 방식이다. hash ( Key ) 형태로 사용 된다. (hash $remote_addr 이는 ip_hash와 같다.)
random 임의의 순서대로 요청을 나눠주는 방식이다.
least_conn 현재 요청에 대한 연결이 가장 작은 서버를 선택적으로 분배하는 방식이다.
least_time 현재 요청에서 연결수가 가장 적으면서 평균 응답시간이 가장 적은 쪽에 분배하는 방식이다.

++++

 

Nginx 왜 사용하는가, 캐싱기능 + 외부에서 어디서 온 데이터인지 알 수 없게 끔
암호화를 수행해주는 보안기능이 존재하다. + 외부에서 온 데이터를 서버 앞에서
먼저 받아주기 때문에 불순한 요청을 걸러주는 역할을 한다 .

Nginx를 proxy 서버 앞단에 두고 node.js를 뒤에 둔다면 버퍼오버플로우
취약점에 의한 공격을 방지할 수 있다.

버퍼 = 메모리, 메모리의 한계점을 넘어가면 오버플로우 

was로 node를 두고 그 앞에 nginx 

클라이언트가 http로 접속을하면 80포트, https로 접속하면 443으로 접속하는데
nginx가 80포트로 들어오면 443으로 redirect으로 리다이텍트

http로접속해도 https로 접속한 효과를 가질 수 있다.

왜 Nginx를 사용했는가, Nginx가 서버정보를 Client로부터 숨길 수 있기 때문이다.
클라이언트가 서버 정보를 요청할 때, 서버에 Direct하게 접근할 수 없고
Reverse Proxy로 요청을 전달하게 되고, 이 요청이 다시 서버에 전달하게 된다.
외부에서 요청이나 응답이 어디에서 온 것인지 알 수 없게 끔 만들어 주기 때문에 암호화를 수행하는 보안기능과 http 80포트로 요청이 들어오면 https 443포트로
Redirect 해주는 기능 때문에 https 서버를 효과적으로 구축할 수 있어서 사용했다.

 

+++++

 

location을 통해 기능적으로 분할해서 포트를 사용할게 아니라면, 포트를 숨기는 의미가 없다.

어처피 프론트에서 /api에 443포트로 접근하면 되기 때문, 사용하는 의미가 있을 떄도 있지만

우리 프로젝트는 없다.

Comments