Jun 22, 2009

Protección del sistema con iptables

Una vez configuradas todas las características del Sistema de Alta Disponibilidad y Balanceo de Carga que hemos venido desarrollando durante los últimos meses, vamos a ver la forma de proteger correctamente todas las máquinas del cluster a través de la herramienta iptables.

Iptables es una aplicación firewall vinculada al kernel de Linux, que se ocupa entre otras cosas de filtrar (a nivel de enlace y red) aquellos paquetes que llegan al sistema en base a unas determinadas reglas.

Lo que vamos a hacer es crear un script en cada uno de los nodos, iptables.sh, que sea el encargado de establecer las reglas necesarias durante el arranque de la máquina. Este script lo ubicaremos dentro del directorio /etc/init.d/ para que sea automáticamente ejecutado por el demonio init al iniciar el equipo. Para ello, crearemos un enlace dentro del directorio /etc/rc[nivel].d/.

A continuación se muestra el fichero de configuración de iptables para el primer nodo (HA1). Lo primero que hace este script es vaciar las reglas ya existentes de las tablas de filtrado (FILTER) y nat (NAT). Después abre todos los servicios permitidos hacia la máquina: ICMP, SSH, HTTP, MySQL, y FTP. Todos estos servicios estarán disponibles para cualquier usuario que los solicite a través de Internet.

También será necesario abrir aquellos puertos por los que se reciba tráfico a través del resto de nodos del cluster. Por ejemplo, el nodo HA1 recogerá los latidos del nodo HA2 (192.168.1.11) a través del puerto 694 UDP. El nodo HA1 gestionará los demonios de almacenamiento del cluster de MySQL, los cuales enviarán información a través del puerto 1186 TCP proveniente de los dos nodos traseros (10.0.0.12 y 10.0.0.13). Y el mismo procedimiento para los puertos utilizados por Zabbix.

Lo que tiene que quedar claro es que los servicios brindados por el sistema se han dejado abiertos a cualquier dirección IP, y los servicios internos utilizados por los nodos del cluster se han delimitado exclusivamente a las direcciones IP origen correspondientes.

Para que los nodos traseros puedan acceder a Internet, también será necesario habilitar una regla SNAT en los dos nodos frontales que lo habilite.

Una vez establecidas todas las reglas que serán permitidas, pondremos al final del script una regla que loguee en el fichero /var/log/messages todos los accesos no permitidos y una última regla que rechace todas esas conexiones no aceptadas.
root@ha1:~# cat /etc/init.d/iptables.sh
#!/bin/sh

# Borrar todas las reglas de la tabla de filtrado
iptables -F

# Borrar todas las reglas de la tabla nat
iptables -t nat -F

# Reglas permitidas para la cadena de filtrado
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p icmp -j ACCEPT
iptables -A INPUT -p tcp --dport ssh -j ACCEPT
iptables -A INPUT -p tcp --dport http -j ACCEPT
iptables -A INPUT -p tcp --dport mysql -j ACCEPT
iptables -A INPUT -p tcp --dport ftp -j ACCEPT
iptables -A INPUT -p tcp --dport ftp-data -j ACCEPT

# heartbeat
iptables -A INPUT -s 192.168.1.11 -p udp --dport 694 -j ACCEPT

# nbd_mgmd
iptables -A INPUT -s 10.0.0.12 -p tcp --dport 1186 -j ACCEPT
iptables -A INPUT -s 10.0.0.13 -p tcp --dport 1186 -j ACCEPT

# zabbix (agente)
iptables -A INPUT -s 192.168.1.11 -p tcp --dport 10050 -j ACCEPT

# zabbix (servidor)
iptables -A INPUT -s 192.168.1.11 -p tcp --dport 10051 -j ACCEPT
iptables -A INPUT -s 10.0.0.12 -p tcp --dport 10051 -j ACCEPT
iptables -A INPUT -s 10.0.0.13 -p tcp --dport 10051 -j ACCEPT

# Reglas SNAT para la tabla nat
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -j SNAT --to 192.168.1.10

# Loguear las conexiones bloqueada
iptables -A INPUT -j LOG

# Bloquear el resto de conexiones
iptables -A INPUT -j REJECT
El script de iptables para el segundo nodo frontal, HA2, será similar al del primer nodo. Lo único que cambiará serán algunas direcciones IP origen.
root@ha2:~# cat /etc/init.d/iptables.sh
#!/bin/sh

# Borrar todas las reglas de la tabla de filtrado
iptables -F

# Borrar todas las reglas de la tabla nat
iptables -t nat -F

# Reglas permitidas para la cadena de filtrado
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p icmp -j ACCEPT
iptables -A INPUT -p tcp --dport ssh -j ACCEPT
iptables -A INPUT -p tcp --dport http -j ACCEPT
iptables -A INPUT -p tcp --dport mysql -j ACCEPT
iptables -A INPUT -p tcp --dport ftp -j ACCEPT
iptables -A INPUT -p tcp --dport ftp-data -j ACCEPT

# heartbeat
iptables -A INPUT -s 192.168.1.10 -p udp --dport 694 -j ACCEPT

# nbd_mgmd
iptables -A INPUT -s 10.0.0.12 -p tcp --dport 1186 -j ACCEPT
iptables -A INPUT -s 10.0.0.13 -p tcp --dport 1186 -j ACCEPT

# zabbix (agente)
iptables -A INPUT -s 192.168.1.10 -p tcp --dport 10050 -j ACCEPT

# zabbix (servidor)
iptables -A INPUT -s 192.168.1.10 -p tcp --dport 10051 -j ACCEPT
iptables -A INPUT -s 10.0.0.12 -p tcp --dport 10051 -j ACCEPT
iptables -A INPUT -s 10.0.0.13 -p tcp --dport 10051 -j ACCEPT

# Reglas SNAT para la tabla nat
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -j SNAT --to 192.168.1.11

# Loguear las conexiones bloqueada
iptables -A INPUT -j LOG

# Bloquear el resto de conexiones
iptables -A INPUT -j REJECT
A continuación se muestra el script de iptables para el nodo LB1.
root@lb1:~# cat /etc/init.d/iptables.sh
#!/bin/sh

# Borrar todas las reglas de la tabla de filtrado
iptables -F

# Reglas permitidas para la cadena de filtrado
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p icmp -j ACCEPT
iptables -A INPUT -p tcp --dport ssh -j ACCEPT
iptables -A INPUT -p tcp --dport http -j ACCEPT
iptables -A INPUT -p tcp --dport mysql -j ACCEPT
iptables -A INPUT -p tcp --dport ftp -j ACCEPT
iptables -A INPUT -p tcp --dport ftp-data -j ACCEPT

# glusterfs y ndbd
iptables -A INPUT -s 10.0.0.13 -p tcp -j ACCEPT

# zabbix (agente)
iptables -A INPUT -s 192.168.1.10 -p tcp --dport 10050 -j ACCEPT
iptables -A INPUT -s 192.168.1.11 -p tcp --dport 10050 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 25 -j DROP

# Loguear las conexiones bloqueada
iptables -A INPUT -j LOG

# Bloquear el resto de conexiones
iptables -A INPUT -j REJECT
Y por último, el del nodo LB2:
root@lb2:~# cat /etc/init.d/iptables.sh
#!/bin/sh

# Borrar todas las reglas de la tabla de filtrado
iptables -F

# Reglas permitidas para la cadena de filtrado
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p icmp -j ACCEPT
iptables -A INPUT -p tcp --dport ssh -j ACCEPT
iptables -A INPUT -p tcp --dport http -j ACCEPT
iptables -A INPUT -p tcp --dport mysql -j ACCEPT
iptables -A INPUT -p tcp --dport ftp -j ACCEPT
iptables -A INPUT -p tcp --dport ftp-data -j ACCEPT

# glusterfs y ndbd
iptables -A INPUT -s 10.0.0.12 -p tcp -j ACCEPT

# zabbix (agente)
iptables -A INPUT -s 192.168.1.10 -p tcp --dport 10050 -j ACCEPT
iptables -A INPUT -s 192.168.1.11 -p tcp --dport 10050 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 25 -j DROP

# Loguear las conexiones bloqueada
iptables -A INPUT -j LOG

# Bloquear el resto de conexiones
iptables -A INPUT -j REJECT
Para que los scripts se puedan lanzar durante el proceso de arranque, y a su vez, puedan ser iniciados y detenidos automáticamente, hay que ejecutar las siguientes órdenes.
root@ha1:~# chmod +x /etc/init.d/iptables.sh ; update-rc.d iptables.sh defaults
root@ha2:~# chmod +x /etc/init.d/iptables.sh ; update-rc.d iptables.sh defaults
root@lb1:~# chmod +x /etc/init.d/iptables.sh ; update-rc.d iptables.sh defaults
root@lb2:~# chmod +x /etc/init.d/iptables.sh ; update-rc.d iptables.sh defaults

Jun 14, 2009

Monitorización de servicios con Zabbix (II)

Una vez instalado Zabbix en los cuatro nodos del cluster, vamos a ver cómo realizar una configuración mínima del servidor de Zabbix para obtener y visualizar datos de las máquinas en donde se ha instalado el agente.

Lo primero que tendremos que hacer será entrar a la consola web de administración empleando para ello nuestras credenciales de acceso. A continuación iremos a la pestaña de Configuration, Hosts, y en la lista desplegable seleccionaremos Host groups y pulsaremos seguidamente sobre el botón Create Group. Crearemos un nuevo grupo denominado Cluster.


Después volveremos nuevamente a la lista desplegable y seleccionaremos la opción Hosts. Pulsaremos sobre el botón Create Host. A través de este control registraremos cada uno de los nodos del sistema y se los asignaremos al grupo Cluster.


En la siguiente imagen podemos ver todas las máquinas una vez configuradas dentro del grupo Cluster.


Es importante señalar que la plantilla que se utilizará para cada nodo será la Template_Linux. En una plantilla se especifican el conjunto de parámetros a ser monitorizados y es una herramienta totalmente configurable.

Para modificar las características de una plantilla hay que ir a la sección Configuration, Items.


Para observar los datos recolectados por el servidor a través de los agentes, tendremos que ir a la pestaña Monitoring, Overview. Esta pantalla nos mostrará una tabla con los parámetros de cada uno de los elementos monitorizados en el grupo.

Si queremos analizar la información de forma gráfica, iremos a la sección de Monitoring, Graphs. y a través de las listas desplegables seleccionaremos el parámetro y la máquina de la cual queremos obtener un determinado gráfico.

Por defecto, la pantalla de las gráficas nos ofrece únicamente cuatro parámetros a representar: CPU Loads, CPU Utilization, Disk usage y Network utilization. Si se quieren añadir más parámetros a la lista desplegable, habrá que ir a la sección Configuration, Graphs, y añadirlos.

En este artículo se ha presentado una breve descripción de lo que Zabbix es capaz de hacer. Realmente es una herramienta muy potente, ya que todos los scripts de monitorización son totalmente modificables. También permite configurar alertas a enviar al administrador, vía SNMP o SMTP por ejemplo. Aparte, permite desarrollar medidas activas (scripts) a tomar en caso de detectar alguna incidencia.

Por último, decir también que podemos modificar el idioma de la consola web. Para ello habría que ir a la sección Administration, Locales, seleccionar nuestro lenguaje en la lista desplegable Take for default locale, y pulsar sobre el botón Next. A continuación veríamos una pantalla que nos ofrecería todas las cadenas de texto traducidas.

Pulsaríamos sobre el botón Download y de esta forma, obtendríamos un fichero denominado new_locale.inc.php, con el cual remplazaríamos el archivo /usr/share/zabbix/include/locales.inc.php.

Jun 9, 2009

Monitorización de servicios con Zabbix (I)

En el Sistema de Alta Disponibilidad y Balanceo de Carga que estamos desarrollando, implantamos a través de los artículos anteriores un sistema de monitorización activa, es decir, en cada uno de los nodos del cluster se levantó un demonio que se encargaba de controlar el estado de los servicios configurados, y en caso de detectar algún fallo, ser capaz por si mismo de tomar las medidas oportunas (reiniciar el servicio, reiniciar la máquina, apagarla, etc.).

A parte de este tipo de monitorización, puede llegar a resultar bastante interesante instalar una monitorización pasiva, es decir, una arquitectura que sea capaz de informarnos de otros muchos más parámetros del sistema (aparte de los servicios principales), como por ejemplo el uso de CPU, memoria, espacio en disco, estado de otros procesos, etc.

A través de la monitorización activa se suelen cubrir aquellos puntos críticos del sistema, como por ejemplo que el proceso httpd deje de responder. En este caso, el margen de tiempo de reacción es crucial, ya que si el periodo acontecido (desde que el servicio deja de responder, se percata el administrador y en consecuencia este último toma una medida) es muy alto, las consecuencias pueden ser nefastas.

Por contra, a través de la monitorización pasiva se suelen cubrir otros aspectos que podríamos denominar de "incidencia menor", como por ejemplo que la CPU esté durante bastante tiempo al 90%, que quede menos de un 10% de espacio libre en disco, que exista algún proceso que esté empleando más memoria de lo habitual, etc.

Esta segunda clase de incidencias sí que permiten un margen de maniobra mayor. Cuando por ejemplo quede menos de un 10% de espacio en disco, se le puede enviar una alerta al administrador (SNMP, SMTP, etc.) para que decida cuál es la medida que se debe tomar (por ejemplo añadir un segundo disco duro).

Zabbix está formado por dos componentes principales: el cliente o agente (encargado de recoger los datos en el huésped) y el servidor (se ocupa de recopilar la información que le proporcionan los agentes). A su vez, junto con el servidor se suele instalar un frontend PHP para ofrecer esa información de una manera legible al administrador.

En nuestro Sistema de Alta Disponibilidad y Balanceo de Carga, vamos a instalar en los dos nodos frontales la parte servidora de Zabbix (así como el frontend PHP), y en los cuatro nodos del cluster, el agente.
root@ha1:~# aptitude install zabbix-server-mysql zabbix-frontend-php zabbix-agent

root@ha2:~# aptitude install zabbix-server-mysql zabbix-frontend-php zabbix-agent

root@lb1:~# aptitude install zabbix-agent

root@lb2:~# aptitude install zabbix-agent

Durante la instalación de la parte del servidor de Zabbix, el asistente nos realizará una serie de preguntas:

  • El tipo de base de datos que se va a utilizar para zabbix-frontend-php: mysql.
  • Contraseña de aplicación MySQL para zabbix-frontend-php: ******
  • ¿Desea configurar la base de datos para zabbix-server-mysql con "dbconfig-common"?: Yes
  • Contraseña del usuario root de la base de datos de MySQL: ******
  • Contraseña de aplicación MySQL para zabbix-server-mysql: ******

Lo primero que tenemos que hacer es definir el nombre de cada uno de los nodos en los ficheros de configuración de los agentes (etiqueta Hostname), así como la dirección IP del servidor de Zabbix al cual deberán enviarle la información (etiqueta Server).

root@ha1:~# cat /etc/zabbix/zabbix_agentd.conf
...
Server=127.0.0.1,192.168.1.11
Hostname=ha1
...

root@ha2:~# cat /etc/zabbix/zabbix_agentd.conf
...
Server=127.0.0.1,192.168.1.10
Hostname=ha2
...

root@lb1:~# cat /etc/zabbix/zabbix_agentd.conf
...
Server=192.168.1.10,192.168.1.11
Hostname=lb1
...
root@lb2:~# cat /etc/zabbix/zabbix_agentd.conf
...
Server=192.168.1.10,192.168.1.11
Hostname=lb2
...
A continuación registraremos el puerto que utilizan el agente y el servidor de Zabbix en el fichero /etc/services (10050, en los cuatro nodos, y 10051 en los dos nodos frontales).
~# cat /etc/services
...
zabbix-agent 10050/tcp # Servidor de Zabbix
zabbix-server 10051/tcp # Agente de Zabbix
...
Por último reiniciaremos el agente en los cuatro nodos, y el servidor de Zabbix y Apache en los dos nodos frontales:
root@ha1:~# /etc/init.d/zabbix-agent restart ; /etc/init.d/zabbix-server restart ; /etc/init.d/apache2 restart

root@ha2:~# /etc/init.d/zabbix-agent restart ; /etc/init.d/zabbix-server restart ; /etc/init.d/apache2 restart

root@lb1:~# /etc/init.d/zabbix-agent restart

root@lb2:~# /etc/init.d/zabbix-agent restart
Para acceder al servidor de Zabbix del frontal HA1 pondremos en un navegador web la URL http://192.168.1.11/zabbix/, y para acceder al servidor del frontal HA2, http://192.168.1.12/zabbix/.

Nada más cargar la página web, me encontré con un error PHP: Timezone for PHP is not set. Please set "date.timezone" option in php.ini.

Para solucionarlo, tenemos que editar el parámetro date.timezone dentro de las opciones de configuración de PHP. Además, aprovecharemos para incrementar el valor del parámetro max_execution_time de 30 a 300 (tiempo máximo de ejecución para cada script, en segundos). Esta operación la realizaremos en ambos nodos frontales.
~# cat /etc/php5/apache2/php.ini
...
date.timezone = Europe/Madrid
...
max_execution_time = 300
...

~# /etc/init.d/apache2 restart
Según el manual de Zabbix, para loguearnos en la consola de administración del servidor tenemos que introducir el usuario Admin y la opción de la contraseña dejarla en blanco (deberíamos establecer una en cuanto accediéramos a la consola de administración).

Pues bien, al hacer esto me encontré con el siguiente error: ERROR: Login name or password is incorrect.

Lo primero que hice fue comprobar el log de MySQL para ver si había habido algún problema cuando Zabbix consultó la password del usuario Admin en la base de datos. Todo estaba en orden.

Accediendo a la base de datos de Zabbix en MySQL, pude ver que la contraseña del usuario Admin no se correspondía con la del usuario guest (en teoría y según la documentación, las dos deberían estar en blanco nada más instalar la aplicación).
root@ha1:~# mysql -u root -p
Enter password:

mysql> use zabbix;
Database changed

mysql> select * from users;
+--------+-------+---------+---------------+----------------------------------+-----+-----------+------------+-------+---------+------+-------------+----------------+------------+---------------+
userid alias name surname passwd url autologin autologout lang refresh type theme attempt_failed attempt_ip attempt_clock
+--------+-------+---------+---------------+----------------------------------+-----+-----------+------------+-------+---------+------+-------------+----------------+------------+---------------+
1 Admin Zabbix Administrator 5fce1b3e34b520afeffb37ce08c7cd66 0 900 en_gb 30 3 default.css 0 0
2 guest Default User d41d8cd98f00b204e9800998ecf8427e 0 900 en_gb 30 1 default.css 0 0
+--------+-------+---------+---------------+----------------------------------+-----+-----------+------------+-------+---------+------+-------------+----------------+------------+---------------+

2 rows in set (0.01 sec)
Así que lo primero que probé fue a poner la cadena (MD5) del password del usuario guest, al usuario Admin. Pero esto no funcionó...

Así que lo que tuve que hacer fue generar una nueva suma MD5 para la password que quería utilizar en la consola de administración web, y actualizar el campo passwd para el usuario Admin (tabla users).
root@ha1:~# echo -n "myzabbixpassword"  md5sum
5c0846506fc325f57006336a9436451a -

root@ha1:~# mysql -u root -p
Enter password:

mysql> use zabbix;
Database changed

mysql> update users set passwd="5c0846506fc325f57006336a9436451a" where alias='Admin';
Query OK, 1 row affected (0.01 sec)

Rows matched: 1 Changed: 1 Warnings: 0
Esta actualización también la tuve que hacer en el nodo HA2.

Cuando parecía que todos los problemas estaban solucionados, compruebo que en los nodos traseros no está corriendo el agente, y en los nodos frontales ni el agente ni el servidor. Al abrir los logs pude ver los siguientes mensajes: Cannot create PID file [/var/run/zabbix-server/zabbix_server.pid] [No such file or directory] y Cannot create PID file [/var/run/zabbix-agent/zabbix_agentd.pid] [No such file or directory].
~# cat /var/log/zabbix-server/zabbix_server.log
...
/usr/sbin/zabbix_server [2537]: Cannot create PID file [/var/run/zabbix-server/zabbix_server.pid] [No such file or directory]
/usr/sbin/zabbix_server [2537]: ERROR: Cannot create PID file [/var/run/zabbix-server/zabbix_server.pid] [No such file or directory]
...

~# cat /var/log/zabbix-agent/zabbix_agentd.log
...
zabbix_agentd [2532]: Cannot create PID file [/var/run/zabbix-agent/zabbix_agentd.pid] [No such file or directory]
zabbix_agentd [2532]: ERROR: Cannot create PID file [/var/run/zabbix-agent/zabbix_agentd.pid] [No such file or directory]
...
Para solucionar el problema crearemos las estructuras de directorios que faltan dentro de la carpeta /etc/zabbix/, ya que si no con cada arranque Zabbix volverá a borrar el path /var/run/zabbix-*. Además tendremos que establecer los permisos adecuados.
root@ha1:~# mkdir -p /etc/zabbix/var/run/zabbix-server
root@ha1:~# mkdir -p /etc/zabbix/var/run/zabbix-agent
root@ha1:~# chown zabbix:root /etc/zabbix/var/run/zabbix-server
root@ha1:~# chown zabbix:root /etc/zabbix/var/run/zabbix-agent

root@ha2:~# mkdir -p /etc/zabbix/var/run/zabbix-server
root@ha2:~# mkdir -p /etc/zabbix/var/run/zabbix-agent
root@ha2:~# chown zabbix:root /etc/zabbix/var/run/zabbix-server
root@ha2:~# chown zabbix:root /etc/zabbix/var/run/zabbix-agent

root@lb1:~# mkdir -p /etc/zabbix/var/run/zabbix-agent
root@lb1:~# chown zabbix:root /etc/zabbix/var/run/zabbix-agent

root@lb2:~# mkdir -p /etc/zabbix/var/run/zabbix-agent
root@lb2:~# chown zabbix:root /etc/zabbix/var/run/zabbix-agent
Aparte tendremos que modificar también los ficheros de configuración del cliente y del servidor en cada nodo para incluir la ruta correcta al fichero .pid.
~# cat /etc/zabbix/zabbix_server.conf
...
PidFile=/etc/zabbix/var/run/zabbix-server/zabbix_server.pid
...

~# cat /etc/zabbix/zabbix_agentd.conf
...
PidFile=/etc/zabbix/var/run/zabbix-agent/zabbix_agent.pid
...

Jun 4, 2009

Actualizar Ubuntu Server desde la línea de órdenes

Para realizar una actualización de un servidor Ubuntu Server, por ejemplo de la versión 8.10 a la 9.04, y si no se dispone de un entorno gráfico como KDE o Gnome, se puede realizar desde la línea de órdenes ejecutando el siguiente comando:

~# do-release-upgrade

Si intentamos lanzar dicha orden a través de una sesión SSH, el comando nos informará que no es recomendable realizar una actualización del sistema sobre SSH, ya que en caso de fallo sería más difícil de recuperar.

Para poder emplear el comando do-release-update tendremos que tener instalado previamente el paquete update-manager-core.

May 31, 2009

Monitorización de servicios con mon (III)

Y ya para terminar el capítulo de la monitorización de servicios con mon dentro del Sistema de Alta Disponibilidad y Balanceo de Carga que venimos desarrollando, vamos a ver los tres scripts que implementaremos tanto para la parada (service_down.alert) y arranque (service_up.alert) de servicios, como para la monitorización del estado de un determinado proceso (proc.monitor).

Los scripts de parada y arranque serán comunes para todos los nodos del cluster. El script de parada será invocado por mon desde el archivo mon.cf a través de la siguiente forma: "alert service_down.alert comando fichero_estado".

Como primer argumento le pasaremos el comando que tendrá que ejecutar en caso de caída del servicio, y en el segundo, el fichero donde guardar el estado del servicio. Estos valores serán recibidos por el script a través de las variables de entorno $9 y $10 respectivamente.

En el script de parada lo primero que se hace es comprobar si el fichero de estado existe, y en caso contrario se crea con el valor inicial de "on". Si se produce una primera caída del servicio (el fichero de estado contendrá en ese momento el valor "on"), se ejecutará la orden recibida mediante la variable $9. A continuación se pondrá en el fichero de estado el valor "restarted".

Si el servicio siguiera caído cuando mon volviera a comprobar su situación, vería que el valor del fichero de estado es "restarted" (ya ha sido reiniciado previamente), con lo que a continuación reiniciaría el equipo y pondría el valor "rebooted" en dicho fichero. Si el servicio siguiera sin arrancar, apagaría la máquina completamente.
~# cat /usr/lib/mon/alert.d/service_down.alert
#!/bin/sh

if [ ! -f $10 ]; then
echo "on" > $10
fi

case "$(cat $10)" in
"on")
echo "restarted" > $10
$(/usr/bin/sudo $9);;
"restarted")
echo "rebooted" > $10
$(/usr/bin/sudo reboot);;
"rebooted")
echo "on" > $10
$(/usr/bin/sudo halt);;
esac
Por otra parte, el script de arranque (service_up.alert) lo único que hará será poner el valor "on" en el fichero de estado.
~# cat /usr/lib/mon/alert.d/service_up.alert
#!/bin/sh
echo "on" > $10
También será necesario desarrollar un script encargado de comprobar si un determinado servicio sigue vivo. Este script será empleado exclusivamente en los dos nodos traseros para verificar el estado del proceso ndbd.
~# cat /usr/lib/mon/mon.d/proc.monitor

#!/bin/sh
if [ "$(pgrep $1)" = "" ]; then
exit 1
else
exit 0
fi
Estos scripts que acabamos de diseñar les tendremos que otorgar permisos de ejecución:
~# chmod +x /usr/lib/mon/alert.d/service_down.alert

~# chmod +x /usr/lib/mon/alert.d/service_up.alert

~# chmod +x /usr/lib/mon/mon.d/proc.monitor
Mon es lanzado mediante el usuario daemon y este último no tiene privilegios de root para realizar ciertas operaciones (reiniciar apache2 y proftpd, reiniciar y apagar la máquina, etc). Por lo tanto, dentro del fichero /etc/sudoers de cada una de las máquinas habrá que incluir las órdenes necesarias que permitan ejecutar dichos comandos al usuario daemon con permisos de root.

Para los nodos HA1 y HA2 añadiremos las siguientes líneas al fichero /etc/sudoers:
~# cat /etc/sudoers
...
# User privilege specification
root ALL=(ALL) ALL

daemon ALL=NOPASSWD:/etc/init.d/mysql-ndb-mgm restart,/sbin/reboot,/sbin/halt
...
Y para LB1 y LB2:
~# cat /etc/sudoers
...
# User privilege specification
root ALL=(ALL) ALL

daemon ALL=NOPASSWD:/etc/init.d/apache2 restart,/etc/init.d/vsftpd restart,/etc/init.d/glusterfs-server restart,/etc/init.d/mysql restart",/etc/init.d/mysql-ndb restart,/sbin/reboot,/sbin/halt
...
Debido a que mon va a controlar una serie de servicios que deben de estar previamente arrancados, tenemos que modificar su script de arranque (/etc/init.d/mon) para que sea el último en levantarse (le daremos prioridad 99). En caso de apagado o reinicio de la máquina, ocurrirá lo contrario: deberá ser el primer en apagarse (le daremos prioridad 10).
~# update-rc.d -f mon remove
~# update-rc.d mon start 99 2 3 4 5 . stop 10 0 1 6 .
Esto sólo hay que hacerlo en los dos nodos traseros (LB1, LB2), ya que para los dos nodos frontales (HA1, HA2) será Heartbeat el encargado de levantarlo o apagarlo (recordamos el contenido del fichero /etc/ha.d/haresources):
~# cat /etc/ha.d/haresources
ha1 IPaddr2::192.168.1.20 IPaddr2::10.0.0.20 mysql-ndb-mgm ldirectord::ldirectord.cf LVSSyncDaemonSwap::master mon
Por lo tanto, para que Heartbeat sea el único elemento encargado de detener e iniciar el demonio mon, tendremos que quitarlo del sistema de arranque del proceso init en los nodos HA1 y HA2:
~# update-rc.d -f mon remove

May 26, 2009

Monitorización de servicios con mon (II)

En el artículo anterior del Sistema de Alta Disponibilidad y Balanceo de Carga que venimos desarrollando, se expusieron las características principales del demonio mon.

En este segundo artículo vamos a presentar los ficheros de configuración de mon para los cuatro nodos del cluster. Lo primero que vamos a hacer es instalar los paquetes mon (versión 0.99.2) y sendmail (versión 8.14.3) en las cuatro máquinas:

~# aptitude install mon sendmail

A continuación vamos a editar los ficheros de configuración de mon (/etc/mon/mon.cf) en los dos nodos frontales, HA1 y HA2. Para estos dos nodos, mon únicamente comprobará el estado del servicio ndb_mgmd.

root@ha1:~# cat /etc/mon/mon.cf
# Directorio donde residen los ficheros de mon

cfbasedir = /etc/mon

# Directorio donde residen los scripts de alerta
alertdir = /usr/lib/mon/alert.d

# Directorio donde residen los scripts de monitorización
mondir = /usr/lib/mon/mon.d

# Número máximo de procesos que tendrá el monitor
maxprocs = 20

# Número de eventos que serán guardados en el fichero de históricos
histlength = 100

# Tiempo aleatorio (de 0 a 60sg) transcurrido el cual, se arrancará el monitor
randstart = 60s

# Grupo de máquinas a monitorizar
hostgroup ha1 192.168.1.10

# Grupo a monitorizar
watch ha1

# Servicio a monitorizar
service ndb_mgmd

# Intervalo de monitorización
interval 30s

# Script empleado para monitorizar
monitor tcp.monitor -p 1186

# Periodo de monitorización
period wd {Mon-Sun}

# Enviar un correo electrónico en caso de caída del servicio
alert mail.alert -S "[ha1] ndb_mgmd off"
my_mail@gmail.com

# Reiniciar servicio/reiniciar máquina/apagar máquina
alert service_down.alert "/etc/init.d/mysql-ndb-mgm restart" /var/log/mon/mysql-ndb-mgm_status

# Reiniciar el estado del servicio (pasar a "on")
upalert service_up.alert /var/log/mon/mysql-ndb-mgm_status

# Enviar un correo electrónico en caso de recuperación del servicio
upalert mail.alert -S "[ha1] ndb_mgmd on" my_mail@gmail.com

Y para el nodo HA2:

root@ha2:~# cat /etc/mon/mon.cf
# Directorio donde residen los ficheros de mon

cfbasedir = /etc/mon

# Directorio donde residen los scripts de alerta
alertdir = /usr/lib/mon/alert.d

# Directorio donde residen los scripts de monitorización
mondir = /usr/lib/mon/mon.d

# Número máximo de procesos que tendrá el monitor
maxprocs = 20

# Número de eventos que serán guardados en el fichero de históricos
histlength = 100

# Tiempo aleatorio (de 0 a 60sg) transcurrido el cual, se arrancará el monitor
randstart = 60s

# Grupo de máquinas a monitorizar
hostgroup ha2 192.168.1.11

# Grupo a monitorizar
watch ha2

# Servicio a monitorizar
service ndb_mgmd

# Intervalo de monitorización
interval 30s

# Script empleado para monitorizar
monitor tcp.monitor -p 1186

# Periodo de monitorización
period wd {Mon-Sun}

# Enviar un correo electrónico en caso de caída del servicio
alert mail.alert -S "[ha2] ndb_mgmd off"
my_mail@gmail.com

# Reiniciar servicio/reiniciar máquina/apagar máquina
alert service_down.alert "/etc/init.d/mysql-ndb-mgm restart" /var/log/mon/mysql-ndb-mgm_status

# Reiniciar el estado del servicio (pasar a "on")
upalert service_up.alert /var/log/mon/mysql-ndb-mgm_status

# Enviar un correo electrónico en caso de recuperación del servicio
upalert mail.alert -S "[ha2] ndb_mgmd on" my_mail@gmail.com

La opción randstart se utiliza para aleatorizar el lanzamiento del primer test durante el arranque de la máquina (el test comenzará inicialmente, transcurrido un tiempo entre cero y el valor de randstart).

Como puede observarse, se han declarado grupos de monitorización (ha1 y ha2) sobre los cuales se comprobarán una serie de servicios (ndb_mgmd). Cada uno de los vigilantes serán definidos mediante el parámetro watch. Dentro de cada vigilante se establecerán los servicios a controlar (service). El periodo de muestreo se configurará mediante los parámetros interval (30 sg) y period (toda la semana). En caso de caída de un servicio, lanzaremos sendas alertas (alert), y cuando éste se recupere, se ejecutarán dos scripts (upalert).

Los scripts de parada (service_down.alert) y arranque (service_up.alert) los programaremos a través de comandos del bash y serán comunes para todos los nodos del cluster (estos ficheros serán analizados en el siguiente artículo).

A continuación vamos a ver los ficheros de configuración de mon para los dos nodos traseros (LB1 y LB2). En estas dos máquinas, mon comprobará el estado de los servicios http, ftp, glusterfs, mysql y ndbd.

root@lb1:~# cat /etc/mon/mon.cf
# Directorio donde residen los ficheros de mon

cfbasedir = /etc/mon

# Directorio donde residen los scripts de alerta
alertdir = /usr/lib/mon/alert.d

# Directorio donde residen los scripts de monitorización
mondir = /usr/lib/mon/mon.d

# Número máximo de procesos que tendrá el monitor
maxprocs = 20

# Número de eventos que serán guardados en el fichero de históricos
histlength = 100

# Tiempo aleatorio (de 0 a 60sg) transcurrido el cual, se arrancará el monitor
randstart = 60s

# Grupo de máquinas a monitorizar
hostgroup lb1 127.0.0.1

# Grupo a monitorizar
watch lb1

# Servicio a monitorizar
service http
interval 30s
monitor http.monitor -u /index.html
period wd {Mon-Sun}
alert mail.alert -S "[lb1] Apache off" my_mail@gmail.com
alert service_down.alert "/etc/init.d/apache2 restart" /var/log/mon/apache2_status
upalert service_up.alert /var/log/mon/apache2_status
upalert mail.alert -S "[lb1] Apache on" my_mail@gmail.com

# Servicio a monitorizar
service ftp
interval 30s
monitor ftp.monitor
period wd {Mon-Sat}
alert mail.alert -S "[lb1] vsftpd off" my_mail@gmail.com
alert service_down.alert "/etc/init.d/vsftpd restart" /var/log/mon/vsftpd_status
upalert service_up.alert /var/log/mon/vsftpd_status
upalert mail.alert -S "[lb1] vsftpd on" my_mail@gmail.com

# Servicio a monitorizar
service glusterfs
interval 30s
monitor tcp.monitor -p 6996
period wd {Mon-Sat}
alert mail.alert -S "[lb1] glusterfs off" my_mail@gmail.com
alert service_down.alert "/etc/init.d/glusterfs-server restart" /var/log/mon/glusterfs_status
upalert service_up.alert /var/log/mon/glusterfs_status
upalert mail.alert -S "[lb1] glusterfs on" my_mail@gmail.com

# Servicio a monitorizar
service mysql
interval 30s
monitor msql-mysql.monitor --mode mysql --username=root --password=javier1 --database=clustertest
period wd {Mon-Sat}
alert mail.alert -S "[lb1] mysql off" my_mail@gmail.com
alert service_down.alert "/etc/init.d/mysql restart" /var/log/mon/mysql_status
upalert service_up.alert /var/log/mon/mysql_status
upalert mail.alert -S "[lb1] mysql on" my_mail@gmail.com

# Servicio a monitorizar
service ndbd
interval 30s
monitor proc.monitor ndbd
period wd {Mon-Sat}
alert mail.alert -S "[lb1] ndbd off" my_mail@gmail.com
alert service_down.alert "/etc/init.d/mysql-ndb restart" /var/log/mon/ndbd_status
upalert service_up.alert /var/log/mon/ndbd_status
upalert mail.alert -S "[lb1] ndbd on" my_mail@gmail.com

Y para el nodo LB2:

root@lb2:~# cat /etc/mon/mon.cf
# Directorio donde residen los ficheros de mon
cfbasedir = /etc/mon

# Directorio donde residen los scripts de alerta
alertdir = /usr/lib/mon/alert.d

# Directorio donde residen los scripts de monitorización
mondir = /usr/lib/mon/mon.d

# Número máximo de procesos que tendrá el monitor
maxprocs = 20

# Número de eventos que serán guardados en el fichero de históricos
histlength = 100

# Tiempo aleatorio (de 0 a 60sg) transcurrido el cual, se arrancará el monitor
randstart = 60s

# Grupo de máquinas a monitorizar
hostgroup lb2 127.0.0.1

# Grupo a monitorizar
watch lb2

# Servicio a monitorizar
service http
interval 30s
monitor http.monitor -u /index.html
period wd {Mon-Sun}
alert mail.alert -S "[lb2] Apache off" my_mail@gmail.com
alert service_down.alert "/etc/init.d/apache2 restart" /var/log/mon/apache2_status
upalert service_up.alert /var/log/mon/apache2_status
upalert mail.alert -S "[lb2] Apache on" my_mail@gmail.com

# Servicio a monitorizar
service ftp
interval 30s
monitor ftp.monitor
period wd {Mon-Sat}
alert mail.alert -S "[lb2] vsftpd off" my_mail@gmail.com
alert service_down.alert "/etc/init.d/vsftpd restart" /var/log/mon/vsftpd_status
upalert service_up.alert /var/log/mon/vsftpd_status
upalert mail.alert -S "[lb2] vsftpd on" my_mail@gmail.com

# Servicio a monitorizar
service glusterfs
interval 30s
monitor tcp.monitor -p 6996
period wd {Mon-Sat}
alert mail.alert -S "[lb2] glusterfs off" my_mail@gmail.com
alert service_down.alert "/etc/init.d/glusterfs-server restart" /var/log/mon/glusterfs_status
upalert service_up.alert /var/log/mon/glusterfs_status
upalert mail.alert -S "[lb2] glusterfs on" my_mail@gmail.com

# Servicio a monitorizar
service mysql
interval 30s
monitor msql-mysql.monitor --mode mysql --username=root --password=javier1 --database=clustertest
period wd {Mon-Sat}
alert mail.alert -S "[lb2] mysql off" my_mail@gmail.com
alert service_down.alert "/etc/init.d/mysql restart" /var/log/mon/mysql_status
upalert service_up.alert /var/log/mon/mysql_status
upalert mail.alert -S "[lb2] mysql on" my_mail@gmail.com

# Servicio a monitorizar
service ndbd
interval 30s
monitor proc.monitor ndbd
period wd {Mon-Sat}
alert mail.alert -S "[lb2] ndbd off" my_mail@gmail.com
alert service_down.alert "/etc/init.d/mysql-ndb restart" /var/log/mon/ndbd_status
upalert service_up.alert /var/log/mon/ndbd_status
upalert mail.alert -S "[lb2] ndbd on" my_mail@gmail.com

May 16, 2009

Eliminar los pitidos del sistema en GNU/Linux

Cuando instalamos un sistema operativo GNU/Linux, los pitidos de la shell (provenientes del altavoz interno del equipo) vienen activados por defecto.

Estos sonidos pueden llegar a ser realmente molestos, ya que son emitidos por ejemplo cuando intentamos tabular un comando o fichero para completar su nombre y no existe ninguna posibilidad para completarlo. También por ejemplo cuando borramos algo que hemos escrito en la línea de órdenes y nos pasamos más allá del inicio de la cadena de texto que queremos suprimir.

Para eliminar de un plumazo estos horribles sonidos, lo único que tenemos que hacer es quitar del kernel de Linux el módulo (pcspkr) responsable de tal acción:

# modprobe -r pcspkr

Si queremos que este cambio sea permanente, es decir, la próxima vez que arranquemos la máquina tampoco se escuchen los pitidos, tendremos que añadir dicha orden al fichero /etc/rc.local, de tal forma que se ejecute cada vez que se inicie el sistema:

# echo "modprobe -r pcspkr" >> /etc/rc.local

Para hacer permanente el cambio, inicialmente probé a añadirlo a la lista negra de módulos del kernel:

# echo "blacklist pcspkr" >> /etc/modprobe.d/blacklist
# echo >> /etc/modprobe.d/blacklist

Pero no funcionó... Al arrancar nuevamente el sistema operativo seguía teniendo el pitido. Comentar que las pruebas las hice en un sistema RHEL 5.3.

También probé después de añadir el módulo a la lista negra, a generar una nueva imagen initrd, ya que podría ocurrir que dicho módulo ya estuviese cargado en la RAM disk que se utiliza durante el arranque del sistema y por eso se omitiera su exclusión en la lista negra. Pero ni con esas...