Revertir un servicio a una versión anterior con Nomad

Escrito por picodotdev el .
gnu-linux planeta-codigo
Enlace permanente Comentarios

Los errores no se planifican, se producen de forma inesperada. Además, un error en un entorno de producción es normalmente urgente e importante lo que obliga a cambiar las prioridades del equipo dejando lo que están haciendo y ocuparse de resolver el problema. En ocasiones no será posible resolver el problema y la única solución es revertir la versión de la aplicación a la anterior. Dependiendo de la automatización de los procesos incluso el volver a la versión anterior quizá sea complicado. Los errores no se planifican pero si se puede planificar estar preparado para algunos errores, una forma de estar preparado ante errores es tener un proceso y herramientas para volver a la versión anterior rápido y fácilmente.

Nomad

HashiCorp

En el desarrollo de una aplicación se dedica un considerable esfuerzo a tener la mayor seguridad que los cambios no introducen errores, se realizan numerosas pruebas unitarias, pruebas de integración, si es una aplicación web pruebas funcionales simulando la interacción de un usuario. Todas estas pruebas se ejecutan además del equipo de cada desarrollador en un entorno de integración continua o CI en cada commit al repositorio de control de versiones del código fuente, el equipo de desarrollo recibe una notificación con inmediatez en caso de fallar alguna prueba. Aún se hacen mas pruebas algunas manuales en un entorno de staging que tiene una configuración similar al de producción. Finalmente, se despliega la versión validada en producción todavía haciendo más comprobaciones con estrategias de despliegue blue/green y canary, si no se ha descubierto ningún error se promociona la versión en todas las instancias.

Y aún con todo estos procesos de pruebas en ocasiones se descubren errores en producción. Si la causa del error se descubre rápido y es fácil de solucionar se opta por corregirlo, crear una nueva versión y desplegarla. Si el error es leve y no afecta a una funcionalidad importante se puede esperar a la siguiente versión para corregirlo. Pero algunos errores impactan en una funcionalidad vital para el negocio, tanto que obligan al equipo a dejar las tareas planificadas que están haciendo aún siendo también importantes para descubrir la causa del error y corregirlo, a veces la causa y la corrección se realiza rápido. En otras ocasiones la causa es difícil de determinar o la corrección es compleja, en estas ocasiones la opción adecuada es volver a la versión anterior buena conocida haciendo un revert o rollback. Se pierden las otras nuevas funcionalidades incorporadas en la misma versión pero se evita hacer cambios que introducen deuda técnica por la presión de dar solución rápido al error y se evita al equipo trabajar en un desagradable bajo presión.

En la operación de sistemas lo ideal es que todas las tareas estén automatizadas para evitar errores al realizar operaciones manuales y para evitar complejos procesos que impidan a cualquier miembro de equipo realizar la tarea por falta de conocimiento, complejidad o requerir formación. Dos de estas tareas a automatizar son el despliegue de una aplicación con una nueva versión y también el revertir a una versión anterior en caso de detectar que la nueva versión tiene algún tipo de error.

Nomad es un orquestador de servicios que en gran medida automatiza el despliegue de de las aplicaciones, su actualización a nuevas versiones y revertir a versiones anteriores.

Se inicia con el siguiente comando.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$ nomad agent -dev
==> No configuration files loaded
==> Starting Nomad agent...
==> Nomad agent configuration:

       Advertise Addrs: HTTP: 127.0.0.1:4646; RPC: 127.0.0.1:4647; Serf: 127.0.0.1:4648
            Bind Addrs: HTTP: 127.0.0.1:4646; RPC: 127.0.0.1:4647; Serf: 127.0.0.1:4648
                Client: true
             Log Level: DEBUG
                Region: global (DC: dc1)
                Server: true
               Version: 0.12.0

==> Nomad agent started! Log data will stream in below:

...
nomad-start.sh

Para ejecutar un servicio basta con enviar su definición en su archivo de configuración con un comando.

1
2
3
4
5
6
7
$ nomad run service-1.nomad
==> Monitoring evaluation "56421393"
    Evaluation triggered by job "service"
    Allocation "b51101cc" created: node "5721462c", group "service"
    Evaluation within deployment: "33154ac5"
    Evaluation status changed: "pending" -> "complete"
==> Evaluation "56421393" finished with status "complete"
nomad-run-1.sh

La definición del servicio en Nomad incluye la versión del artecfacto a desplegar. En este caso el servicio es simplemente un comando que emite una traza en la consola, a efectos didácticos no es relevante ya que sería lo mismo si fuera el comando de inicio de la aplicación Java que incluya la versión del jar o si el contenedor Docker estuviese versionado.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
job "service" {
  datacenters = ["dc1"]

  group "service" {

    task "service" {
      driver = "docker"

      config {
        image = "busybox:latest"
        command = "ash"
        args = [
          "-c",
          "while true; do echo \"Version 1\"; sleep 1; done"
        ]
      }
    }
  }
}
service-1.nomad

El servicio emite en la salida el mensaje.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ nomad job status service
...
Allocations
ID        Node ID   Task Group  Version  Desired  Status    Created     Modified
b51101cc  5721462c  service     0        stop     running   40m6s ago   38m31s ago
$ nomad alloc logs b51101cc
Version 1
Version 1
Version 1
Version 1
Version 1
Version 1
Version 1
Version 1
...
service-1-logs.sh

Al hacer cambios en el código de un servicio con nuevas características, correcciones de errores o mejoras de las funcionalidades existentes hay que generar una nueva versión del artefacto a desplegar, en el caso de una aplicación Java con Spring Boot puede ser un nuevo archivo jar con un nuevo nombre que incluye su versión, el artefacto de despligue es un contenedor Docker este también estará versionado. En la definición del servicio para Nomad hay que actualizar la referencia del artefacto a la nueva versión. El comando para realizar el despliegue es el mismo, basta con enviar la nueva definición del servicio a Nomad.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
job "service" {
  datacenters = ["dc1"]

  group "service" {

    task "service" {
      driver = "docker"

      config {
        image = "busybox:latest"
        command = "ash"
          args = [
            "-c",
            "while true; do echo \"Version 2 (error)\"; sleep 1; done"
        ]
      }
    }
  }
}
service-2.nomad

Como una nueva versión implica cambios en el código o la configuración del servicio es posible introducir nuevos errores que aún con todos los procesos de pruebas en diferentes entornos comentados sean descubiertos solo en el entorno de producción como es el típico NullPointerException o en este caso un mensaje de traza erróneo.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$ nomad job status service
...
Allocations
ID        Node ID   Task Group  Version  Desired  Status    Created     Modified
f7bd853e  5721462c  service     1        stop     running   38m33s ago  34m57s ago
b51101cc  5721462c  service     0        stop     complete  40m6s ago   38m31s ago
$ nomad alloc logs f7bd853e
Version 2 (error)
Version 2 (error)
Version 2 (error)
Version 2 (error)
Version 2 (error)
Version 2 (error)
Version 2 (error)
Version 2 (error)
...
service-2-logs.sh

Para la tarea de volver a una versión anterior el proceso con Nomad es igualmente sencillo, Nomad recuerda las definiciones anteriores enviadas con lo que solo se necesita utilizar el comando para restaurar la definición del servicio. Con los artefactos de despliegue versionados ya sea con contenedores Docker o con versiones en archivos jar las definiciones de los servicios contienen la referencia de los artefactos que utilizan.

1
2
3
4
5
6
7
$ nomad job revert service 0
==> Monitoring evaluation "493bcc52"
    Evaluation triggered by job "service"
    Evaluation within deployment: "e13303e3"
    Allocation "fc4fd574" created: node "5721462c", group "service"
    Evaluation status changed: "pending" -> "complete"
==> Evaluation "493bcc52" finished with status "complete"
nomad-revert.sh

El volver a la versión anterior puede hacerse desde la consola gráfica de administración de Nomad o desde la interfaz de línea de comandos introduciendo la definición anterior del servicio. Nomad se encarga de actualizar a la versión anterior de todas las instancias de forma progresiva utilizando estrategias de despliegue blue/green y canary con Nomad que haya del servicio de forma automatizada y el servicio vuelve a la normalidad.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$ nomad job status service
...
Allocations
ID        Node ID   Task Group  Version  Desired  Status    Created     Modified
fc4fd574  5721462c  service     2        run      running   34m58s ago  34m43s ago
f7bd853e  5721462c  service     1        stop     complete  38m33s ago  34m57s ago
b51101cc  5721462c  service     0        stop     complete  40m6s ago   38m31s ago
$ nomad alloc logs fc4fd574
Version 1
Version 1
Version 1
Version 1
Version 1
Version 1
Version 1
Version 1
...
service-3-logs.sh

Desde la consola web de administración también se ven las definiciones de los jobs y sus versiones con sus diferencias.

Versión 1 del servicio Versión 2 del servicio Versiones del servicio

Versiones del servicio
Comparte el artículo: