Computer >> 컴퓨터 >  >> 시스템 >> Linux

고성능 NGINX + PHP-FPM 웹 서버 구축 및 최적화 가이드

PHP-FPM(FastCGI Process Manager)은 PHP 스크립트를 실행하기 위한 FastCGI의 독립적인 구현체입니다. NGINX 웹 서버(정적 콘텐츠 담당)와 PHP-FPM을 조합하면 LAMP 스택(Apache + mod_php)보다 훨씬 빠르고 성능이 뛰어난 웹 서버를 구축할 수 있습니다.

LEMP 스택이란?

LEMP는 웹사이트 호스팅과 웹 애플리케이션 배포를 위해 일반적으로 함께 설치하는 오픈소스 소프트웨어 패키지 세트입니다. 이 약어는 Linux(운영체제), Nginx(웹 서버, 널리 쓰이는 LAMP 스택의 Apache를 대체), MySQL/MariaDB(데이터베이스), PHP(동적 데이터 처리)를 의미합니다.

이 글에서는 CentOS 7 리눅스 서버에서 고트래픽 웹 프로젝트를 운영할 수 있도록 LEMP 스택(NGINX + PHP-FPM + MariaDB/MySQL)을 설치하고 최적화하는 전 과정을 다룹니다.

Nginx 웹 서버 설치 및 설정

새로 설치한 CentOS 서버를 기준으로 진행하므로, 먼저 널리 사용되는 EPEL 저장소를 등록하고 서버의 모든 패키지를 최신 상태로 업데이트합니다.

# yum install epel-release -y
# yum update -y
고성능 NGINX + PHP-FPM 웹 서버 구축 및 최적화 가이드

최신 버전의 Nginx를 설치하려면 아래 명령으로 개발자 공식 저장소를 추가합니다.

# rpm -Uvh https://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm

또는 저장소 설정 파일(/etc/yum.repos.d/nginx.repo)을 직접 생성하고 다음 내용을 입력해도 됩니다.

[nginx]
name=nginx repo
baseurl=https://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1

CentOS 8을 사용하는 경우 URL의 버전 번호만 변경하면 됩니다.

yum(또는 dnf) 패키지 관리자로 Nginx 웹 서버 패키지를 설치합니다.

# yum install nginx -y
고성능 NGINX + PHP-FPM 웹 서버 구축 및 최적화 가이드

설치 후 systemctl을 사용해 nginx를 시작하고 부팅 시 자동 실행되도록 등록합니다.

# systemctl start nginx
# systemctl enable nginx
Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service

웹 서버가 정상 동작하는지 확인하려면 브라우저에서 서버 IP 주소를 열어보세요.

고성능 NGINX + PHP-FPM 웹 서버 구축 및 최적화 가이드

테스트 페이지가 나타나지 않으면 firewalld에서 허용된 서비스, 포트, 존(zone) 설정을 점검해야 합니다.

도메인별 가상 호스트 설정

예시 도메인 woshub-linux.com에 대한 설정 파일을 만들어 보겠습니다. 먼저 사이트 디렉터리와 로그 디렉터리를 생성합니다.

# mkdir -p /var/www/woshub-linux.com && mkdir -p /var/www/woshub-linux.com/log

설정 파일을 열고 아래 내용을 추가합니다.

# nano /etc/nginx/conf.d/woshub-linux.com.conf
server {
    listen 80;
    server_name woshub-linux.com;
    root /var/www/woshub-linux.com;
    index index.php index.html index.htm;
    access_log /var/www/woshub-linux.com/log/access.log main;
    error_log /var/www/woshub-linux.com/log/error.log;
   location / {
    return 301 https://woshub-linux.com$request_uri;
   }
   location ~* ^.+.(js|css|png|jpg|jpeg|gif|ico|woff)$ {
   return 301 https://woshub-linux.com$request_uri;
   }
   location ~ \.php$ {
   return 301 https://woshub-linux.com$request_uri;
   }
   location = /favicon.ico {
   log_not_found off;
   access_log off;
   }
   location = /robots.txt {
   rewrite ^ /robots.txt break;
   allow all;
   log_not_found off;
   access_log off;
   }
   location ~ /\.ht {
   deny all;
   }
}
server {
   listen 80;
   server_name www.woshub-linux.com;
   rewrite ^ https://woshub-linux.com$request_uri? permanent;
}
server {
   listen 443 ssl http2;
   server_name woshub-linux.com;
   root /var/www/woshub-linux.com;
   index index.php index.html index.htm;
   access_log /var/www/woshub-linux.com/log/ssl-access.log main;
   error_log /var/www/woshub-linux.com/log/ssl-error.log;
   keepalive_timeout 60;
   ssl_certificate /etc/letsencrypt/live/woshub-linux.com/fullchain.pem;
   ssl_certificate_key /etc/letsencrypt/live/woshub-linux.com/privkey.pem;
   ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
   ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
   add_header Strict-Transport-Security 'max-age=604800';
   location / {
   try_files $uri $uri/ /index.php?$args;
   }
   location ~* ^.+.(js|css|png|jpg|jpeg|gif|ico|woff)$ {
   access_log off;
   expires max;
   }
   location ~ \.php$ {
   try_files $uri =404;
   fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
   fastcgi_index index.php;
   fastcgi_param DOCUMENT_ROOT /var/www/woshub-linux.com;
   fastcgi_param SCRIPT_FILENAME /var/www/woshub-linux.com/$fastcgi_script_name;
   fastcgi_param PATH_TRANSLATED /var/www/woshub-linux.com/$fastcgi_script_name;
   include fastcgi_params;
   fastcgi_param QUERY_STRING $query_string;
   fastcgi_param REQUEST_METHOD $request_method;
   fastcgi_param CONTENT_TYPE $content_type;
   fastcgi_param CONTENT_LENGTH $content_length;
   fastcgi_param HTTPS on;
   fastcgi_intercept_errors on;
   fastcgi_ignore_client_abort off;
   fastcgi_connect_timeout 60;
   fastcgi_send_timeout 180;
   fastcgi_read_timeout 180;
   fastcgi_buffer_size 128k;
   fastcgi_buffers 4 256k;
   fastcgi_busy_buffers_size 256k;
   fastcgi_temp_file_write_size 256k;
   }
   location = /favicon.ico {
   log_not_found off;
   access_log off;
   }
   location = /robots.txt {
   allow all;
   log_not_found off;
   access_log off;
   }
   location ~ /\.ht {
   deny all;
   }
}
server {
   listen 443 ssl http2;
   server_name www.woshub-linux.com;
   rewrite ^ https://woshub-linux.com$request_uri? permanent;
}

위 설정 파일에는 보안 HTTP(HTTPS) 프로토콜을 통한 사이트 접근 설정이 포함되어 있습니다. 대부분의 인기 CMS가 HTTPS를 기본으로 사용하기 때문입니다. 이후 무료 Let's Encrypt 인증서를 발급받아 적용하면 됩니다(Windows Server IIS에 Let's Encrypt 인증서를 적용하는 방법과 유사합니다).

PHP-FPM 설치

Nginx에는 내장된 PHP 처리기가 없기 때문에, PHP 스크립트를 처리하려면 php-fpm과 몇 가지 PHP 모듈을 별도로 설치해야 합니다.

PHP-FPM은 가볍고 빠른 PHP 프로세스 관리자입니다. Apache와 달리 HTTP 프로토콜을 사용하지 않고 특수한 FastCGI 프로토콜로 동작하며, 요청 처리 속도가 더 빠르고 Apache 구성 대비 메모리 사용량도 적습니다. 반면 Nginx는 정적 콘텐츠 반환에 탁월합니다. 즉, 이 구성에서 Nginx는 프론트엔드이자 캐싱 프록시 서버 역할을 하고, PHP-FPM은 백엔드 역할을 수행합니다.

최신 PHP 버전을 설치하려면 REMI 저장소를 사용합니다.

# rpm -ivh https://rpms.famillecollet.com/enterprise/remi-release-7.rpm

설치가 끝나면 /etc/yum.repos.d/remi-php74.repo 파일을 편집하여 원하는 PHP 버전 저장소를 활성화합니다.

고성능 NGINX + PHP-FPM 웹 서버 구축 및 최적화 가이드

이제 php-fpm과 자주 사용되는 PHP 모듈들을 함께 설치합니다.

# yum install php-fpm php-cli php-mysql php-gd php-ldap php-odbc php-pdo php-opcache php-pear php-xml php-xmlrpc php-mbstring php-snmp php-soap php-zip
고성능 NGINX + PHP-FPM 웹 서버 구축 및 최적화 가이드

php-fpm 데몬을 시작하고 부팅 시 자동 실행되도록 등록합니다.

# systemctl start php-fpm
# systemctl enable php-fpm
Created symlink from /etc/systemd/system/multi-user.target.wants/php-fpm.service to /usr/lib/systemd/system/php-fpm.service.

서비스가 정상적으로 시작되었는지 확인하려면 다음 명령을 실행하세요.

# lsof -i:9000
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
php-fpm 1551 root 7u IPv4 2078435 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 1552 apache 9u IPv4 2078435 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 1553 apache 9u IPv4 2078435 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 1554 apache 9u IPv4 2078435 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 1555 apache 9u IPv4 2078435 0t0 TCP localhost:cslistener (LISTEN)
php-fpm 1556 apache 9u IPv4 2078435 0t0 TCP localhost:cslistener (LISTEN)

TCP 대신 Unix 소켓 사용

php-fpm을 Unix 소켓 방식으로 구동하면 성능이 크게 향상됩니다. /etc/php-fpm.d/www.conf 파일에서 "listen = 127.0.0.1:9000" 줄을 삭제하고 아래 내용으로 교체하세요.

listen = /var/run/php-fpm/php-fpm.sock
listen.mode = 0660
listen.owner = nginx
listen.group = nginx

php-fpm을 기본값인 apache 계정이 아닌 다른 사용자로 구동하려면 같은 설정 파일에 다음 파라미터를 지정합니다.

user = nginx
group = nginx

php-fpm 설정 변경 후에는 서비스를 재시작합니다.

# systemctl restart php-fpm

웹 서버에 MySQL/MariaDB 설치

이 단계는 생략하겠습니다. 이미 "리눅스에서 MariaDB 설치 및 최적화"라는 제목의 상세 가이드를 별도로 게시했으므로, 해당 문서를 참고하시기 바랍니다.

고부하 대비 Nginx + PHP-FPM 튜닝

웹 서버가 대량의 클라이언트 요청(높은 트래픽)을 안정적으로 처리하려면 nginxphp-fpm을 적절히 튜닝하는 것이 매우 중요합니다.

Nginx 설정 최적화

/etc/nginx/nginx.conf 파일을 열고 다음과 같이 설정을 조정합니다.

  • worker_processes 2; — 워커 프로세스 수를 서버의 CPU 코어 수와 동일하게 설정
  • worker_connections 1024; — 워커 프로세스 하나당 처리 가능한 연결 수(1024~4096 권장)
  • use epoll; — 리눅스 환경에서 가장 효율적인 이벤트 처리 방식
  • multi_accept on; — nginx가 대기 중인 연결을 최대한 많이 한 번에 수락

http 블록에는 다음 항목을 추가합니다.

  • tcp_nodelay on; — 응답 헤더와 파일 시작 부분을 하나의 패킷으로 전송
  • tcp_nopush on;

정적 파일이 많은 웹 프로젝트라면 gzip 압축을 활성화하세요.

gzip on;

Google PageSpeed 검사를 모두 통과하려면 아래처럼 다양한 파일 형식을 gzip_types에 추가합니다.

gzip_types application/atom+xml application/javascript text/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json font/ttf application/x-font-ttf application/vnd.ms-fontobject application/font-woff application/font-woff2 application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vnd.wap.wml text/vtt text/x-component text/x-cross-domain-policy;

타임아웃 관련 설정도 조정합니다.

  • keepalive_timeout 30; — keepalive 연결을 종료하기 전까지 대기 시간(30초)
  • keepalive_requests 100; — 클라이언트 하나당 허용되는 최대 keepalive 요청 수
  • reset_timedout_connection on; — 응답하지 않는 클라이언트의 연결을 강제 해제
  • client_body_timeout 10; — 클라이언트의 요청 확인 대기 시간(10초), 초과 시 연결 재설정
  • send_timeout 2; — 클라이언트가 응답을 읽지 않으면 nginx가 연결을 종료

대용량 파일 업로드가 필요 없는 사이트라면 업로드 용량 제한을 걸어두는 것이 좋습니다.

  • client_max_body_size 2m; — 2MB를 초과하는 요청은 서버가 거부
고성능 NGINX + PHP-FPM 웹 서버 구축 및 최적화 가이드

프로젝트 콘텐츠가 자주 변경되지 않는다면 'expires max;' 캐싱을 활용하거나, 필요한 파일 형식에 대해 가상 호스트 설정 파일에 캐시 옵션을 추가할 수 있습니다. 예시는 다음과 같습니다.

location ~* ^.+.(js|css|png|jpg|jpeg|gif|ico|woff)$ {
expires 7d;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

이렇게 지정된 파일 형식은 클라이언트 측에 7일간 캐시됩니다.

설정을 변경한 후에는 반드시 nginx를 재시작하세요.

# systemctl restart nginx

PHP-FPM 설정 최적화

앞서 php-fpm을 Unix 소켓 방식으로 전환했는데, 이것만으로도 성능이 크게 향상됩니다. 실제 벤치마크 기준으로 처리량이 2~3배까지 늘어난다는 평가가 있습니다. 나머지 php-fpm 파라미터는 프로젝트마다 개별적으로 조정해야 합니다. 여기서는 vCPU 1개, RAM 1,024MB 서버 기준의 예시를 살펴보겠습니다.

php-fpm에는 약 512MB를 할당하고, 나머지는 데이터베이스와 nginx에 남겨두는 방식입니다.

/etc/php-fpm.d/www.conf 설정 파일에 다음 값을 추가합니다.

  • pm.max_children = 18 — 최대 자식(child) 프로세스 수
  • pm.start_servers = 6 — 시작 시 생성되는 자식 프로세스 수
  • pm.min_spare_servers = 4 — 유휴 상태로 유지되는 최소 서버 프로세스 수
  • pm.max_spare_servers = 16 — 유휴 상태로 유지되는 최대 서버 프로세스 수
  • pm.max_requests = 400 — 자식 프로세스가 지정 횟수만큼 요청을 처리한 후 재생성(restart)되는 임계값

MariaDB 최적화 방법은 별도 문서에서 자세히 다루고 있으므로 여기서는 생략합니다. 필자는 해당 문서를 참고해 my.cnf 파라미터를 프로젝트에 맞게 설정했고, 데이터베이스가 매우 우수한 성능을 보여주었습니다.

웹사이트를 운영해 보면 nginx + php-fpm 조합이 apache2 + mod_php보다 요청을 훨씬 빠르게 처리하고 페이지를 반환한다는 것을 육안으로도 확인할 수 있을 것입니다.