Nginx 에 대하여 (Nginx Basic Usage)

infra | 16 January 2018

Tags | nginx proxy cgi http server loadbalance WAS

notice!

유료 nginx가 아닌 opensource nginx documentation 을 참고했습니다. 그리고 CGI, WSGI 관련 내용이 부정확할 수 있습니다. 피드백 주시면 감사히 받겠습니다.

현재 개발중인 웹 앱 서비스에서 대규모 데이터베이스 쿼리 연산을 하던중 502 서버 에러를 마주했다. 그동안은 주먹구구식으로 구글링해가면서 문제가 생길 때마다 대충 해결하고 넘어갔지만, 이번 에러는 쉽게 해결되지 않았다. 그래서, 자세히 모르면서 그냥 사용해 온 Nginx Http Server 에 대해 자세히 알아보고, 에러가 발생하는 원인과 해결 방법을 스스로 찾을 수 있는 능력을 기르고자 Nginx 공식 Documentation 을 정독한 후기를 작성하려 한다.

nginx 의 구조

nginx 는 하나의 master process 와 여러 worker processes 로 이루어져있다. master process 는 configuration file 을 읽고 worker processes 를 관리한다.

configuration file 구조

nginx 의 configuration file 은 /etc/nginx/nginx.config 에 위치한다. 파일을 열면 directives(지시어) 가 나열되어있다. 이 지시어들은 nginx 의 modules 의 동작을 기술한다.

directives 는 simple directives 와 block directives 로 나뉜다.

simple directives 예시

 client_max_body_size 20M;
 listen 80;
 server_name localhost;
 keepalive_timeout  0;

block directives 예시

upstream web {
 ip_hash;
 server web:8000;
}

block directives 들 중에 그 안에 다른 block directives 를 기술할 수 있다면 그것은 context 라고 부른다. 대표적 context 로는 main,http,server,location 등이 있고, 순서대로 포함관계에 있다. (공식 documentation 을 읽다보면 각 지시어directives 를 사용 가능한 context를 지정해주는데, 그때의 context 가 바로 이것이다. )

간단한 Static file 을 Serving

기본 구조를 작성한다.

http {
  server {

    location / {
      root /path/to/html ;
    }

    location /images/ {
      root /path/to/image ;
    }

  }
}

http context 안에 server context 안에 location context 가 있다. location 뒤에 있는 url(/) 로 접속 시에 서버의 디렉터리(/path/to/html) 에 있는 정적 파일을 제공하겠다는 내용을 담고있다.

Proxy Server 로 이용하기

proxy server 가 무엇인지에 대해서는 Wiki : 프록시서버 를 참조하면 된다. 간단히 말하면 서버로 들어온 request를 다른 서버에 전달하고, 그 서버로부터 response 를 전달받아 다시 클라이언트로 전달하는 중간상인 같은 서버가 proxy server 이다.

새로운 server 블록 디렉티브를 만들어 프록시서버를 기술한다.

http {
  server {
    listen 8080;  #기술 안하면 default 는 80 port

    location / {
      proxy_pass http://localhost:8080;
    }

  }
}

NGINX 프록시 -> CGI서버 -> WAS (Proxy Server 로 사용하는 대표적 사례)

프록시 서버를 사용하는 경우 보통 다양한 언어로 쓰여진 내부의 WAS(Web Application Service) 와 통신하는 경우가 많다. WAS 와 정적 웹 서버가 통신하기 위한 규칙을 CGI(Common Gateway Interface) 라고 하고, 각 언어마다 다른 CGI 를 가진다. (python 은 WSGI, ruby는 Rack, perl은 PSGI 등)

이런 CGI 통신이 가능한 서버를 CGI서버(fastCGI, uWSGI, SCGI 등)라고 하고, python WAS 에서 가장 널리 쓰이는 CGI서버의 이름은 uWSGI 이다. (실제로는 uWSGI 는 python 의 WSGI, ruby의 Rack, perl 의 PSGI 규칙 모두와 통신할 수 있다. ) CGI서버 자체로도 웹서버 기능을 사용하고 클라이언트에 데이터를 제공할 수 있지만, 보통 NGINX 와 같은 정적 웹서버의 정적 파일(Static file) 서빙 성능이 좋아서, Nginx 를 프록시 서버로 사용하여 Nginx 로 들어온 request 를 CGI서버로 넘기는 구조를 많이 채택한다. (uWSGI의 공식 문서에도 이런 방식에 대한 설명이 나와있다.)

주의할 점 : 각 CGI 규칙의 이름들과 각 CGI서버의 이름들이 굉장히 헷갈릴 수 있다.

CGI 규칙 : WSGI, Rack, PSGI 등

CGI 서버 : uWSGI, fastCGI, SCGI 등

Nginx에는 기본적으로 몇가지 CGI서버의 프록시서버로 사용시에 유용한 기능들을 탑재되어있다. 대표적으로 uwsgi_module 이라는 nginx 모듈의 다양한 기능을 이용하면 uwsgi 서버의 프록시 서버로 쉽게 이용이 가능하다. (Nginx 공식 Doc : uwsgi_module 에 가면 다양한 기능들을 살펴볼 수 있다. )

proxy_pass 라는 directive 대신에 uwsgi_pass 라는 directive 를 사용한다. 그리고 uwsgi_param 이라는 지시어를 통해 인자를 전달할 수 있다.

http {
  server {
    listen 8080;  #기술 안하면 default 는 80 port

    uwsgi_param HTTPS $https if_not_empty;
    # HTTPS 라는 parameter 에 $https 값을 담는다.
    # (if_not_empty 를 적으면 비어있을 경우 전달하지 않는다.)

    location / {
      include uwsgi_params;      #uwsgi_param 을 전달한다.  
      uwsgi_pass http://localhost:8080;
    }

  }
}

Load Balancer 로 이용하기

관련 공식 doc link: Using Nginx as HTTP load balancer

하나의 Proxy server(Nginx) 에서 여러 WAS+CGI서버로 적절히 부하를 나누어 연결하는 기술을 load balancing 이라고 하고, 웹서버에서 아주 많이 사용되는 기술이다.

configuration file 에 로드밸런싱을 명령하는 방법은 아래와 같다.

http{

  # load balancing 할 서버 그룹을 정의한다.
  upstream servergroup1 {
    # load balance 기법을 정한다.
    ip_hash;

    # 서버 (주소+port+[옵션])를 나열한다.
    server srv1.example.com:80;
    server srv2.example.com:3030 weight=3;
    server srv3.example.com;
  }

  # 프록시 서버로서의 nginx 동작을 기술한다.
  server {
    listen 80;

    location / {
      proxy_pass http://servergroup1 ; #(프로토콜명+로드밸런싱 그룹 이름)을 적어주면 된다
    }
  }

}

Nginx 에서 제공하는 Load Balancing method 는 3가지로 소개되어있다.

round-robin : requests to the application servers are distributed in a round-robin fashion

least-connected : next request is assigned to the server with the least number of active connections

ip-hash : a hash-function is used to determine what server should be selected for the next request (based on the client’s IP address)

Customizing 사례1 : Proxy 요청 Timeout 늘리기

Nginx 는 다양한 처리에 Timeout 이라는 limit 이 설정되어있다. (Nginx 공식 Documentation 의 Directives dictionary 에 timeout 이라고만 검색해도 엄청나게 많은 timeout 설정 관련한 directives 가 존재한다 )

예를 들어 WAS(Web Application Service) 와 CGI서버의 프록시서버로 사용되고 있는 Nginx 에서는, CGI서버와 WAS로 전달된 요청(request)이 일정 시간 안에 응답(response)되지 않으면 client 에 504:Timeout Error 를 발생시킨다.

이러한 문제를 timeout 설정에 관련된 directives 를 configuration file 에 기술해줌으로써 해결할 수 있다.

http{
  server {
      ...

      location / {
          proxy_connect_timeout 300s;
          proxy_send_timeout 300s;
          proxy_read_timeout 300s;
          send_timeout 300s;
      }
  }
}

공식 documentation 을 읽어보면 각 timeout 의 종류와 default 값들을 확인할 수 있다.

Customizing 사례2: Client 업로드 파일 용량 제한 늘리기

웹 앱을 운영하다보면 사용자로부터 대용량 업로드 파일을 받아야하는 경우가 생긴다. 이럴 경우에는 간단히 server 안에 client_max_body_size directive 만 작성해주면 된다.

http{
  server {
    server_name nextop.com;
    listen 80;
    # 용량 업로드 제한 : default=10M
    client_max_body_size 20M;

    location / {
      ...
    }
  }
}