Servidor virtual para WordPress con Subversion

Última actualización: 26 de abril de 2020

Tabla de contenidos

Si alguna vez te has planteado contribuir a WordPress, existen muchas formas de hacerlo. Desde aquí te planteamos los primeros pasos para ello con la creación de una máquina virtual en la que sincronizar WordPress con Subversion, de forma que siempre esté al día y que te permita trabajar con el mismo material en el que lo hace la comunidad de desarrolladores.

Requisitos

En principio no es necesario ningún requisito especial, simplemente disponer de acceso al SVN de WordPress.

Infraestructura

En principio se puede montar el sistema en cualquier tipo de máquina, ya sea una virtual, un Docker o un VPS. En este caso, para que esté al alcance de todos y sin requisitos mínimos, usaremos un VPS de cualquier proveedor. Si te interesa, puedes encontrar algunos VPS para desarrolladores.

En este caso hemos usado una máquina con 1 CPU, 2 GB de RAM y 10 GB de disco SSD. Con la mitad de recursos debería funcionar sin problema. Lo que vamos a mostrar se basa en un Ubuntu 18 LTS. Usaremos PHP 7.4, MariaDB 10.4 y otros servicios.

Creamos una máquina

Como comentaba antes, podemos crear una máquina en cualquier sitio. Puede ser en un VPS de una empresa de hosting, o puede ser un Docker que tengamos en local. También hay opciones de usar el propio sistema de Vagrant. En este caso crearemos la máquina desde cero con nuestra configuración medianamente personalizada.

Como sistema operativo de este ejemplo vamos a usar Ubuntu 18 LTS.

Actualización del sistema

Lo primero que haremos es poner al día el sistema.

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

Una vez esto, pondremos en hora el sistema.

timedatectl set-timezone UTC
timedatectl set-ntp on

Además, instalaremos algunas herramientas básicas.

apt -y install software-properties-common curl vim unzip

Servidor de Base de Datos

Para la base de datos vamos a usar MariaDB 10.4; esta versión tiene un gran cambio en cuanto al sistema de claves con respecto a su versión predecesora, por lo que, para evitar problemas, vamos a usar esta última versión.

Lo primero será descargar e instalar el servidor de base de datos.

curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version="mariadb-10.4"
apt -y update
apt -y install mariadb-server mariadb-client
systemctl restart mysql.service

Ahora que tenemos la base de datos instalada, ejecutaremos el sistema de configuración por primera vez.

mysql_secure_installation

Aquí se nos darán algunas opciones y preguntas. Prepara tu contraseña de root de la base de datos y guárdala bien.

Enter current password for root (enter for none):
Switch to unix_socket authentication [Y/n] n
Change the root password? [Y/n] y
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

Una vez hayamos contestado las preguntas, reiniciaremos la base de datos para dejarla funcionando.

systemctl restart mysql.service

Servidor Web

Para el servidor web vamos a utilizar nginx. Este servidor web funciona muy bien con WordPress a la hora de desarrollar o mantener sitios grandes, aunque no permite el uso de los ficheros .htaccess (se ha de configurar previamente, sin que los usuarios o plugins puedan cambiar la configuración).

add-apt-repository ppa:ondrej/nginx
apt -y update
apt -y install nginx
systemctl stop nginx.service
systemctl enable nginx.service
systemctl start nginx.service

Servidor PHP

Para que WordPress funcione necesitaremos instalar PHP, el intérprete del código. En este caso usaremos la versión de PHP 7.4. Además, instalaremos las extensiones recomendadas, lo que supondrá un trabajo extra de configuración.

Instalación base de PHP

Comenzaremos con el núcleo de PHP y de PHP-FPM, además de las extensiones básicas que vienen pre-compiladas.

add-apt-repository ppa:ondrej/php
apt -y update
apt -y install php7.4 php7.4-fpm php7.4-curl php7.4-gd php7.4-mbstring php7.4-xml php7.4-zip php7.4-mysql php7.4-mysqlnd php7.4-bcmath php7.4-gmp php7.4-tidy php7.4-dev php-pear pkg-config imagemagick libmagickwand-dev

Instalación de ImageMagick

Esta extensión permite la gestión de imágenes mejoradas sobre GD.

pecl channel-update pecl.php.net
pecl install imagick
echo 'extension=imagick.so' >> /etc/php/7.4/mods-available/imagick.ini
ln -s /etc/php/7.4/mods-available/imagick.ini /etc/php/7.4/fpm/conf.d/30-imagick.ini

Instalación de XDiff

Esta extensión permite la aplicación de patch que incluyen diferencias de ficheros.

cd /usr/src
wget http://www.xmailserver.org/libxdiff-0.23.tar.gz
tar -xzf libxdiff-0.23.tar.gz
cd libxdiff-0.23
./configure
make
make install
pecl install xdiff
echo 'extension=xdiff.so' >> /etc/php/7.4/mods-available/xdiff.ini
ln -s /etc/php/7.4/mods-available/xdiff.ini /etc/php/7.4/fpm/conf.d/30-xdiff.ini

Instalación de APCu

Esta extensión mejora el sistema de caché del código PHP.

cd
pecl install apcu
echo 'extension=apcu.so' >> /etc/php/7.4/mods-available/apcu.ini
ln -s /etc/php/7.4/mods-available/apcu.ini /etc/php/7.4/fpm/conf.d/30-apcu.ini

Instalación de Redis

También instalaremos la extensión del servidor de caché Redis.

pecl install redis
echo 'extension=redis.so' >> /etc/php/7.4/mods-available/redis.ini
ln -s /etc/php/7.4/mods-available/redis.ini /etc/php/7.4/fpm/conf.d/30-redis.ini

Configuración de PHP

Para el desarrollo, haremos algunos cambios en el fichero de configuración de PHP, sobre todo para dar más margen de maniobra a la memoria y a la visualización de errores por pantalla y a la hora de hacer pruebas.

Lo primero que haremos será abrir el fichero de configuración.

vim /etc/php/7.4/fpm/php.ini

Y allí haremos algunos cambios en la configuración.

max_execution_time = 60
memory_limit = 256M
error_reporting = E_ALL
display_errors = On
post_max_size = 32M
upload_max_filesize = 32M
date.timezone = 'UTC'

Una vez esto, configuraremos PHP-FPM para que se active automáticamente con el sistema.

systemctl stop php7.4-fpm.service
systemctl enable php7.4-fpm.service
systemctl start php7.4-fpm.service

Servidor de caché Redis

Instalaremos el servidor Redis y le daremos algunos cambios a la configuración para que no se sature.

Primero lo instalaremos y lo sincronizaremos con PHP.

apt -y install redis-server php-redis

Abriremos el fichero de configuración.

vim /etc/redis/redis.conf

Y haremos algunos cambios en la configuración.

maxmemory 256mb
maxmemory-policy allkeys-lru

Finalmente lo reiniciaremos, junto a PHP para que se aplique su extensión.

systemctl stop redis-server.service
systemctl enable redis-server.service
systemctl start redis-server.service
systemctl restart php7.4-fpm.service

Servidor de certificados TLS

Hoy en día todos los sitios deberían funcionar bajo HTTPS, por lo que para hacer más real y parecido el entorno de desarrollo usaremos un certificado de Let’s Encrypt. Para esto necesitaremos que el sitio tenga un hostname / dominio público. Normalmente los proveedores de VPS suelen ponerle un hostname a las máquinas, por lo que podemos usar ese.

Generaremos una cláve única además de instalar el sistema de Certbot para nginx.

openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
add-apt-repository ppa:certbot/certbot
apt -y update
apt -y install python-certbot-nginx

Y para que no se nos olvide, configuraremos una tarea programada que actualice automáticamente el certificado. Para ello abriremos el editor de crones.

crontab -e

Y allí añadiremos la ejecución, cada día a las 06:45.

45 6 * * * certbot renew

Instalación de Subversion

Para funcionar necesitaremos las herramientas de cliente de Subversion, por lo que instalaremos la herramienta.

apt -y install subversion subversion-tools libsvn-dev

Instalación de NodeJS

Para que podamos hacer funcionar todo el sistema de desarrollo y algunas otras pruebas necesitaremos NPM y NodeJS, por lo que procederemos a instalarlo.

cd
curl -sL https://deb.nodesource.com/setup_13.x | sudo -E bash -
apt -y install nodejs

Finalización de la configuración

En principio ya tenemos todo lo que necesitamos instalado, por lo que, antes de seguir, vamos a hacer una actualización completa del sistema (de nuevo) para eliminar software antiguo o incompatible con todo el que hemos instalado.

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

Configuración del sitio

Ahora que ya tenemos todo listo a nivel de sistema y herramientas, lo que haremos será configurar el software para que podamos trabajar con él como si de un sitio web normal y corriente se tratase, aunque con esa versión de desarrollo.

Lo primero que haremos será eliminar el sitio web por defecto que trae el sistema y lo sustituiremos por uno que de poca información.

rm /var/www/html/index.*
vim /var/www/html/index.html

Aquí añadiremos el siguiente código HTML por defecto.

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

Lo mismo haremos con el fichero robots.txt

vim /var/www/html/robots.txt

En el que bloquearemos su rastreo.

User-Agent: *
Disallow: /

Ahora que tenemos el sitio por defecto corregido, vamos a retocar la configuración de nginx para poder trabajar de forma más adecuada con nuestro sitio de desarrollo. Primero eliminaremos la configuración por defecto y la sustituiremos por una retocada.

cd /etc/nginx/sites-enabled/
rm default
cd /etc/nginx/
cp nginx.conf nginx.conf.original
vim nginx.conf

Y sustituiremos la configuración por la siguiente.

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;
  log_not_found off;
  types_hash_max_size 2048;
  client_max_body_size 64m;
  keepalive_timeout 65;
  server_names_hash_bucket_size 128;
  server_names_hash_max_size 1024;
  # MIME
  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;
  # ssl
  ssl_protocols TLSv1.2;
  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/*;
}

Además, añadiremos una configuración para trabajar con PHP y WordPress.

vim wordpress_fastcgi.conf

Que contendrá lo siguiente.

fastcgi_pass unix:/var/run/php/php7.4-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_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;

Una vez tengamos esto, deberíamos poder reiniciar nginx sin problema y dejarlo en marcha.

nginx -t
nginx -s reload

A partir de aquí vamos a hacer una copia del software de WordPress dependiendo del Subversion oficial de WordPress. En este caso vamos a montar todo el sistema en la carpeta /webs/ desde la raíz del sistema, pero se puede hacer sobre cualquiera.

mkdir /webs/
cd /webs/
mkdir /webs/wordpress-svn/
cd /webs/wordpress-svn/
svn co https://develop.svn.wordpress.org/trunk/ .

Ahora que tenemos el software clonado, vamos a hacer que sea navegable como de cualquier sitio web se tratase.

cd /etc/nginx/sites-available/
vim wordpress-svn.conf

Donde crearemos una configuración mínima.

server {
  listen 80;
  listen [::]:80;
  server_tokens off;
  server_name example.com;
  root /webs/wordpress-svn/src;
  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;
  }
}

Y reiniciaremos nginx para que se apliquen los cambios.

ln -s /etc/nginx/sites-available/wordpress-svn.conf /etc/nginx/sites-enabled/
nginx -t
nginx -s reload

Ahora hemos de crear e instalar el certificado TLS para disponer de un sitio con HTTPS.

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

Cuando nos pregunte por la tura de nuestro software, le diremos que está en la carpeta correspondiente.

/webs/wordpress-svn/src

Ahora hemos de crear la base de datos para nuestro sitio web. Necesitaremos la contraseña de root que configuramos previamente.

mysql -u root -p

Necesitarás una contraseña para esta base de datos exclusiva para nuestro WordPress de desarrollo. Por favor, crea tu propia contraseña que sea algo segura.

CREATE DATABASE wordpress CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin;
GRANT ALL ON wordpress.* TO 'wordpress'@'localhost' IDENTIFIED BY '_PASSWORD_';
GRANT ALL ON wordpress.* TO 'wordpress'@'127.0.0.1' IDENTIFIED BY '__PASSWORD__';
FLUSH PRIVILEGES;
quit

Ahora que tenemos el certificado TLS y la base de datos, vamos a crear nuestro sitio con toda la configuración segura, además de cierta configuración específica para WordPress.

cd /etc/nginx/sites-available/
vim wordpress-svn.conf

Aquí podremos sustituir la configuración inicial básica por una más avanzada.

# 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/ssl/certs/dhparam.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;
  ssl_session_timeout 1d;
  ssl_session_cache shared:SSL:128m;
  ssl_session_tickets off;
  # SSL modern configuration
  ssl_protocols TLSv1.2;
  ssl_prefer_server_ciphers on;
  ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256;
  # 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/WordPress-access.log combined buffer=64k flush=5m;
  error_log /var/log/nginx/WordPress-error.log;
  #CONFIG
  server_name example.com;
  root /webs/wordpress-svn/src;
  index index.php;
  # ROOT
  location / {
    try_files $uri $uri/ /index.php;
  }
  # ROOT PHP
  location ~ \.php$ {
    include wordpress_fastcgi.conf;
  }
  location ~ wp-config {
    deny all;
  }
  # HIDDEN FILES
  location ~ /\.well-known {
    allow all;
  }
  location ~ /\.ht {
    deny all;
  }
  # WEB FILES
  location ~ /favicon.(ico|png) {
    log_not_found off;
    access_log off;
  }
  location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;
  }
  # STATIC CACHES
  location ~* \.(aac|avi|bmp|bz2|cur|docx?|eot|exe|flv|gif|gz|heic|htc|ico|jpe?g|m4a|midi?|mov|mp3|mp4|mpe?g|ogg|ogv|otf|pdf|png|pptx?|rar|rtf|svgz?|tar|tgz|tiff?|ttc|ttf|txt|wav|webm|webp|wmv|woff|woff2|xlsx?|zip)$ {
    expires max;
    add_header Cache-Control "public";
    log_not_found off;
    access_log off;
  }
  location ~* \.(atom|css|js|rss)$ {
    expires 7d;
    add_header Cache-Control "public";
    log_not_found off;
    access_log off;
  }
  location ~* \.(?:eot|otf|ttf|woff|woff2)$ {
    add_header Access-Control-Allow-Origin "*";
  }
  location ~* \/wp-admin\/load-(?:scripts|styles)\.php {
    if ( $query_string ~* "^.{512,}$" ) {
      return 444;
    }
  }
}

Reiniciaremos nginx y accederemos a la carpeta de nuestro nuevo WordPress.

nginx -t
nginx -s reload
cd /webs/wordpress-svn/

Configuramos Subversion para WordPress

Ahora hemos de configurar nuestro software para que se pueda mantener al día con el código fuente de desarrollo de WordPress. Hay que recordar que esta versión del software suele ser una alpha o beta, por lo que las probabilidades de que haya errores son grandes, y por tanto de deben reportar en el Trac de WordPress.

Para empezar deberemos instalar NPM para que podamos hacerlo funcionar según sea necesario.

cd /webs/wordpress-svn/
npm install
npm run dev

Con esto tendremos una gran cantidad de herramientas y configuraciones específicas para el desarrollo de WordPress.

Instalación de WordPress

Antes de entrar en nuestro WordPress de desarrollo, lo mejor será crear un fichero de configuración bastante avanzado. Por defecto lo instalaremos en la carpeta raíz de nuestra instalación.

cd /webs/wordpress-svn/src/
vim wp-config.php

Que contendría un contenido similar al siguiente. Puedes crear tu propia configuración desde wp-config.pro. NOTA: Recuerda actualizar los datos de la base de datos con tus propios datos.

<?php

/* Database connection */
define( 'DB_NAME', 'wordpress' );
define( 'DB_USER', 'wordpress' );
define( 'DB_PASSWORD', '__PASSWORD__' );
define( 'DB_HOST', 'localhost' );
define( 'DB_CHARSET', 'utf8mb4' );
define( 'DB_COLLATE', 'utf8mb4_bin' );

/* Tables */
$table_prefix = 'wp_';

/* Security */
/* Security Keys */
define( 'AUTH_KEY', 'put your unique phrase here' );
define( 'SECURE_AUTH_KEY', 'put your unique phrase here' );
define( 'LOGGED_IN_KEY', 'put your unique phrase here' );
define( 'NONCE_KEY', 'put your unique phrase here' );
define( 'AUTH_SALT', 'put your unique phrase here' );
define( 'SECURE_AUTH_SALT', 'put your unique phrase here' );
define( 'LOGGED_IN_SALT', 'put your unique phrase here' );
define( 'NONCE_SALT', 'put your unique phrase here' );
/* HTTPS */
define( 'FORCE_SSL_LOGIN', true );
define( 'FORCE_SSL_ADMIN', true );
define( 'WP_DISABLE_FATAL_ERROR_HANDLER', false );

/* URL / Path */

/* Cookies */

/* Content */
define( 'AUTOSAVE_INTERVAL', 30 );
define( 'WP_POST_REVISIONS', 5 );
define( 'MEDIA_TRASH', true );
define( 'EMPTY_TRASH_DAYS', 7 );
define( 'WP_MAIL_INTERVAL', 86400 );

/* Memory */
define( 'WP_MEMORY_LIMIT', '128M' );
define( 'WP_MAX_MEMORY_LIMIT', '256M' );

/* Updating */
define( 'AUTOMATIC_UPDATER_DISABLED', true );
define( 'WP_AUTO_UPDATE_CORE', 'minor' );
define( 'CORE_UPGRADE_SKIP_NEW_BUNDLED', true );

/* File edition */
define( 'DISALLOW_FILE_MODS', false );
define( 'DISALLOW_FILE_EDIT', false );
define( 'IMAGE_EDIT_OVERWRITE', true );

/* Performance */
define( 'WP_CACHE', true );
define( 'WP_CACHE_KEY_SALT', 'aaaaaaaaaa:' );
define( 'COMPRESS_CSS', true );
define( 'COMPRESS_SCRIPTS', true );
define( 'CONCATENATE_SCRIPTS', false );
define( 'ENFORCE_GZIP', true );

/* Cron */
define( 'DISABLE_WP_CRON', false );
define( 'ALTERNATE_WP_CRON', false );
define( 'WP_CRON_LOCK_TIMEOUT', 60 );

/* FTP Access */

/* Plugins Must-Use */

/* Filtering */
define( 'DISALLOW_UNFILTERED_HTML', false );
define( 'ALLOW_UNFILTERED_UPLOADS', false );

/* Feed reader */
define( 'MAGPIE_CACHE_ON', true );
define( 'MAGPIE_CACHE_DIR', 'cache' );
define( 'MAGPIE_CACHE_AGE', 3600 );
define( 'MAGPIE_CACHE_FRESH_ONLY', false );
define( 'MAGPIE_DEBUG', false );
define( 'MAGPIE_USER_AGENT', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0' );
define( 'MAGPIE_FETCH_TIME_OUT', 5 );
define( 'MAGPIE_USE_GZIP', true );

/* MultiSite */
define( 'WP_ALLOW_MULTISITE', false );
define( 'WP_DEFAULT_THEME', 'twentytwenty' );

/* External URL Requests */

/* File permissions */

/* Proxy */

/* Debug */
define( 'WP_DEBUG', true );
if ( WP_DEBUG ) {
  define( 'WP_DEBUG_DISPLAY', true );
  define( 'WP_DEBUG_LOG', false );
}
define( 'SCRIPT_DEBUG', false );
define( 'SAVEQUERIES', false );

/* Do not change anything else after this line! Thank you! */

if ( ! defined( 'ABSPATH' ) )
  define( 'ABSPATH', dirname( __FILE__ ) . '/' );
require_once ABSPATH . 'wp-settings.php';

Ahora que ya tenemos todo listo a nivel de sistemas para tener un WordPress listo para mostrar errores y problemas, comenzaremos la instalación como de cualquier WordPress se trata. Para ello simplemente accederemos a la dirección web de nuestro hostname / dominio que hayamos configurado previamente.

Mantenimiento del sistema

A partir de este momento, podemos ir actualizando y sincronizando nuestro código, ejecutando una actualización de SVN y, de paso, de todo el sistema.

apt -y update && apt -y upgrade && apt -y dist-upgrade && apt -y autoremove
cd /webs/wordpress-svn/
svn up

Y hasta aquí tenemos una instalación desde cero del SVN principal de desarrollo de WordPress, donde podemos analizar o verificar nuestros desarrollos para futuras versiones, o aplicar parches ya desarrollados por la comunidad pero pendientes de verificar.