Está en: » Artículos »

Crear copias de seguridad en servidor web de hosting mediante php

Crear copias de seguridad en servidor web de hosting mediante php

Cuando tenemos un cms alojado, normalmente suele tener algún sistema para las copias de seguridad. Pero también es cierto que la mayoría simplemente hacen un backup de la base de datos, dejando los adjuntos (imágenes, vídeos, ficheros…) descartados.

Otras veces, por necesidades del cliente, desarrollamos nosotros mismos la aplicación.

La cuestión es que sea cual sea el caso, se suele necesitar un sistema de backups medianamente funcional. Y para dicho propósito, he creado un script en php.

El funcionamiento del archivo es bien sencillo:

Lo editamos para modificar los valores que vienen detallados al principio y ajustarlo a nuestro sitio.
Añadimos una entrada al demonio cron de nuestro servidor pasándole el parámetro de copia que queremos realizar:

  • ?copia=completa -> Hace una copia de la sql y de los archivos
  • ?copia=sql -> Sólo hace una copia de la sql
  • ?copia=directorios -> Sólo hace copia de los directorios especificados

De esta manera, tenemos un control mucho mejor de nuestros backup, ya que podemos hacer un backup diario de la base de datos y otro semanal completo.

Es verdad que muchos hosting no dejan añadir entradas al demonio cron del sistema, por lo que podemos crearnos un script, lo incluimos en un archivo php de nuestro sitio que siempre se ejecute (por ejemplo el header de nuestra plantilla o alguno del estilo) y comprobar el nombre del archivo (ya que contiene la fecha) y así hacer la acción a determinar. Aunquea este método, le veo el problema de que a la persona que le toque al acceder a la página, le va a tocar esperar algo más mientras se ejecuta.

Tambien podemos añadir una tarea programada en nuestro sistema (si tenemos una máquina que no se suele apagar, es más recomendable usarlo así) de manera que lance una petición http a la url y se ejecute la copia.

Bueno, detallo el contenido del archivo (tambien lo adjunto al final para descargar):

 Hace una copia de la sql y de los archivos
//		sql -> Sólo hace una copia de la sql
//		directorios -> Sólo hace copia de los directorios especificados

// dominio del backup
$dominio = 'miguelcarmona.com';

// establezca el directorio temporal. Ruta relativa a este script o ruta absoluta al directorio
// si está vacío, usaremos la carpeta temp del mismo directorio de este archivo
// asegúrese que la carpeta temp tiene permisos de escritura
$temp = '';
if ( empty($temp) ) $temp = './temp/';

// establezca la ruta absoluta o relativa a este script para depositar la copia final
// si no se establece esta ruta, se depositará en la carpeta $temp definida anteriormente
$dir_copia = '';
if ( empty($dir_copia) ) $dir_copia = $temp;

// Información de la conexión a la base de datos
$dbname = 'nombre de la base de datos';
$dbhost = 'ip o nombre de equipo que contiene la base de datos'; //normalmente localhost
$dbuser = 'usuario para la base de datos';
$dbpassword = 'contraseña para el usuario de la base de datos';

//establecemos los directorios a salvar
$ruta_relativa = '../../'; //establece la ruta relativa al directorio desde el que partir para los directorios listados abajo
$directorio[] = $ruta_relativa.'imagenes';
$directorio[] = $ruta_relativa.'videos';

//establecemos los valores para enviar el registro
$para = '[email protected]';
$de = 'backup@'.$dominio;

//función para ejecutar el comando en consola
function ejecutar($comando)
{
	//shell_exec($comando, $salida);
	exec($comando, $salida);
	//passthru($comando, $salida);
	//system($comando, $salida);
	return $salida;
}

//función para eliminar recursivamente directorios
//fuente: http://es.php.net/unlink
function rm($fileglob)
{
   if (is_string($fileglob)) {
       if (is_file($fileglob)) {
           return unlink($fileglob);
       } else if (is_dir($fileglob)) {
           $ok = rm("$fileglob/*");
           if (! $ok) {
               return false;
           }
           return rmdir($fileglob);
       } else {
           $matching = glob($fileglob);
           if ($matching === false) {
               trigger_error(sprintf('No files match supplied glob %s', $fileglob), E_USER_WARNING);
               return false;
           }
           $rcs = array_map('rm', $matching);
           if (in_array(false, $rcs)) {
               return false;
           }
       }
   } else if (is_array($fileglob)) {
       $rcs = array_map('rm', $fileglob);
       if (in_array(false, $rcs)) {
           return false;
       }
   } else {
       trigger_error('Param #1 must be filename or glob pattern, or array of filenames or glob patterns', E_USER_ERROR);
       return false;
   } 

   return true;
}

//establecemos la variable $mensaje_error vacía para añadir los errores que vayan ocurriendo
$mensaje_error = '';

//establecemos la variable $log que nos servirá para guardar un registro de lo que vamos haciendo
$log = '';

//establecemos la variable $para_comprimir vacía para añadir los campos concatenados de lo que vamos a comprimir en el archivo final
$para_comprimir = '';

if ( empty($_GET['copia']) )
{
	$mensaje_error .= 'No se ha especicado el tipo de copia.';
}
elseif ( $_GET['copia'] =='completa' | $_GET['copia'] == 'sql' | $_GET['copia'] == 'directorios' )
{

	//borramos el directorio temp
	rm( $temp.'/*' );

	//comprobamos si se necesita copia de la base de datos
	if ( $_GET['copia'] == 'sql' | $_GET['copia'] == 'completa' )
	{
		$log .= "Inicio de la copia de seguridad de la base de datos $dbName en el equipo $dbHost:n";
		$comando = "mysqldump -p --user=$dbUser --password=$dbPassword --host=$dbHost --add-drop-table $dbName > $temp$dbName.sql";
		$retorno = ejecutar($comando);
		if ( empty($retorno) ) $retorno = '(sin retorno)';
		$log .= "Retorno del comando mysqldump encargado de la copia de seguridad de la base de datos:n$retornonn";
		if ( file_exists( $temp.$dbName.'.sql' ) )
		{
			$tamano = filesize($temp.$dbName.'.sql') / 1024;
			$log .= "El archivo $dbName.sql con un tamaño de $tamano Kb. fue creado correctamentennn";
			$para_comprimir .= $temp.$dbName.'.sql ';
		}
		else
		{
			$log .= "No pudo crearse $temp$dbName.sqlnnn";
		}
	}

	//comprobamos si se necesita copia de los datos
	if ( $_GET['copia'] == 'directorios' | $_GET['copia'] == 'completa' )
	{
		$log .= "Inicio de la copia de seguridad de los datos contenidos en:n";
		if ( !empty($directorio) && is_array($directorio) )
		{
			$directorios_ok = '';
			foreach ( $directorio as $dire )
			{
				if ( !empty($dire) && file_exists($dire) )
				{
					$directorios_ok .= $dire.' ';
					$log .= $dire."n";
				}
				else
				{
					$directorios_mal[] = $dire.' ';
				}
			}

			//recorremos los directorios que no sean accesibles y lo pasamos a errores y logs
			if ( !empty($directorios_mal) && is_array($directorios_mal) )
			{
				$mensaje_error .= "Los siguientes directorios no son accesibles:n";
				$log .= "Los siguientes directorios no son accesibles:n";
				foreach ( $directorios_mal as $dir_mal )
				{
					$mensaje_error .= $dir_mal."n";
					$log .= $dir_mal."n";
				}
			}

			$comando = 'tar cf '.$temp."datos.tar $directorios_ok";
			$retorno = ejecutar($comando);
			if ( empty($retorno) ) $retorno = '(sin retorno)';
			$log .= "Retorno del comando tar encargado de empaquetar los directorios selecionados:n$retornonn";
			if ( file_exists( $temp.'datos.tar' ) )
			{
				$tamano = filesize($temp.'datos.tar') / 1024;
				$log .= "El archivo datos.tar con un tamaño de $tamano Kb. fue creado correctamentennn";
				$para_comprimir .= $temp.'datos.tar';
			}
			else
			{
				$log .= "No pudo crearse $temp/datos.tarnnn";
			}
		}
		else
		{
			$mensaje_error .= "No ha definido los directorios a copiar.n";
		}
	}

	//Creamos el comprimido final
	$log .= "Inicio del generador del comprimido final con los datos:n";
	if ( !empty ( $para_comprimir ) )
	{
		$ruta_con_nombre = $dir_copia.$dominio.'-'.date("Y-m-d").'.tgz';
		$comando = 'tar czf '.$ruta_con_nombre." $para_comprimir";
		$retorno = ejecutar($comando);
		if ( empty($retorno) ) $retorno = '(sin retorno)';
		$log .= "Retorno del comando tar encargado de generar el comprimido final:n$retornonn";

		if ( file_exists( $ruta_con_nombre ) )
		{
			$tamano = filesize($ruta_con_nombre) / 1024;
			$log .= "El archivo datos.tar con un tamaño de ".$tamano."Kb. fue creado correctamentennn";
		}
		else
		{
			$log .= "No pudo crearse $ruta_con_nombrennn";
		}
	}

}
else
{
	$mensaje_error .= 'El tipo de copia especificado es incorrecto.';
}

$cabeceras = "From: $dern" .
    'X-Mailer: PHP/' . phpversion();

$mensaje .= "Sistema de copias de seguridadnDerechos atribuibles a Miguel Angel Carmonanhttps://miguelcarmona.comnn";
$mensaje .= 'Tipo de copia de seguridad: '.$_GET['copia'];
$mensaje .= "nnn###########Registro de acciones:############n";
$mensaje .= $log;
$mensaje .= "nnn###########Registro de errores encontrados:############n";
if ( empty( $mensaje_error ) ) $mensaje_error = '(sin errores en la ejecución)';
$mensaje .= $mensaje_error;

mail($para, 'Copia de seguridad - site:'.$dominio, $mensaje, $cabeceras);
?>

En el principio del archivo, debemos especificar los datos de la conexión a la base de datos (sólo mysql), los datos de correo electrónico para mandar el registro de acciones y los posibles errores, y los directorios a salvar.

Si no se le especifica un directorio temporal para el proceso, utilizará el directorio ./temp contenido donde esté el archivo. Es importante que le establezca permisos de escritura al mismo.

El archivo con la copia se dejará en el mismo directorio temp (si no se especifica la ruta en la variable $dir_copia), y como este directorio es borrado cada vez que se ejecuta el script, recomiendo que se establezca en otra ruta (en la variable $dir_copia).

Este script se puede combinar con algún método de envío a un servidor remoto y así obtenemos un sistema de copias de seguridad desatendido (lo explico en este post – Backup de hosting contínuo desatendido).

Espero que les sea de utilidad

Archivo:  Script php para backups

Comentarios

  1. ZercosZ dice:

    Buenas.

    Gracias por el script pero tengo una duda con lo que dices al final.

    Este script se puede combinar con algún método de envío a un servidor remoto y así obtenemos un sistema de copias de seguridad desatendido (lo explico en este post – Backup de hosting contínuo desatendido).

    He buscado el post que indicas pero no he encontrado nada.

    Gracias por tu ayuda.

Deje su comentario

Previsualización de comentario
  1. Anónimo dice:





Pings para esta entrada