Continuous Delivery en profundidad: problemas y soluciones alternativas comunes de los pipelines de Jenkins

Continuous Delivery en profundidad: problemas y soluciones alternativas comunes de los pipelines de Jenkins
Sin comentarios Facebook Twitter Flipboard E-mail

Articulo original publicado en el blog de Stratio.

En esta parte nos ponemos el gorro del mítico Pitfall Harry para echarle un vistazo a algunos de los problemas con los que nos hemos cruzado y el impacto que han tenido en nuestro trabajo diario. También incluimos algunos consejos sobre cómo solventar estos problemas.

Lo primero es lo primero: los pipelines de Jenkins son una gran mejora en cuanto a las funcionalidades básicas de Jenkins y nos permiten montar de forma sencilla flujos completos de continuous delivery con atributos con mucha capacidad de reutilización y mantenimiento.

En este artículo vamos a echar un vistazo a algunos de los defectos, escollos y limitaciones de nuestros (geniales) pipelines de Jenkins, estableciendo algunas de las soluciones alternativas para esquivarlos.

Timeout!

Realizar un timeout es lo mejor que podemos hacer cuando una ejecución se cuelga. Si la ejecución dura de media X minutos, X*3 significa, en prácticamente el 100% de los casos, que se ha colgado. A nadie le gusta tener a zombies pululando por casa, así que mejor deshacernos de ellos.

Sin embargo (y esto va a cambiar) los timeouts no pondrán freno al código incluido en los pasos withEnv o withCredentials. Existe un problema, para el que se está buscando actualmente una solución, con una PR de github.

Debido a que el 90% de nuestros métodos se ejecutan incluidos en elementos withX, nos estamos ocupando de este problema, forzándonos a abortar las ejecuciones de forma manual.

Solución alternativa: Espera y cruza los dedos. Se pueden realizar acciones manuales en caso necesario.

NonSerializableException

Jenkins serializa objetos en diferentes puntos para que estén listos a la hora de sobrevivir a reinicios o a fallos en el nodo. Esta es un función muy interesante para afrontar la pérdida temporal del servicio y los reinicios. La serialización en el disco permite remitir el proceso cuando es necesario reiniciar algunas de las acciones.

El plugin de checkpoints probablemente utilizará está serialización por su propio bien. Cada vez que exista un objeto que no sea pueda liberar ni serializar y Jenkins intente serializarlo… ¡BOOM! De repente NonSerializableException.

Saltará NonSerializableException y se detendrá la ejecución ¿Un impacto bastante grande, verdad?

Solución alternativa: Presta atención a cualquier iterador, matcher, jsonsluper o cualquier otra clase que no esté implementando el uso de la interfaz serializable de java. Es difícil deducir con qué clase estás trabajando, porque Groovy puede no tener tipo o incluso las clases internas pueden ser desconocidas (JsonSlurper utiliza un LazyMap de forma interna y no un serializable), hacer una prueba te echará una mano.

La anotación @NonCPS es útil para superar este problema, puesto que no se realizará una serialización en el método con anotación. No es una solución al 100 % porque no puedes invocar pasos de pipeline dentro de dichas soluciones.

Pruebas

Necesitamos hacer pruebas para asegurarnos de que la función implementada está escrita de forma correcta. Podemos tratar los casos concretos y no saltará ningún NonSerializableException. Sin embargo, y por mucho que nos pese, no hay ningún arnés de pruebas para los pipelines complejos.

Sin pruebas no hay diversión. Para asegurarnos de que nuestro código es estable, es obligatorio probarlo.

Solución alternativa: Hay que hacer las pruebas de forma manual usando la consola de script de Jenkins, trabajos auxiliares o volviendo a ejecutar trabajos. Otra opción es limitar el margen de error posible y dejar que la ejecución continúe.

Manazas

Las bibliotecas de pipeline las escriben personas y como no hay forma de montar el pipeline de manera local, un código incorrecto podría acabar en la herramienta SCM. El código mal escrito supondrá problemas a la hora de analizarlo, por lo que las ejecuciones afectadas no continuarán.

Solución alternativa: Cómete tus propios marrones. PRs para cada función con su propio pipeline. Hacer análisis estáticos de código y revisar el código te ayudarán.

Pérdida de conexión con el nodo

¿Te acuerdas del error NonSerializableException? Se suponía que era para permitir a jenkins sobrevivir a un reinicio o a reconexiones del nodo, pero surge un problema cuando el agente está en la nube. Dichos agentes tendrán un nombre variable (normalmente con una encriptación autogenerada) que lo identifique de forma única. Esto hace que un trabajo no pueda cambiar a otro agente, incluso si el primero ya no esté en activo.

Nuestro entorno está compilado sobre un enjambre de docker, por lo que es algo que pasa bastante a menudo.

Solución alternativa: Acéptalo o prueba con un cambio en el plugin del docker. En Cloudbees han resuelto este problema en su Jenkins Operations Center.

Evaluación final

Como código, los pipelines están muy bien (¿acaso no lo he dicho ya?). Nos permiten implementar estructuras completas como, por ejemplo, un mapa <String, GString> donde cada GString está compuesto por varias GStrings bastante descuidadas (¿alguien ha dicho Inception?), donde cada una a su vez tiene otra variable bastante descuidada (o lo que es lo mismo, otra GString). Esta estructura, que probablemente viene de un problema CPS, nos lleva a la peor forma de evaluación de GString. Los Gstrings son objetos con dos matrices: strings y valores. Estas matrices se combinan para una llamada o para invocar toString, lo que resulta en un objeto eat String (?).

Probablemente sea el problema más difícil al que nos hemos enfrentado y casi hace que tengamos que interrumpir nuestro proyecto. Las estructuras de datos que necesitábamos sufrieron mucho por el tema de los descuidos y otros GStrings.

Solución alternativa: La primera idea sería hacer un NonCPS en el código afectado: ¡FALSO! El string resultante también era incorrecto, cortado en el primer elemento GString (de la matriz de los strings). Finalmente acabamos analizando un objeto con clase GStringImpl y volviendo a poner en su lugar correcto las matrices de strings y valores. Esto se conoce como el famoso método gStringyHackifier.

Estos son algunos de los problemas más importantes con los que hemos tenido que lidiar. Los compartimos con vosotros a modo de referencia por si os encontráis con situaciones parecidas y os sirven a la hora de trabajar con pipelines.

Javier Delgado es evangelista no oficial del uso de Jenkins para tareas continuas (inspection, testing, delivery), apasionado de la automatización y orador en diferentes conferencias sobre estas materias. Ingeniero informático y fiel seguidor del aprendizaje continuo, actualmente trabaja como ingeniero DevOps en Stratio, compañía de big data española-americana pionera en ofrecer a grandes empresas una transformación digital completa en torno a sus datos a través de un único producto.

Comentarios cerrados
Inicio