Instalación de Owncloud con Nginx
Hoy decidí pasar mi actual instalación de Owncloud del muy recorrido Apache a Nginx principalmente para intentar descartar un problema de performance en la sincronización de un gran numero de archivos. La experiencia definitivamente no fue como un paseo en el parque. Me tope con varios problemas, tantos que lo que empezó como un «para mediodía empezaré con las pruebas» para la la noche solo quería no volver a ver un error 404 más.
Empezamos con los detalles.
El resultado esperado
Se espera terminar con un servidor de archivos WebDAV utilizando el paquete de software Owncloud. El servidor debe poder atender un alto numero de solicitudes HTTP por el puerto 80 y utilizar solo software libre.
Los materiales
* OpenSuse 13.1
* Nginx 1.4.4
* PHP 5.5
* MariaDB (o MySQL Community Server)
* Owncloud 6
* phpMyAdmin (para crear la base de datos y el usuario que usara Owncloud)
* nano (si prefieres otro editor eres libre de utilizarlo)
Suelo usar Ubuntu Server como plataforma para servidores pero esta vez preferí usar OpenSuse porque Owncloud (la versión comercial) ofrece una máquina virtual con Owncloud preinstalado y esta VM usa OpenSuse. Suponiendo que hayan elegido esta distribución por alguna razón, decidí ir por el camino seguro y repetir la misma decisión.
El uso de phpMyAdmin es opcional. Si te sientes más cómodo ingresando las sentencias desde el teclado puedes hacerlo a tu manera.
Instalación de OpenSuse
La instalación de openSuse es bastante simple y amigable. En lo único que me puse a jugar fue en las particiones. Utilice LVM con la siguiente configuración:
- sd1 400Mb
- sd2 1Tb
- 40Gb para la raíz /
- 960Gb para la partición /cloudfiles que es donde almacenaré los archivos
- swap 4Gb
Estoy redactando el mapa de particiones de memoria así que debes perdonarme si los tamaños no son exactamente precisos.
Durante la instalación elijo usar el escritorio Xfce porque nunca sabes cuando puede ser necesario contar con un entorno gráfico pero como no estamos instalando un equipo de escritorio Xfce será suficiente para salir de apuros.
Me limito a instalar solo los paquetes básicos para tener el servidor funcionando. Luego instalaremos los paquetes requeridos por este tutorial conforme los vayamos necesitando.
Instalación de Nginx
Una vez que la instalación de OpenSuse ha terminado procedemos a instalar el servidor Nginx. Ejecutamos el siguiente comando en la consola:
1 |
zypper in nginx |
Finalizada la instalación arrancamos el servicio y además le pedimos a Suse que inicie el servicio cada vez que el servidor arranque.
1 2 |
systemctl start nginx.service systemctl enable nginx.service |
Podemos probar que nuestro servidor funciona dirigiéndonos al url http://localhost en el mismo servidor (es aquí donde el entorno gráfico resulta útil). Si el resultado es un error 403 Forbidden (porque no hay ninguna página web que mostrar) entonces nuestro servidor esta funcionando correctamente.
Instalación de PHP-FPM
Php-fpm es una versión de PHP orientada a servidores con alta demanda de solicitudes. A diferencia de la versión de php que solemos usar como un módulo de Apache o CGI esta versión se comporta como un servicio con quien interactuamos a traves de sockets.
Su instalación es tan simple como ejecutar el siguiente comando:
1 |
zypper in php5-fpm php5-cli php5-mysql php5-curl |
Puedes instalar más módulos de PHP si los necesitas. La instalación de módulos se realiza tal como con el PHP de siempre.
Finalizada la instalación arrancamos el servicio y le pedimos a Suse que inicie el servicio cada vez que el servidor arranque.
1 2 |
systemctl start php-fpm.service systemctl enable php-fpm.service |
Configuración de PHP-FPM
Ahora que nuestro servidor PHP esta corriendo hagamosle cambios para adecuarlo a nuestras necesidades. Nos vamos al directorio /etc/php5/fpm/ .
1 |
cd /etc/php5/fpm/ |
y empezamos a editar el fichero php-fpm.conf . Busca y edita las siguientes lineas:
1 2 3 4 5 6 |
; Error log file ; If it's set to "syslog", log is sent to syslogd instead of being written ; in a local file. ; Note: the default prefix is /usr/var ; Default Value: log/php-fpm.log error_log = /var/log/php-fpm.log |
1 2 3 4 5 |
; Unix user/group of processes ; Note: The user is mandatory. If the group is not set, the default user's group ; will be used. user = nginx group = nginx |
1 2 3 4 5 6 7 8 9 |
; The address on which to accept FastCGI requests. ; Valid syntaxes are: ; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific address on ; a specific port; ; 'port' - to listen on a TCP socket to all addresses on a ; specific port; ; '/path/to/unix/socket' - to listen on a unix socket. ; Note: This value is mandatory. listen = 127.0.0.1:9000 |
1 2 3 4 5 6 7 |
; List of ipv4 addresses of FastCGI clients which are allowed to connect. ; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original ; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address ; must be separated by a comma. If this value is left blank, connections will be ; accepted from any ip address. ; Default Value: any listen.allowed_clients = 127.0.0.1 |
Necesitaremos hacer tambien cambios en php.ini que debería estar en este mismo directorio. Si no es así puedes copiar este fichero de la ubicación ya conocida /etc/php5/cli/php.ini
1 |
cp /etc/php5/cli/php.ini /etc/php5/fpm/php.ini |
Ahora abrimos php.ini para aplicarle algunos cambios.
1 2 3 4 5 |
; Maximum size of POST data that PHP will accept. ; Its value may be 0 to disable the limit. It is ignored if POST data reading ; is disabled through enable_post_data_reading. ; http://php.net/post-max-size post_max_size = 1G |
1 2 3 4 5 6 7 8 |
; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI. PHP's ; previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok ; what PATH_INFO is. For more information on PATH_INFO, see the cgi specs. Setting ; this to 1 will cause PHP CGI to fix its paths to conform to the spec. A setting ; of zero causes PHP to behave as before. Default is 1. You should fix your scripts ; to use SCRIPT_FILENAME rather than PATH_TRANSLATED. ; http://php.net/cgi.fix-pathinfo cgi.fix_pathinfo = 0 |
1 2 3 |
; Maximum allowed size for uploaded files. ; http://php.net/upload-max-filesize upload_max_filesize = 1G |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
; Argument passed to save_handler. In the case of files, this is the path ; where data files are stored. Note: Windows users have to change this ; variable in order to use PHP's session functions. ; ; The path can be defined as: ; ; session.save_path = "N;/path" ; ; where N is an integer. Instead of storing all the session files in ; /path, what this will do is use subdirectories N-levels deep, and ; store the session data in those directories. This is useful if you ; or your OS have problems with lots of files in one directory, and is ; a more efficient layout for servers that handle lots of sessions. ; ; NOTE 1: PHP will not create this directory structure automatically. ; You can use the script in the ext/session dir for that purpose. ; NOTE 2: See the section on garbage collection below if you choose to ; use subdirectories for session storage ; ; The file storage module creates files using mode 600 by default. ; You can change that by using ; ; session.save_path = "N;MODE;/path" ; ; where MODE is the octal representation of the mode. Note that this ; does not overwrite the process's umask. ; http://php.net/session.save-path session.save_path = "/var/lib/php5/session" |
Una vez que hayamos guardado los cambios reiniciamos el servidor PHP.
1 |
systemctl restart php-fpm.service |
Configuración de Nginx
Vamos a hacerle algunos retoques a nuestro servidor web para acomodarlo a nuestra necesidades y para poder usarlo con PHP. Empecemos editando el archivo de configuración de Nginx.
1 2 3 |
cd /etc/nginx cp nginx.conf nginx.conf.original nano nginx.conf |
A continuación tienes el script de configuración del servidor que debe encontrarse en nginx.conf. He agregado comentarios a la mayor parte del código para que entiendas que es lo que hace cada línea. La función detallada de cada comando o parámetro la puedes encontrar en la documentación de Nginx cuyo link se encuentra al final de este tutorial.
Me tomé la libertad de traducir algunos comentarios ya existentes del ingles al español.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# Uno por CPU. Si estas sirviendo una gran cantidad de ficheros estáticos que # requieren bloqueos de lectura tu deberás incrementar este valor al número de # núcleos de cpu disponibles en tu sistema. # ¿Para que esperar? Defino desde el inicio el valor de este parámetro para que # iguale el número de nucleos del procesador de mi servidor. # # El número máximo de conexiones para Nginx es calculado de la siguiente forma: # max_clients = worker_processes * worker_connections worker_processes 4; # Máximo número de descriptores que pueden ser abiertos por proceso # Debería ser > worker_connections worker_rlimit_nofile 8192; events { # Cuando requieras > 8000 * cpu_cores connections, deberás de empezar a # optimizar tu SO, y cuando llegues a este punto probablemente tendrás que # empezar a contratar a gente más inteligente que tú. # Esto es "un huevo" de peticiones. worker_connections 8000; } #Donde debe escribirse el log de errores del servidor error_log /var/log/nginx/error.log debug; pid /var/run/nginx.pid; http { # Codificacion por defecto a usar charset utf-8; # Defino los mime-types reconocidos usando el archivo externo mime.types include mime.types; # mime-type por defecto default_type application/octet-stream; # Donde escribir los logs de acceso access_log /var/log/nginx/access.log; # Ocultar la version de Nginx server_tokens off; # ~2 segundos es suficiente para HTML/CSS, pero las conexiones en # Nginx suelen ser baratas, así que generalmente es seguro aumentar el # valor de este parámetro keepalive_timeout 20; # Este parámetro permite enviar directamente al buffer el contenido de archivos sin # procesar su contenido. Para nuestro caso, esto mejorará notablemente la performance # de nuestro servidor de archivos ya que la transferencia de ficheros es la tarea que # más realizará nuestro servidor. sendfile on; # off may be better for Comet/long-poll stuff tcp_nopush on; # on may be better for Comet/long-poll stuff tcp_nodelay off; server_name_in_redirect off; types_hash_max_size 2048; # Activar la compresión de documentos de texto (no lo he probado, pero debería funcionar) gzip on; gzip_http_version 1.0; gzip_comp_level 5; gzip_min_length 512; gzip_buffers 4 8k; gzip_proxied any; gzip_types # text/html is always compressed by HttpGzipModule text/css text/plain text/x-component application/javascript application/json application/xml application/xhtml+xml application/x-font-ttf application/x-font-opentype application/vnd.ms-fontobject image/svg+xml image/x-icon; # This should be turned on if you are going to have pre-compressed copies (.gz) of # static files available. If not it should be left off as it will cause extra I/O # for the check. It would be better to enable this in a location {} block for # a specific directory: # gzip_static on; gzip_disable "msie6"; gzip_vary on; #Defino un backend llamado "php" que consistirá tan solo en enviar los datos recibidos a #una drección IP por un puerto especial. En nuestro caso, localhost por el puerto 9000 #que es justo donde nuestro servidor php esta escuchando peticiones. upstream php { server 127.0.0.1:9000; } # Buscar e incluir los scripts de configuracion de sitios web que nuestro servidor va a # ofrecer include sites-enabled/*; } |
Con esto tenemos la configuración básica del servidor web pero no nos será de utilidad si no definimos los sitios virtuales que va a servir. Hacemos eso a continuación.
En el directorio /etc/nginx (donde estamos ahora) creamos el directorio sites-enabled y en su interior colocaremos la configuración respectiva a cada sitio web que nuestro servidor es capaz de servir.
1 2 |
mkdir sites-enabled cd sites-enabled |
Para crear nuestro site por defecto escribimos:
1 |
nano default |
Y pegamos la siguiente configuración en el editor nano que acaba se abrirse:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
server { #Escuchar peticiones del puerto 80 listen 80 default; listen [::]:80 default ipv6only=on; #Definiendo el nombre del servidor server_name localhost; server_name_in_redirect off; #Codificacion a usar charset utf-8; #Donde escribir los logs de acceso y errores access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; #Ruta que quedará accesible por este sitio web root /srv/www/htdocs; #Nombres de los ficheros que serán buscados cuando no se especifique el archivo (solo el directorio) index index.php index.html index.htm; #Primer intento de servir el archivo solicitado, sino se encuentra se considera como un directorio #y si aún no es encontrado, se entrega el error 404 location / { try_files $uri $uri/ =404; } #Delegar la solicitud de script PHP al servicio FPM location ~ ^((?U).+\.php)(.*)$ { #Este método divide la URL recibida utilizando una regexp y el resultado lo almacena en las #variables $fastcgi_script_name y $fastcgi_path_info fastcgi_split_path_info ^((?U).+\.php)(.*)$; # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini #Indica a que upstream debe entregarse esta solicitud fastcgi_pass php; fastcgi_index index.php; #Definiendo las variables que serán enviadas a php fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; # send bad requests to 404 fastcgi_intercept_errors on; #Esa linea establece el limite de tamaño para subida de archivos del cliente (uploads) client_max_body_size 1G; include fastcgi_params; } } |
Obviamente tu debes reemplazar el valor localhost en el parámetro server_name por el nombre asignado a tu servidor.
Puedes probar la expresión regular ^((?U).+\.php)(.*)$ usada para obtener la ruta del archivo solicitado y las variables entregadas con la herramienta web regex101.com. Hasta el momento esta expresión regular ha cumplido mis expectativas.
Instalación de la base de datos
Para este servidor he elegido usar MariaDB como SGBD pero bien puedes usar otro como MySQL o PostgreSQL. Para el caso de este tutorial si decides usar MySQL los pasos no variarán en absoluto.
Instalámos MariaDB.
1 |
zypper in mariadb mariadb-tools |
Y luego arrancamos el servicio y le pedimos a Suse que arranque el servicio cada vez que se encienda el servidor.
1 2 |
systemctl start mysql.service systemctl enable mysql.service |
Nota: MariaDb es un fork de Mysql Community Server. Esa es la razón de la similitud entre ambos servidores de base de datos. Tanta similitud que el nombre del script del servicio para controlar la base de datos es mysql.service en vez de mariadb.service.
Por defecto el script post-instalación creará el usuario root sin contraseña. Esto debe ser cambiado por supuesto. Puede hacerlo usando el cliente de la base de datos:
1 |
mysql -h localhost -u root |
o usando un administrador web como phpMyAdmin. Recuerda que phpMyAdmin no admite contraseñas vacías desde su pantalla de logeo. Para ello deberás cambiar el valor del parámetro de configuración de phpMyAdmin AllowNoPassword a true.
1 2 3 4 |
#Si mi instalación de phpMyAdmin está en /svr/www/htdocs/phpmyadmin cd /svr/www/htdocs/phpmyadmin cp config.sample.inc.php config.inc.php nano config.inc.php |
Luego busco y edito la linea a:
1 |
$cfg['Servers'][$i]['AllowNoPassword'] = true; |
Creación de la base de datos
La creación de schemas y asignación de privilegios es un tema para otro tutorial. Crea una base de datos y un usuario para que sea usada por Owncloud y limita el acceso de este usuario a solo esta base de datos. Si tienes dificultad puedes hacerlo a través de phpMyAdmin que cuenta con una interfase gráfica para realizar estas acciones.
Instalación de Owncloud
Con Nginx y Php-fpm configurados la instalación de Owncloud no debería da mayores problemas. No hay problema de permisos de archivos que chown y chmod no puedas resolverlo. En mi caso, como cree una partición especial para almacenar los archivos que deseo administrar le ordeno a Owncloud durante su instalación que deseo que use /cloudfiles (mi partición creada durante la instalación de openSuse). Para ello me aseguro que /cloudfiles sea accesible por Nginx para evitar problemas de permisos.
1 |
chown -R nginx:nginx /cloudfiles |
Recursos de información
Las siguientes fuentes fueron muy útiles para salvar los problemas encontrados. No fueron todas las fuentes que use, pero si las mas útiles, las que importan.
Nginx + PHP-FPM + MySQL + phpMyAdmin on Ubuntu 12.04
http://www.lonelycoder.be/nginx-php-fpm-mysql-phpmyadmin-on-ubuntu-12-04/
Tutorial para instalar y configurar un LEMP en Ubuntu Pangolin.
How to Install Nginx With PHP-FPM And MySQL On openSUSE 12.1
http://www.itzgeek.com/how-tos/linux/opensuse/how-to-install-nginx-with-php-fpm-and-mysql-on-opensuse-12-1.html
Debugging Nginx Configuration Trick
http://www.justincarmony.com/blog/2012/01/13/debugging-nginx-configuration-trick/
Una idea para probar resultados de reglas rewrites en Nginx.
Nginx: 413 Request Entity Too Large Error and Solution
http://www.cyberciti.biz/faq/linux-unix-bsd-nginx-413-request-entity-too-large/
De aquí obtuve la solución al error «413 Request Entity Too Large» al intentar cargar archivos de más de 2Mb.
Regular Expressions 101 — an online regex tester for javascript, php, pcre and python.
http://regex101.com/
Extraordinaria herramienta para probar expresiones regulares.
What’s means this ‘(?U)’ on nginx regex
http://stackoverflow.com/questions/15657486/whats-means-this-u-on-nginx-regex
Encontre este ?U en un ejemplo de ocnfiguración de Nginx. Sospeche que podría hacer referencia a la opcion U de las expresiones regulares. De suerte tenia razón.
Nginx documentation
http://nginx.org/en/docs/
Infaltable. Una documentación completa. Cada comando disponible para configurar nginx está aquí, incluso con algunos ejemplos.
Nginx Community
http://wiki.nginx.org/Modules
También encontré documentación aquí pero, a diferencia de la documentación oficial, esta es agradable a la vista.