유료 nginx가 아닌 opensource nginx documentation 을 참고했습니다. 그리고 CGI, WSGI 관련 내용이 부정확할 수 있습니다. 피드백 주시면 감사히 받겠습니다.
현재 개발중인 웹 앱 서비스에서 대규모 데이터베이스 쿼리 연산을 하던중 502 서버 에러를 마주했다. 그동안은 주먹구구식으로 구글링해가면서 문제가 생길 때마다 대충 해결하고 넘어갔지만, 이번 에러는 쉽게 해결되지 않았다. 그래서, 자세히 모르면서 그냥 사용해 온 Nginx Http Server 에 대해 자세히 알아보고, 에러가 발생하는 원인과 해결 방법을 스스로 찾을 수 있는 능력을 기르고자 Nginx 공식 Documentation 을 정독한 후기를 작성하려 한다.
nginx 는 하나의 master process 와 여러 worker processes 로 이루어져있다. master process 는 configuration file 을 읽고 worker processes 를 관리한다.
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 가 바로 이것이다. )
기본 구조를 작성한다.
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 가 무엇인지에 대해서는 Wiki : 프록시서버 를 참조하면 된다. 간단히 말하면 서버로 들어온 request를 다른 서버에 전달하고, 그 서버로부터 response 를 전달받아 다시 클라이언트로 전달하는 중간상인 같은 서버가 proxy server 이다.
새로운 server
블록 디렉티브를 만들어 프록시서버를 기술한다.
http {
server {
listen 8080; #기술 안하면 default 는 80 port
location / {
proxy_pass http://localhost:8080;
}
}
}
프록시 서버를 사용하는 경우 보통 다양한 언어로 쓰여진 내부의 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;
}
}
}
하나의 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)
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 값들을 확인할 수 있다.
웹 앱을 운영하다보면 사용자로부터 대용량 업로드 파일을 받아야하는 경우가 생긴다. 이럴 경우에는 간단히 server 안에 client_max_body_size
directive 만 작성해주면 된다.
http{
server {
server_name nextop.com;
listen 80;
# 용량 업로드 제한 : default=10M
client_max_body_size 20M;
location / {
...
}
}
}