Con la llegada de Amazon AWS y conceptos como el autoescalado nos surge un reto al que tenemos que enfrentarnos: como guardamos los logs de las máquinas que pueden ser creadas y borradas en cualquier momento?
Soluciones
Para solventar este problema las soluciones son varias. A continuación se enumeran las virtudes y defectos de algunas de ellas y veremos como poner en marcha la solución de rotar los logs a un storage basado en objetos como S3.
- Servicio de logs en cloud: Hay muchos servicios de este tipo: loggly, sumologic, papertrail, …. Las ventajas de un servicio de estas características es que nos despreocupamos de un componente de nuestra arquitectura y nos ahorramos dolores de cabeza, a parte de las funcionalidades extras para la visualización y análisis de logs. El problema pero cómo podéis ver en los enlaces de tarifas de loggly y papertrail son los precios, sobretodo si tenemos un volumen considerable de logs.
- FIleSystem Compartido: Esta solución tiene la desventaja que tenemos un punto más de fallo en nuestra arquitectura.
- Syslog-ng: Con esta solución la ventaja es que tenemos una máquina con los logs centralizados, pero tenemos que tener un servicio mas corriendo y si no tenemos muchas máquinas corriendo no es una solución muy óptima por los costes añadidos.
- Rotación de logs a storage basado en objetos: Las ventajas de esta solución es que es económica y sencilla, a parte de no tener dependencias de servicios que puedan fallar al tratarse de S3 el cual tiene una disponibilidad de 99,99% y una durabilidad de un 99.999999999%. Cómo desventajas destacaría su tratamiento que como veremos existe una alta fragmentación y que existe la posibilidad aunque sea poco probable de perder logs .
Rotación de logs a S3
Para rotar los logs de apache a S3 utilizaremos la herramienta logrotate, la idea es rotar los logs cada hora y sincronizarlos con un bucket de S3 con el comando s3cmd. A parte de rotar los logs cada hora, tenemos el añadido de que si nuestra máquina está corriendo en un autoescaling group, ésta puede ser eliminada en cualquier momento. Teniendo en cuenta que primero las máquinas són paradas con un soft shutdown, podemos crear un servicio que cuando la máquina sea parada fuerce a rotar los logs para no perderlos. En caso de que la sincronización tardara demasiado Amazon haría un hard shutdown y podríamos perder los logs de las últimas dos horas.
Instalación s3cmd
En primer lugar vamos a instalar s3cmd.
El primer paso es añadir los repositorios de epel para instalar pip:
# rpm -ivh http://mirror-fpt-telecom.fpt.net/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm
Desactivamos el repositorio epel, para que sólo sea utilizado cuando lo indiquemos explícitamente en yum.
# sed -i "s/enabled=1/enabled=0/g" /etc/yum.repos.d/epel.repo
Instalamos pip y python-magic indicando que nos active temporalmente el repositorio epel.
# yum -y install python-pip python-magic --enablerepo=epel
Instalamos s3cmd:
# pip install s3cmd
Para la configuración del s3cmd creamos el fichero de configuración correspondiente en /root/.s3cfg, sustituyendo <acces_key> y <secret_key> por las credenciales correspondientes a nuestro usuario:
[default] access_key = <acces_key> access_token = add_encoding_exts = add_headers = bucket_location = EU cache_file = cloudfront_host = cloudfront.amazonaws.com default_mime_type = binary/octet-stream delay_updates = False delete_after = False delete_after_fetch = False delete_removed = False dry_run = False enable_multipart = True encoding = UTF-8 encrypt = False follow_symlinks = False force = False get_continue = False gpg_command = /usr/bin/gpg gpg_decrypt = %(gpg_command)s -d --verbose --no-use-agent --batch --yes --passphrase-fd %(passphrase_fd)s -o %(output_file)s %(input_file)s gpg_encrypt = %(gpg_command)s -c --verbose --no-use-agent --batch --yes --passphrase-fd %(passphrase_fd)s -o %(output_file)s %(input_file)s gpg_passphrase = testtt guess_mime_type = True host_base = s3.amazonaws.com host_bucket = %(bucket)s.s3.amazonaws.com human_readable_sizes = False invalidate_default_index_on_cf = False invalidate_default_index_root_on_cf = True invalidate_on_cf = False list_md5 = False log_target_prefix = mime_type = multipart_chunk_size_mb = 15 preserve_attrs = True progress_meter = True proxy_host = proxy_port = 0 recursive = False recv_chunk = 4096 reduced_redundancy = False secret_key = <secret_key> send_chunk = 4096 simpledb_host = sdb.amazonaws.com skip_existing = False socket_timeout = 300 urlencoding_mode = normal use_https = True verbosity = WARNING website_endpoint = http://%(bucket)s.s3-website-%(location)s.amazonaws.com/ website_error = website_index = index.html
Para testear que funciona bien el s3cmd, vamos a sincronizar un archivo, supondremos que ya tenemos creado un bucket de nombre logs:
# mkdir test # cd test # echo "This is a test" > test # s3cmd sync . s3://logs/
En este punto deberíamos ver cómo se ha sincronizado nuestro fichero con el bucket. Para verlo con el s3cmd:
# s3cmd ls s3://logs/
Configuración logrotate
Vamos a configurar el logrotate para que al rotar los logs del apache sincronice estos con nuestro bucket de S3. Sustituimos el fichero /etc/logrotate.d/httpd con el siguiente contenido:
/var/log/httpd/*log { daily rotate 4 size 5k dateext dateformat +%Y-%m-%d.%s compress missingok sharedscripts postrotate /sbin/service httpd reload > /dev/null 2>/dev/null || true BUCKET=logs INSTANCE_ID=`/usr/bin/curl --silent http://169.254.169.254/latest/meta-data/instance-id` /usr/bin/s3cmd -c /root/.s3cfg -m text/plain sync /var/log/httpd/*gz* s3://${BUCKET}/${INSTANCE_ID}/ > /dev/null endscript }
Con esta configuración, los parámetros a tener en cuenta son:
- daily: Nos rotará los logs diariamente, aunque en nuestro caso los vamos a rotar cada hora si ocupan más de 5Kbytes. Más adelante veremos cómo.
- rotate 4: Nos borrará los logs a partir de 4 rotaciones.
- size 5k: Éste parámetro hace que los logs siempre sean rotados, aunque tengamos la política daily, si éstos ocupan más de 5KB.
- dateext: Hace que los logs sean guardados poniendo la fecha en el nombre, el formato es el de dateformat.
- dateformat +%Y-%m-%d.%s: Formato de la fecha: Año, mes, día del mes y segundos des de 1970.
- compress: Al hacer la rotación los logs antiguos son comprimidos.
- sharedscripts: Corre la sección de postrotate una sola vez para todos los ficheros.
- postrotate: Sección a correr una vez se han rotado los logs, en nuestro caso se hace un reload del apache, se recoge el nombre de instancia de nuestra máquina en EC2 y se sincronizan los logs que están comprimidos con el bucket logs dentro de un directorio con nuestro nombre de instancia.
Tenemos que tener en cuenta, aunque la página del man diga lo contrario el postrotate es ejecutado antes de la compresión, por lo que no nos sincronizará los logs actuales hasta la siguiente rotación de logs, que como veremos será al cabo de una hora.
Debido a que la rotacion de logs ocurre una sola vez al dia, y en nuestro caso queremos que se haga cada hora, vamos a crear el fichero /etc/cron.hourly/1logrotatehttpd con el siguiente contenido:
#!/bin/sh /usr/sbin/logrotate /etc/logrotate.d/httpd >/dev/null 2>&1 EXITVALUE=$? if [ $EXITVALUE != 0 ]; then /usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]" fi exit 0
Le damos permisos de ejecución:
# chmod +x /etc/cron.hourly/1logrotatehttpd
Rotando logs al parar la máquina
Con lo que hemos visto hasta ahora, ya tenemos preparada nuestra máquina para que sincronice los logs cada hora, con la particularidad que siempre nos sincroniza los de la última hora y no los actuales. Si la máquina es borrada de nuestro autoscaling group perderemos nuestros logs, por lo que necesitamos crear un script en el /etc/init.d que sincronice los logs antes de apagar la máquina. Creamos el script /etc/init.d/s3sync con el siguiente contenido:
#!/bin/bash # # s3sync Sync httpd logs to S3 # # chkconfig: - 86 14 # description: s3sync logs of apache # processname: s3sync # # ### BEGIN INIT INFO # Provides: s3sync # Required-Start: $local_fs $remote_fs $network $named # Required-Stop: $local_fs $remote_fs $network # Short-Description: s3sync logs of apache # Description: s3sync logs of apache ### END INIT INFO # Source function library. . /etc/init.d/functions start() { touch /var/lock/subsys/s3sync return 0 } stop() { /usr/sbin/logrotate -f /etc/logrotate.d/httpd BUCKET=logs INSTANCE_ID=`/usr/bin/curl --silent http://169.254.169.254/latest/meta-data/instance-id` /usr/bin/s3cmd -c /root/.s3cfg sync /var/log/httpd/*gz* s3://${BUCKET}/${INSTANCE_ID}/ > /dev/null return 0 } restart() { stop start } case "$1" in start) start RETVAL=$? ;; stop) stop RETVAL=$? ;; restart) restart RETVAL=$? ;; *) echo $"Usage: s3sync {start|stop|restart|condrestart|status|panic|save}" RETVAL=2 ;; esac exit $RETVAL
Hacemos ejecutable el script, lo configuramos para que arranque al arrancar la máquina y le hacemos un start:
# chmod +x /etc/init.d/s3sync # chkconfig s3sync on # service s3sync start
Como hemos visto mediante el logrotate no se rotan los últimos logs y en caso de que ocupen menos de 5k tampoco. Así pues necesitaremos hacer una sincronización después de rotados los logs. A parte para que los logs de menos de 5k sean también sincronizados necesitaremos ejecutar el logrotate con el flag -f (force). En CentOs para indicar que un servicio está corriendo y por lo tanto para que al parar la máquina ejecute el stop del servicio en cuestión se debe crear un fichero con el mismo nombre del fichero en /var/lock/subsys
Con esto ya tenemos nuestros logs de apache rotando a S3 y con el mínimo peligro de que se pierdan, como ya os podéis imaginar éste manual se podría aplicar a muchos otros servicios que tengan logs.
Un saludo cloudadmins !!
Oricloud