Epicuro sobre el bien

«Yo exhorto a placeres continuos y no a esas virtudes vanas y necias que comportan embrolladoras ilusiones de frutos futuros [..] Debemos apreciar lo bello, las virtudes y las cosas por el estilo si es que producen placer; y si no, mandarlas a paseo […] Pues no sé yo que idea puedo hacerme del bien, si suprimo los placeres del gusto, suprimo los de la sexualidad, y suprimo los movimientos placenteros que de las formas bellas recibe la vista»

Epicuro

Continuar Leyendo…

Por sábado, 12 agosto, 2017 0 , Permalink

Cuando ejecuto virt-builder en Ubuntu 16.04 aparece un error con el que entra en un bucle:

RTNETLINK answers: File exists
/sbin/dhclient-script: 34: /sbin/dhclient-script: cannot open /etc/fstab: No such file

Puede solucionarse sustituyendo en el script /sbin/dhclient-script la línea:

exec 9>&0 </etc/fstab

Por ésta:

exec 9>&0 </proc/mounts

Entonces, borra el caché de virt-builder:

sudo rm -R .guestfs*

Salud

Puppet y relaciones de dependencia entre recursos

Por miércoles, 1 marzo, 2017 0 Permalink

Puppet declara dependencias de manera implícita, pero no siempre se cargan los recursos de la manera esperada. En Puppet hay tres maneras de declaraciones implícitas

  • title hash: Es una ordenación aleatoria pero consistente
  • manifest: Ordena siguiendo el orden de declaración en el manifiesto (por defecto en Puppet 4)
  • random: Aleatorio en cada ejecución. Es útil para debuggar problemas con relaciones de dependencia

Los metaparámetros de dependencia explicita proveen un medio de organización de dependencias muy potente. Veámoslo.

Ordenando recursos

En la siguiente declaración se declara una dependencia:

package { 'nginx':
       ensure => 'present',
       before => Service['nginx']

}

service { 'nginx':
       ensure => 'running',
       enabled => 'true',
       require => Package['nginx']
}

En este caso, el servicio ‘nginx’ se ejecutará sólo si el packete ‘nginx’ está presente. Los metaparámetros before y require son reduntanes en este caso y no ofrecen datos a otros recursos. En la declaraciones explicitas no es necesario declarar relaciones innecesarias, ya que hace más frágil el manifiesto.

Disparadores de eventos

Los metaparámetros notify y subscribe funcionan de manera similar a los dos anteriores, pero también enviarán eventos de refresco (refresh events) al recurso dependiente si la dependencia cambia. Por ejemplo, un servicio dependiente se recargará si su configuración cambia.

package { 'nginx':
       ensure => 'latest',
       notify => Service['nginx']

}

service { 'nginx':
       ensure => 'running',
       enabled => 'true',
       subscribe => Package['nginx']
}

Cuando el paquete nginx se actualice a la última versión, el recurso enviará un refresh event al servicio nginx para que se recargue. Ambos metaparámetros son redundantes.

El evento de refresco tiene un significado especial para recursos exec con el atributo refreshonly. Estos recursos sólo se aplicarán sí, y sólo si, la dependencia cambia.

file {'nombre':
  ensure  => 'file',
  source  => 'puppet://puppetserver/modules/module_name/name.sh',
  path => '/usr/local/bin/name.sh',
  notify => Exec['name.sh']
}

exec {'name.sh'':
  path => '/usr/local/bin',
  logoutput => 'true'
  refreshonly => true,
}

En script name.sh no se ejecutará a menos que el archivo se haya creado.

Flechas para encadenar recursos

La relaciones dependencia anteriores pueden declararse usando flechas aunque, sin embargo, su uso está desaconsejado por ser de lectura más difícil. Por ejemplo, para instalar Nginx antes que iniciar el servicio:

Package ['nginx'] -> Service['nginx']

Para envíar un evento de refresco:

Package ['nginx'] ~> Service['nginx']

Es posible expresarlo a la inversa, pero esto no habría que hacerlo nunca:

 Service['nginx'] <~ Package ['nginx']

Colectores

Un colector es un grupo de varios recursos. Hay que usarlo con precaución, ya que puede tener consecuencias inintencionadas nefastas. Se declara con el tipo de recurso seguido de <| atributo |> Por ejemplo:

User <||>                                          # Todos los usuarios declarados 
Service <| ensure == running |>    # Todos los servicios que están ejecutándose
Package <| tag == 'http' |>         # Todos los paquetes etiquetado con 'http'

La condición de búsqueda puede ser booleana:

Service <| (ensure == running) or (enabled == true ) |>
Service <| (ensure == running) and (title != system' ) |>

En resumen

Hemos visto los siguientes controles:

  • before y la flecha ->    Expresa una dependencia
  • notify y la flecha ~>    Envía un refresh event
  • require                           Exprea un recurso dependiente
  • subscribe                       Se aplica cuando recibe un refresh event
  • refreshonly                   Se ejectuta sólo cuando recibe un refresh event

Varias recetas básicas con Varnish Cache

Por sábado, 25 febrero, 2017 0 Permalink

En este artículo publico varias recetas muy básicas con Varnish: arranque de Varnish, redirección de un dominio a un origen con otro nombre, redirección en función de una expresión regular y el contenido de una cookie, y una terminación SSL utilizando Nginx como front-end. Reconozco que la información del post está un poco apretada. Estos temas merecen expandirse y profundizar más. Lo haré en sucesivos posts. Escribo, en parte, este post rápido por si más adelante me es útil a mí.

En Debian Jessie ya no se utiliza la variable DAEMON_OPTS, por lo que definirla no tendrá efecto. En su lugar, hay que configurar el archivo /etc/system.d/system/varnish.service con las opciones de arranque.

Para arrancar con el puerto 80 a la escucha y 1024 megabytes de RAM, hay dejar la variable ExecStart así:

ExecStart=/usr/sbin/varnishd -a :80 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,1024m

Para recargar la configuración:

systemctl restart varnish

Comprobamos que Varnish esté a la escucha por el puerto 80

ps aux | grep varnish
root       884  0.0  1.4 126872 86084 ?        SLs  09:51   0:00 /usr/sbin/varnishd -a :80 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,1024m
nobody     887  0.2  1.4 320916 86672 ?        Sl   09:51   0:02 /usr/sbin/varnishd -a :80 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,1024m
root      2149  0.0  0.0  12772  2200 pts/1    S+   10:04   0:00 grep varnish

Redirigir de un dominio a otro origen

Para redirigir una petición de un dominio a otro, suponiendo el siguiente escenario:

  • Un servidor web a la escucha en el puerto 8080 con una página generada por la funcion phpinfo(). El sitio tiene el imaginativo nombre de sitio.local.
  • Un cliente que hace peticiones a foo.sitio.local.
  • Varnish se encargára de redigir del dominio que pide el cliente al origien del servidor web.

En el archivo /etc/varnish/default.vcl definimos el backend:

backend sitio {
        .host = "192.168.122.173"; 
        .port = "8080";
}

En el bloque vcl_recv:

sub vcl_recv {
        set req.http.x-host = req.http.host;
        set req.http.x-url = req.url;



        if (req.method != "GET" && req.method != "HEAD") {
                return (pass);
        }

        if (req.http.Host=="foo.sitio.local") {
                set req.http.Host = "sito.local";
                set req.url = "/info.php";
                set req.backend_hint = sitio;
                return(hash);
        }
}

En el bloque vcl_backend_response:

sub vcl_backend_response {
        set beresp.http.host = beresp.http.x-host;
}

Esto cambiará el nombre de host en el encabezado de respuesta que recibirá el cliente, para que le llegue la información del mismo nombre del host de la petición que hizo el cliente.

Cambiando origen en función del contenido de una cookie

En el mismo servidor web, editamos un archivo llamado info.php e introducimos el siguiente contenido:

<?php

$cookie_name = "galletita";
$cookie_value = "azucar";
echo "Name: " . $cookie_name . "<br />";
echo "Value: " . $cookie_value;
setcookie($cookie_name, $cookie_value, time() + (86400 * 30));
if(count($_COOKIE) > 0) {
    echo "Cookies are enabled.";
} else {
    echo "Cookies are disabled.";
}

 ?>

Con esto creamos una cookie llamada «galletita» con el valor «azucar».

Definimos un nuevo backend, al que será redirigida la petición en función del contenido de una cookie.

backend pagina {
        .host = "192.168.122.173";
        .port = "8080";
}

En el bloque vcl_recv escribimos una condición para que reenvíe a la siguiente acción del cache con un return(pass):

if (req.http.Host == "foo.sitio.local" && req.http.Cookie ~ "galletita=azucar") {
                set req.http.host = "pagina.local";
                set req.backend_hint = pagina;
        }

Esta condición cambia el origen a pagina.local cuando el req.http.host del encabezado sea foo.sitio.local y una cookie llamada «galletita» contega el valor «azucar».

Cambiar origen en función de una expresión regular

El infinito tema de las expresiones regulares. Sólo a modo de ejemplo, usaré una expresión regular simple. Las expresiones regulares están muy bien documentadas en Internet y no es difícil encontrar buenos tutoriales y manuales, como éste y con mayor profundidad éste otro.

if (req.http.Host == "foo.sitio.local" && req.url ~ "^/blog(\W|$)") {
                set req.http.host = "blog.local";
                set req.url = "/index.php";
                set req.backend_hint = blog;
                return(hash);
        }

Manual introductorio de Varnish Cache

Cuando una url sea «/blog» sin ningún otro carácter, dirigirá las peticiones del cliente al origen blog.local. El backend previamente habrá sido definido del siguiente modo:

backend blog{
        .host = "192.168.122.173";
        .port = "8000";
}

Limitando acceso al PURGE del caché

El primer paso es crear una ACL con los hosts o direcciones IP permitidas para lanzar la acción cache. Para limitar el acceso a una sóla IP:

acl purge {
        "192.168.122.10";
}

Esta variable la usamos en vcl_recv:

if (req.method == "PURGE"){
                if (!client.ip ~ purge) {
                        return(synth(405,"Forbidden - Not allowed"));
                }
                return(purge);
        }

Tan solo si la IP coincide con alguna de las IP’s de variable purge, pasa a la acción purge.

Añadimos a la subrutina vcl_purge:

sub vcl_purge {
        if (req.method != "PURGE") {
                set req.http.X-Purge = "Yes";
                return(restart);
        }

        return (synth(200,"Purged"));
}

Para borrar una caché de manera remota:

curl -X PURGE pagina.local

Salida:
<!DOCTYPE html>
<html>
  <head>
    <title>200 Purged</title>
  </head>
  <body>
    <h1>Error 200 Purged</h1>
    <p>Purged</p>
    <h3>Guru Meditation:</h3>
    <p>XID: 32811</p>
    <hr>
    <p>Varnish cache server</p>
  </body>
</html>

Terminación Nginx-SSL con backend Apache

Para redirigir las peticiones https al puerto 80 de Varnish y que éste conecte al backend Apache, por ejemplo a pagina.local en el puerto 8000, es posible utilizar Nginx como  front-end para dirigir las peticiones a Varnish

CLIENTE —> NGINX:443 —> VARNISH:80 —> APACHE:8000

La configuración del front-end en Nginx:

server {
        server_name pagina.local;

        listen 443 ssl;
        ssl_certificate /etc/nginx/ssl/nginx.crt;
        ssl_certificate_key /etc/nginx/ssl/nginx.key;

        location / {
                proxy_pass //127.0.0.1:80;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-Proto https;
                proxy_set_header X-Forwarded-Host $host;
                proxy_set_header HTTPS "on";
       }
}

Aquí escuchamos en el puerto 443 y utilizamos los certificados. La línea proxy_pass redirige el tráfico desde el puerto 443 al puerto 80 del host local.

En el bloque vcl_recv añadimos:

if ( (req.http.host ~ "^(?i)pagina.local") && req.http.X-Forwarded-Proto !~ "(?i)https") {

         set req.http.x-Redir-Url = "//" + req.http.host + req.http.url;
         return(synth(750, ""));
        }

En vcl_synth:

sub vcl_synth {
 if (resp.status == 750) {
     set resp.http.Location = req.http.x-redir;
     set resp.status = 301;
     return (deliver);
 }
}

En próximos posts, más y mejor sobre Varnish.

Facter en Puppet

Por domingo, 19 febrero, 2017 0 Permalink

En mi último post, vimos como instalar Puppet en la arquitectura master-agent y la creación de un módulo para declarar algunos recursos. En este, quiero mostrar cómo configurar un parámetro utilizando la aplicación Facter y despues, cómo extender Facter para tomar ese parámetro desde un archivo que lo contenga, creado «a mano» en el agente. Utilizaré la configuración del post anterior de la serie automatizaciones para el ejemplo un servicio PHP-FPM

Modulo «zona horaria»

El modulo que crearemos se llamará zonahoraría. Tomará la zona horaria desde un parámetro del sistema y en la segunda parte del post modificaré el manifiesto para lea desde un fact a medida.

Instalando PHP

Instalamos el módulo de PHP:

cd /etc/puppetlabs/code/environment/test/modules
/opt/puppetlabs/bin/puppet module install mayflower-php --version 4.0.0-beta1

 Creando módulo «zonahoraria»

En el directorio de módulos:

/opt/puppetlabs/bin/puppet module generate enredadera-zonahoraria

 Creando init.pp

El init.pp del módulo utilizará la clase del módulo recién instalado (podéis encontar más información acerca del módulo en este enlace).

class zonahoraria::setup {

}

 Configuración global

class { '::php::globals':
    php_version			=> '7.0',
    config_root			=> '/etc/php/7.0',
}

 Configuración de PHP

class { '::php':
      ensure               => latest,
      manage_repos         => true,
      fpm                  => true,
      dev                  => true,
      composer             => true,
      pear                 => true,
      phpunit              => false,
      settings             =>	{
            'PHP/max_execution_time'  => '90',
            'PHP/max_input_time'      => '300',
            'PHP/memory_limit'        => '64M',
            'PHP/post_max_size'       => '32M',
            'PHP/upload_max_filesize' => '32M',
            'Date/date.timezone'      => $timezone,
            },
      extensions    => {
                          imagick   => {
                                           provider => pecl,
                         }
      }
}

La variable $timezone tiene el valor (en el agente):

/opt/puppetlabs/bin/facter timezone

CET

Precendecia

La clase ::php::global debe ejecutarse antes de la clase ::php, por lo que ha de utilizarse algún parámetro como require para garantizar el orden de ejecución o un operador de precedencia.

En el post anterior utilizamos el operador -> de un modo que en esta clase podríamos usar:

Class[::php::global] -> Class['::php]

No obstante, puede definirse la precedencia en las propias declaraciones de los recursos y, de este modo, la clase «zonahoraria» mostraría la siguiente estructura:

class zonahoraria::setup {

  class { '::php::globals':
            
            ...

 	}->
  class { '::php':
            
            ...

        }
}

Ejecutando el manifiesto

Pedimos el manifiesto desde el agente para su ejecución:

/opt/puppetlabs/bin/puppet agent -t

 Comprobación

Comprobamos que parámetro timezone.

cat /etc/php/7.0/fpm/php.ini | grep timezone

; Defines the default timezone used by the date functions
; http://php.net/date.timezone
;date.timezone =
date.timezone =CET

 Leyendo el parámetro desde un archivo en el agente

Para ello hay que escribir un archivo a mano en la máquina del agente y ampliar las funcionalidades de Facter para que lea este archivo.

Archivo con parámetro

Creamos el archivo con la zona horaria:

echo " Europe/Madrid" > /usr/local/etc/timezone

 Montaje del directorio

Editamos de nuevo el archivo fileserver.conf  y añadimos el punto de montaje /usr/local/etc:

[files]
  path /usr/local/bin:/usr/local/etc
  allow *

 Ampliando el comportamiento facter

Facter permite añadir nuevos «hechos», que pueden escribirse en Ruby, y utilizarse en los manifiestos.

Creamos un fact en el módulo «zonahoraria»:

mkdir -p /etc/puppetlabs/code/environments/test/modules/zonahoraria/lib/facter
vim /etc/puppetlabs/code/environments/test/modules/zonahoraria/lib/facter/zonahoraria.rb

 Fact

Facter.add(:zonahoraria) do
  setcode do
    if File.exists? '/usr/local/etc/timezone'
      zone = File.readlines('/usr/local/etc/timezone')
      zone[0]
    end
  end
end

 Cambiando el fact en el manifiesto

Con la siguiente línea reemplazmos la variable de facter timezone por la que hemos creado, zonahoraria:

find modules/zonahoraria/manifests/init.pp | xargs perl -pi -e 's/timezone/zonahoraria/g'

 Actualizando el parámetro en el agente

Pedimos el manifiesto al master:

/opt/puppetlabs/bin puppet agent -t

 Por ultimo

Comprobamos que el parámetro leído del archivo se haya actualizado:

cat /etc/php/7.0/fpm/php.ini | grep timezone
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
;date.timezone =
date.timezone = Europe/Madrid

Esta es la idea básica de declaración de facts en Puppet. Por supuesto, hay mucho más. Una lista completa de los facts del sistema pueden obtenserse con el comando /opt/puppetlabs/bin/facter

Configuración de Puppet en Debian Jessie y creación de un módulo

Por domingo, 19 febrero, 2017 0 , Permalink

Cuando tienes hacer un despliegue en muchos servidores o automatizar tareas repetitivas, Puppet es una gran opción. En base a manifiestos declarativos, Puppet se asegura de que existan recursos, los crea sin aún no existen y los utiliza. A diferencia de muchos otros lenguajes, en los manifiestos no se definen los procesos para aprovechar los recursos, sino que se describe «lo que debe haber» y Puppet se encarga de hacerlo realidad. Así ha sido dicho y así será hecho.

Continuar Leyendo…

GNU Social en YunoHost

Yunohost es una distribución de Linux orientada a que el máximo número de personas tengan un hosting propio. Como algunos sabéis, soy responsable de la aplicación GNU Social en el proyecto. Aún no había hablado en mi blog de ello porque, en mis aportes, está pendiente aún el Single Sign On, que no es tan automático como suele ser en la distribución (lista de aplicaciones) . Aunque queda perfilar algunos temas,  el incio de sesión con LDAP ya es posible, por lo que a excepción del SSO la aplicación es plenamente funcional. Con un merge que hice ayer, la instalación está traducida también al francés, por lo que ya son tres idiomas junto al español e inglés.

yunohost apps

Os ánimo a que probéis la distribución. Es muy útil en un micrordenador con chip ARM, aunque como es lógico en una máquina muy potente tendría un desempeño mayor.

Por si alguien quiere colaborar con los scripts de GNU Social, os dejo en este enlace el repositorio en git.

 

Solucionar burn in en monitor LCD

Por domingo, 11 diciembre, 2016 0 Permalink

¡Hola, buenas! En este post vamos a ver como solucionar un burn in en un monitor LCD. Créase o no, no es un problema exclusivo de monitores CRT y aún existe este problema hasta en los últimos monitores del mercado.

Hace unas semanas apareció inesperadamente en mi monitor una línea brillante de aproximadamente medio centímetro de ancho.

Burn in en monitor benq
Linea  por burn in

Este fallo es debido a una sobrecarga de voltaje en algunos pixeles, en los monitores LCD no es persistente y tiene arreglo, excepto que se trate de una avería de hardware. En mi caso, la sobrecarga vino por una línea negra de alto contraste del Firefox. En los monitores LCD el color de reposo es el blanco puro, mientras que el color negro es el que más voltaje utiliza.

La solución pasa por dejar la pantalla con el color blanco durante varias noches o, mejor aún, una pantalla que vaya cambiando los colores blanco y negro. Ayuda que el monitor esté en la frecuencia más baja posible.

Hay algunas aplicaciones de pago para solucionar el burn in, pero hay otro modo de conseguir lo mismo. Durante toda esta semana, he estado utilizando un vídeo en Youtube de diez horas de duración y, tras varias noches y algún día entero, la línea que se ve en la foto de arriba ha desaparecido por completo.

Para un monitor 16:9, este es el vídeo:

Para prevenir que vuelva a tener otro burn in como éste, es tan sencillo como no dejar las ventanas de los programas abiertas si no hay necesidad de hacerlo y activar el salvapantallas más a menudo.

Probad esto antes de llevarlo a reparar.

Un saludo

Automatización de backups de WordPress

Por jueves, 1 diciembre, 2016 0 , Permalink

En este post vamos a seguir con la serie automatización para ver como realizar un backup de la instalación de WordPress. Conviene realizar backups de manera periódica, por lo que seria conveniente planificar con qué frecuencia se realizan, para automatizar también la ejecución del script de backup. Quiero recordar que estos scripts de la serie van en conjunto, es decir, que hay que tener instalado WordPress con el script del primer post de esta serie, que podéis leer aquí.

La copia de seguridad será almacenada en un directorio de un servidor remoto, los cuales serán definidos en un archivo junto con otros parámetros.

Usaremos algunas funciones que ya fueron explicadas en el post sobre el script de la desinstalación con los mismo objetivos:

  • iferror. Utilizada para detener la ejecución el script con un mensaje diciendo en qué parte de la ejecución ha fallado
  • read_root_path.  Utilizada para definir el directorio root de la instalación que respaldamos.
  • get_config_parameters. Utilizada para definir los parámetros para acceder a la base de dato.

Para leer estas tres funciones podéis consultar el post de la desinstalación o visitar mi repositorio en Github desde aquí.

Para la ejecución de este script necesitaremos un archivo con los siguientes parámetros:

  • Nombre de dominio de la instalación a respaldar.
  • Usuario remoto de backup. El usuario que tendrá permisos para escribir en el directorio de almacenamiento.
  • IP o FQDN del host remoto.
  • Directorio remoto para almacenar la copia de respaldo, que será una copia con fecha de la base de datos y archivos.
domain=torestore.org
backup_user=gustavo	
backup_host=192.168.1.254
backup_remote_dir=backup

La primera función específica de este script es mysql_dump. Esta función utiliza los datos leídos con get_config_parameters para acceder a la base de datos con las tablas de la instalación de WordPress y envía una copia al directorio definido en el archivo de parámetros mediante una conexión ssh. El script crea un directorio con nombre igual al nombre de dominio de la instalación en el host remoto de backups:

# function: mysql_dump
# Produces a backup of full database in a tar.gz file

function mysql_dump {
  ssh $backup_user@$backup_host "mkdir -p $backup_remote_dir/$domain"
  mysqldump --user $db_user --password=$db_password  \
  $db_name | gzip -c | ssh $backup_user@$backup_host "cat > \
  $backup_remote_dir/$domain/$(date +%Y%m%d)$db_name.sql.gz" \
  || iferror "Backup for database named $db_name has failed" \
  && wp_simple_backup_files;
}

La función wp_simple_backup_files realiza una copia de todo el directorio raíz de WordPress. «Todo» quiere decir que también hace un respaldo del directorio upload, dónde están las imágenes y otros archivos que pueden tener un gran peso. Los archivos son comprimidos y empaquetados con una nomenclatura que incluye la fecha en el nombre del archivo. La copia será enviada con el comando scp al host remoto:

# function: wp_simple_backup_files
# Produces a tar.gz to a remote host

function wp_simple_backup_files {
  wp_targz="/tmp/$(date +%Y%m%d)$domain.tar.gz";
  if [[ -d $wp_path ]]; then
    tar czfp $wp_targz -C $wp_path . \
    && scp $wp_targz $backup_user@$backup_host:$backup_remote_dir/$domain/. \
    && rm -f $targz_file \
    || iferror "Backup file not sended to $backup_host"
  fi
}

La función nginx_site_backup hace una copia del servidor virtual de Nginx que usa la instalación de WordPress y la envía al directorio del backup:

# function: nginx_site_backup
# Produces a file with Nginx configurarion of the given server

function nginx_site_backup {
  if [[ -e /etc/nginx/sites-enabled/$domain.conf ]]; then
    scp /etc/nginx/sites-enabled/$domain.conf \
    $backup_user@$backup_host:$backup_remote_dir/$domain/.
  fi
}

Como en scripts anteriores de la serie, el script de backup también comprueba que lo haya ejecutado el usuario root y que exista el archivo de parámetros:

if [[ -e ./params/backup.conf ]];then
 		source ./params/backup.conf;
else
    iferror "First you need configure parameters";
fi

if [ "$(id -u)" != "0" ]; then
  echo "This script must be run as root" 1>&2
  exit 1
fi

La funcion start, que es llamada para iniciar el programa:

function start {
 read_root_path \
 && get_config_parameters \
 && mysql_dump \
 && nginx_site_backup
}

El script que he explicado en este post puede lanzarse desde cron para planificar la ejecución de las copias de respaldo. En el siguiente post veremos como restaurar la copia de seguridad. Al haber copiado el blog entero y la configuración servidor virtual, para restaurar la copia no habrá que instalar necesariamente otro WordPress y ambos scripts pueden ser muy útiles para realizar migraciones. Aunque todo eso lo veremos en el próximo post.

Hasta entonces.