Nginx

엔진엑스 nginx는 러시아 개발자(Igor Sysoev)가 혼자서 만든 프로젝트이지만, 메모리와 성능이 좋아 입소문으로 많이 사람들이 알게 되었다. 2002년부터 시작되어 최근 사용하는 곳이 급속히 증가하고 있다.

http://nginx.com/

특히 nginx는 http://wordpress.com 에 적용되면서 많이 유명해졌다. 현재 Nginx를 쓰는 유명한 곳으로는 페이스북, Netflix, WordPress.com, 깃헙 (Github), 사운드클라우드 (Soundcloud), Zynga, Sourceforge 등이 있으며, 한국에서는 네이버 첫페이지, 백괴사전, 일베저장소, 나무위키 미러,카카오톡 공지사항 서버,XpressEngine 공식 홈페이지등이 있다.

nginx 는 비동기(async) 이벤트 기반(ioctl, send, recv, epoll)으로 만들어졌고, Apache Http 서버는 프로세스 또는 쓰레드를 태생으로 만들어졌다.

따라서, 더 적은 자원으로 더 빠르게 데이터를 서비스 할 수 있다.

성능

성능은 접속자가 어느 정도 이상 되고 다양한 사이트가 운영되면 차이가 많이 난다. 동접자가 많을수록 체감할수 있다 물론 설정을 시스템에 맞게 잘 해주면 효과가 극대화 되겠다. 서버가 리눅스일경우 그리고 멀티코어일 경우 엔진엑스와 아파치의 차이가 많이 난다.

한국에서 엔진엑스를 많이 안쓰는 이유는 한글 문서들이 많이 없기 때문이다. 호스팅 서비스의 경우 rewrite 관련 부분을 사용하는 호스팅 사용자가 많은 경우 상당히 귀찮아 질 수 있다.

보통 접속자가 많은 사이트 (토렌트, 유머 등등) 인 경우 엔진엑스를 많이 쓰고 있는 추세이다, 성능을 원하면 Nginx로 셋팅하자.

아파치 2.4와의 비교

Use nginx in the front as proxy, and serving all static content (css, images, js) and leaving the php process to Apache.

기본구조비교

Apache Http 서버는 요청 하나당 프로세스(뜨는 쓰레드)가 처리하는 구조이다 보니, 만약 Apache Http 서버의 프로세스가 blocking(DB나 파일)이 되면 요청을 계속 처리하지 못하고 처리 완료때까지 대기하는 일이 생긴다. 그래서 Timeout이나 Keep Alive 체크를 정말 잘써야 하는 이슈가 생기게 되었다.

또한 MaxClient의 수치를 높여 dos 공격에 대해서 조금이라도 막을 수 있는 형태를 취하게 된다. 따라서 프로세스(또는 쓰레드) 메모리를 많이 할당/해제하는 상황이 생길 수 있다.

nginx는 쓰레드를 적게 사용하는 구조이다. 또한 쓰레드를 적게 쓰다보니 쓰레드당 할당되는 메모리도 적게 되면서 메모리도 적게 사용되는 구조가 된다. 하나의 쓰레드에서 이벤트를 처리하다 보니 blocking이 되는 일이 없는 셈이 된다. 또한 쓰레드를 적게 쓴 다는 것은 context swithing 비용도 적게 되기 때문에 cpu의 소모도 상대적으로 작게 된다. 하나의 프로세서는 하나의 쓰레드처럼 사용하여 적은 worker에서 동작이 된다. 다만 많은 요청을 처리하기 위해서 client 개수는 늘릴 필요는 있다.

Nginx 관련한 성능 테스트한 자료

단점

최대한 system call을 적게 하고 비동기 이벤트 처리를 통해서 높은 성능이 있다는 큰 장점과 함께 백엔드와 ajp 통신이 어렵고, 모듈 개발이 어려우며, Apache Http 서버처럼 다양한 모듈이 없다는 단점이 있다.

윈도용

윈도우용 Nginx는 native win32 api를 이용하며, select() 연결만 사용하기 때문에 향상된 성능을 기대하기는 어렵다. 이외의 몇몇 제약 때문에 아직 베타버전으로 간주된다.

http://nginx.org/en/docs/windows.html 참고한다.

부가기능

nginx는 name, ip virtual host나 기본적인 기능외에 다음 기능이 있다.

  1. SSL
  2. load balancing
  3. gzip
  4. url rewriting
  5. webdav
  6. access authentication
  7. smtp, pop3, imap
  8. flv/mp4 streaming
  9. speed limitation

설치

우분투에서 패키지를 이용해 설치하면된다. Nginx에서 PHP5(FastCGI)를 구동하기 위해서는 다음 명령으로 PHP-FPM (데몬 프로세스) 패키지를 설치해야 한다.

sudo apt-get install php5-fpm

이제 다음 명령으로 Nginx를 설치.

sudo -s
nginx=stable # use nginx=development for latest development version
add-apt-repository ppa:nginx/$nginx
apt-get update 
apt-get install nginx

혹시 업그레이드가 잘 안먹으면

sudo apt-get dist-upgrade

설정

Nginx 설정은 /etc/nginx/nginx.conf, /etc/nginx/sites-available/default 에서 할 수 있다.

PHP5와 Nginx 연동하기위해 사이트 설정 파일에 다음 줄을 추가한다.

        location ~ [^/]\.php(/|$)  {
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                if (!-f $document_root$fastcgi_script_name) {
                        return 404;
                }
                fastcgi_pass unix:/var/run/php5-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                include fastcgi_params;
        }

예제 참조

설정을 모두 마쳤으면 Nginx를 재시작. (줄 끝 ; 잊지말 것.)

fpm unix socket 연결

/etc/php5/fpm/pool.d/www.conf 수정

Look for the line

listen = 127.0.0.1:9000

and change it to something like

listen = /var/run/php5-fpm.sock
listen = /run/php/php8.3-fpm.sock

After doing so, restart PHP FPM:

sudo service php5-fpm restart
sudo service nginx restart

소켓 연결 에러

유닉스소켓 연결이 조금 더 빠르기는 하지만 TCP/IP 만큼 Scalable 하지는 않다. 일일 방문자가 만명 수준인 본 사이트(http://openwiki.kr) 에서도 유닉스 소켓 연결로 설정 후 종종 php가 연결이 끊기는 경우 (갑자기 사이트 먹통, nginx 와 php 재시작 후 살아남)가 생겼었다. 이를 다시 TCP연결로 복구한 뒤 해결되었다.

이런 경우 nginx가 502 에러를 뿜는다고 하는데, 그냥 먹통이 되기도 한다. 소켓 세팅을 만지거나 그냥 TCP/IP 로 돌아가면 된다.

In your fastcgi conf change:

  fastcgi_pass unix:/var/run/php5-fpm.sock;

to:

  fastcgi_pass 127.0.0.1:9000;

http://IP주소 로 접속해본다.

기본 자료 폴더/usr/share/nginx/www
기본 디먼www-data
로그/var/log/nginx/error.log
로그/var/log/nginx/access.log

Rewrite

 http://당신의도메인/test.php?id=$1 ㅡ>  http://당신의도메인/$1
 rewrite ^/([a-zA-Z0-9_]+)$ /test.php?id=$1 last;
  • last vs rewrite
    • Okay, so "break", means "if regexp matches the request, then rewrite the request and then stop right here and serve the request as being a static file". .. and then stop right here (in this location) and serve the request by the location's handler (static/proxy/fastcgi/etc)
    • "last" means "if regexp matches the request then rewrite the request, then do not move on to any subsequent rewriting rule, but now try to match the request with a location in the config file"

서브도메인

server {
    server_name m.openwiki.kr;
  -    return 301 $scheme://openwiki.kr$request_uri;
       rewrite ^(.*) http://openwiki.kr$1 permanent;
}

Url Redirect 301 vs 302 참조.

핫링크막기

location ~ \.(gif|png|jpg|jpeg|JPG|GIF|JPEG|PNG)$ {
    valid_referers none blocked 
         yourwebsitewithcoolcontent.com 
         *.yourwebsitewithcoolcontent.com;
        if ($invalid_referer) {
            rewrite \.(gif|png|jpg|jpeg|JPG|GIF|JPEG|PNG)$
                       http://cfile10.uf.tistory.com/image/2618E03B516F888815E0BC 
            redirect;
        }
}

Dokuwiki (도메인루트 & try_files 사용)

server {
        listen   80;
        root /home/www/wiki;
        server_name vaslor.net;

        location / {
                try_files $uri $uri/ @dk;
                rewrite ^/$ /doku.php last;
        }

        location @dk {
                rewrite ^/(.*) /doku.php?id=$1 last;
        }

        rewrite ^/_media/(.*) /lib/exe/fetch.php?media=$1 last;
        rewrite ^/_detail/(.*) /lib/exe/detail.php?media=$1 last;
        rewrite ^/_export/([^/]+)/(.*) /doku.php?do=export_$1&id=$2 last;

        include common.conf;
}

도쿠위키 & 엔진엑스참고

Expire

        # 브라우져 캐쉬 세팅
        location ~* .(jpg|jpeg|png|gif|ico|css|js)$ {
                expires 65d;
                 access_log off;
        }

htaccess

보안을 위하여 아파치의 htaccess를 감춘다.

        location ~ /\.ht {
                deny all;
        }

Compression

/etc/nginx/nginx.conf

http section

  - enable gzip compression
gzip on;
gzip_min_length  1100;
gzip_buffers  4 32k;
gzip_types    text/plain application/x-javascript text/xml text/css;
gzip_vary on;
  - end gzip configuration

domain

## Server Block one
server {
server_name www.domain1.com;
access_log logs/domain1.access.log main;
root /var/www/domain_1;
}
## Server Block two
server {
server_name www.domain2.com;
access_log logs/domain2.access.log main;
root /var/www/domain_2;
}

subdomain

이런식이다.

server {
        listen 80;
        root /var/www/test;
        index index.php;
        server_name test.vaslor.net;

        location / {
                try_files $uri $uri/ index.php;
        }
        include common.conf;
}

redirect subdomain to port

server {
    listen 80;
    server_name app.example.com;

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

max children

/etc/php/8.1/fpm/pool.d/www.conf

pm.max_children = 128

pm.start_servers = 10

pm.min_spare_servers = 4

pm.max_spare_servers = 16

pm.max_requests = 500

성가신 로그 끄기

/var/log

  - Avoids filling up the error logs with commonly requested files.
  - Note that if you have a custom 404 page, the request may be logged anyway.
location = /robots.txt { log_not_found off; }
location = /favicon.ico { log_not_found off; }
location = /apple-touch-icon.png { log_not_found off; }
location = /apple-touch-icon-precomposed.png { log_not_found off; }
location = /apple-touch-icon-120x120.png { log_not_found off; }
location = /apple-touch-icon-120x120-precomposed.png { log_not_found off; }

접근 로그는 nginx.conf에서 설정하는 것이 편하다.

access_log off;

에러 로그도 끌 수 있다.

error_log off;

아파치 호환성

getallheaders()

getallheaders는 아파치의 헤더를 받아오는 것으로 nginx에서는 작동하지 않는다. 호환성을 위해 다음과 같은 함수를 만들어 쓰면 된다.

if (!function_exists('getallheaders')) 
{ 
    function getallheaders() 
    { 
           $headers = ''; 
       foreach ($_SERVER as $name => $value) 
       { 
           if (substr($name, 0, 5) == 'HTTP_') 
           { 
               $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value; 
           } 
       } 
       return $headers; 
    } 
} 

* 참고: php.net

참고

연결문서