09 ene

Remplazar Cron jobs con Systemd.timers


cron-vs-systemdSystemd marcó un antes y un después en la gestión de servicios en Linux, dentro de las mil y una opciones que nos aporta también lo podemos usar como gestor de tareas programadas, con systemd.timer, al estilo del bien conocido cron

¿Cómo crear una tarea programada con systemd.timer?

El ejemplo que explicaré esta basado en la necesidad de que un servicio (syncthing) arranque después de un tiempo definido, desde el inicio del sistema, al ser un servicio bastante pesado buscamos que el sistema tenga una carga menor a la que suelen tener al arranque, normalmente alta.

Creación de las unidades  systemd.timer y systemd.service

Crearemos un fichero syncthing.service 

sudo vim /usr/lib/systemd/user/syncthing.service

[Unit]

Description=Syncthing - Open Source Continuous File Synchronization
Documentation=man:syncthing(1)
Wants=syncthing-inotify.service

[Service]
Type=simple
ExecStart=/usr/bin/syncthing -no-browser -no-restart -logflags=0
Restart=on-failure
SuccessExitStatus=3 4
RestartForceExitStatus=3 4

En nuestro caso nos interesa que el servicio anterior arranque 10 minutos después de que el sistema haya arrancado, para ello usaremos systemd.timer.

Crearemos un fichero syncthing.timer ya que el nombre debe ser igual al servicio que hemos creado anteriormente. syncthing.service

sudo vim /usr/lib/systemd/user/syncthing.timer

[Unit]
Description=Run syncthing 60 min after boot

[Timer]
OnBootSec=60min

[Install]
WantedBy=timers.target

Una vez creados, deberemos activarlo para el usuario que deseemos, únicamente la unidad timer.

systemctl enable --user syncthing.timer

Podremos comprobar la lista de tareas programadas usando el siguiente comando

[email protected] ~ $ systemctl --user list-timers
NEXT                         LEFT       LAST PASSED UNIT            ACTIVATES
Thu 2018-01-04 13:18:44 CET  42min left n/a  n/a    syncthing.timer syncthing.service

1 timers listed.

Ahí podemos ver que nuestra tarea y será ejecutada por primera vez en 42 minutos, en ese momento arrancara el servicio de syncthing

Si comprobamos el servicio.

[email protected] ~ $ systemctl status --user syncthing.timer
_ syncthing.timer - Run syncthing 60 min after boot
   Loaded: loaded (/usr/lib/systemd/user/syncthing.timer; enabled; vendor preset: enabled)
   Active: active (waiting) since Thu 2018-01-04 12:33:59 CET; 1min 26s ago

Podemos ver que esta activa y a la espera de ser lanzada, con ello ya tendremos nuestra tarea programada con systemd en vez de cron.

 Pros

  1. Podemos lanzar la tarea programada sin necesidad de esperar a que se auto lance.
  2. Cada tarea puede ser configurada para ser lanzada en un entorno especifico (systemd.exec)
  3. Cada tarea puede ser asignada a un cgroup
  4. Las tareas tendrán independencia gracias a que cada una será una unidad
  5. Podremos usar journalctl para comprobar la salida de cada tarea.

Contras

  1. Necesitamos crear dos unidades de systemd (timer y service) para programar una tarea.
  2. En caso de fallo no se envía el mail automáticamente, deberemos usar la opción OnFailure=
26 dic

HaProxy con Consul-Template y zero downtime reload

¿Por qué HaProxy con Consul-template?

HaProxy con Consul-template en Docker

HaProxy con Consul-template en Docker

Llevamos un tiempo trabajando en Docker con el dinamismo que ello nos supone, HaProxy con Consul-template nos ofrece una solucion muy similar a marathon-lb, el cual habíamos hablado en como usar marathon-lb para conectar nuestras aplicaciones dentro del cluster de Apache Mesos.

Marahon-lb es una maravilla, pero no dispone de algunas características que nos hacen falta, así que pensamos, bueno pues nos lo montamos nosotros.

Idea principal

La idea principal es utilizar la información que tenemos de nuestros servicios registrados en Consul, para poder generar la configuración de HaProxy con Consul-template. Fácil.

Lo haremos en docker así lo podremos lanzar en la plataforma de Apache Mesos, y claro esta si queremos que HaProxy tenga una configuración dinámica deberemos implementar el zero downtime reload

¿Qué es el Zero downtime reload HaProxy?

Cada vez que tengamos un cambio de configuración en HaProxy deberemos hacer una recarga del servicio, en el caso que tengamos conexiones establecidas esta recarga nos llevara a perderlas y claro, eso no es para nada deseable, ¿cómo solventamos este problema?

Pues seguiremos las indicaciones de Willy Tarreau CTO de HAPROXY,  lo que nos comenta es que podemos implementar una solución para descartar los paquetes SYN durante el reinicio de HaProxy, y aprovecharnos del funcionamiento de TCP para que se recupere automáticamente, añadiremos la solución en la imagen.

A esta solución hay una vuelta de tuerca mas, que lo explican maravillosamente bien la gente de Yelp, True Zero Downtime HAProxy Reloads.

Creación de los Dockers

Vamos a crear dos imágenes de Docker, por un lado la imagen con Consul-Templat y por otro la imagen de HaProxy que heredara de la primera imagen.

Toda la explicación y los ficheros necesarios están en mi github y la imagen de haproxy la podeis bajar de https://hub.docker.com/r/maauso/docker-consul-template/

Consul-template

Una de las partes mas importantes es entender el Consul-template que utilizaremos para HaProxy, gracias a Raul Fiestas por la creación del mismo :), podéis pasaros por su github, que tiene cosas muy interesantes https://github.com/rfiestas o su tumblr http://sudevops.tumblr.com/

La idea no es hacer una clase de consul-template, pero lo importante es saber las opciones que podemos usar, explicadas a continuación y que también podéis encontrar en https://github.com/maauso/docker-haproxy

¿Qué necesitamos para añadir un nuevo servicio a nuestro HaProxy?

Toda la información del servicio deberá estar en Consul, haremos un ejemplo con el servicio “www”

SERVICE_NAME=www

Este será en nombre de nuestro backend y frontend en la configuración de HaProxy, también será la url de acceso, que se concatenará al valor de HAPROXY_DOM, www.maauso.com

frontend app_http_in
  bind *:8080
  mode http
  acl host_www.maauso.com hdr(host) -i www.maauso.com
  use_backend www.maauso.com if host_www.maauso.com

Deberemos usuar Consul Tags, para decidir que servicios de Consul queremos añadir a nuestro HaProxy

HAPROXY.ENABLE=true

Y tendremos otras opciones del backend usando tags.

HAPROXY.PROTOCOL=http
HAPROXY.BACKEND.BALANCE=roundrobin
HAPROXY.BACKEND.MAXCONN=10000
backend www.maauso.com
  balance roundrobin
  mode http
  option forwardfor
  option tcp-check
  default-server inter 10s fall 1
  http-request set-header X-Forwarded-Port %[dst_port]
  http-request add-header X-Forwarded-Proto https if { ssl_fc }
Opciones de configuración para HaProxy 

Variables Globales que podemos modificar

HAPROXY_MAXCONN_GLOBAL=50000
HAPROXY_SPREAD_CHECKS=5
HAPROXY_MAX_SPREAD_CHECKS=15000
HAPROXY_SPREAD-CHECKS=5

Variables que no tienen valor por defecto

HAPROXY_DOM=maauso.com

Variables por defecto que podemos cambiar

HAPROXY_RETRIES=3
HAPROXY_BACKLOG=10000
HAPROXY_MAXCONN=10000
HAPROXY_TIMEOUT_CONNECT=3s
HAPROXY_TIMEOUT_CLIENT=30s
HAPROXY_TIMEOUT_SERVER=30s
HAPROXY_TIMEOUT_HTTP_KEEP_ALIVE=1s
HAPROXY_TIMEOUT_HTTP_REQUEST=15s
HAPROXY_TIMEOUT_QUEUE=30s
Por último zero downtime reload. 

Para conseguir el zero downtime reload consul-template enviará un SIGHUB al proceso que maneja a HaProxy, este proceso recibirá la señal con un trap y realizará el DROP de los paquetes SYN con iptables.

Acto seguido creara un nuevo proceso de HaProxy con la opción -sf $LATEST_HAPROXY_PID y eliminara la regla de iptables

 

28 jun

¿Cómo usar Marathon-lb en Apache Mesos?

marathon-lb

Voy a explicar como trabajar con Marathon-lb (sustituto de marathon-haproxy-bridge) para Apache Mesos, hay parte teórica y parte práctica :),  hace un tiempo expliqué qué era Apache-Mesos y cómo empezar a trabajar con uno de su Frameworks llamado Marathon, también comentamos como utilizar marathon-haproxy-bridge para balancear peticiones de los Dockers que tengamos esparcidos por todo el cluster.

Antes de empezar nos debemos poner un poco en contexto, os recomiendo que leáis las entradas anteriores, para entender el ecosistema.

Parte teórica 

¿Qué es marathon-lb?

Marathon-lb es una solución que nos da la gente de Mesosphere para poder balancear de forma sencilla los servicios que tenemos en nuestro cluster de Apache Mesos, como sabemos por norma general cuando vamos desplegando Dockers en Apache Mesos estos empezaran a correr en slaves de Mesos aleatorios y en puertos aleatorios, esta dinámica hace complicado que podamos en trabajar con esos Dockers a no se que tengamos algún punto de entrada. Eso exactamente es lo que nos da marathon-lb.

¿Qué nos ofrece?

  1. Integración directa con Marathon, dependencia únicamente del Framework de Marathon
  2. Punto de entrada a los Dockers que tengamos corriendo en Apache Mesos
  3. Escalabilidad horizontal
  4. Actualizaciones de configuracion en tiempo real, conectando marathon-lb a Martahon usando SSE
  5. HealthChecks, tanto del propio HAProxy como a través de  Marathon
  6. TLS/SSL
  7. HaProxy Templates (la configuracion de HAProxy se realiza a través de labels de Docker, sublime)
  8. Imagen Docker para correrlo en Apache Mesos

¿Cómo funciona?

El funcionamiento en si es de lo más sencillo, marathon-lb “solo” es un script hecho en python que una vez conectado a Marathon  a través de SSE, y va actualizando la configuración del HAProxy que lleva en su interior, por cada aplicación que lancemos en Apache Mesos marathon-lb hará la asociación entre el punto de entrada que le hayamos dicho y el los Dockers con sus respectivos puertos.

La asociación entre HAProxy y los Dockers lo podemos hacer por dos métodos, puerto o usando un virtual host.

En el caso de querer una asociación por puerto, o sea que la url de acceso sea del tipo lo

http://marathon-lb.company.com:10001

Deberemos lanzar el Docker definiendo un puerto de servicio que será utilizado por marathon-lb como puerta de entrada, en este caso el 10001

Otro caso de uso es la asociación por virtual host, en este caso la url de entrada podría ser algo como.

http://miservicio.company.com

Para usar esta opción deberemos lanzar el Docker con la label

HAPROXY_{n}_VHOST

Por ejemplo

HAPROXY_VHOST = "http://miservicio.company.com" 

Antes de pasar a la parte practica decir que

Parte práctica

Objetivos

  • Tener un único punto de entrada que nos de acceso a todos los Dockers de un servicio que estén corriendo en nuestro Apache Mesos, en este ejemplo será un Nginx

Características de práctica

  1. Utilizaremos el acceso por Virtual Host
  2. Conectaremos marathon-lb a Marathon usando SSE
  3. Lanzaremos el docker de marathon-lb en modo Host.

¿Como lanzamos marathon-lb en Apache Mesos ?

Como es lógico primero de todo deberemos montar el JSON para poder lanzar marathon-lb en nuestro Apache Mesos a través del Framework Marathon, el JSON tiene una pinta tal que así y explicamos cada punto más abajo

Campos:

“cmd” :

  •  /marathon-lb/run sse : Ejecucion de marathon-lb en modo sse
  • –marathon: Url de nuestro Marathon
  • –group : Con esta opcion estamos diciendo cuales son los dockers que queremos que se usen a través de marathon-lb, en nuestro caso aquello que lleven HAPROXY_0_GROUP: “qa“.
  • –health-check:  Con esta opcion estamos diciendo que HAProxy no active el docker hasta que el healthcheck de Marathon este ok. 

 “ports”:

  • Por seguridad al ejecutar el Docker en modo HOST, deberemos asegurar que los puertos que utiliza nuestro HAProxy están disponibles en el Slave de Mesos, junto con “requirePorts” : true, añadiendo los puertos tendremos  en PORT0-1-2 que los utilizaremos para hacer el healtcheck de HAProxy

“constraints”:

  • Con estas dos constraints nos aseguramos que no vayamos a tener dos Marathon-lb en el mismo host (puede ser redundante con la opción de ports) y decidimos en que datacenter queremos lanzadlos.

 “healthchecks”:

  • Con este COMMAND comprobamos que el servicio este levantado y Marathon pueda darnos información del estado de HAProxy

Lanzamos marathon-lb

curl -X POST -H "Content-Type: application/json"  --data @marathon-lb-test.json 'http://marathon.company.com:8080/v2/apps'

Veremos en Marathon:

marathon-lb

¿Cómo lanzamos Nginx para que trabaje con Marathon-lb?

Ejemplo de JSON que deberíamos lanzar para que trabajara con marathon-lb, la explicación mas abajo.

Aquí la parte más importante, los labels que hemos añadido en el JSON, a través de labels en nuestro Docker haremos la configuración de nuestro HAProxy, tenemos una lista bastante extensa de opciones, en este ejemplo explicare los que me parecieron más comunes de utilizar.

Podemos encontrar la lista en el github de Mesosphere

“labels”

  • “HAPROXY_BACKEND_WEIGHT”: Peso que tendrá esta backend en HAProxy respecto a otros.
  • “HAPROXY_MODE”: http o tcp
  • “HAPROXY_BALANCE”: Tipo de balance
  • “HAPROXY_GROUP”:  Grupo al que pertenece, con esta opcion asociaremos nuestro nginx al HAProxy que queramos.
  • “HAPROXY_BACKEND_STICKY_OPTIONS” :  Una sticky cookie 
  • “HAPROXY_VHOST”:  Pues lo más importante a que fqdn nuestro HAPoxy nos dará acceso 

Lanzamos nginx

curl -X POST -H "Content-Type: application/json"  --data @nginx.json 'http://marathon.company.com:8080/v2/apps'

Veremos en Marathon:

Apache Mesos Nginx Marathon

Escalamos a 5 nginx

Apche Mesos Marathon scale

Si nos fijamos tenemos, como esperábamos cada nginx en un puerto diferente.

Si comprobamos HAProxy veremos en el log interno qué es lo que hizo.

marathon_lb: received event of type deployment_success
marathon_lb: received event of type health_status_changed_event
marathon_lb: fetching apps
marathon_lb: received event of type deployment_step_success
marathon_lb: GET http://marathon.company.com:8080/v2/apps?embed=apps.tasks
marathon_lb: got apps ['/test/ha/nginx/nginx', '/tools/docker/registry', '/prova-logs','/test/ha/marathon-lb-test']
marathon_lb: generating config
marathon_lb: HAProxy dir is /marathon-lb
marathon_lb: configuring app /test/ha/nginx/nginx
marathon_lb: frontend at *:10001 with backend test_ha_nginx_nginx_10001
marathon_lb: adding virtual host for app with hostname miguel.haproxy.com
marathon_lb: adding virtual host for app with id /test/ha/nginx/nginx
marathon_lb: backend server 192.168.1.100:50071 on server
marathon_lb: backend server 192.168.1.101:58437 on server
marathon_lb: backend server 192.168.1.102:12213 on server
marathon_lb: backend server 192.168.1.103:45197 on server
marathon_lb: backend server 192.168.1.104:14673 on server
marathon_lb: reading running config from /marathon-lb/haproxy.cfg
marathon_lb: updating tasks finished, took 0.24720096588134766 seconds

Podemos ver como marathon-lb

  1. Detecto un nuevo deploy
  2. Comprobó las aplicaciones y únicamente se quedo con las que cumplen la opción –group que explicamos mas arriba
  3. Creo el backend para nuestro ngnix y asigno el virtul host miguel.haproxy.com
  4. Añade todos los dockers que tenemos en nuestro mesos slave en
  5. Actualiza la configuracion
  6. Todo esto en 0.2472 segundos.

Si  comprobamos la pagina de stats de HAProxy podremos ver .

Apache Mesos HAPoxy

Por último comprobamos que nos contesta

curl -H "Host: miguel.haproxy.com" http://192.168.1.10
Welcome to nginx!
Thank you for using nginx.

Todo funcionando!