Instalar un servidor web LEMP (II). Instalación y configuración del MySQL, nginx y PHP

En la anterior entrada de esta serie expliqué cómo cómo instalar y configurar el sistema operativo base (Debian 9 Stretch) en un servidor físico.

En esta entrada voy a explicar cómo, partiendo de la instalación descrita en el artículo anterior, instalar los servicios y demás software necesario para poder ejecutar dos instalaciones de WordPress.

Esta es la segunda entrada de un conjunto de entradas relacionadas, en las que explico cómo instalar un servidor web LEMP con WordPress y Let’s Encrypt en Debian 9 Stretch.

  1. Instalación y configuración del sistema operativo.
  2. Instalación y configuración del MySQL, nginx y PHP.
  3. Migración de WordPress desde el servidor antiguo al nuevo.
  4. Obtención y configuración de un certificado SSL/TLS de Let’s Encrypt.

Convenciones

# – indica que el comando que viene a continuación tiene que ser ejecutado con permisos de root directamente con el usuario root o mediante el comando sudo.
$ – indica que el comando que viene a continuación puede ser ejecutado por un usuario normal sin privilegios de administración.

MySQL

Lo primero que voy a hacer es instalar el servidor de base de datos. En Debian 9 han decidido cambiar MySQL por MariaDB como variante por defecto (más información en este enlace y en este). Yo he decidido seguir utilizando MySQL Community Server, ya que no tengo ningún motivo técnico para el cambio.

Lo primero que hago es añadir el repositorio APT de MySQL. Para ello voy a la web de descargas de MySQL y descargo el paquete mysql-apt-config_0.8.7-1_all.deb. Puede que en el momento que lo vayas a descargar la versión sea una mayor.

$ wget https://dev.mysql.com/get/mysql-apt-config_0.8.7-1_all.deb

Para instalar el paquete ejecuto

# dpkg -i mysql-apt-config_0.8.7-1_all.deb

Aparece un menú de selección que me permite gestionar los elementos que quiero configurar.

Por ejemplo, si pulso la tecla Intro con la primera opción seleccionada, entro en una pantalla donde voy a poder seleccionar la versión del servidor que instalaré. Cuando tenga seleccionada la adecuada (quiero la 5.7, por lo que dejo esa opción seleccionada), selecciono la opción Ok y pulso la tecla Intro.

La salida del comando es

(Reading database ... 27471 files and directories currently installed.)
Preparing to unpack mysql-apt-config_0.8.7-1_all.deb ...
Unpacking mysql-apt-config (0.8.7-1) over (0.8.7-1) ...
Setting up mysql-apt-config (0.8.7-1) ...
Warning: apt-key should not be used in scripts (called from postinst maintainerscript of the package mysql-apt-config)
OK

En estos momentos ya tengo añadido el repositorio oficial de MySQL.

Lo puedo comprobar visualizando el archivo de configuración de APT creado:

# cat /etc/apt/sources.list.d/mysql.list

### THIS FILE IS AUTOMATICALLY CONFIGURED ###
 # You may comment out entries below, but any other modifications may be lost.
 # Use command 'dpkg-reconfigure mysql-apt-config' as root for modifications.
 deb http://repo.mysql.com/apt/debian/ stretch mysql-apt-config
 deb http://repo.mysql.com/apt/debian/ stretch mysql-5.7
 deb http://repo.mysql.com/apt/debian/ stretch mysql-tools
 #deb http://repo.mysql.com/apt/debian/ stretch mysql-tools-preview
 deb-src http://repo.mysql.com/apt/debian/ stretch mysql-5.7

A continuación actualizo la información de los paquetes

# apt-get update

Ahora ya puedo proceder a instalar el servidor

# apt-get install mysql-server

Tras introducir la contraseña de root de MySQL durante la instalación, compruebo que el servicio está en ejecución con el comando

#  systemctl status mysql.service

Para comprobar que tengo acceso al servidor, ejecuto el cliente de MySQL con el usuario root (root de MySQL, no del sistema operativo)

# mysql -uroot -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.7.19 MySQL Community Server (GPL)

Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

Para comprobar que puedo ejecutar un comando, muestro las bases de datos que MySQL crea por defecto, con el comando “show databases;”

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.00 sec)

Para salir de la consola del cliente de MySQL ejecuto el comando “exit”

mysql> exit
Bye

Ya tengo el servidor de base de datos MySQL 5.7  Community Server

A continuación ejecuto el comando “mysql_secure_installation”, que de una forma guiada me permite ajustar la seguridad del servidor MySQL. Tras el comando dejo el resultado de mi ejecución mía mostrar los valores que he usado cuando el script me pregunta algo

# mysql_secure_installation

Securing the MySQL server deployment.

Enter password for user root:

VALIDATE PASSWORD PLUGIN can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD plugin?

Press y|Y for Yes, any other key for No: y

There are three levels of password validation policy:

LOW Length >= 8
MEDIUM Length >= 8, numeric, mixed case, and special characters
STRONG Length >= 8, numeric, mixed case, special characters and dictionary file

Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 2
Using existing password for root.

Estimated strength of the password: 50
Change the password for root ? ((Press y|Y for Yes, any other key for No) : n

... skipping.
By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production
environment.

Remove anonymous users? (Press y|Y for Yes, any other key for No) : y
Success.

Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y
Success.

By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production
environment.

Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y
- Dropping test database...
Success.

- Removing privileges on test database...
Success.

Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y
Success.

All done!

Reinicio el servidor

# systemctl restart mysql.service

Tras instalar MySQL 5.7 paso a instalar el servidor web nginx y php-fpm (FastCGI Process Manager), una implementación FastCGI de PHP.

nginx y PHP7.0

Para llevar a cabo la instalación ejecuto el siguiente comando, que instala nginx, php (versión 7.0), php-fpm y alguna otra extensión necesaria para realizar la comunicación con MySQL o para realizar la comunicación XML desde PHP.

# apt-get install nginx php7.0 php7.0-fpm php7.0-mysql php7.0-xml php7.0-curl php7.0-mbstring

Tras realizar la instalación, si accedo con un navegador a la IP del servidor o al dominio cuyo DNS apunta a esa IP, la salida es la siguiente

nginx instalado por defecto

Si compruebo el contenido de /var/www/html/

# ls /var/www/html/
index.nginx-debian.html

veo que se encuentra la página que acabo de cargar.

Si consulto los sitios disponibles de nginx

# ls /etc/nginx/sites-available/
default

aparece el archivo de configuración por defecto, cuyo contenido, sin comentarios, es el siguiente:

# cat /etc/nginx/sites-available/default
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    root /var/www/html;
    index index.html index.htm index.nginx-debian.html;
    server_name _;
    location / {
        try_files $uri $uri/ =404;
    }
}

Si consulto los sitios habilitados de nginx

# ls /etc/nginx/sites-enabled/ -l
total 0
lrwxrwxrwx 1 root root 34 Jul 20 17:22 default -> /etc/nginx/sites-available/default

Veo que este sitio está habilitado, ya que el archivo “default” es un enlace simbólico al archivo “default” del directorio donde se encuentran los archivos de los sitios disponibles: /etc/nginx/sites-available/

Lo que voy a hacer es configurar dos hosts virtuales (en nginx su nombre es bloque de servidor o server block, el concepto y la nomenclatura de host virtual es “heredado” de Apache), cada uno con un pool fpm que pertenece a un usuario distinto, para poder alojar los dos sitios web: example.com y subdominio.example.com. De esta forma puedo aislar los recursos asignados a cada dominio, de tal forma que un consumo excesivo en un sitio web no tire el servidor o degrade su rendimiento, afectando al resto de sitios web instalados o a otros servicios que existan en la máquina.

Si los sitios los tengo en producción lo que hago es cambiar el archivo hosts de mi máquina local, apuntando a la IP del nuevo servidor, para que las resoluciones DNS hagan que solo en mi máquina esa resolución apunte a este servidor. El archivo de hosts de la máquina depende del sistema operativo. Puedes ver la ruta de tu sistema operativo en este enlace.

Si aún no tengo los sitios en producción lo que hago es cambiar las dos entradas DNS de tipo A en el proveedor con el que tengo registrado el dominio o contratado el servicio de DNS, apuntando a la IP del nuevo servidor.

Configuración del primer sitio

Lo primero que voy a hacer es crear un usuario bajo el que va a correr el pool del primer sitio: example.com

# adduser --disabled-login --gecos 'Usuario para example.com' --system --group --shell=/bin/false --no-create-home example_com
Adding system user `example_com' (UID 119) ...
Adding new group `example_com' (GID 126) ...
Adding new user `example_com' (UID 119) with group `example_com' ...
Not creating home directory `/home/example_com'.

Compruebo que el sitio ha sido creado.

# cat /etc/passwd | grep example_com
example_com:x:119:126:Usuario para example.com,,,:/home/example_com:/bin/false

Y que no se ha creado el home del usuario

# ls /home/ | grep example_com

Ya que este comando devuelve una salida vacía.

Lo siguiente es crear un pool propio para este usuario, para que pueda ejecutar los PHP para su sitio con ese usuario.

# cd /etc/php/7.0/fpm/pool.d/
# cp www.conf example_com.conf

En este archivo:

  • cambio el nombre del pool (no puede haber 2 iguales)
; pool name ('www' here)
[www]

por

; pool name ('www' here)
[example_com]
  • el usuario y el grupo bajo el que correrá el proceso, que es el que acabo de crear con el comando “adduser”. Cambio:
; 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 = www-data
group = www-data

por

; 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 = example_com
group = example_com
  • el socket en el que escuchará peticiones. Este socket lo usaré posteriormente en el virtual host de nginx para realizar las llamadas FastCGI
; The address on which to accept FastCGI requests.
listen = /run/php/php7.0-fpm.sock

por

; The address on which to accept FastCGI requests.
listen = /run/php/php7.0-fpm-example_com.sock

Por ahora no hago más cambios en este archivo de configuración. En este archivo es donde asigno los recursos de procesado PHP, para poder asignar más o menos recursos a casa sitio web, evitando que un consumo alto de recursos de un sitio sature la máquina y tire el resto de sitios instalados en la máquina.

Recargo la configuración de fpm

# systemctl reload php7.0-fpm.service

Lo siguiente es crear y configurar el host virtual (virtual host) de nginx.

Antes de eso creo el directorio donde estará la raíz de este primer WordPress

# mkdir -p /var/www/example_com/wordpress

Y creo el archivo de configuración

# cd /etc/nginx/sites-available/
# cp default example_com

Creo el archivo de configuración. Si visualizo el archivo sin líneas vacías ni comentarios (ver explicación) el contenido, que explico a continuación, es el siguiente:

# grep -v '^$\|^\s*\#' /etc/nginx/sites-available/example_com
server {
    listen 80;
    listen [::]:80;
    root /var/www/example_com/wordpress;
    index index.php index.html index.htm;
    server_name example.com www.example.com;
    location / {
        try_files $uri $uri/ /index.php?q=$uri&$args;
    }
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.0-fpm-example_com.sock;
    }
}

Las dos primeras líneas indican que escucha el puerto 80, tanto en IPv4 como en IPv6.

listen 80; 
listen [::]:80;

La tercera línea indica el directorio raíz donde se encuentra el sistema web.

root /var/www/example_com/wordpress;

La cuarta línea define los elementos que serán usados como index.

 index index.php index.html index.htm;

La quinta línea indica los dominios y/o subdominios que atenderá este archivo de configuración.

server_name example.com www.example.com;

La líneas 6 a 8 comprueban la existencia de archivos en el orden indicado y usa la primera ocurrencia para devolver el archivo.

location / { 
    try_files $uri $uri/ /index.php?q=$uri&$args; 
}

Las líneas 9 a 12 lo que hacen es cargar un código externo de configuración, mediante el include, y pasar la llamada al servicio FastCGI, encargado de procesar el código.

location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php7.0-fpm-example_com.sock;
 }

Recomendable este tutorial de nginx de “Pitfalls and Common Mistakes“.

Tras crear el archivo de configuración (en el directorio /etc/nginx/sites-available/), lo habilito, creando un enlace simbólico del archivo de configuración al directorio /etc/nginx/sites-enabled/. Esto es lo mismo que realizar un a2ensite en Apache

ln -s /etc/nginx/sites-available/example_com /etc/nginx/sites-enabled/

Para comprobar que el archivo es correcto, ejecuto

# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Recargo la configuración de nginx

# systemctl reload nginx.service

Ahora que tengo configurado nginx, lo que voy a hacer es crear un archivo index.html y otro index.php para comprobar que la configuración de nginx y del gestor de procesos php-fpm es correcta

# nano /var/www/example_com/wordpress/index.html

Añado el texto

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>example.com</title>
    </head>
    <body>
    Contenido del documento example.com
    </body>
</html>

Creo el archivo index.php

# nano /var/www/example_com/wordpress/index.php

y le añado el contenido

<?php
phpinfo();

Actualizo los permisos

# chown example_com.example_com /var/www/example_com/ -R
# chmod 775 /var/www/example_com/ -R

Si visualizo con el navegador la ruta por defecto debe de aparecer el texto plano, mientras que si lo hago añadiendo “index.php” debe de salir el resultado de la función phpinfo().

Configuración del segundo sitio

Para el segundo sitio voy a llevar a cabo el mismo procedimiento: subdominio.example.com

Creo el usuario

# adduser --disabled-login --gecos 'Usuario para subdominio.example.com' --system --group --shell=/bin/false --no-create-home subdominio_example_com
 Adding system user `subdominio_example_com' (UID 111) ...
 Adding new group `subdominio_example_com' (GID 115) ...
 Adding new user `subdominio_example_com' (UID 111) with group `subdominio_example_com' ...
 Not creating home directory `/home/subdominio_example_com'.

Compruebo que el sitio ha sido creado.

# cat /etc/passwd | grep subdominio_example_com
subdominio_example_com:x:111:115:Usuario para subdominio.example.com,,,:/home/subdominio_example_com:/bin/false

Y que no se ha creado el home del usuario

# ls /home/ | grep subdominio_example_com

Ya que este comando devuelve una salida vacía.

Lo siguiente es crear un pool propio para este usuario, para que pueda ejecutar los PHP para su sitio con ese usuario.

# cd /etc/php/7.0/fpm/pool.d/

# cp example_com.conf subdominio_example_com.conf

Edito el archivo para adaptarlo al subdominio, con los siguientes cambios

  • cambio el nombre del pool (no puede haber 2 iguales)
; pool name ('www' here)
[example_com]

por

; pool name ('www' here)
[subdominio_example_com]
  • el usuario y el grupo bajo el que correrá el proceso. Cambio:
; 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 = example_com
group = example_com

por

; 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 = subdominio_example_com
group = subdominio_example_com
  • el socket en el que escuchará peticiones. Este socket lo usaré posteriormente en el virtual host de nginx para realizar las llamadas FastCGI
; The address on which to accept FastCGI requests.
listen = /run/php/php7.0-fpm-example_com.sock

por

; The address on which to accept FastCGI requests.
listen = /run/php/php7.0-fpm-subdominio_example_com.sock

Recargo la configuración de fpm

# systemctl reload php7.0-fpm.service

Creo el directorio donde estará la raíz de este segundo WordPress

# mkdir -p /var/www/subdominio_example_com/wordpress

Creo el archivo de configuración

# cd /etc/nginx/sites-available/
# cp example_com subdominio_example_com

Actualizo la información del archivo, quedando de la siguiente forma

Creo el archivo de configuración. Si visualizo el archivo sin líneas vacías ni comentarios (ver explicación)

# grep -v '^$\|^\s*\#' /etc/nginx/sites-available/subdominio_example_com
server {
    listen 80;
    listen [::]:80;
    root /var/www/subdominio_example_com/wordpress;
    index index.php index.html index.htm;
    server_name subdominio_example_com;
    location / {
        try_files $uri $uri/ /index.php?q=$uri&$args;
    }
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.0-fpm-subdominio_example_com.sock;
    }
}

Tras crear el archivo de configuración (en el directorio /etc/nginx/sites-available/), lo habilito, creando un enlace simbólico del archivo de configuración al directorio /etc/nginx/sites-enabled/

# ln -s /etc/nginx/sites-available/subdominio_example_com /etc/nginx/sites-enabled/

Compruebo que el archivo de configuración es correcto

# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Recargo la configuración de nginx

# systemctl reload nginx.service

Ahora que tengo configurado nginx para este segundo dominio, lo que voy a hacer es crear un archivo index.html y otro index.php para comprobar que la configuración de nginx y del gestor de procesos php-fpm es correcta

# nano /var/www/subdominio_example_com/wordpress/index.html

Añado el texto

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>subdominio.example.com</title>
    </head>
    <body>
    Contenido del documento subdominio.example.com
    </body>
</html>

Creo el archivo index.php

# nano /var/www/subdominio_example_com/wordpress/index.php

y le añado el contenido

<?php
phpinfo();

Actualizo los permisos

# chown subdominio_example_com.subdominio_example_com/var/www/subdominio_example_com/ -R
# chmod 775 /var/www/subdominio_example_com/ -R

Si visualizo con el navegador la ruta por defecto debe de aparecer el texto plano, mientras que si lo hago añadiendo “index.php” debe de salir el resultado de la función phpinfo().

Más información:

6 comments

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.