PHP 5.3, PHP-FPM, XCache, Nginx no Debian Lenny – Performance extrema

janeiro 15th, 2010 por caferrari Leave a reply »

Neste post vou exemplificar os passos para configurar um Webserver que consome poucos recursos e é extremamente rápido no Debian Lenny.

PHP-FPM: é um gerenciador de processos CGI do php, similar ao Phpcgi, porém com várias vantagens como:

  • Gerenciador de Processos muito eficiente, com capacidade de parar e iniciar seus deamons caso necessário
  • Possibilidade de Iniciar processos com usuários diferentes e com arquivos php.ini diferentes
  • Uploads otimizados
  • Capacidade de reinicializar processos caso ocorra algum problema
  • Escalabilidade de processos, ele pode criar mais processos de acordo com a demanda
  • e muito mais.

XCache: é um gerenciador de cache, que armazena os scripts php em opcode para uso futuro, é extremamente rápido e muito usado no mundo em servidores com alta demanda.

Nginx: Conheci este webserver lendo o blog da boo-box, ele é um Servidor web e proxy reverso extremamente veloz e que vem ganhando mercado nos últimos meses devido a sua facilidade de uso e capacidade de carga.

Market Share de navegadores no final de 2009

Para agilizar a configuração, vamos usar alguns pacotes (php5.3, php-fpm) pré compilados e mantidos www.dotdeb.org. os testes que eu realzei usando estes pacotes tiveram um ganho de performance enorme.

Instalação:

Vamos começar adicionado os repositórios do dotdeb no /etc/apt/sources.list:

deb http://php53.dotdeb.org stable all
deb-src http://php53.dotdeb.org stable all

Vamos instalar os pacotes e algumas dependências para o nginx:

aptitude update
aptitude install php5-fpm php5-xcache php5-gd php5-curl \
php5-mysql php5-pgsql libssl-dev libpcre3-dev build-essential -y

Agora temos o php instalado, vamos compilar o nginx:

cd
mkdir src
cd src
wget http://nginx.org/download/nginx-0.7.64.tar.gz
tar -zxvf nginx-0.7.64.tar.gz
cd nginx-0.7.64
./configure --prefix=/usr/local/nginx --user=www-data --group=www-data \
    --without-mail_pop3_module --without-mail_imap_module \
    --without-mail_smtp_module --with-http_stub_status_module \
    --with-http_ssl_module --http-log-path=/var/log/nginx/access.log \
    --conf-path=/etc/nginx/nginx.conf  --pid-path=/var/run/nginx.pid
make
make install

Se tudo ocorreu bem, você já tem o nginx instalado, vamos apenas coloca-lo para inicializar automáticamente, crie o arquivo /etc/init.d/nginx e coloque o conteúdo:

#! /bin/sh
 
### BEGIN INIT INFO
# Provides:          nginx
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the nginx web server
# Description:       starts nginx using start-stop-daemon
### END INIT INFO
 
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local
DAEMON=/usr/local/nginx/sbin/nginx
NAME=nginx
DESC=nginx
 
test -x $DAEMON || exit 0
 
# Include nginx defaults if available
if [ -f /etc/default/nginx ] ; then
        . /etc/default/nginx
fi
 
set -e
 
case "$1" in
  start)
        echo -n "Starting $DESC: "
        start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \
                --exec $DAEMON -- $DAEMON_OPTS || true
        echo "$NAME."
        ;;
  stop)
        echo -n "Stopping $DESC: "
        start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid \
                --exec $DAEMON || true
        echo "$NAME."
        ;;
  restart|force-reload)
        echo -n "Restarting $DESC: "
        start-stop-daemon --stop --quiet --pidfile \
                /var/run/$NAME.pid --exec $DAEMON || true
        sleep 1
        start-stop-daemon --start --quiet --pidfile \
                /var/run/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS || true
        echo "$NAME."
        ;;
  reload)
        echo -n "Reloading $DESC configuration: "
        start-stop-daemon --stop --signal HUP --quiet --pidfile /var/run/$NAME.pid \
            --exec $DAEMON || true
        echo "$NAME."
        ;;
  configtest)
        echo -n "Testing $DESC configuration: "
        if nginx -t > /dev/null >&1
        then
          echo "$NAME."
        else
          exit $?
        fi
        ;;
  *)
        echo "Usage: $NAME {start|stop|restart|reload|force-reload|configtest}" >&2
        exit 1
        ;;
esac
 
exit 0

depois vamos torna-lo executável e atualizar o rc.d:

chmod +x /etc/init.d/nginx
update-rc.d nginx defaults

Com isso o processo de instalação está concluído, vamos configurar!

Configuração do XCache

Coloque a seguinte configuração no arquivo /etc/php5/fpm/conf.d/xcache.ini:

; configuration for php xcache module
extension=xcache.so
 
[xcache.admin]
xcache.admin.user = "secom"
; xcache.admin.pass = md5($your_password)
xcache.admin.pass = ""
[xcache]
; ini only settings, all the values here is default unless explained
; select low level shm/allocator scheme implemenation
xcache.shm_scheme =        "mmap"
; to disable: xcache.size=0
; to enable : xcache.size=64M etc (any size > 0) and your system mmap allows
xcache.size  =                64M
; set to cpu count (cat /proc/cpuinfo |grep -c processor)
xcache.count =                 1
; just a hash hints, you can always store count(items) > slots
xcache.slots =                8K
; ttl of the cache item, 0=forever
xcache.ttl   =                 0
; interval of gc scanning expired items, 0=no scan, other values is in seconds
xcache.gc_interval =           0
; same as aboves but for variable cache
xcache.var_size  =            64M
xcache.var_count =             1
xcache.var_slots =            8K
; default ttl
xcache.var_ttl   =             0
xcache.var_maxttl   =          0
xcache.var_gc_interval =     300
xcache.test =                Off
; N/A for /dev/zero
xcache.readonly_protection = Off
; for *nix, xcache.mmap_path is a file path, not directory.
; Use something like "/tmp/xcache" if you want to turn on ReadonlyProtection
; 2 group of php won't share the same /tmp/xcache
; for win32, xcache.mmap_path=anonymous map name, not file path
xcache.mmap_path =    "/dev/zero"
 
; leave it blank(disabled) or "/tmp/phpcore/"
; make sure it's writable by php (without checking open_basedir)
xcache.coredump_directory =   ""
; per request settings
xcache.cacher =               On
xcache.stat   =               On
xcache.optimizer =            On
[xcache.coverager]
; per request settings
; enable coverage data collecting for xcache.coveragedump_directory and xcache_coverager_start/stop/get/clean() functions (will hurt executing performance)
xcache.coverager =          Off
; ini only settings
; make sure it's readable (care open_basedir) by coverage viewer script
; requires xcache.coverager=On
xcache.coveragedump_directory = ""

Configuração do Nginx

Edite o arquivo em /etc/nginx/nginx.conf:

user  www-data;
 
#Coloque quantos processos simultaneos, o ideal é um para cada nucleo do sistema:
worker_processes  4;
 
error_log  /var/log/nginx/error.log;
 
events {
    # numero de conexões simultaneas por worker
    worker_connections  1024;
}
 
http {
    include       mime.types;
    default_type  application/octet-stream;
 
    access_log  /var/log/nginx/access.log;
    sendfile        on;
    # isso é interessante, dá um ganho enorme na navegação, e o nginx aguenta tranquilamente
    keepalive_timeout  60;
    tcp_nodelay        on;
 
    ## Compressão
    gzip on;
    gzip_http_version 1.0;
    gzip_comp_level 2;
    gzip_proxied any;
    gzip_min_length  1100;
    gzip_buffers 16 8k;
    # mime types para zipar e adicionar header de cache
    gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript image/gif image/jpeg image/png;
    # Desabilita compressão em algumas versões do IE6
    gzip_disable "MSIE [1-6].(?!.*SV1)";
    # Impede que servidores proxy mandem versões em gzip do arquivo para os IE6
    gzip_vary on;
 
    server {
	root  /var/www/;
	server_name     localhost;
	listen          80;
	location / {
		index  index.php index.html index.htm;
	}
 
	#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   /var/www/nginx-default;
	}
 
	# Joga requisições para arquivos .php para o php-fpm
	location ~ \.php$ {
		fastcgi_pass   127.0.0.1:9000;
		fastcgi_index  index.php;
		include fastcgi_params;
        	fastcgi_param SCRIPT_FILENAME /var/www/$fastcgi_script_name;
        	fastcgi_param SERVER_NAME $http_host;
        	fastcgi_ignore_client_abort on;
	}
    }
}

Configuração do php-fpm

Edite o arquivo: /etc/php5/fpm/php5-fpm.conf e faça os ajustes que você achar interessante, aqui eu só aumento os max_childrens (Numero de clientes CGI) de 5 para 50, esta configuração depende do seu hardware.

Configuração do Logrotate

Vamos configurar agora o logrotate para não enchermos os discos com logs do nginx, edite o arquivo /etc/logrotate.d/nginx:

/var/log/nginx/*.log {
        daily
        missingok
        rotate 7
        compress
        delaycompress
        notifempty
        create 640 root adm
        sharedscripts
        postrotate
                [ ! -f /var/run/nginx.pid ] || kill -USR1 `cat /var/run/nginx.p$
        endscript
}

Finalizando

Agora que está tudo configurado, vamos reinicalizar os serviços:

/etc/init.d/php5-fpm restart
/etc/init.d/nginx restart

crie um arquivo /var/www/index.php e vamos testar o php:

 
e acesse: http://ip_do_servidor/
Advertisement

29 comments

  1. Erick disse:

    Fala ai Carlos,

    você fez alguma comparação de performance entre as soluções padrões (like, apache+php)?
    Tem algum gráfico de performance ou algum “vmstat 1″ p/ analisarmos?

    Mas foi uma ótima dica ! Obrigado!

  2. Diego disse:

    Também gostei do passo-a-passo. Porém faltou um comparativo como o Erick citou.
    Se der tempo vou testar essa solução em breve.
    Estou meio “viciado” em otimização de perfomance em servidores web. Ultimamente tudo que vejo sobre o assunto estou lendo e pesquisando.

    Obrigado pela contribuição

  3. Olá,

    kra, não fiz nenhum teste localmente, mas pesquisei vários no google e por isso decidi implementar…

    o que posso dizer, eh que nosso servidor (Do governo do Tocantins) é um Pentium 4, com 1gb de memória… com o apache ele começava a engasgar com 200 conexões… com o nginx já passou de 1000 conexões! e o uso da maquina não passa de 5%

    fiz até um post sobre isso: http://ferrari.eti.br/nginx-webserver-proxy-reverso-e-perfeito/ dá uma conferida

    Dá uma navegada nos sites:
    http://to.gov.br

    []‘s

  4. Silas Ribas disse:

    Salve,

    É Carlos só falta um comparativo entre apache2+php e nginx+php. Com configurações “iguais”, se otimiza em 1, faz o mesmo no outro.

    Está bem legal o artigo, valeu pela dica.

  5. sid disse:

    Thanks you very much for this tutorial, i was stopped by php-fpm install to make a full great install ! (only install nginx with fastCGI)

  6. Henrique Dias disse:

    olá Carlos. excelente post e blog também !
    pergunta básica: nada contra a distro Debian, muito pelo contrário, mas como tenho mais contato com Ubuntu, como você vê a possibilidade de implementação desta configuração em um servidor Ubuntu ?

  7. Diego disse:

    @Henrique Dias
    No Ubuntu não vai mudar muita coisa, aliás, acho que não muda nada visto que a base do Ubuntu é o próprio Debian.
    Os pacotes possivelmente serão os mesmos.

  8. Carlos André Ferrari disse:

    @Henrique Dias, @Diego

    Pode executar os processos sem nenhuma mudança no Ubuntu Server ou Desktop que funciona sem problemas.

    []‘s

  9. Diego disse:

    Acabei de instalar e testar a solução proposta e estou impressionado.
    Configurei o jMeter pra 2000 threads em 60 segundos e nem fez cócegas no servidor (virtual). Tudo bem que o teste foi acessar a página do PHP Info, mas mesmo assim a performance foi muito boa.
    Vou configurar outra máquina virtual de testes para replicar o mesmo ambiente, mas dessa vez com Apache e fazer um teste comparativo.

  10. Jose Nilton disse:

    Carlos, você já testou o Webserver Lighthttpd,
    pois estou testando e ele está aguentando o tranco, ou o cherokee -> http://www.cherokee-project.com/

    Esse ultimo também está muito rápido, mas prefiro o LIghtHTTPD

    benchmark:
    http://www.rkblog.rk.edu.pl/w/p/lighttpd-and-cherokee-benchmark/
    http://www.rkblog.rk.edu.pl/w/p/pylons-benchmark-various-servers/

  11. Jose Nilton disse:

    Tem outro benchmark Legal, mas eu prefiro o LightHTTPD, pois ele é mais fácil de instalar e configurar.

    http://www.alobbs.com/1345/Cherokee_0_8_1_benchmark.html

  12. Pessoal,

    Atendendo pedidos, e com ajuda do Ferrari na configuração, fiz um teste real: Apache X Nginx.

    Primeiro Server: CentOS+Apache+2core500+1GbRam
    Segundo Server: Ubuntu+Nginx+2core300+300MbRam

    Quem leva a melhor nessa? façam suas apostas. :)

    Coloquei o mesmo site nos 2 servers, um site de noticias, com MySQL e várias consultas na home. Medi o tempo de execução da home do site nos 2 servidores e peguei os maiores valores depois de algumas tentativas.

    Primeiro Server (apache): 0.6 segundos
    Segundo Server (nginx): 0.023 segundos

    Venceu o Nginx com 2600% de dianteira.

    Valeu Ferrari.

  13. Diego disse:

    Não consigo colocar o Zend Framework pra rodar com o Nginx. É possível? Alguém conseguiu? Ou tenho que fazer proxy com o Apache?

  14. caferrari disse:

    @Diego,

    é possivel sim, nunca usei o ZF, mas acredito que ele faz o uso do .htaccess, o htacces é do apache, no caso do nginx vc tem que configurar as rotas no arquivo de configuração, é tão simples quanto o metodo do apache e também é muito mais veloz.

    http://wiki.nginx.org/NginxHttpRewriteModule

    []‘s

  15. Diego disse:

    @caferrari Exatamente, o Zend Framework utiliza o .htaccess. Achei vários blogs com várias configurações diferentes para rodar o ZF no Nginx, porém nenhuma funciona. Estou sem tempo agora mas assim que possível vou verificar a wiki que você indicou. Obrigado.

  16. Diego disse:

    Eita. Acho que era problema na minha aplicação de testes. Peguei o exemplo do Quickstart http://framework.zend.com/manual/en/learning.quickstart.intro.html
    e funcionou.

  17. Clecio disse:

    Grande artigo, estou procurando uma alternativa mais rápida para rodar um sistema desenvolvido em PHP com JS (Ext).
    Como uso centos e esse usa o PHP 5.1.6 não tenho muita experiencia no Debia.
    Tenho uma duvida, como instalar suporte a MSSQL no PHP5?
    Muito obrigado.

  18. Ferrari, obrigado pelo ótimo tutorial. Encontrei esse script para benchmark: http://benchmarks.nickbarrett.org/

    E esse site com um comparativo entre PHP-FPM vs Spawn-FCGI vs FastCGI: http://vpsbible.com/php/php-benchmarking-phpfpm-fastcgi-spawnfcgi/

  19. caferrari disse:

    Obrigado pelo comentário @Rogério Madureira

    Bom apesar desse seu teste mostrar que o PHP-FPM é mais lento um pouco que as outras soluções, o FPM ainda tem vantagens.. como o spawn automático de threads dependendo da necessidade e os controles internos dele que garantem que seja fail-safe.

    []‘s

  20. Será que eu compreendi errado? havia entendido que o benchmarking havia apontado o PHP-FPM como a solução mais rápida, com tempo médio de 374ms para completar as operações do teste, contra 413ms do SPAWN-FCGI e 461ms do PHP-FASTCGI…

  21. caferrari disse:

    Não.. eu entendi errado mesmo… então pronto.. php-fpm é perfeito.. XD

    vlw!

Deixe uma resposta