WordPress en Ubuntu 20 con Base de Datos master-slave

Última revisión: 19 de enero de 2021

Este tutorial ha sido creado en un VPS de Clouding. Puedes crear tu propio VPS desde 3€/mes.

Además, tienes la posibilidad de crear tu VPS directamente con la imagen WordPress.

COLABORACIÓN

En algunos momentos podemos tener la necesidad de incrementar el rendimiento de una instalación de WordPress a un sistema de Alta Disponibilidad, y un primer punto por donde empezar es el uso de un sistema de distribución de las bases de datos.

Este ejemplo es sencillo, en una misma localización y con direcciones IP privadas, pero se podría usar como base para un sistema entre distintos centros de datos. En cualquier caso, aunque los centros de datos sean distintos, se recomienda que sean de la misma empresa ya que suelen disponer de conectividad entre ellos directamente, lo que reduce los tiempos de respuesta.

Distribución de infraestructura

El objetivo de este tutorial es el de distribuir las bases de datos, y lo haremos de forma que tengamos una base de datos principal (master) y una base de datos secundaria (slave). Además, los ficheros del propio WordPress estarán separados.

Máquina 1: Ficheros (nginx + PHP) – IP privada: 10.0.0.1
Máquina 2: SQL (primaria / master) – IP privada: 10.0.0.2
Máquina 3: SQL (secundaria / slave) – IP privada: 10.0.0.3

En el ejemplo se utilizan las direcciones IP privadas porque, habitualmente, no suelen tener ningún tipo de consumo a la hora de enrutamiento en la mayoría de proveedores cloud, pero se puede usar de la misma manera con direcciones IP públicas.

Hay que tener en cuenta que, al menos, para los rangos de IP privada, el puerto del MySQL / MariaDB debe estar accesible desde el exterior. El puerto es el 3306.

IMPORTANTE: Aunque la CPU y RAM de las máquinas del SQL pueden ser distintas (según el uso que se les vaya a dar), sí que se recomienda que el tamaño de disco sea exactamente el mismo.

Versiones a instalar

Sistema Operativo: Ubuntu 20
Panel de Control: ninguno
Servidor web: nginx
Base de Datos: MariaDB 10.5 (master-slave)
Procesador: PHP 8.0
Caché: Redis

Preparación de datos

Cosas que vamos a necesitar tener a mano:

  • Direcciones IP públicas y privadas de las 3 máquinas
  • Crear una clave de root para el SQL principal
  • Crear una clave de root para el SQL secundaria
  • Usuario para la replicación SQL (en el ejemplo será «replica») y una contraseña.
  • Base de datos, usuario y contraseña para la base de datos del WordPress.

Servidor SQL primario (master)

Configurando el Sistema Operativo

Una vez esté instalado el sistema operativo, lo primero que configuraremos será la hora del servidor. En este caso configuraremos la zona horaria global UTC.

timedatectl set-timezone 'UTC'
timedatectl set-ntp on

Lo siguiente que haremos es comprobar la versión del sistema operativo y, posteriormente, hacer una actualización completa del mismo.

lsb_release -a
apt -y update && apt -y upgrade && apt -y dist-upgrade && apt -y autoremove

Una vez esta todo actualizado, instalamos algunas herramientas y software base que puede ser útil tener en el sistema.

apt -y install software-properties-common curl vim zip unzip apt-transport-https

Antes de seguir configuraremos que se apliquen las actualizaciones automáticas de seguridad.

apt -y install unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades

Instalación de MariaDB

El siguiente paso será la instalación de la base de datos. En este caso vamos a utilizar MariaDB 10.5. Lo primero que haremos será configurar la descarga, y posteriormente su instalación.

curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version="mariadb-10.5"
apt -y update && apt -y upgrade && apt -y dist-upgrade && apt -y autoremove
apt -y install mariadb-server mariadb-client

Ahora que está instalada, procederemos a la configuración inicial. Para ello usaremos el sistema se instalación segura, que nos hará algunas preguntas.

mysql_secure_installation

A la pregunta de si queremos cambiar la contraseña, dependiendo de si hemos puesto o no en la instalación, la cambiaremos. En caso de no haber puesto ninguna, es muy recomendable ponerle una contraseña segura.

Set root password? [Y/n]: Y

Al resto de preguntas, contestaremos lo siguiente:

Remove anonymous users? [Y/n]: Y
Disallow root login remotely? [Y/n]: Y
Remove test database and access to it? [Y/n]: Y
Reload privilege tables now? [Y/n]: Y

En este momento ya tendremos la base de datos configurada. Ahora haremos que se ejecute en los re inicios del sistema y la iniciaremos.

systemctl stop mysql.service
systemctl enable mysql.service
systemctl start mysql.service
systemctl status mysql.service

Configuraremos el sistema para que se considere este el servidor principal. Para ello tendremos que modificar algunos detalles del fichero de configuración.

vim /etc/mysql/mariadb.conf.d/50-server.cnf

Los tres elementos ya existen (algunos comentados, otros no). Lo mejor será editar lo existente. recuerda indicar la IP de este servidor.

[mysqld]
bind-address = 10.0.0.2
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log

Para que se aplique la configuración, reiniciaremos el servidor de base de datos.

systemctl restart mysql.service

Ahora que ya tenemos configurado el servidor como servidor 1, vamos a crear un usuario que permita que otras máquinas accedan a recoger la información. Lo primero será acceder al MariaDB.

mysql -p

Aquí crearemos un usuario y le daremos permiso para que la máquina secundaria pueda acceder. Es importante que la dirección IP sea la correcta.

CREATE USER 'replica'@'10.0.0.3' IDENTIFIED BY 'contraseña_del_usuario_replica';
GRANT REPLICATION SLAVE ON . TO 'replica'@'10.0.0.3';
FLUSH PRIVILEGES;

Si todo va bien, podremos confirmar que se ha creado el sistema y está listo para recibir peticiones.

SHOW MASTER STATUS;

Esto nos devolverá datos del estilo a:

+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 | 328 | | |
+------------------+----------+--------------+------------------+

Que serán necesario para un uso posterior. Debemos quedarnos con el dato del File (mysql-bin.000001) y Position (328).

Podemos dejar la ventana abierta, ya que después tendremos que volver para comprobar que la sincronización funciona.

Servidor SQL secundario (slave)

Configurando el Sistema Operativo

Una vez esté instalado el sistema operativo, lo primero que configuraremos será la hora del servidor. En este caso configuraremos la zona horaria global UTC.

timedatectl set-timezone 'UTC'
timedatectl set-ntp on

Lo siguiente que haremos es comprobar la versión del sistema operativo y, posteriormente, hacer una actualización completa del mismo.

lsb_release -a
apt -y update && apt -y upgrade && apt -y dist-upgrade && apt -y autoremove

Una vez esta todo actualizado, instalamos algunas herramientas y software base que puede ser útil tener en el sistema.

apt -y install software-properties-common curl vim zip unzip apt-transport-https

Antes de seguir configuraremos que se apliquen las actualizaciones automáticas de seguridad.

apt -y install unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades

Instalación de MariaDB

El siguiente paso será la instalación de la base de datos. En este caso vamos a utilizar MariaDB 10.5. Lo primero que haremos será configurar la descarga, y posteriormente su instalación.

curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version="mariadb-10.5"
apt -y update && apt -y upgrade && apt -y dist-upgrade && apt -y autoremove
apt -y install mariadb-server mariadb-client

Ahora que está instalada, procederemos a la configuración inicial. Para ello usaremos el sistema se instalación segura, que nos hará algunas preguntas.

mysql_secure_installation

A la pregunta de si queremos cambiar la contraseña, dependiendo de si hemos puesto o no en la instalación, la cambiaremos. En caso de no haber puesto ninguna, es muy recomendable ponerle una contraseña segura.

Set root password? [Y/n]: Y

Al resto de preguntas, contestaremos lo siguiente:

Remove anonymous users? [Y/n]: Y
Disallow root login remotely? [Y/n]: Y
Remove test database and access to it? [Y/n]: Y
Reload privilege tables now? [Y/n]: Y

En este momento ya tendremos la base de datos configurada. Ahora haremos que se ejecute en los re inicios del sistema y la iniciaremos.

systemctl stop mysql.service
systemctl enable mysql.service
systemctl start mysql.service
systemctl status mysql.service

Configuraremos el sistema para que se considere este el servidor principal. Para ello tendremos que modificar algunos detalles del fichero de configuración.

vim /etc/mysql/mariadb.conf.d/50-server.cnf

Los tres elementos ya existen (algunos comentados, otros no). Lo mejor será editar lo existente. recuerda indicar la IP de este servidor.

[mysqld]
bind-address = 10.0.0.3
server-id = 2
log_bin = /var/log/mysql/mysql-bin.log

Para que se aplique la configuración, reiniciaremos el servidor de base de datos.

systemctl restart mysql.service

Ahora que ya tenemos configurado el servidor como servidor 2, vamos a decirle que acceda y sincronice los datos con el servidor principal. Lo primero será acceder al MariaDB.

mysql -p

Pararemos el servidor esclavo, indicaremos desde dónde ha de leer la información, y lo volveremos a activar.

recuerda que debes usar la IP del servidor principal, el usuario y contraseña creado para la replicación, y el nombre de fichero e identificador que nos ha devuelto la última consulta.

STOP SLAVE;
CHANGE MASTER TO MASTER_HOST='10.20.10.5', MASTER_USER='replica', MASTER_PASSWORD='nddH4nhQrgrDf9z5GjAYB5n7bNg9nzTDWUSc', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=328;
START SLAVE;

Podemos dejar la ventana abierta, ya que después tendremos que volver para comprobar que la sincronización funciona.

Comprobación de que todo funciona bien

Volveremos a la pantalla del servidor principal de SQL y crearemos una base de datos de ejemplo.

CREATE DATABASE replicatest;

En las dos máquinas, si ejecutamos el siguiente comando nos debería devolver que se ha creado la base de datos.

SHOW DATABASES;

Resultado:

MariaDB> SHOW DATABASES;
 +--------------------+
 | Database           |
 +--------------------+
 | information_schema |
 | mysql              |
 | performance_schema |
 | replicatest        |
 +--------------------+

A partir de este momento ya tenemos configuradas las dos bases de datos, tanto la primaria que permite todos los comandos (lectura / escritura) y la secundaria que sólo permite comandos de lectura.

Servidor de ficheros

Configurando el Sistema Operativo

Una vez esté instalado el sistema operativo, lo primero que configuraremos será la hora del servidor. En este caso configuraremos la zona horaria global UTC.

timedatectl set-timezone 'UTC'
timedatectl set-ntp on

Lo siguiente que haremos es comprobar la versión del sistema operativo y, posteriormente, hacer una actualización completa del mismo.

lsb_release -a
apt -y update && apt -y upgrade && apt -y dist-upgrade && apt -y autoremove

Una vez esta todo actualizado, instalamos algunas herramientas y software base que puede ser útil tener en el sistema.

apt -y install software-properties-common curl vim zip unzip apt-transport-https

Antes de seguir configuraremos que se apliquen las actualizaciones automáticas de seguridad.

apt -y install unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades

Instalación de nginx

A partir de aquí tenemos la base de datos configurada y vamos a proceder a la instalación del servidor web. En este caso vamos a usar nginx. Para estar al día, no usaremos la versión que viene con el sistema operativo, sino una más actualizada y mantenida.

add-apt-repository -y -s ppa:ondrej/nginx
apt -y install nginx nginx-extras

Ahora que tenemos nginx instalado, lo vamos a configurar para que se inicie en los re inicios del sistema automáticamente.

systemctl stop nginx.service
systemctl enable nginx.service
systemctl start nginx.service
systemctl status nginx.service

Instalación de MariaDB

El siguiente paso será la instalación del cliente de base de datos. En este caso vamos a utilizar MariaDB 10.5. Lo primero que haremos será configurar la descarga, y posteriormente su instalación.

curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version="mariadb-10.5"
apt -y update && apt -y upgrade && apt -y dist-upgrade && apt -y autoremove
apt -y install mariadb-client

Instalación de PHP

En este momento ya tenemos el servidor web, por lo que vamos a instalar y a configurar PHP para que funcione correctamente con la base de datos y el servidor web. En este caso vamos a instalar la versión PHP 8.0. Primero haremos la instalación de los paquetes más actualizados (que no son los que vienen con el sistema operativo) y que en caso de necesitarlo, además, nos permitirían tener varias versiones de PHP en paralelo.

add-apt-repository -y -s ppa:ondrej/php
apt -y install php8.0 php8.0-fpm php8.0-common php8.0-dev php8.0-cli php8.0-bcmath php8.0-curl php8.0-gd php8.0-imap php8.0-mbstring php8.0-mysql php8.0-opcache php8.0-soap php8.0-xml php8.0-zip php8.0-xdebug libgeoip-dev php-pear pkg-config imagemagick libmagickwand-dev php8.0-imagick

Ahora que ya tenemos PHP correctamente instalado, vamos a activarlo de forma que cuando se reinicie el sistema se ejecute automáticamente.

systemctl stop php8.0-fpm.service
systemctl enable php8.0-fpm.service
systemctl start php8.0-fpm.service
systemctl status php8.0-fpm.service
php -v

También aprovecharemos en actualizar el PECL.

pecl channel-update pecl.php.net

Instalación de Redis

Para trabajar con unas mejoras en el rendimiento de la caché de objetos, vamos a dejar listo Redis como sistema de almacenamiento.

apt -y update && apt -y upgrade && apt -y dist-upgrade && apt -y autoremove
apt -y install redis-server php8.0-redis

Posteriormente, y de la misma forma que el resto de elementos, lo vamos a configurar para que se inicie automáticamente si se reinicia el servidor.

systemctl stop redis-server.service
systemctl enable redis-server.service
systemctl start redis-server.service
systemctl status redis-server.service

Configuración del HTTPS

Como vamos a montar nuestra web sobre un servidor web seguro (HTTPS), necesitaremos instalar el generador de certificados de Let’s Encrypt. Para ello instalaremos el sistema de creación de certificados certbot.

snap install core && snap install --classic certbot
ln -s /snap/bin/certbot /usr/bin/certbot

Para que los certificados se actualicen automáticamente, activaremos una tarea programada (cron) una vez al día que automáticamente renueve los certificados.

crontab -e

Una vez dentro, configuraremos, por ejemplo, que se ejecute a las 06:45 cada mañana.

45 6 * * * certbot renew

Limpiando los sitios por defecto

Por defecto, el sistema responde a la dirección IP y a otros elementos. Para evitar que los sitios por defecto se muestren, eliminaremos las páginas por defecto.

Eliminamos todos los ficheros por defecto.

rm /var/www/html/index.*

Y crearemos la página por defecto.

vim /var/www/html/index.html

Donde añadiremos el contenido de la página por defecto.

<!DOCTYPE html>
<p>Hello World!</p>

Y crearemos un fichero de robots.txt para que no se indexen estas páginas por defecto.

vim /var/www/html/robots.txt

Donde añadiremos el contenido del fichero que impida la indexación.

User-Agent: *
Disallow: /

Instalación de WP-CLI

Para la creación de nuestros sitios WordPress, usaremos la instalación mediante WP-CLI.

cd /root/
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
chown www-data: wp-cli.phar
chmod +x wp-cli.phar
mv wp-cli.phar /usr/local/bin/wp

Y validamos que esté instalado y actualizado.

wp --info
wp cli update

Configuración de los servicios

Configuración de nginx

Haremos unas copias de seguridad de los ficheros de configuración de sitios para posteriormente poner los nuestros.

cd /etc/nginx/sites-enabled/
rm default
cd /etc/nginx/sites-available/
rm default

Configuraremos la configuración global de nginx.

cd /etc/nginx/
rm nginx.conf
vim nginx.conf

Con el siguiente contenido:

user www-data;
pid /run/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 65535;
include /etc/nginx/modules-enabled/*.conf;
events {
  multi_accept on;
  worker_connections 65535;
  use epoll;
}
http {
  charset utf-8;
  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  server_tokens off;
  more_clear_headers Server;
  log_not_found off;
  types_hash_max_size 2048;
  client_max_body_size 64m;
  keepalive_timeout 10;
  server_names_hash_bucket_size 128;
  server_names_hash_max_size 1024;
  include /etc/nginx/mime.types;
  default_type application/octet-stream;
  # logging
  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log;
  # TLS
  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_prefer_server_ciphers on;
  # gzip
  gzip on;
  gzip_vary on;
  gzip_proxied any;
  gzip_comp_level 9;
  gzip_disable "msie6";
  gzip_buffers 16 8k;
  gzip_min_length 1100;
  gzip_types application/atom+xml application/javascript application/json application/x-javascript application/xml application/xml+rss image/svg+xml text/css text/javascript text/plain text/xml;
  # more
  include /etc/nginx/conf.d/*.conf;
  include /etc/nginx/sites-enabled/*;
}

Y configuramos las opciones de PHP 8 para WordPress.

cd /etc/nginx/
vim wordpress_fastcgi_8_0.conf

Con el siguiente contenido:

fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
fastcgi_index index.php;
fastcgi_buffers 256 16k;
fastcgi_buffer_size 128k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_hide_header X-Powered-By;
fastcgi_hide_header X-Pingback;
fastcgi_hide_header Link;
fastcgi_intercept_errors off;
fastcgi_split_path_info ^(.+.php)(/.+)$;
try_files $fastcgi_script_name =404;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PHP_ADMIN_VALUE open_basedir=$document_root/:/usr/lib/php/:/tmp/;
fastcgi_param PATH_INFO $path_info;
set $path_info $fastcgi_path_info;
include fastcgi.conf;

Probamos y reiniciamos nginx.

nginx -t
systemctl restart nginx.service

Configuración de PHP 8

Hacemos una copia de seguridad de la configuración de PHP para crear la nuestra propia.

cd /etc/php/8.0/fpm/
rm php.ini
vim /etc/php/8.0/fpm/php.ini

Con el siguiente contenido:

[PHP]
engine = On
short_open_tag = Off
precision = 14
output_buffering = 4096
zlib.output_compression = Off
implicit_flush = Off
unserialize_callback_func =
serialize_precision = -1
disable_functions = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
disable_classes =
zend.enable_gc = On
zend.exception_ignore_args = On
zend.exception_string_param_max_len = 0
expose_php = Off
max_execution_time = 60
max_input_time = 60
memory_limit = 256M
error_reporting = E_ALL
display_errors = Off
display_startup_errors = Off
log_errors = On
log_errors_max_len = 1024
ignore_repeated_errors = Off
ignore_repeated_source = Off
report_memleaks = On
html_errors = On
variables_order = "GPCS"
request_order = "GP"
register_argc_argv = Off
auto_globals_jit = On
post_max_size = 32M
auto_prepend_file =
auto_append_file =
default_mimetype = "text/html"
default_charset = "UTF-8"
doc_root =
user_dir =
enable_dl = Off
file_uploads = On
upload_max_filesize = 32M
max_file_uploads = 20
allow_url_fopen = On
allow_url_include = Off
default_socket_timeout = 60
[CLI Server]
cli_server.color = On
[Date]
date.timezone = 'UTC'
[Pdo_mysql]
pdo_mysql.cache_size = 2000
pdo_mysql.default_socket=
[mail function]
SMTP = localhost
smtp_port = 25
mail.add_x_header = Off
[ODBC]
odbc.allow_persistent = On
odbc.check_persistent = On
odbc.max_persistent = -1
odbc.max_links = -1
odbc.defaultlrl = 4096
odbc.defaultbinmode = 1
[MySQLi]
mysqli.max_persistent = -1
mysqli.allow_persistent = On
mysqli.max_links = -1
mysqli.cache_size = 2000
mysqli.default_port = 3306
mysqli.default_socket =
mysqli.default_host =
mysqli.default_user =
mysqli.default_pw =
mysqli.reconnect = Off
[mysqlnd]
mysqlnd.collect_statistics = On
mysqlnd.collect_memory_statistics = Off
[bcmath]
bcmath.scale = 0
[Session]
session.save_handler = files
session.use_strict_mode = 0
session.use_cookies = 1
session.use_only_cookies = 1
session.name = PHPSESSID
session.auto_start = 0
session.cookie_lifetime = 0
session.cookie_path = /
session.cookie_domain =
session.cookie_httponly =
session.serialize_handler = php
session.gc_probability = 0
session.gc_divisor = 1000
session.gc_maxlifetime = 1440
session.referer_check =
session.cache_limiter = nocache
session.cache_expire = 180
session.use_trans_sid = 0
session.sid_length = 26
session.trans_sid_tags = "a=href,area=href,frame=src,form="
session.sid_bits_per_character = 5
[Assertion]
zend.assertions = -1
[Tidy]
tidy.clean_output = Off
[soap]
soap.wsdl_cache_enabled=1
soap.wsdl_cache_dir="/tmp"
soap.wsdl_cache_ttl=86400
soap.wsdl_cache_limit = 5
[ldap]
ldap.max_links = -1
[opcache]
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.max_wasted_percentage=15
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.enable_cli=1

Y reiniciamos PHP.

systemctl restart php8.0-fpm.service

Configuración de Redis

Hacemos una copia de los ficheros de configuración.

cd /etc/redis/
rm redis.conf
vim /etc/redis/redis.conf

Con el siguiente contenido:

# include /path/to/local.conf
# include /path/to/other.conf
# loadmodule /path/to/my_module.so
# loadmodule /path/to/other_module.so
# bind 192.168.1.100 10.0.0.1
# bind 127.0.0.1 ::1
bind 127.0.0.1 ::1
protected-mode yes
port 6379
tcp-backlog 511
# unixsocket /var/run/redis/redis-server.sock
# unixsocketperm 700
timeout 0
tcp-keepalive 300
daemonize yes
supervised no
pidfile /var/run/redis/redis-server.pid
loglevel notice
logfile /var/log/redis/redis-server.log
# syslog-enabled no
# syslog-ident redis
# syslog-facility local0
databases 4
always-show-logo yes
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /var/lib/redis
# slaveof  
# masterauth 
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
# repl-ping-slave-period 10
# repl-timeout 60
repl-disable-tcp-nodelay no
# repl-backlog-size 1mb
# repl-backlog-ttl 3600
slave-priority 100
# min-slaves-to-write 3
# min-slaves-max-lag 10
# min-slaves-max-lag is set to 10.
# slave-announce-ip 5.5.5.5
# slave-announce-port 1234
# requirepass foobared
# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
# rename-command CONFIG ""
# maxclients 10000
maxmemory 256mb
# maxmemory-policy noeviction
# maxmemory-samples 5
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
slave-lazy-flush no
appendonly no
appendfilename "appendonly.aof"
# appendfsync always
appendfsync everysec
# appendfsync no
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble no
lua-time-limit 5000
# cluster-enabled yes
# cluster-config-file nodes-6379.conf
# cluster-node-timeout 15000
# cluster-slave-validity-factor 10
# cluster-migration-barrier 1
# cluster-require-full-coverage yes
# cluster-slave-no-failover no
# cluster-announce-ip 10.1.1.5
# cluster-announce-port 6379
# cluster-announce-bus-port 6380
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
#  notify-keyspace-events Elg
#  notify-keyspace-events Ex
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 10
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
# client-query-buffer-limit 1gb
# proto-max-bulk-len 512mb
hz 10
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
# lfu-log-factor 10
# lfu-decay-time 1
# activedefrag yes
# active-defrag-ignore-bytes 100mb
# active-defrag-threshold-lower 10
# active-defrag-threshold-upper 100
# active-defrag-cycle-min 25
# active-defrag-cycle-max 75

Reiniciamos Redis y PHP para cargar su configuración.

systemctl restart redis-server.service
systemctl restart php8.0-fpm.service

Creación de un sitio web

Para estos ejemplos usaremos la carpeta raíz «/webs/» y el dominio «example.com«.

Creamos la carpeta donde alojamermos nuestros sitios web.

mkdir /webs/
cd /webs/

Y aquí crearemos tantos sitios como queramos. En este caso nuestro sitio de ejemplo.

mkdir /webs/example.com/

Configuración del sitio en nginx

Accedemos y creamos el fichero de configuración.

cd /etc/nginx/sites-available/
vim example.com.conf

Con el siguiente contenido:

server {
  listen 80;
  listen [::]:80;
  server_tokens off;
  server_name example.com www.example.com;
  root /webs/example.com;
  index index.php index.html;
  location = /favicon.ico {
    log_not_found off;
    access_log off;
  }
  location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;
  }
  location ~ /.well-known {
    allow all;
  }
  location ~ /.ht {
    deny all;
  }
}

Creamos el enlace simbólico y reiniciamos nginx para activar esta configuración.

ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/
nginx -t
nginx -s reload

Configuración del certificado TLS

Lanzamos el CertBot para la creación de certificados TLS.

certbot --email example@example.com --agree-tos --authenticator webroot --installer nginx

Seleccionaremos el sitio, y nos pedirá la ruta, que sería esta:

/webs/example.com

Actualización de nginx para HTTPS

Volvemos a acceder al fichero de configuración.

cd /etc/nginx/sites-available/
vim example.com.conf

Actualizando el contenido anterior con el que activa el HTTPS.

#All HTTP traffic will be sent to HTTPS
server {
  listen 80;
  listen [::]:80;
  server_name example.com;
  return 301 https://example.com$request_uri;
  access_log off;
}
#REAL SITE
server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  # SSL
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
  ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
  include /etc/letsencrypt/options-ssl-nginx.conf;
  # SSL OCSP Stapling
  ssl_stapling on;
  ssl_stapling_verify on;
  resolver 208.67.222.222 8.8.8.8 valid=300s;
  resolver_timeout 2s;
  # Security headers
  add_header Referrer-Policy "strict-origin-when-cross-origin" always;
  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
  #logs
  access_log /var/log/nginx/example.com-access.log combined buffer=64k flush=5m;
  error_log /var/log/nginx/example.com-error.log;
  #CONFIG
  server_name example.com;
  root /webs/example.com;
  index index.php;
  # ROOT
  location / {
    try_files $uri $uri/ /index.php;
  }
  # ROOT PHP
  location ~ .php$ {
    include wordpress_fastcgi_8_0.conf;
  }
  # HIDDEN FILES
  location ~ /.well-known {
    allow all;
  }
  location ~ /.ht {
    deny all;
  }
}

Reiniciamos el nginx para cargar la nueva configuración.

nginx -t
nginx -s reload

Configuración de la base de datos

La creación de la base de datos será en el servidor primario. En general cualquier cosa que hagamos siempre será en el servidor principal.

Crearemos la base de datos para el sitio web que vamos a usar. Primero accedemos a la base de datos primaria.

mysql -p

Y crearemos la base de datos, primero en la base de datos del servidor primario, el usuario y contraseña y activaremos los privilegios.

CREATE DATABASE example CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci;
GRANT ALL ON example.* TO 'example'@'localhost' IDENTIFIED BY 'la_contraseña_aqui';
GRANT ALL ON example.* TO 'example'@'10.0.0.1' IDENTIFIED BY 'la_contraseña_aqui';
FLUSH PRIVILEGES;
quit

Primero accedemos a la base de datos secundaria.

mysql -p

Y en la base de datos del servidor secundario, el usuario y contraseña y activaremos los privilegios.

GRANT ALL ON example.* TO 'example'@'localhost' IDENTIFIED BY 'la_contraseña_aqui';
GRANT ALL ON example.* TO 'example'@'10.0.0.1' IDENTIFIED BY 'la_contraseña_aqui';
FLUSH PRIVILEGES;
quit

Creación del sitio web

Ahora ya tenemos todos los elementos necesarios para la creación de nuestro sitio.

cd /webs/example.com/
wp core download --allow-root
wp config create --dbprefix=wpprefix_ --locale=en_US --dbhost=10.0.0.2 --dbname=example --dbuser=example --dbpass='la_contraseña_aqui' --dbcharset=utf8mb4 --dbcollate=utf8mb4_general_ci --allow-root

Y haremos la instalación del sitio:

wp core install --url="example.com" --title="Mi WordPress" --admin_user="AdminDeWordPress" --admin_password="PasswordDeWordPress" --admin_email="example@example.com" --allow-root

Configurando la disponibilidad múltiple

Existen varios sistemas para utilizar la distribución de los contenidos. El principal es el de HyperDB creado por Automattic para WordPress.com, y el segundo es LudicrousDB, que está basado en el mismo sistema de HyperDB, pero mantenido por un grupo de la comunidad WordPress y más funcionalidades.

Con el plugin HyperDB

El plugin HyperDB es el que utiliza Automattic para su sistema distribuido de bases de datos para WordPress.com.

Lo importante de este plugin es que no ha de activarse, pero sí descargarse y configurarlo. Para empezar bajaremos el fichero ZIP.

Dentro encontraremos los siguientes ficheros:

  • db.php
  • db-config.php

El fichero db.php lo deberemos subir a /wp-content/, y no hay que hacer nada más.

Editaremos el fichero db-config.php y le configuraremos un contenido como este:

<?php
/** Variable settings **/
$wpdb->save_queries = false;
$wpdb->persistent = false;
$wpdb->max_connections = 10;
$wpdb->check_tcp_responsiveness = true;
/** Configuration Functions **/
$wpdb->add_database(array(
	'host'     => '10.0.0.3',
	'user'     => 'example',
	'password' => 'la_contraseña_aqui',
	'name'     => 'example',
	'write'    => 0,
	'read'     => 1
));
$wpdb->add_database(array(
	'host'     => '10.0.0.2',
	'user'     => 'example',
	'password' => 'la_contraseña_aqui',
	'name'     => 'example',
	'write'    => 1,
	'read'     => 0
));

El fichero db-config.php lo deberemos subir a la misma carpeta donde esté el wp-config.php, en principio en /.

Una recomendación es la de editar el fichero wp-config.php y comentar las líneas de la conexión a la base de datos:

<?php
/*
define( 'DB_NAME', 'example' );
define( 'DB_USER', 'example' );
define( 'DB_PASSWORD', 'la_contraseña_aqui' );
define( 'DB_HOST', '10.0.0.2' );
*/

Con el plugin LudicrousDB

El plugin LudicrousDB es un fork de HyperDB con algunas mejoras y participado por algunos componentes del Core de WordPress.

Descargamos la ultima versión de LudicrousDB y debemos hacer algunas copias de los ficheros.

Para empezar subimos una copia de los ficheros a:

  • /wp-content/plugins/ludicrousdb/
  • /wp-content/mu-plugins/ludicrousdb/

Además, dentro de la carpeta /ludicrousdb/drop-ins/ tenemos tres ficheros.

  • db.php
  • db-config.php
  • db-error.php

El fichero db.php hay que copiarlo a /wp-content/db.php.

El fichero db-error.php hay que copiarlo a /wp-content/db-error.php.

Editaremos el fichero db-config.php y le configuraremos un contenido como este:

<?php
defined( 'ABSPATH' ) || exit;
 $wpdb->charset = 'utf8mb4';
 $wpdb->collate = 'utf8mb4_general_ci';
 $wpdb->save_queries = false;
 $wpdb->recheck_timeout = 0.1;
 $wpdb->persistent = false;
 $wpdb->allow_bail = false;
 $wpdb->max_connections = 10;
 $wpdb->check_tcp_responsiveness = true;
 $wpdb->cache_group = 'ludicrousdb';
 $wpdb->add_database(array(
     'host'     => '10.0.0.3',
     'user'     => 'example',
     'password' => 'la_contraseña_aqui',
     'name'     => 'example',
     'write'    => 0,
     'read'     => 1
 ));
 $wpdb->add_database(array(
     'host'     => '10.0.0.2',
     'user'     => 'example',
     'password' => 'la_contraseña_aqui',
     'name'     => 'example',
     'write'    => 1,
     'read'     => 0
 ));

El fichero db-config.php lo deberemos subir a la misma carpeta donde esté el wp-config.php, en principio en /.

Una recomendación es la de editar el fichero wp-config.php y comentar las líneas de la conexión a la base de datos:

<?php
/*
define( 'DB_NAME', 'example' );
define( 'DB_USER', 'example' );
define( 'DB_PASSWORD', 'la_contraseña_aqui' );
define( 'DB_HOST', '10.0.0.2' );
*/

Validación del funcionamiento

Para empezar la validación, lo primero es entrar en el WordPress y comprobar que funciona todo normalmente.

Si queremos validar si las consultas que se hacen en cada una de las máquinas, podemos activar el sistema de logs general en la propia base de datos. No es nada recomendable tener esto activado en producción, es simplemente para validar que funciona.

vim /etc/mysql/mariadb.conf.d/50-server.cnf

Y buscaremos para incorporar estas dos últimas líneas:

[mariadb]
log_output=TABLE
general_log

A partir de este momento podemos ejecutar las siguientes consultas en cada una de las máquinas para validar las últimas consultas en cada una de las bases de datos. La idea es que en el slave estén los SELECTS, y en el master los INSERT, UPDATE, DELETE y similares.

SELECT event_time, argument FROM mysql.general_log WHERE user_host LIKE '%10.0.0.1%' ORDER BY event_time DESC LIMIT 25;

Finalizando la configuración

Finalmente, haremos una actualización de los ficheros y sus permisos.

cd /webs/example.com/
chown -R www-data:www-data ./
find /webs/example.com/ -type d -exec chmod 755 {} \;
find /webs/example.com/ -type f -exec chmod 644 {} \;

En este momento podríamos reiniciar la máquina por completo para dejarlo todo activo y validar que si se reinicia la máquina todos los servicios funcionan.

Una vez entremos en el WordPress, activaremos el plugin que aparecerá en el listado.


Sobre este documento

Este documento está regulado por la licencia EUPL v1.2, publicado en WP SysAdmin y creado por Javier Casares. Por favor, si utilizas este contenido en tu sitio web, tu presentación o cualquier material que distribuyas, recuerda hacer una mención a este sitio o a su autor, y teniendo que poner el material que crees bajo licencia EUPL.

Servicios de Administración de Sistemas WordPress

¿Tienes un sitio web con WordPress de alto tráfico? ¿Eres una Agencia con servidores con cPanel, Plesk u otro panel en los que mantienes WordPress para tus clientes?

Si es así y te interesa un servicio profesional de mantenimiento de infraestructura WordPress y de mejora del rendimiento de tus sitios web o los de tus clientes, contacta conmigo.