De los scripts a nuestra propia plataforma: cómo automatizamos el desarrollo en CIAN

De los scripts a nuestra propia plataforma: cómo automatizamos el desarrollo en CIAN

En RIT 2019, nuestro colega Alexander Korotkov hizo reportar sobre la automatización del desarrollo en CIAN: para simplificar la vida y el trabajo, utilizamos nuestra propia plataforma Integro. Realiza un seguimiento del ciclo de vida de las tareas, libera a los desarrolladores de operaciones rutinarias y reduce significativamente la cantidad de errores en producción. En esta publicación, complementaremos el informe de Alexander y le contaremos cómo pasamos de scripts simples a combinar productos de código abierto a través de nuestra propia plataforma y qué hace nuestro equipo de automatización independiente.
 

Nivel cero

“No existe el nivel cero, no lo sé”
Maestro Shifu de la película "Kung Fu Panda"

La automatización en CIAN comenzó 14 años después de la fundación de la empresa. En ese momento había 35 personas en el equipo de desarrollo. Difícil de creer, ¿verdad? Por supuesto, la automatización existía de alguna forma, pero en 2015 comenzó a tomar forma una dirección separada para la integración continua y la entrega de código. 

En ese momento, teníamos un enorme monolito de Python, C# y PHP, implementado en servidores Linux/Windows. Para implementar este monstruo, teníamos un conjunto de scripts que ejecutamos manualmente. También estuvo el montaje del monolito, que trajo dolor y sufrimiento debido a los conflictos al fusionar ramas, corregir defectos y reconstruir "con un conjunto diferente de tareas en la construcción". Un proceso simplificado se veía así:

De los scripts a nuestra propia plataforma: cómo automatizamos el desarrollo en CIAN

No estábamos contentos con esto y queríamos crear un proceso de compilación e implementación repetible, automatizado y manejable. Para ello necesitábamos un sistema CI/CD, y elegimos entre la versión gratuita de Teamcity y la versión gratuita de Jenkins, ya que trabajamos con ellas y ambas nos convenían en cuanto al conjunto de funciones. Elegimos Teamcity como producto más reciente. En ese momento, todavía no habíamos utilizado la arquitectura de microservicios y no esperábamos una gran cantidad de tareas y proyectos.

Llegamos a la idea de nuestro propio sistema.

La implementación de Teamcity eliminó solo una parte del trabajo manual: lo que queda es la creación de Pull Requests, la promoción de issues por estado en Jira y la selección de issues para su lanzamiento. El sistema Teamcity ya no podía hacer frente a esto. Era necesario elegir el camino de una mayor automatización. Consideramos opciones para trabajar con scripts en Teamcity o cambiar a sistemas de automatización de terceros. Pero al final decidimos que necesitábamos la máxima flexibilidad, que sólo nuestra propia solución puede ofrecer. Así surgió la primera versión del sistema de automatización interna denominado Integro.

Teamcity se ocupa de la automatización al nivel del lanzamiento de los procesos de construcción e implementación, mientras que Integro se centró en la automatización de alto nivel de los procesos de desarrollo. Era necesario combinar el trabajo con issues en Jira con el procesamiento del código fuente asociado en Bitbucket. En esta etapa, Integro comenzó a tener sus propios flujos de trabajo para trabajar con tareas de diferente tipo. 

Debido al aumento de la automatización en los procesos comerciales, la cantidad de proyectos y ejecuciones en Teamcity ha aumentado. Entonces surgió un nuevo problema: una instancia gratuita de Teamcity no era suficiente (3 agentes y 100 proyectos), agregamos otra instancia (3 agentes más y 100 proyectos) y luego otra. Como resultado, obtuvimos un sistema de varios clusters, que era difícil de gestionar:

De los scripts a nuestra propia plataforma: cómo automatizamos el desarrollo en CIAN

Cuando surgió la cuestión de una 4ª instancia, nos dimos cuenta de que no podíamos seguir viviendo así, porque los costes totales de mantener 4 instancias ya no estaban dentro de ningún límite. Surgió la pregunta sobre comprar Teamcity pago o elegir Jenkins gratis. Hicimos cálculos sobre instancias y planes de automatización y decidimos que viviríamos de Jenkins. Después de un par de semanas, cambiamos a Jenkins y eliminamos algunos de los dolores de cabeza asociados con el mantenimiento de múltiples instancias de Teamcity. Por lo tanto, pudimos concentrarnos en desarrollar Integro y personalizar Jenkins por nosotros mismos.

Con el crecimiento de la automatización básica (en forma de creación automática de Pull Requests, recopilación y publicación de cobertura de Código y otras comprobaciones), existe un fuerte deseo de abandonar en la medida de lo posible las publicaciones manuales y dejar este trabajo a los robots. Además, la empresa comenzó a pasar a microservicios dentro de la empresa, que requerían lanzamientos frecuentes y por separado unos de otros. Así es como llegamos gradualmente a los lanzamientos automáticos de nuestros microservicios (actualmente estamos lanzando el monolito manualmente debido a la complejidad del proceso). Pero, como suele ocurrir, surgió una nueva complejidad. 

Automatizamos pruebas

De los scripts a nuestra propia plataforma: cómo automatizamos el desarrollo en CIAN

Debido a la automatización de los lanzamientos, los procesos de desarrollo se han acelerado, en parte debido a que se saltaron algunas etapas de prueba. Y esto provocó una pérdida temporal de calidad. Suena trivial, pero junto con la aceleración de los lanzamientos, fue necesario cambiar la metodología de desarrollo de productos. Era necesario pensar en la automatización de las pruebas, inculcando la responsabilidad personal (aquí estamos hablando de "aceptar la idea en la cabeza", no de multas monetarias) del desarrollador por el código publicado y los errores que contiene, así como por la decisión de liberar/no liberar una tarea mediante la implementación automática. 

Eliminando los problemas de calidad, tomamos dos decisiones importantes: comenzamos a realizar pruebas canary e introdujimos el monitoreo automático del fondo de errores con una respuesta automática a su exceso. La primera solución permitió encontrar errores obvios antes de que el código se lanzara completamente a producción, la segunda redujo el tiempo de respuesta a problemas en producción. Los errores, por supuesto, ocurren, pero dedicamos la mayor parte de nuestro tiempo y esfuerzo no a corregirlos, sino a minimizarlos. 

Equipo de automatización

Actualmente contamos con una plantilla de 130 desarrolladores y continuamos crecer. El equipo de integración continua y entrega de código (en adelante, equipo de Implementación e Integración o DI) está formado por 7 personas y trabaja en 2 direcciones: desarrollo de la plataforma de automatización Integro y DevOps. 

DevOps es responsable del entorno Dev/Beta del sitio CIAN, el entorno Integro, ayuda a los desarrolladores a resolver problemas y desarrolla nuevos enfoques para escalar entornos. La dirección de desarrollo de Integro se ocupa tanto del propio Integro como de los servicios relacionados, por ejemplo, complementos para Jenkins, Jira, Confluence, y también desarrolla utilidades y aplicaciones auxiliares para los equipos de desarrollo. 

El equipo de DI trabaja en colaboración con el equipo de Plataforma, que desarrolla la arquitectura, las bibliotecas y los enfoques de desarrollo internamente. Al mismo tiempo, cualquier desarrollador dentro de CIAN puede contribuir a la automatización, por ejemplo, hacer que la microautomatización se adapte a las necesidades del equipo o compartir una idea interesante sobre cómo mejorar aún más la automatización.

Torta de capas de automatización en CIAN

De los scripts a nuestra propia plataforma: cómo automatizamos el desarrollo en CIAN

Todos los sistemas involucrados en la automatización se pueden dividir en varias capas:

  1. Sistemas externos (Jira, Bitbucket, etc.). Los equipos de desarrollo trabajan con ellos.
  2. Plataforma integral. La mayoría de las veces, los desarrolladores no trabajan con él directamente, pero es lo que mantiene en funcionamiento toda la automatización.
  3. Servicios de entrega, orquestación y descubrimiento (por ejemplo, Jeknins, Consul, Nomad). Con su ayuda, implementamos código en servidores y nos aseguramos de que los servicios funcionen entre sí.
  4. Capa física (servidores, SO, software relacionado). Nuestro código opera en este nivel. Puede ser un servidor físico o virtual (LXC, KVM, Docker).

Con base en este concepto, dividimos áreas de responsabilidad dentro del equipo DI. Los dos primeros niveles están en el área de responsabilidad de la dirección de desarrollo de Integro, y los dos últimos niveles ya están en el área de responsabilidad de DevOps. Esta separación nos permite centrarnos en las tareas y no interfiere en la interacción, ya que estamos cerca unos de otros e intercambiamos conocimientos y experiencias constantemente.

Integro

Centrémonos en Integro y comencemos con la pila de tecnología:

  • CentOs 7
  • Docker + Nómada + Cónsul + Bóveda
  • Java 11 (el antiguo monolito Integro permanecerá en Java 8)
  • Spring Boot 2.X + Configuración de Spring Cloud
  • PostgreSql 11
  • RabbitMQ 
  • apache encender
  • Camunda (incrustado)
  • Grafana + Grafito + Prometeo + Jaeger + ELK
  • Interfaz de usuario web: Reaccionar (CSR) + MobX
  • SSO: capa de teclas

Nos adherimos al principio de desarrollo de microservicios, aunque tenemos un legado en forma de monolito de una versión anterior de Integro. Cada microservicio se ejecuta en su propio contenedor Docker y los servicios se comunican entre sí mediante solicitudes HTTP y mensajes RabbitMQ. Los microservicios se encuentran a través de Consul y le realizan una solicitud, pasando la autorización a través de SSO (Keycloak, OAuth 2/OpenID Connect).

De los scripts a nuestra propia plataforma: cómo automatizamos el desarrollo en CIAN

Como ejemplo de la vida real, considere la interacción con Jenkins, que consta de los siguientes pasos:

  1. El microservicio de gestión del flujo de trabajo (en lo sucesivo, microservicio Flow) quiere ejecutar una compilación en Jenkins. Para hacer esto, utiliza Consul para encontrar la IP:PUERTO del microservicio para la integración con Jenkins (en lo sucesivo, microservicio de Jenkins) y le envía una solicitud asincrónica para iniciar la compilación en Jenkins.
  2. Después de recibir una solicitud, el microservicio Jenkins genera y responde con un ID de trabajo, que luego puede usarse para identificar el resultado del trabajo. Al mismo tiempo, activa la compilación en Jenkins mediante una llamada a la API REST.
  3. Jenkins realiza la compilación y, una vez completada, envía un webhook con los resultados de la ejecución al microservicio de Jenkins.
  4. El microservicio Jenkins, al recibir el webhook, genera un mensaje sobre la finalización del procesamiento de la solicitud y le adjunta los resultados de la ejecución. El mensaje generado se envía a la cola RabbitMQ.
  5. A través de RabbitMQ, el mensaje publicado llega al microservicio Flow, que aprende sobre el resultado del procesamiento de su tarea al hacer coincidir el ID del trabajo de la solicitud y el mensaje recibido.

Ahora tenemos unos 30 microservicios, que se pueden dividir en varios grupos:

  1. Gestión de configuración.
  2. Información e interacción con los usuarios (mensajería, correo).
  3. Trabajando con código fuente.
  4. Integración con herramientas de implementación (jenkins, nomad, consul, etc.).
  5. Seguimiento (liberaciones, errores, etc.).
  6. Utilidades web (UI para gestionar entornos de prueba, recopilación de estadísticas, etc.).
  7. Integración con rastreadores de tareas y sistemas similares.
  8. Gestión del flujo de trabajo para diferentes tareas.

Tareas de flujo de trabajo

Integro automatiza las actividades relacionadas con el ciclo de vida de la tarea. En términos simplificados, el ciclo de vida de una tarea se entenderá como el flujo de trabajo de una tarea en Jira. Nuestros procesos de desarrollo tienen varias variaciones de flujo de trabajo dependiendo del proyecto, el tipo de tarea y las opciones seleccionadas en una tarea en particular. 

Veamos el flujo de trabajo que utilizamos con más frecuencia:

De los scripts a nuestra propia plataforma: cómo automatizamos el desarrollo en CIAN

En el diagrama, el engranaje indica que la transición es llamada automáticamente por Integro, mientras que la figura humana indica que la transición es llamada manualmente por una persona. Veamos varios caminos que puede tomar una tarea en este flujo de trabajo.

Pruebas completamente manuales en DEV+BETA sin pruebas canary (normalmente así es como lanzamos un monolito):

De los scripts a nuestra propia plataforma: cómo automatizamos el desarrollo en CIAN

Puede haber otras combinaciones de transición. A veces, la ruta que seguirá un problema se puede seleccionar a través de opciones en Jira.

movimiento de tareas

Veamos los pasos principales que se realizan cuando una tarea avanza a través del flujo de trabajo "Pruebas DEV + Pruebas Canary":

1. El desarrollador o PM crea la tarea.

2. El desarrollador lleva la tarea a trabajar. Una vez completado, cambia al estado EN REVISIÓN.

3. Jira envía un Webhook al microservicio Jira (responsable de la integración con Jira).

4. El microservicio Jira envía una solicitud al servicio Flow (responsable de los flujos de trabajo internos en los que se realiza el trabajo) para iniciar el flujo de trabajo.

5. Dentro del servicio Flow:

  • A los revisores se les asigna la tarea (microservicio de usuarios que sabe todo sobre los usuarios + microservicio Jira).
  • A través del microservicio Source (conoce repositorios y sucursales, pero no trabaja con el código en sí), se realiza una búsqueda de repositorios que contengan una rama de nuestro número (para simplificar la búsqueda, el nombre de la rama coincide con el número número en Jira). La mayoría de las veces, una tarea tiene solo una rama en un repositorio; esto simplifica la administración de la cola de implementación y reduce la conectividad entre los repositorios.
  • Para cada rama encontrada, se realiza la siguiente secuencia de acciones:

    i) Actualización de la rama maestra (microservicio Git para trabajar con código).
    ii) El desarrollador bloquea la rama para que no pueda realizar cambios (microservicio Bitbucket).
    iii) Se crea una solicitud de extracción para esta rama (microservicio Bitbucket).
    iv) Se envía un mensaje sobre una nueva solicitud de extracción a los chats de desarrolladores (microservicio de notificación para trabajar con notificaciones).
    v) Las tareas de compilación, prueba e implementación se inician en DEV (microservicio Jenkins para trabajar con Jenkins).
    vi) Si todos los pasos anteriores se completan con éxito, Integro coloca su Aprobación en la Solicitud de extracción (microservicio Bitbucket).

  • Integro espera la aprobación en la solicitud de extracción de los revisores designados.
  • Tan pronto como se hayan recibido todas las aprobaciones necesarias (incluidas las pruebas automatizadas que hayan pasado positivamente), Integro transfiere la tarea al estado Test on Dev (microservicio Jira).

6. Los evaluadores prueban la tarea. Si no hay problemas, la tarea se transfiere al estado Listo para construir.

7. Integro "ve" que la tarea está lista para su lanzamiento y comienza su implementación en modo canary (microservicio Jenkins). La preparación para la liberación está determinada por un conjunto de reglas. Por ejemplo, la tarea está en el estado requerido, no hay bloqueos en otras tareas, actualmente no hay cargas activas de este microservicio, etc.

8. La tarea se transfiere al estado Canary (microservicio Jira).

9. Jenkins inicia una tarea de implementación a través de Nomad en modo canario (generalmente de 1 a 3 instancias) y notifica al servicio de monitoreo de lanzamiento (microservicio DeployWatch) sobre la implementación.

10. El microservicio DeployWatch recopila el fondo del error y reacciona ante él, si es necesario. Si se excede el fondo de error (la norma de fondo se calcula automáticamente), los desarrolladores reciben una notificación a través del microservicio Notify. Si después de 5 minutos el desarrollador no ha respondido (ha hecho clic en Revertir o Permanecer), se inicia una reversión automática de las instancias canary. Si no se excede el fondo, entonces el desarrollador debe iniciar manualmente la implementación de la tarea en Producción (haciendo clic en un botón en la interfaz de usuario). Si en 60 minutos el desarrollador no ha iniciado la implementación en producción, las instancias canary también se revertirán por razones de seguridad.

11. Después de iniciar la implementación en Producción:

  • La tarea se transfiere al estado de Producción (microservicio Jira).
  • El microservicio Jenkins inicia el proceso de implementación y notifica al microservicio DeployWatch sobre la implementación.
  • El microservicio DeployWatch comprueba que todos los contenedores en producción se hayan actualizado (hubo casos en los que no todos se actualizaron).
  • A través del microservicio Notify, se envía una notificación sobre los resultados del despliegue a Producción.

12. Los desarrolladores tendrán 30 minutos para comenzar a revertir una tarea desde Producción si se detecta un comportamiento incorrecto del microservicio. Después de este tiempo, la tarea se fusionará automáticamente con master (microservicio Git).

13. Después de una combinación exitosa con master, el estado de la tarea cambiará a Cerrado (microservicio Jira).

El diagrama no pretende ser completamente detallado (en realidad hay incluso más pasos), pero permite evaluar el grado de integración en los procesos. No consideramos que este esquema sea ideal y estamos mejorando los procesos de soporte de implementación y lanzamiento automático.

¿Qué sigue

Tenemos grandes planes para el desarrollo de la automatización, por ejemplo, eliminar las operaciones manuales durante los lanzamientos de monolitos, mejorar el monitoreo durante la implementación automática y mejorar la interacción con los desarrolladores.

Pero detengámonos aquí por ahora. En la revisión de la automatización cubrimos muchos temas de forma superficial, algunos no se abordaron en absoluto, por lo que estaremos encantados de responder sus preguntas. Estamos esperando sugerencias sobre qué cubrir en detalle, escriba en los comentarios.

Fuente: habr.com

Añadir un comentario