본문 바로가기

Infra

[Nginx] Nginx란

Nginx란

웹 서버의 일종으로 Nginx는 웹 서비스, 리버스 프록시, 캐싱, 로드 밸런싱, 미디어 스트리밍 등을 위한 오픈소스 소프트웨어이다. 이벤트 기반 아키텍처로 적은 자원으로 빠른 처리가 가능하다.

간단히 웹서버와  웹 애플리케이션 서버에 대해 살펴보겠다.

Web Server

  • 웹 브라우저의 클라이언트로부터 HTTP 요청을 받아 정적인 콘텐츠를 제공(HTML, CSS, 사진) →Web Server선에서 처리
  • 클라이언트가 동적인 콘텐츠 제공을 요청하면 WAS에게 그 요청을 전달한다.
  • WAS에서 처리한 결과를 클라이언트에게 응답한다.
  • 대표적인 웹서버로는 Apache, Nginx 등이 있다.

WAS(Web Application Server)

  • 클라이언트 요청에 대한 동적인 콘텐츠를 제공
  • DB 접근, 회원가입, 로그인 등의 처리 로직을 담당
  • Web Container 또는 Servlet Container라고도 불린다.

웹 서버와 웹 애플리케이션 서버를 따로 둠으로써 웹 서버는 정적인 콘텐츠만 제공하도록 하여 서버 부하를 방지하고 WAS는 동적인 콘텐츠 제공을 위해 필요한 처리만 하게 함으로써 분산처리로 효율성을 높였다.

 

Apache VS Nginx

Nginx가 생기기 이전 웹 서버의 역할은 Apache가 담당했었다. 이 둘의 차이는 클라이언트의 요청을 처리하는 방식이다.

Apache Server

1995년에 만들어진 아파치 서버는 NCSA HTTPd로부터 파생되었다. NCSA HTTPd의 경우 단일 프로세스/쓰레드 모델로 설계되어 있어 효율성과 성능문제가 있었고, 새로운 요구사항과 보안 이슈에 대응하는 데 어려움이 있었다. 따라서 몇 가지를 문제를 해결하고, 구조를 변경하면서 Apache 서버를 개발하게 되었다.

 

Apache의 동작방식

Apache 서버는 프로세스 또는 쓰레드를 생성하여 클라이언트 요청을 처리할 수 있도록 설계되었다. 클라이언트로부터 요청이 들어오면 커넥션을 형성하기 위해 프로세스를 생성한다.(이는 유닉스 계열 OS가 네트워크 커넥션을 형성하는 모델을 그대로 적용한 것) 하지만 커넥션을 형성할 때마다 프로세스를 매번 생성하니 시간이 오래 소요되기에 미리 프로세스를 만들어 놓고 사용하는 Prefork 방식을 사용했다. 만약 커넥션 요청이 많아져 프로세스가 모두 할당되었다면, 추가로 프로세스를 생성하여 사용했다. 이로써 클라이언트 요청이 증가할 때 빠르게 대응할 수 있으며, 프로세스의 생성과 종료에 따른 오버헤드를 줄일 수 있었다.

 

하지만 시대가 발전하며 인터넷 트래픽의 양도 늘어나게 되었다. 커넥션을 형성하기 위한 프로세스를 생성하는 것은 아파치 서버의 구조적으로 한계가 있어 다음과 같은 문제가 발생했다.

 

C10K Problem [참고]

Concurrent 10K Users Problem이라고 불리는 문제는 하나의 웹서버에 10000개의 클라이언트의 요청을 동시에 처리할 수 없어 발생했다. 커넥션 요청마다 프로세스를 할당하기에 메모리 및 CPU의 낭비가 심했다. 이러한 문제가 있어 아파치 서버는 확실한 한계가 존재했다.

Nginx

Nginx가 나온 초기에는 아파치 서버를 보완하기 위한 목적으로 만들어졌다. Nginx는 비동기 처리로 동시 커넥션에 대해 효율적으로 처리가 가능하고, 이벤트 기반 아키텍처이기에 하나의 쓰레드 또는 하나의 프로세스가 많은 연결을 처리할 수 있다. 아파치 서버에서 발생하는 문제점을 Nginx를 통해 극복하려고 했다. 정적 리소스 요청과 동시 커넥션을 Nginx에서 처리하고, 이후의 클라이언트의 동적 파일 요청만 아파치가 처리하여 넘겨주었다. 

Nginx의 동작방식

Nginx에서는 Master ProcessWorker Process가 중요한 역할을 수행한다. 서버 시작 시 Master Process는 설정 파일을 읽고, Worker Process를 생성한다. 생성된 Worker Process는 클라이언트의 요청을 받아 처리한다. 커넥션은 Keep-Alive 속성 때문에 일정 시간 연결된 상태를 유지하게 되는데, 이때 Worker Process는 그 시간만큼 커넥션을 담당하고 있지 않는다. 형성된 커넥션에 아무런 요청이 없을 경우 다른 요청 작업을 처리하고, 해당 커넥션에 요청이 들어왔을 경우 다른 프로세스가 처리한다. 즉, 이벤트가 들어왔을 때 프로세스가 처리함으로써 자원을 효율적으로 관리할 수 있게 되었다.

 

Master Process

  • 부모 프로세스
  • 설정파일을 읽고, 서버 초기화를 담당
  • Worker Process를 생성하는 역할
  • 설정을 변경하거나 재시작 시 Master Process는 감지하고, 새로운 Worker process 재생성
  • Worker Process 모니터링

Worker Process

  • 자식 프로세스
  • 클라이언트의 요청을 처리하는 주체
  • 독립적으로 동작하며, 다수의 클라이언트 연결을 비동기적으로 처리(이벤트 기반 아키텍처)
  • Master Process의 지시에 따라 동적으로 생성되거나 종료되며, 독립적으로 동작

Nginx의 기능 및 특징

1. 장애 대응하기

Upstream Module을 사용하여 서버 상태를 확인하고, 이상이 있을 시 백업 서버를 활성화시켜 장애에 대응한다.

  • max_fails=n : n으로 지정된 횟수만큼 연결 실패하면 서버 다운된 것으로 판단.
  • fail_timeout=n : max_fails가 지정된 상태에서 n값이 설정만큼 서버가 응답하지 않으면 서버가 다운된 것으로 판단.

2. Proxy

[프록시에 대해 궁금하다면?]

  • 캐싱 : 정적 리소스를 웹서버에 저장하여 클라이언트 요청 시 WAS를 거치지 않고 바로 제공
  • 로드밸런싱 : 여러 대의 서버에 클라이언트의 요청을 분할하여 처리
  • 서브 도메인 처리 : 하나의 웹 서버에서 여러 서비스를 호스팅

3. SSL 터미네이션

Nginx를 통해 SSL 터미네이션 역할을 수행할 수 있다. SSL 터미네이션은 SSL/TLS 연결을 웹 서버에서 처리하여 일반 HTTP 연결로 변환하는 프로세스를 의미한다. 클라이언트와 웹 서버 간의 통신에서 SSL/TLS 암호화를 해제함으로써, 웹 서버는 일반 HTTP 트래픽을 처리하고 SSL 인증서를 관리할 수 있게 된다.

SSL에 대해 궁금하다면?

  • 서버 부담 감소
  • 유연성 및 관리 용이성
  • 로드 밸런싱 및 스케일링

4. 그 외

  • HSTS
  • CORS 처리
  • 리다이렉션
  • TCP/UDP 커넥션 부하 분산
  • HTTP/2 [참고]

Nginx 설정파일[참고]

Nginx의 설정파일은 /etc/nginx/nginx.conf에 위치한다. 설정은 디렉티브(directives)로 관리되는데 블록{}으로 감싸져 있는 블록 디렉티브, 블록으로 감싸져 있지 않은  심플 디렉티브로 분류된다. 설정의 끝은 세미콜론으로 표시하며, include를 사용해 설정 파일을 분리할 수도 있다.

# user: worker process 를 실행할 사용자를 설정. 사용자에 따라서 권한 등이 달라질 수 있다.
user  nginx;

# worker_processes: worker process 의 개수 정의 defalut는 1
worker_processes  auto;

# error_log: nginx logging 설정. log file 과 logging level 을 설정한다.
error_log  /var/log/nginx/error.log warn;

# pid: main process 의 pid 를 저장할 파일을 지정한다.
pid /var/run/nginx.pid;

# events 는 nginx 서버와의 접속과 관련된 설정들을 정의하는 지시어 블럭이다.
events {
    # worker process 에 최대 동시 접속 수를 지정한다. 워커 프로세스가 4이고 커넥션이 1024라면 4*1024의 커넥션을 처리할 수 있다.
    worker_connections  1024;
}

# http 접속에 대한 설정을 하는 지시어 블럭. 웹, 프록시 관련 서버 설정
http {
    # 외부의 파일을 읽어와서 파일에 설정된 지시어와 블럭들을 설정에 적용한다.
    Include /etc/nginx/mime.types;

    # response 의 MIME 타입을 정의한다.
    default_type application/octet-stream;

    # log_format: log 형식을 설정한다.
    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: logging 파일을 설정한다. Logging 형식은 log_format 에서 정의한 main 형식으로 저장한다.
    access_log  /var/log/nginx/access.log  main;

    # upstream 을 서버로 들어온 request 들을 전달할 server 들의 그룹을 설정한다.
    # upstream group 의 이름은 후에 proxy_pass 에서 참조하여 사용할 수 있다.
    # 해당 upstream 으로 pass 된 request 들은 group 에 정의된 server 들 중 하나로 전달되어 처리된다.
    # 만약 아무런 알고리즘 설정이 되어있지 않으면 Round-Robin 방식으로 타겟 서버가 결정된다.
    upstream web-web {
        server web:3000;
    }

    # upstream 에 load balancing 알고리즘을 설정하여 request 들을 분배할 수 있다.
    upstream web-api {
        Ip_hash; # ip address 를 hash 값으로 사용하여 request 를 분배한다.

        # weight: 서버의 가중치로 가중치가 1 (기본값) 인 서버보다 3배 많은 request 를 할당한다.
        server web:8080 weight=3;

        # max_conns: worker 별로 동시에 연결할 수 있는 최대 request 를 설정한다.
        server web:8081 max_conns=256;

        # max_fails: request 의 최대 실패를 설정한다. max_fails 를 초과하면 다른 서버에게 request 가 전달된다.
        # fail_timeout: timeout 시간을 설정한다. timeout 시간을 넘어가면 request fail 로 간주한다.
        server web:8082 max_fails=3 fail_timeout=30;
   
        # backup: backup 서버로 지정하여 주요 서버들에 장애가 생기면 그때부터 해당 서버로 request 가 pass 된다.
        server web:8083 backup;
    }

    # server: 어떤 서버가 http request 를 처리할지 설정한다.
    # 하나의 웹사이트를 선언하는데 사용되고, 하나의 서버로 2개를 동시에 운영할 때 사용
    server {
        # listen: port 를 설정한다.
        listen 80;

        # root: request 의 root address 를 설정한다.
        root /static/;

        # location: request 의 URI 별로 request 를 처리하는 설정을 한다.
        location /static {
          root /static/;
        }

        location /api {
            # proxy_pass: 해당 location request 를 proxy_pass server 와 매핑하여 해당 서버로 request 를 전달한다.
            # 여기서 web-api 는 upstream 으로 지정되어 있기 때문에 해당 server group 에서 request 를 처리한다.
            proxy_pass         http://web-api/
        }

        location / {
            proxy_pass         http://web-web/;
        }
    }
}

 


참고 사이트

https://www.nginx.com/resources/glossary/nginx/

https://github.com/h5bp/server-configs-nginx

https://www.nginx.com/resources/faq/

https://wonit.tistory.com/333

https://tecoble.techcourse.co.kr/post/2021-09-20-http2/

https://blex.me/@baealex/nginx

https://www.youtube.com/watch?v=6FAwAXXj5N0&t=651s

https://jammdev.tistory.com/217

https://www.nginx.com/resources/wiki/start/