jueves, 27 de marzo de 2008

Haciendo un mirror de subversion

Subversion -al igual que CVS- es un software de control de versiones, todo desarrollador de software debe contar con una herramienta de este tipo y el no hacerlo simplemente es una gran irresponsabilidad (o estupidez).

Una (pero no la principal) de las ventajas de utilizar subversion es que sirve como una herramienta de respaldo de código. Cuando uno mantiene respaldos, es importante que dichos respaldos sean almacenados en instalaciones diferentes a la fuente de información original, esto permite afrontar situaciones extremas como incendios o terremotos.

Esta condición básica de seguridad no se da en mi caso ya que en mi empresa nuestro servidor subversion está en las mismas instalaciones donde trabajamos, por lo tanto nos vimos enfrentados al problema de respaldar en servidores remotos para lo cual implementé un mecanismo de mirroring de subversion mediante el comando svnsync.

Modelo de operación
El mecanismo que escogí para respaldar subversion es un mirroring que se actualiza cada vez que se hace COMMIT en el repositorio maestro, replicando los cambios en el servidor de respaldo. Este modelo de respaldo tiene las siguientes características:
  • El mirror está siempre al día.
  • El mirror es actualizado sólo con los deltas enviados en el COMMIT por lo que la transferencia de datos es mínima.
  • En caso de pérdida del servidor principal, basta con sacar una copia del mirror para poder restaurar el servidor principal.
  • El mirror presta las mismas funcionalidades del servidor principal con la excepción que es de sólo lectura.

En términos resumidos la implementación del mirroring se realiza sacando una copia del repositorio, instalarla en un servidor de respaldo y finalmente se configura el servidor principal para que luego de cada COMMIT se ejecute el comando svnsync que replicará todos los cambios en el servidor de respaldo (mirror).


Prerrequisitos
Se asume que tienes conocimientos básicos de LINUX y de cómo administrar un servidor subversion, en caso que no seas muy experimentado puedes consultar el manual de subversion.

IMPORTANTE: El procedimiento aquí descrito se enfoca en el proceso de creación y configuración del mirror. Debes asegurarte de realizar las configuraciones de control de acceso y seguridad necesarias.

Debemos contar con dos servidores (principal y respaldo) que tengan instalado subversion 1.4 o superior, es deseable que ambos servidores tengan la misma versión.

El repositorio a respaldar lo vamos a denominar proyecto. En los ejemplos, el servidor de mirror usará el nombre de dominio mirror.com.


Procedimiento de configuración
El primer paso es crear el repositorio principal (si ya tienes un repositorio que quieres respaldar, puedes omitir este paso).
svnadmin create proyecto
Luego, hacemos una copia del repositorio recién creado, lo comprimimos y eliminamos la copia temporal. (Utilizamos hotcopy para evitar eventuales errores si justo al momento de hacer la copia alguien realizó un COMMIT en el repositorio):
svnadmin hotcopy proyecto proyectobackup
tar cfz proyectobackup.tgz proyectobackup
rm -rf proyectobackup
A continuación transferimos el archivo comprimido al servidor de respaldo, donde lo descomprimimos y restauramos el nombre del repositorio a proyecto:
tar xfz proyectobackup.tgz
mv proyectobackup proyecto
cd proyecto
Ahora configuramos el repositorio mirror para no permitir que los usuarios hagan COMMIT, solamente se permitirá que haga COMMIT una cuenta de usuario especial que llamaremos syncuser, con contraseña secret (debes definir escoger otra contraseña) y le damos permisos de escritura completos:
echo "syncuser = secret" >> conf/passwd
echo "[/]
syncuser = rw" >> conf/authz
(las ultimas 2 lineas son un solo comando)

Para evitar que el resto de usuarios pueda hacer COMMIT creamos el archivo (con permisos de ejecución) hooks/start-commit dentro del repositorio con el siguiente contenido:

#!/bin/sh

USER="$2"
if [ "$USER" = "syncuser" ]; then exit 0; fi

echo "No se permite hacer commit en el repositorio de respaldo" >&2
exit 1

Luego debemos crear el script (con permisos de ejecución) hooks/pre-revprop-change el cual es requerido por el comando svnsync:

#!/bin/sh

USER="$3"

if [ "$USER" = "syncuser" ]; then exit 0; fi

echo "No se permite hacer commit en el repositorio de respaldo" >&2
exit 1
Con estos pasos hemos creado una copia del repositorio proyecto, hemos creado el usuario syncuser y configuramos el repositorio para que nadie más pueda hacer COMMIT. A continuación volvemos al servidor principal.



En el servidor principal vamos a configurar el repositorio para indicarle que tiene un mirror (referenciado en el siguiente ejemplo con el nombre de dominio mirror.com):
svnsync init svn://mirror.com/proyecto svn://localhost/proyecto --username syncuser --password secret
Finalmente ejecutamos el proceso de sincronización, el cual copiará los datos hacia el servidor de respaldo:
svnsync sync svn://mirror.com/proyecto --username syncuser --password secret
Con estos pasos hemos configurado el servidor principal para que apunte al mirror previamente creado. El paso final es automatizar el proceso de respaldo.

Hay dos estrategias para sincronizar el mirror. La primera es realizar la sincronización en un proceso aparte, por ejemplo todas las noches. La segunda estrategia es que la sincronización se realice inmediatamente después de cada COMMIT.

Alternativa 1: Respaldo asincrónico
Esta estrategia es útil cuando la velocidad del enlace entre el servidor principal y el mirror es muy lenta o cuando los COMMITs que se realizan son muy pesados.

Basta con agregar una linea al cron, a continuación se muestra un ejemplo que realiza la sincronización todas las noches a las 04:15 hrs.:
15 04 * * * svnsync sync svn://mirror.com/proyecto  --username syncuser --password secret

Alternativa 2: Respaldo
en tiempo real
Esta estrategia tiene la ventaja que el mirror permanecerá siempre actualizado cada vez que hagamos un COMMIT.

Implica editar (o crear) el script ejecutable hooks/post-commit y agregarle el siguiente código:

# Determino el nombre del repositorio
NOMBREREPO=$(echo $REPOS | cut -d"/" -f4)

svnsync sync svn://mirror.com/$NOMBREREPO --username syncuser --password secret

Luego de esto tendremos nuestro mirror de subversion configurado.

8 comentarios:

Dario dijo...
Este comentario ha sido eliminado por el autor.
Dario dijo...

Es mas facil con GIT:

http://gitevangelism.blogspot.com/2011/01/git-mirror-con-git.html

http://gitevangelism.blogspot.com/2011/01/svn-mirror-con-git.html

José Rodríguez dijo...

Claro, GIT está diseñado para tener repositorios distribuidos, no es necesario contar con un único repositorio central. En muchos aspectos es superior a SVN y seguramente tal como SVN fue el reemplazo de CVS, GIT es el reemplazo de SVN.

Anónimo dijo...

Hola!

En primer lugar felicitarte por el excelente tutorial, estaba buscando justo esto para tener una copia de mi SVN fuera de la oficina.

Tengo una pregunta.

¿Que ocurre si el servidor de respaldo pierde la conexion digamos por varios dias mientras que en el servidor principal siguen haciendose "commits"?

Supongo que en el primer sync que ocurra tras el retorno de las comunicaciones se actualizarán todos los cambios pendientes ¿no?

José Rodríguez dijo...

Efectivamente, no se perderán datos internedios. Con el siguiente sync se van a respaldar todos los COMMITS anteriores

Alan Morales dijo...

Hola te saludo desde Monterrey, Mexico. Que pasa si quiero que mi servidor mirror sea totalmente funcional? puedo omitir la parte de prohibir los commits en el Mirror? De esta forma si algo le pasa a mi servidor principal, solo muevo los usuarios al servidor mirror y una vez que se reactive el servdior principal, éste pasara a ser servidor de Mirror. puede funcionar asi?

Anónimo dijo...

Hola Jose

Enhorabuena por tu post!!
Estoy teniendo un problema a la hora de configurar el post-commit como explicas en tu post.

El tema es que no entiendo de donde cogeria el script la variable $REPOS?
Haciendo un echo $REPOS en principio me devuelve nada...

Aun asi he intentado poner directamente la ruta absoluta al comando, o sea sin declarar la variable, pero me sigue dando un error un poco raro.
Ahora mismo el post commit lo tengo asi:
svnsync sync svn://mirror.com/ruta _asoluda_destino --username syncuser --password secret

y el error que me devuelve despues de haber hecho el commit en el servidor principal es :

post-commit hook failed(exit code 255)with no output

Sabrias a que se debe este error?

En principio lanzando la sincronizacion manualmente desde linea de comandos (svnsync sync svn://mirror.com/proyecto --username syncuser --password secret) el mirror me lo sincroniza sin problemas....

Muchas gracias por tu ayuda.
Un saludo

Anónimo dijo...

Ejecutando directamente el post-commit con ./post-commit ademas veo que me sincroniza el mirror sin errores :(
Que raro

Un saludo