CGI
CGI는 Common Gateway Interface의 약자로 웹서버와 외부 프로그램을 연결해주는 표준화된 프로토콜이다. 웹이 처음 등장했을 때는 HTML과 이미지를 전달해주는 웹서버 밖에 없었다. 하지만 웹에 대한 수요가 증가함에 따라서 정적인 HTML만을 가지고 정보를 제공하는 것에 한계가 생겼고, 이를 극복하기 위해서 등장한 기술이 CGI다. 웹서버로 요청이 들어왔을 때 그것이 웹서버가 처리 할 수 없는 정보일 때 그 정보를 처리 할 수 있는 외부 프로그램을 호출해서 외부 프로그램이 처리한 결과를 웹서버가 받아서 브라우저로 전송하는 것이다. 외부 프로그램은 C, C++, 펄, PHP, Python등 어떤 언어로든 작성될 수 있는데, 이것이 가능한 것은 웹서버와 외부 프로그램 사이에 통용되는 공통의 규칙이 정의되어 있기 때문이다. (위키피디아 참고)
CGI와 FastCGI가 무엇인지 이해하지 못해도 NGINX와 PHP를 연동하는 것에는 아무런 문제가 없다.
FastCGI
CGI 는 하나의 요청(Request)에 하나의 프로세스를 생성한다. 이것은 프로세스를 생성하고 삭제하는 과정에서 많은 부하가 발생한다. 당연히 느리다. 이를 개선하기 위해서 등장한 것이 FastCGI이다. FastCGI는 요청이 있을 때마다 프로세스가 만들어지는 것이 아니라 만들어진 프로세스가 계속해서 새로운 요청들을 처리한다. 덕분에 프로세스를 생성하고 제거하는데 들어가는 부하가 줄어든다. (위키피디아 참고)
PHP-FPM
PHP-FPM는 PHP FastCGI Process Manager의 약자로 PHP를 FastCGI 모드로 동작하도록 해준다. PHP5.4 RC부터는 PHP에 기본 내장 되었다. PHP-FPM을 사용하면 아래와 같은 이점이 생긴다.
- Adaptive process spawning
- Basic statistics (ala Apache's mod_status)
- Advanced process management with graceful stop/start
- Ability to start workers with different uid/gid/chroot/environment and different php.ini (replaces safe_mode)
- Stdout & stderr logging
- Emergency restart in case of accidental opcode cache destruction
- 업로드를 빠르게 처리해준다.
- "slowlog"를 통해서 느리게 동작하는 부분을 추적할 수 있게 한다.
- Enhancements to FastCGI, such as fastcgi_finish_request() - 요청을 일단 끝내고 후처리가 요구되는 작업을 백그라운드로 처리할 수 있도록 해준다. (예제)
PHP-FPM 설치 - Ubuntu
MySQL 설치
MySQL을 사용한다면 우선 MySQL을 설치한다.
sudo apt-get install mysql-client mysql-server;
PHP-FPM 설치
이 과정에서 php도 설치된다.
sudo apt-get install php5-fpm;
MySQL과 PHP-FPM의 연결
sudo apt-get install php5-mysql
NGINX 설정
Nginx의 설정 파일을 수정한다.
설정파일은 아래의 위치 중의 하나에 있다.
- /etc/nginx/conf.d/default.conf
- /etc/nginx/sites-available/default
다음은 필자의 설정파일이다.
server { listen 80; server_name localhost; #charset koi8-r; #access_log /var/log/nginx/log/host.access.log main; root /usr/share/nginx/html; location / { index index.html index.htm index.php; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # location ~ \.php$ { fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} }
위의 내용을 그대로 사용하면 PHP 보안 문제가 있다. 아래 링크를 따라가면 몇가지 보안 이슈를 해결 할 수 있는 정보가 있다. 참고하자. http://phpschool.com/gnuboard4/bbs/board.php?bo_table=tipntech&wr_id=77007&page=#c_77013
위의 설정 파일에서 주목해야 할 부분을 설명하겠다.
location ~ \.php$ {..}
location
location은 .php 확장자로 끝나는 요청을 처리하기 위한 부분이다.
fastcgi_param
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param 은 FastCGI의 규칙에 따라서 에플리케이션(php-fpm)에게 전달할 데이터를 지정한다. SCRIPT_FILENAME은 CGI 의 규격 중 SCRIPT_NAME에 해당하는 값인데, 이 값으로 전달된 $document_root는 루트 디렉토리 즉 /usr/share/nginx/html 을 의미한다. $fastcgi_script_name은 파일의 이름을 의미한다. 이 두개의 값이 조합된 결과가 PHP-FPM으로 전달되는 것이다. PHP-FPM은 전달된 값에 해당되는 파일을 읽어서 실행하게 된다.
fastcgi_pass
socket
fastcgi_pass는 php-fpm과 NGINX를 연결하기 위한 인터페이스를 지정하는 것이다. 이 값은 php-fpm의 listen 설정값과 일치해야 한다. 필자의 php-fpm 버전에서는 이 설정값을 /etc/php5/fpm/pool.d/www.conf에서 찾을 수 있었다. 위의 예제에서 지정된 unix:/var/run/php5-fpm.sock는 유닉스 소켓으로 NGINX와 PHP-FPM이 같은 (컴퓨터를 의미하는) 호스트에 설치된 경우 사용한다. 이것을 그림으로 나타내면 아래와 같다.
TCP Connection
만약 NGINX와 PHP-FPM이 서로 다른 머신에 설치되었다면 TCP Connection의 값을 지정해야 한다. 만약 PHP-FPM이 192.168.125.142에 설치되어 있다면 NGINX 측의 설정은 아래와 같아야 한다.
fastcgi_pass 192.168.125.142:9000;
이에 대응해서 PHP-FPM 측의 listen 값은 아래와 같아야 한다.
listen = 192.168.125.142:9000
이것을 그림으로 나타내면 아래와 같다.
Upstream Module
Upstream Module는 NGINX를 일종의 부하분배장치(Load Balancer)로 이용할 수 있게 해주는 NGINX의 모듈이다. 자세한 내용은 Upstream Module를 참고한다. Upstream Module 바로가기
Trouble Shooting
TCP Connection을 통해서 접속이 되지 않을 때 문제 해결 방법
증상 : File not found.라고 출력된다.
원인 : NGINX가 fastCGI 인터페이스를 통해서 PHP-FPM이 실행할 파일을 지정할 때 사용하는 변수가 SCRIPT_FILENAME 인데 이 값이 잘못 전달되서 PHP-FPM이 실행 할 파일을 찾지 못하고 있다.
원인 분석 : PHP-FPM이 설치된 머신에서 tcpdump로 9000 포트로 들어오는 패킷을 감청해서 CGI 환경변수가 잘 전달되고 있는지 확인한다.
tcpdump port 9000 -A | strings
필자는 /usr/local/nginx/html에 test.php 파일을 위치시켰는데 NGINX는 /usr/share/nginx/html 에서 파일을 호출하고 있었다.
문제해결 : php-fpm 쪽의 디렉토리를 변경한다.
소켓 파일에 엑세스 할 수 있는 권한이 없다고 오류가 발생하는 경우
error.log에 아래와 같은 에러가 발생하는 경우는 소켓파일에 접근하는 nginx의 소유자 퍼미션이 맞지 않기 때문이다.
2014/12/29 21:59:12 [crit] 8116#0: *1 connect() to unix:/var/run/php5-fpm.sock failed (13: Permission denied) while connecting to upstream, client: 192.168.222.1, server: o2.org, request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/var/run/php5-fpm.sock:", host: "o2.org"
이 문제에 대한 자세한 설명은 아래 링크를 참고하자.