본문 바로가기

server

Nginx에 대해서

이번 포스팅에서는 Nginx에 대해서 알아보기로 하자.

 

우선 웹 서버에 대해서 먼저 알아볼 것이다.

 

웹 서버란

인터넷을 기반으로 웹 서버에게 주소를 가지고 통신 프로토콜에 맞게 요청을 하면 정적 파일을 응답으로 받는다.

서버 입장에서는 클라이언트의 요청을 기다리고 웹 요청에 대한 데이터를 만들어 응답한다.

데이터는 웹에서 처리 가능한 html, css, 이미지 등의 정적 파일로 한정된다.

 

 

위 이미지는 웹 서버의 동작 원리를 볼 수 있다.

클라이언트는 자신이 필요한 정적인 페이지의 주소를 가지고 요청을 해 필요한 정적인 데이터를 얻는다.

웹 서버는 정적인 페이지만 처리 가능하다는 한계가 있기 때문에 비즈니스 로직이 필요하면 DB 접근 등 동적인 페이지는 처리하지 못한다.

이를 위해서 WAS인 Web Application Server가 생기게 된다.

더보기

웹 서버가 정적인 페이지만 제공한다는 한정을 하는 것으로 보이지만 규모가 작은 동적인 페이지도 CGI를 통해서 제공할 수도 있다.

웹 서버에서 대표적으로 Apache Server와 Nginx가 있다.

엔진엑스를 알아보기 이전에 아파치도 알아보자.

 

Apache Sever

가장 많이 사용하는 웹 서버이다. Apache 재단에서 만든 Http Web Server이고 기능적인 면에서 뛰어나다.  거의 모든 OS에서 실행되고 다른 소프트웨어 프로젝트와 문서화가 잘되어 있고 통합 지원 등의 이점이 있다.

 

아파치 서버의 구조와 동작 원리를 알아보자.

아파치 서버는 클라이언트의 요청이 들어오면 Connection을 생성한다. 요청마다 Thread를 만들어 처리한다.

클라이언트 요청 하나당 하나의 쓰레드가 처리하는 구조로 사용자가 많을수록 스레드가 생성되고 메모리나 CPU의 낭비가 생긴다.

 

Prefork MPM 방식

클라이언트의 요청에 대해 아파치 자식 프로세스를 생성하여 처리하며 부모 프로세스로 부터 생성된 자식 프로세스는 하나의 스레드를 갖는 구조이며 응답 프로세스를 미리 띄우고 클라리언트 요청에  자식 프로세스가 반응한다.

 

클라이언트 요청에 대해 아파치 자식 프로세스를 생성하며 처리한다.

해당 방식은 아파치의 기본 값이다.

하나의 자식 프로세스 당 하나의 쓰레드를 갖는 구조로 자식 프로세스는 최대 1024개까지 가능하다.

각 자식 프로세스는 독립된 메모리 공간을 가진다.

응답 프로세스를 미리 띄워놓고 클라이언트 요청 시 자식 프로세스가 반응하는 방식이다.

일반적으로 싱글 CPU 또는 Dual CPU에서 성능이 좋다.

 

Apache Server의 한계

만약 대량의 클라이언트가 동시에 커넥션을 맺는다면 CPU와 메모리의 사용량이 증가하고 프로세스와 스레드 생성 비용이 드는 요청에 한계가 존재한다.

 

이를 C10k(Connection 10000 problem)라고 한다. 많은 요청 수가 동시 Connection에는 아파치 서버에 문제가 생기게 된다.

또한 아파치 서버의 프로세스가 blocking 될 때 요청을 처리하지 못하고 처리가 완료될 때까지 대기상태에 있는다.

Keep-Alive 방식으로 해결할 수 있지만 효율이 떨어지게 된다.

이런 구조적인 문제를 해결한 Nginx가 등장하게 된다.

 

Nginx

 

초창기의 Nginx는 아파치 서버의 구조적인 문제를 해결하기 위해 함께 사용되었다.

위 이미지처럼 클라이언트의 요청이 동시에 많이 발생하면 Connection을 Nginx가 유지하며 동시에 정적 파일에 대한 요청을 스스로 처리한다.

만약 클라이언트로부터 동적 파일 요청을 받을때만 아파치 서버와의 Connection을 형성해 아파치 서버의 부하를 줄일 수 있다.

 

구조와 동작 원리

 

Nginx는 master process를 생성하는 worker process를 가진다.

worker process는 실제로 일을 하는 프로세스이며 worker process가 만들어질 때 지정된 listen 소켓을 배정받는다.

해당 소켓에 새로운 클라이언트 요청이 들어오면 connection을 형성하고 처리한다.

이와 같이 connection 형성과 제거, 새로운 요청을 처리하는 것을 이벤트라 하며 이 방식을 이벤트 기반 방식이라 한다.

커넥션은 정해진 Keep-Alive 시간만큼 유지되고 중요한 점은 커넥션이 형성됐다 해도 worker process는 해당 커넥션 하나만 담당하진 않는다.

 worker process로 전달된 큐에 담긴 이벤트들은 비동기 상태로 대기하며 worker process는 큐에서부터 이벤트를 하나씩 꺼내서 처리한다. worker process는 하나의 스레드로 동작하고 이 동작 덕분에 커넥션 마다 쓰레드를 생성한 아파치와 달리 효율적인 자원 사용과 요청 처리율도 증가하게 된다.

 

이벤트 기반 방식의 Nginx는 이벤트 핸들러를 통해 비동기 방식으로 여러 커넥션을 처리한다.

 

흐름

master process는 worker process를 생성하고 실제 커넥션에 대한 처리는 worker process가 처리한다.

worker process는 단일 쓰레드로 수행된다.

Nginx는 os 커널로부터 큐 형식으로 이벤트를 전달받으며 하나씩 처리한다.

 

Nginx 장점

자원의 효율적인 사용: 아파치 서버와 달리 worker process인 단일 스레드를 이용해 자원의 효율성을 보장한다. 이로 인해 적은 메모리와 CPU 사용을 보장한다.

동시적인 요청 처리: 여러 요청을 비동기적인 이벤트 기반으로 동시에 처리한다.

강력한 설정: Nginx의 설정 파일 구문은 간단하고 유연하여 사용자가 서버 동작을 세밀하게 제어할 수 있다.

Reverse Proxy: Nginx는 우수한 Reverse Proxy 기능을 제공하여 백엔드 서버로의 요청을 분산하고 로드 밸런싱을 할 수 있다.

커뮤니티 지원: Nginx는 방대한 커뮤니티와 다양한 문서, 자료, 지원을 제공한다.

 

Nginx 단점

동적인 콘텐츠를 처리할 수 없다.

동적 컨텐츠를 처리하기 위해서는 외부 자원과 연계가 필수다.

 

주요 기능

 

HTTP 서버
정적 콘텐츠, 동적 HTTP 콘텐츠 서빙.
FastCGI, SCGI, uWSGI 서버와의 통합.
URL 리라이팅 및 리디렉션.
SSL/TLS 지원.

 

리버스 프록시 서버
부하 분산(로드 밸런싱): 여러 백엔드 서버로 요청을 분산.
캐싱: 응답을 캐싱하여 성능 향상.
HTTP/2 및 WebSocket 지원.
건강 체크: 백엔드 서버의 상태를 모니터링.

 

메일 프록시 서버
IMAP, POP3, SMTP 프로토콜 지원.
메일 서버 앞단에서 인증 및 프로토콜 처리를 담당.

 

TCP/UDP 프록시 서버
일반 TCP 및 UDP 트래픽 프록시 가능.
부하 분산, SSL/TLS 터미네이션.

 

Nginx 디렉터리 구조
/etc/nginx: Nginx 설정 파일들이 위치한 디렉터리다.
/etc/nginx/nginx.conf: Nginx의 주요 설정 파일로, 전체적인 서버의 동작 방식 및 설정을 포함하고 있다.
/etc/nginx/sites-available, /etc/nginx/sites-enabled: 가상 호스트(Virtual Host) 설정 파일들이 위치한 디렉터리다.

sites-available에 설정 파일을 작성하고, 필요시 sites-enabled로 심볼릭 링크를 생성하여 활성화할 수 있다.
/var/log/nginx: Nginx의 로그 파일들이 위치한 디렉터리다. 주요 로그 파일은 access.log (접속 로그)와 error.log (에러 로그)다.
/usr/share/nginx/html: 기본적으로 Nginx가 서빙하는 정적 파일들이 위치한 디렉토리다.

 

주요 설정 파일과 역할
nginx.conf: Nginx의 주요 설정 파일로, 전반적인 동작을 제어하는 데 사용된다.
sites-available/, sites-enabled/: 가상 호스트 설정 파일들이 위치한 디렉터리로, 웹 서버의 가상 호스트에 대한 설정을 포함한다.
conf.d/*.conf: 추가적인 설정 파일들이 위치한 디렉터리로, 모듈별로 설정을 정의할 때 사용한다.

 

nginx.conf 파일 구조 및 설정 블록
nginx.conf 파일은 중요한 설정들을 블록으로 구성하여 관리한다. 각 블록은 중괄호({})로 감싸져 있다

main : 전반적인 설정을 관리하는 블록으로, 전역 설정들을 포함한다.
Core 모듈 : 코어 모듈은 대부분 환경 설정 파일의 최상단에 위치하며 한 번만 사용할 수 있다. nginx의 기본적인 동작 방식을 정의한다.
http 블록 : 웹서버에 대한 동작을 설정하는 영역으로, server 블록과 location 블록의 루트 블록이다.
server 블록 : 하나의 웹사이트를 선언하는 데 사용된다. 가상 호스팅(Virtual Host)의 개념이다.
location 블록 : server 블록 내에서 특정 URL을 처리하는 방법을 정의한다.
events : 주로 네트워크 동작에 관련된 설정하는 영역으로, 이벤트 모듈을 사용한다.
보통 /etc/nginx/* 아래에 설정파일이 위치해 있고, 로그파일은 /var/log/nginx/* 에 위치해 있다.

 

user  nginx; # 프로세스의 실행되는 권한. 보안상 root를 사용하지 않는다.
worker_processes  1; # 프로세스를 생성할 것인지를 지정. 1이면 모든 요청을 하나의 프로세스로 실행, CPU 멀티코어 시스템에서 1이면 하나의 코어만으로 요청을 처리. 보통 auto로 적용한다.
error_log  /var/log/nginx/error.log warn; # 로그 레벨을 설정. 로그레벨은 [ debug | info | notice | warn | error | crit ] 같은 종류가 있다.
pid        /var/run/nginx.pid; # nginx의 마스터 프로세스 id정보가 저장된다.
events {
    worker_connections  1024; # 하나의 프로세스가 처리할 수 있는 커넥션의 숫자
}
http {
    include       /etc/nginx/mime.types; # 옵션 항목을 설정해둔 파일의 경로를 지정하는데 보통 파일 확장명과 MIME 타입 목록을 지정한다.
    default_type  application/octet-stream; # 옥텟 스트림 기반의 http를 사용한다는 지시어
    
    # orgin 서버라고도 하는데, 여기서는 WAS, 웹 어플리케이션 서버를 의미하며 nginx는 downstream에 해당한다. nginx와 연결한 웹 어플리케이션 서버를 지정하는데 사용되며 하위에 있는 server 지시어는 연결할 웹 어플리케이션 서버의 host주소:포트를 지정한다.
    upstream docker-server { 
        server server:8080;
    }
    # 하나의 웹 사이트를 선언하는데 사용된다. server 블록이 여러 개이면 한 개의 머신(호스트)에 여러 웹사이트를 서빙할수있으며 가상 호스트라고 한다. 실제로 호스트는 1개인데, 여러 개 인것처럼 보이게 만든다.
    server {
        listen 80; # 이 웹 사이트가 바라보는 포트이다.
        server_name localhost; # 클라이언트가 접속하는 서버(주로 도메인). 이것과 실제로 들어온 request의 header에 명시된 값이 일치하는지 확인해서 server를 분기해준다.
  # server 블록 안에서 특정 웹 사이트의 url을 처리하는데 사용한다. 예를 들어 https://web.com/internal과 https://web.com/external로 접근하는 요청을 다르게 처리하고 싶을 때 사용한다. 내부의 root는 웹사이트가 바라보는 root 폴더의 경로를 의미한다.
        location / {
            root /usr/share/nginx/html;
            index index.html index.htm;
            try_files $uri $uri/ /index.html =404;
        }
        location /api {
            proxy_pass         http://docker-server;
            proxy_redirect     off;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        location /socket {
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $host;
            proxy_pass http://docker-server;
        }
    }
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
 server_tokens     off; # 헤더에 nginx버전을 숨기는 기능을 한다. 보안상 off로 설정을 권장
    keepalive_timeout  65; # 접속시 커넥션 유지 시간을 지정한다.
    include /etc/nginx/conf.d/*.conf;
}

 

 

 

 

 

 

 

 

'server' 카테고리의 다른 글

STOMP를 활용한 실시간 채팅 프로그래밍  (0) 2024.06.29
WebSocket Programming 채팅 예제  (0) 2024.06.28
Socket Programming 예제  (0) 2024.06.28
Socket과 WebSocket  (0) 2024.06.28
Nginx 사용해보기  (0) 2024.06.23