miércoles 24 de agosto de 2011

Rsync y optimización del espacio con enlaces duros o hard links

ACTUALIZACION 24-Agosto-2011 16:41: He añadido una nota al final sobre los links duros.

Hola a todos. Hace bastante tiempo que no incluyo una entrada nueva en el blog y creo que esta le va a gustar a muchos administradores de sistemas que necesitan optimizar el espacio porque los datos que necesitan guardar son muy pesados.

Hace poco me encontré con un problema de espacio que tenia en un servidor web. El caso es que, por contrato, la empresa donde trabajo necesitaba guardar un cierto número de rotaciones que no podíamos mantener ni por asomo. Cada copia eran 154GB, para que os hagáis una idea. La única forma de mantener un sistema de copias de seguridad así es haciendo copias incrementales, por supuesto, pero ¿como podríamos hacer que gastando un mínimo espacio pudiésemos tener copias de seguridad completas en cada copia y tener rotaciones cada dos horas?

La respuesta la tenemos en el uso de los links duros o hard links y en rsync.

Cuando tenemos un fichero en el disco, sus datos ocupan un espacio de bits en el. A grandes rasgos, el nombre, inodo, comienzo del bloque de datos en el disco y final, etc. vamos los metadatos del fichero, apuntan a ese espacio de disco. Si los datos del disco del fichero no cambian, podríamos crear un link duro que apuntara a esos datos e incluirlo en la copia de seguridad en vez de copiar el fichero con la duplicidad de los mismos datos en disco que eso conlleva. Un link duro es, a todos los efectos, el mismo fichero que el fichero original y comparte el mismo inodo que el fichero original. Podemos ver el inodo de un fichero con el comando ls y la opción -i.

Imaginaros que, de los 154 GB de los que tengo que hacer la copia de seguridad, solo cambian, realmente, 2 o 3 MB cada 2 horas, o incluso menos (imágenes no cambian, vídeos no cambian,etc.)... ¿Os vais dando cuenta del espacio que podemos ahorrar creando links duros en vez de copias, no? :-D y, por tanto, también os dais cuenta de la frecuencia que podemos tener de copias de seguridad en comparación con otros sistemas de copia. Pues bien, para poder conseguir esto he creado un script que realiza una copia de seguridad usando rsync y links duros y generando un directorio nuevo "incremental" con la frecuencia que pongáis ejecutándolo con cron. ¿Por que pongo incremental con comillas? por que en realidad, cada directorio con la nueva copia donde solo se crearán copias reales de los ficheros que cambien o sean nuevos, serán copias de seguridad completas del directorio de origen. No hará falta copiar varias copias incrementales en orden para tener la copia completa que queremos recuperar, podemos copiar el contenido del directorio del día/hora que queramos directamente y listo (otra ventaja, sin duda)

#!/bin/bash

# Nombre: rsync_hard_link.sh
# Localización del script: HOST /root/bin 
# Versión: 1.0.0
# Permisos: 770 usuario: root grupo: root
# Autor: Francisco J. Bejarano
# Descripción:
# Script que genera copias de seguridad completas "incrementales"
# de un directorio.
#
# Log: DIR_LOG
#
# Comentarios: usar una version de rsync que permita la opcion
# --link-dest=DIR. En GNU\Linux CentOS 5.6 tenemos:
# rsync  version 2.6.8  protocol version 29
# Con la que funciona sin problemas. Si usáis Fedora, Ubuntu, etc. (más nuevos) no deberíais de
# preocuparos.
#
# Frecuencia: crontab -u root -l
# # Ejecución del script cada dos horas en punto.
# 00 */2 * * * /root/bin/rsync_hard_link.sh


################################ Parámetros de configuración

# Inicializamos las variables que vayamos a usar y los directorios.
FECHA=`date +%Y%m%d%-H%M%S`
DIR_BACKUP=/var/backup/
DIR_BASE_PREVIOUS=${DIR_BACKUP}web/
ORIGEN=${DIR_BACKUP}www/
DESTINO=${DIR_BASE_PREVIOUS}${FECHA}/
DIR_LOG=${DIR_BACKUP}web/log/
LOG_FILE=${DIR_LOG}incremental_www-${FECHA}.log

# Los directorios y logs de copias con más antigüedad que los días que pongamos aquí serán
# eliminados. Por defecto 180 días o 6 meses. Ajustar a lo deseado.
DIAS=180

################################ Aplicaciones

# Detectamos la ubicación de la aplicación rsync por si lo ejecutamos en diferentes sistemas.
APP_RSYNC=`whereis rsync | cut -d" " -f2`

################################ Funciones

# Crea el nuevo directorio con la copia de seguridad nueva usando los hard links y copias 
# de ficheros nuevos o modificados.

function rsync_incremental {
   
   # Los directorios que tenemos aquí son lo que más puede confundir. El directorio que ponemos
   # en --link-dest debe ser el directorio de la última copia que realizamos previamente.
   # Vamos la anterior a la que vamos a hacer ahora. En este caso, crearemos un link blando
   # al directorio anterior cada vez que ejecutemos el script llamado previous.
   # El directorio de origen (acabado en /, importante) es el directorio del que queremos hacer
   # la copia de seguridad.
   # El directorio destino es al que vamos a hacer la copia de seguridad nueva con los hard
   # links que sean necesarios.
   # La opcion delete elimina los ficheros/directorios en el destino que se hayan eliminado
   # en el origen.

   $APP_RSYNC -avh --stats --delete --link-dest=${DIR_BASE_PREVIOUS}previous $ORIGEN $DESTINO &> $LOG_FILE

   # Borramos el anterior link blando que apuntaba a la copia previa
   rm -f ${DIR_BASE_PREVIOUS}previous
   
   # Creamos el link blando nuevo que ahora apunta a la copia de seguridad que acabamos de hacer
   # y que será la previa para la siguiente.
   ln -s $DESTINO ${DIR_BASE_PREVIOUS}previous
   
   # Comprimimos el log con bzip2. Con bzcat podemos ver el contenido sin descomprimir.
   # Así optimizamos más a un el espacio.
   bzip2 $LOG_FILE
}

# Eliminamos los ficheros necesarios para controlar el crecimiento sin control.

function eliminar_ficheros {

   # Eliminamos los directorios con antigüedad mayor a los días que pongamos en los parámetros
   # Además solo borramos los directorios que empiecen por 2 (no creo que este script siga
   # funcionando en el año 3000 :D) y solo inspeccionamos el nivel 1 de directorios no queremos
   # que borre directorios dentro de los que hay en el 1 nivel que empiecen por 2 y que sean 
   # de una copia actual que debamos mantener, ¿no?

   find $DIR_BASE_PREVIOUS -maxdepth 1 -type d -iname "2*" -ctime +${DIAS} -exec rm -rf {} \; &> /dev/null

   # Eliminamos los ficheros de log comprimidos con antigüedad mayor a los días que pongamos.
   find $DIR_LOG -type f -iname "*.bz2" -ctime +${DIAS} -exec rm -rf {} \; &> /dev/null

}

################################ Programa Principal

# Si no existe una copia previa hacemos la primera copia completa
# Esto solo se hace la primera vez. Si existe hacemos la incremental.

if [ ! -h ${DIR_BASE_PREVIOUS}previous ] ; then
   $APP_RSYNC -azh --stats --delete $ORIGEN ${FECHA}_origen/ &> $LOG_FILE
   ln -s ${FECHA}_origen/ ${DIR_BASE_PREVIOUS}previous   
else
   rsync_incremental
   eliminar_ficheros
fi

################################ Fin de Script

Y listo. Cada vez que lo ejecutemos veremos como tenemos un nuevo directorio con una copia completa que solo ocupa el espacio de disco nuevo de los ficheros nuevos o modificados en el origen.

Podemos ejecutar el script, hacer un df, modificar el contenido de ORIGEN, ejecutar de nuevo el script y hacer otro df y veremos como el consumo es mínimo y tenemos copias completas en cada directorio. También usad ls -li para ver los inodos en diferentes directorios de destino y comprobar que son los mismos si el fichero no ha cambiado.

Imaginad un desastre, se ha ido al garete todo el directorio origen de la web. Tenemos copias cada 2 horas.

# rsync --azh --delete DIR_DESTINO_O_COPIA/ DIR_ORIGEN_O_DOCUMENTROOT/

Esperáis a que se sincronice y tendréis de nuevo la copia exacta realizada en el momento en que se creo DIR_DESTINO_O_COPIA.

Saludos y espero que os sea útil ;-) y recordad que las copias de seguridad de las bases de datos debéis realizarlas también a parte si las usáis en vuestra web jeje... eso os lo dejo a vosotros. Si solo son ficheros no hay problema.

NOTA: Los links duros solo se pueden usar en el mismo sistema de ficheros. Esta limitación debemos tenerla en cuenta. Primero deberíamos hacer una sincronización del servidor web al de backup con ssh (por ejemplo) y después realizar esta copia. Esta sincronización ssh habría que hacerla cada 2 horas si usáis un servidor remoto de producción antes de la copia para que refleje los cambios o cada frecuencia que queráis. Podéis añadirlo al script mediante otra función como ejercicio jeje. De hecho, asi funcionan mis servidores.

jueves 23 de junio de 2011

RedMine 1.2.0 en CentOS 5.6 con Apache y Mysql compilados

Vamos a ver como instalar este sistema gestor de incidencias y proyectos. Es software libre y dispone de muchas características como diagramas de gantt, exportar a pdf, multiples proyectos, calendario, wiki, foro, gestión de documentos y archivos, temas... Lo instalaremos en un sistema GNU\Linux CentOS 5.6 de 64 bits.

http://www.redmine.org/

Nuestra instalación va a ser particular ya que tendremos instalado de forma compilada mysql 5.5, apache 2.2.17 que no es objeto del manual. Solo debemos saber que los tenemos en el path y que están instalados en /usr/local/apache y /usr/local/mysql.

Como CentOS es un sistema bastante antiguo dispone de paquetes anticuados que no acepta RedMine. Deberemos descargar y compilar previamente dichos paquetes.

Prerrequisitos

RedMine 1.2.0, la versión que instalaremos, tiene los siguientes prerrequisitos para funcionar:

  • Ruby 1.8.6, 1.8.7
  • Rails 2.3.11
  • Rack 1.1.1
  • ImageMagick >= 6.7.0 para la gema rmagick

ImageMagick

Necesitamos una versión de ImageMagick mayor a la que trae el sistema CentOS 5.6 que es la 6.2.8 Por tanto bajamos el código fuente de la web con el siguiente comando.

# wget ftp://ftp.imagemagick.org/pub/ImageMagick/ImageMagick.tar.gz

Descomprimimos y entramos en el directorio

# tar -xzf ImageMagick.tar.gz
# cd ImageMagick-6.7.0-8

Compilamos e instalamos en /usr/local/imagemagick

# ./configure --prefix=/usr/local/imagemagick
# make && make install

Comprobamos que está instalado y metemos imagemagick en el path

# /usr/local/imagemagick/bin/Magick-config --version
6.7.0 Q16

# vim /etc/profile

…
PATH=$PATH:/usr/local/mysql/bin:/usr/local/apache2/bin:/usr/local/php/bin:/usr/local/php/lib/php:/usr/local/php/lib/php/phing:/usr/local/imagemagick/bin:/usr/local/ruby/bin
…

# export PATH=$PATH:/usr/local/imagemagick/bin

Ruby

Instalamos Ruby 1.8.7 para ello descargamos el paquete comprimido desde el ftp de ruby.

# wget ftp://ftp.ruby-lang.org//pub/ruby/1.8/ruby-1.8.7.tar.bz2

Descomprimimos

# tar -xjf ruby-1.8.7.tar.bz2
# cd  ruby-1.8.7

Configuramos, compilamos e instalamos ruby en /usr/local/ruby

# ./configure --prefix=/usr/local/ruby --enable-shared --enable-pthread --enable-install-doc
# make && make install

Una vez que acabe la instalación con la siguiente salida, tendremos instalado ruby.

...
Generating RI...

Files:   549
Classes: 1630
Modules: 835
Methods: 8452
Elapsed: 265.272s
./miniruby -I./lib -I.ext/common -I./- -r./ext/purelib.rb  ./instruby.rb --make="make" --dest-dir="" --extout=".ext" --mflags="" --make-flags="" --data-mode=0644 --prog-mode=0755 --installed-list .installed.list --install=rdoc --rdoc-output=".ext/rdoc"
installing rdoc

Añadimos ruby al PATH del sistema para tener los comandos disponibles y comprobamos la versión.

# vim /etc/profile

...
PATH=$PATH:/usr/local/mysql/bin:/usr/local/apache2/bin:/usr/local/php/bin:/usr/local/php/lib/php:/usr/local/php/lib/php/phing:/usr/local/ruby/bin
...

# export PATH=$PATH:/usr/local/ruby/bin

# ruby --version
ruby 1.8.7 (2008-05-31 patchlevel 0) [x86_64-linux]

Si hubiéramos instalado ruby con los paquetes del sistema, lo suyo sería instalar lo necesario a través del mismo sistema de paquetería, es decir, yum en CentOS, pero como tenemos compilado ruby habrá que instalar su propio sistema de paquetería.

RubyGems

Ruby usa un sistema de paquetería propio denominado RubyGems que usa paquetes gem o gemas. Hay que descargar y compilar gems para poder usarlo. La última versión es la 1.8.5 pero esta no funciona correctamente con rake y redmine debido a un bug. Hay que bajar he instalar la versión 1.6.2. Podemos ver una lista de paquetes aquí https://rubyforge.org/frs/?group_id=126

# wget http://rubyforge.org/frs/download.php/74445/rubygems-1.6.2.tgz
# tar -xzf rubygems-1.6.2.tgz
# cd rubygems-1.6.2

Ejecutamos la instalación mediante ruby

# ruby setup.rb --rdoc --ri
RubyGems 1.6.2 installed

=== 1.6.2 / 2011-03-08

Bug Fixes:

* require of an activated gem could cause activation conflicts.  Fixes
  Bug #29056 by Dave Verwer.
* `gem outdated` now works with up-to-date prerelease gems.


------------------------------------------------------------------------------

RubyGems installed the following executables:
        /usr/local/ruby/bin/gem

gem --version
1.6.2

Esto instalará rubygems en /usr/local/ruby/bin/. Ahora disponemos del comando gem en el PATH que es el que usaremos para buscar, instalar gemas o actualizar.

NOTA: NO DEBEMOS Actualizar rubygems con el siguiente comando hasta que se resuelva el bug, pero serviría para actualizar a la ultima versión.

gem update --system


Con los siguientes comandos podemos ver las gemas instaladas

# gem list
# gem list --local

O buscar o listar las remotas

# gem list -r
# gem search -r nombregema

Podemos ver un listado de gemas aquí http://rubygems.org/gems

Instalación de Gemas

Ahora instalaremos las gemas necesarias para RedMine

# gem install rails -v=2.3.11
# gem install i18n -v=0.4.2
# gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
# gem install rmagick
# gem install passenger

Esto puede tardar un rato. Además, al tener mysql compilado debemos decirle al sistema donde están las librerías de mysql. Para ello creamos un fichero de librerías de mysql y ejecutamos ldconfig. Si no lo hacemos, al ejecutar la instalación de la base de datos con rake en un paso posterior dará error.

# vim /etc/ld.so.conf.d/mysql.conf

/usr/local/mysql/lib
/usr/local/mysql/lib64

# ldconfig

comprobamos que tenemos instalado los requisitos

# gem list

*** LOCAL GEMS ***

actionmailer (2.3.11)
actionpack (2.3.11)
activerecord (2.3.11)
activeresource (2.3.11)
activesupport (2.3.11)
daemon_controller (0.2.6)
fastthread (1.0.7)
i18n (0.4.2)
mysql (2.8.1)
passenger (3.0.7)
rack (1.1.2, 1.1.0)
rails (2.3.11)
rake (0.9.2)
rmagick (2.13.1)

Instalación de RedMine 1.2.0

Efectivamente, lo que hemos hecho hasta ahora es instalar los prerrequisitos :-D. Ahora vamos a instalar el gestor de proyectos e incidencias

Creamos la base de datos redminedb en mysql 5.5 y el usuario de mysql redmine con todos los permisos para la base de datos. Necesitamos el usuario root de mysql y la contraseña.

# mysql -u root -p -e "create database redminedb character set utf8;"

# mysql -u root -p -e "use mysql; grant all privileges on redminedb.* to 'redmine'@'localhost' identified by 'micontraseña';"

Descargamos la versión 1.2.0

# wget http://rubyforge.org/frs/download.php/74944/redmine-1.2.0.tar.gz
# tar -xzf redmine-1.2.0.tar.gz

Configuramos el archivo database.yml con los parámetros necesarios de nuestra base de datos.

# cd redmine-1.2.0/config/
# cp database.yml.example database.yml

# vim database.yml

...
# MySQL (default setup).

production:
  adapter: mysql
  database: redminedb
  host: localhost
  username: redmine_user
  password: redmine-pass
  encoding: utf8
...

En el caso de usar otro puerto, podríamos añadir la directiva port: numpuerto, por ejemplo, port: 3307.

Yo pondré redmine en /var/lib/redmine. El directorio /var/lib/redmine/public es el que contiene el sitio web de redmine y es el que deberemos poner en apache.

# mv /redmine-1.2.0 /var/lib/redmine
# cd /var/lib/redmine/

Creamos un almacén de secretos de sesión con rake. Lanzará una serie de warnings sobre opciones obsoletas que ignoramos.

# rake generate_session_store

Creamos la estructura de la base de datos del entorno de producción que creará las tablas y la cuenta de administración por defecto.

# RAILS_ENV=production rake db:migrate

# RAILS_ENV=production rake redmine:load_default_data
rake/rdoctask is deprecated.  Use rdoc/task instead (in RDoc 2.4.2+)
WARNING: 'task :t, arg, :needs => [deps]' is deprecated.  Please use 'task :t, [args] => [deps]' instead.
    at /var/lib/redmine/lib/tasks/email.rake:170

Select language: bg, bs, ca, cs, da, de, el, en, en-GB, es, eu, fa, fi, fr, gl, he, hr, hu, id, it, ja, ko, lt, lv, mk, mn, nl, no, pl, pt, pt-BR, ro, ru, sk, sl, sr, sr-YU, sv, th, tr, uk, vi, zh, zh-TW [en] es
====================================
Default configuration data loaded.

Configuramos el módulo ruby de apache con passenger

# passenger-install-apache2-module

Pulsamos intro y debemos cumplir los requisitos que nos pida.

Checking for required software...                                                                                                                                           
                                                                                                                                                                            
 * GNU C++ compiler... found at /usr/bin/g++                                                                                                                                
 * Curl development headers with SSL support... found                                                                                                                       
 * OpenSSL development headers... found                                                                                                                                     
 * Zlib development headers... found                                                                                                                                        
 * Ruby development headers... found                                                                                                                                        
 * OpenSSL support for Ruby... found                                                                                                                                        
 * RubyGems... found                                                                                                                                                        
 * Rake... found at /usr/local/ruby/bin/rake                                                                                                                                
 * rack... found                                                                                                                                                            
 * Apache 2... found at /usr/local/apache2/bin/httpd                                                                                                                        
 * Apache 2 development headers... found at /usr/local/apache2/bin/apxs                                                                                                     
 * Apache Portable Runtime (APR) development headers... found at /usr/bin/apr-1-config                                                                                      
 * Apache Portable Runtime Utility (APU) development headers... found at /usr/bin/apu-1-config

Añadimos las siguientes lineas al final del fichero de configuración de apache

# vim /usr/local/apache2/conf/httpd.conf

# Módulo necesario para redmine y ruby
LoadModule passenger_module /usr/local/ruby/lib/ruby/gems/1.8/gems/passenger-3.0.7/ext/apache2/mod_passenger.so                                                          
PassengerRoot /usr/local/ruby/lib/ruby/gems/1.8/gems/passenger-3.0.7                                                                                                     
PassengerRuby /usr/local/ruby/bin/ruby

Comprobamos la sintaxis

# apachectl configtest
Syntax OK

El usuario de sistema que usará redmine será daemon y el grupo daemon que es el usuario de apache compilado. Damos los permisos necesarios del sistema a los directorios.

# chown -R root:root /var/lib/redmine
# chown -R daemon:daemon tmp public log files vendor

Creamos un link simbólico que apunte desde el Documentroot de apache al directorio público de redmine.

# ln -s /var/lib/redmine/public /var/www/redmine

Creamos un alias para apache en el fichero de configuración principal.

# vim /usr/local/apache2/conf/httpd.conf

Alias /redmine /var/lib/redmine/public
RailsBaseURI /redmine
<Directory /var/lib/redmine/public>
        RailsEnv production
        Options ExecCGI FollowSymLinks
        Order allow,deny
        Allow from all
        AllowOverride all
</Directory>


Cambiamos el archivo de configuración para el email añadiendo al final del archivo la parte en negrita. Vosotros deberéis añadir vuestra propia configuración de mail.

# cp -a /var/lib/redmine/config/configuration.yml.example /var/lib/redmine/config/configuration.yml

# vim /var/lib/redmine/config/configuration.yml

...
# specific configuration options for production environment
# that overrides the default ones
production:
   email_delivery:
       delivery_method: :smtp
     smtp_settings:
       address: "localhost"
       port: 25
...

Por último reiniciamos Apache

# /etc/init.d/httpd restart

Podemos ir a http://localhost/redmine entrar con el usuario admin y contraseña admin por defecto.

Un ejemplo de proyecto


NOTA: Lo primero a hacer debería ser cambiar la contraseña del usuario admin.

domingo 19 de junio de 2011

Los Bancos, los verdaderos culpables de las crisis y 15M

Hoy os traigo un par de videos que he visto por Kriptopolis. Hablan sobre la situación que tenemos en España y son nada más y nada menos que los accionistas del Santander los que le ponen los puntos sobre las ies al mismísimo Botín. Esto es algo que se debería haber echo hace mucho tiempo. Los bancos son a los que debemos exigir que paguen esta situación.

Aprovecho para decir que hoy Domingo 19 de Junio habrá una manisfestación general en toda España a las 19:00 en cada ciudad para protestar por esto y por todo lo demás Crisis, corrupción política, especulación, cambio de leyes electorales, reforma laboral, etc.

Creo que vale la pena manifestarse por todas estas cuestiones y allí estaré y os animo a ir. Yo he sido de los pocos en España que ha conseguido un trabajo y no quiero ver a más gente en mi situación anterior mientras otros se forran sin contemplaciones. Os recomiendo ver los videos.



Related Posts with Thumbnails