Desde scripts á nosa propia plataforma: como automatizamos o desenvolvemento en CIAN

Desde scripts á nosa propia plataforma: como automatizamos o desenvolvemento en CIAN

No RIT 2019, fixo o noso compañeiro Alexander Korotkov informe sobre a automatización do desenvolvemento en CIAN: para simplificar a vida e o traballo, usamos a nosa propia plataforma Integro. Rastrexa o ciclo de vida das tarefas, alivia aos desenvolvedores das operacións rutineiras e reduce significativamente o número de erros na produción. Nesta publicación, complementaremos o informe de Alexander e contarémosche como pasamos de simples scripts a combinar produtos de código aberto a través da nosa propia plataforma e o que fai o noso equipo de automatización separado.
 

Nivel cero

"Non existe un nivel cero, non o sei"
Mestre Shifu da película "Kung Fu Panda"

A automatización en CIAN comezou 14 anos despois da fundación da empresa. Nese momento había 35 persoas no equipo de desenvolvemento. Difícil de crer, non? Por suposto, a automatización existía dalgún xeito, pero en 2015 comezou a tomar forma unha dirección separada para a integración continua e a entrega de código. 

Nese momento, tiñamos un enorme monolito de Python, C# e PHP, despregado en servidores Linux/Windows. Para implementar este monstro, tiñamos un conxunto de scripts que executamos manualmente. Tamén houbo a montaxe do monolito, que trouxo dor e sufrimento debido aos conflitos á hora de fusionar ramas, corrixir defectos e reconstruír "cun conxunto diferente de tarefas na construción". Un proceso simplificado parecía o seguinte:

Desde scripts á nosa propia plataforma: como automatizamos o desenvolvemento en CIAN

Non estabamos satisfeitos con isto e queriamos crear un proceso de creación e implantación repetible, automatizado e manexable. Para iso necesitábamos un sistema CI/CD, e escollemos entre a versión gratuíta de Teamcity e a versión gratuíta de Jenkins, xa que traballamos con elas e ambas nos axeitaban no conxunto de funcións. Escollemos Teamcity como produto máis recente. Nese momento, aínda non utilizabamos a arquitectura de microservizos e non esperabamos un gran número de tarefas e proxectos.

Chegamos á idea do noso propio sistema

A implementación de Teamcity eliminou só parte do traballo manual: o que queda é a creación de Pull Requests, a promoción de problemas por estado en Jira e a selección de problemas para o seu lanzamento. O sistema Teamcity xa non puido facer fronte a isto. Era necesario elixir o camiño dunha maior automatización. Consideramos opcións para traballar con scripts en Teamcity ou cambiar a sistemas de automatización de terceiros. Pero ao final decidimos que necesitabamos a máxima flexibilidade, que só a nosa propia solución pode proporcionar. Así apareceu a primeira versión do sistema de automatización interna chamado Íntegro.

Teamcity ocúpase da automatización a nivel de lanzamento dos procesos de construción e despregue, mentres que Integro centrouse na automatización de primeiro nivel dos procesos de desenvolvemento. Era necesario combinar o traballo con problemas en Jira co procesamento do código fonte asociado en Bitbucket. Nesta fase, Íntegro comezou a ter os seus propios fluxos de traballo para traballar con tarefas de diferentes tipos. 

Debido ao aumento da automatización dos procesos empresariais, aumentou o número de proxectos e execucións en Teamcity. Entón veu un novo problema: unha instancia gratuíta de Teamcity non era suficiente (3 axentes e 100 proxectos), engadimos outra instancia (3 axentes máis e 100 proxectos), despois outra. Como resultado, acabamos cun sistema de varios clusters, que era difícil de xestionar:

Desde scripts á nosa propia plataforma: como automatizamos o desenvolvemento en CIAN

Cando xurdiu a cuestión dunha 4ª instancia, decatámonos de que non podíamos seguir vivindo así, porque os custos totais de soportar 4 instancias xa non estaban dentro de ningún límite. A pregunta xurdiu sobre a compra de Teamcity de pago ou a elección de Jenkins gratuíto. Fixemos cálculos sobre instancias e plans de automatización e decidimos que viviríamos en Jenkins. Despois dun par de semanas, cambiamos a Jenkins e eliminamos parte da dor de cabeza asociada ao mantemento de varias instancias de Teamcity. Polo tanto, puidemos centrarnos no desenvolvemento de Integro e en personalizar Jenkins para nós.

Co crecemento da automatización básica (en forma de creación automática de Pull Requests, recollida e publicación de cobertura de Código e outras comprobacións), hai un forte desexo de abandonar o máximo posible as versións manuais e dar este traballo aos robots. Ademais, a empresa comezou a pasar aos microservizos dentro da empresa, que requirían lanzamentos frecuentes e por separado. Así foi como chegamos aos lanzamentos automáticos dos nosos microservizos (actualmente estamos a lanzar o monolito manualmente debido á complexidade do proceso). Pero, como adoita suceder, xurdiu unha nova complexidade. 

Automatizamos as probas

Desde scripts á nosa propia plataforma: como automatizamos o desenvolvemento en CIAN

Debido á automatización dos lanzamentos, os procesos de desenvolvemento aceleráronse, en parte debido ao salto dalgunhas fases de proba. E isto levou a unha perda temporal de calidade. Parece trivial, pero xunto coa aceleración dos lanzamentos, foi necesario cambiar a metodoloxía de desenvolvemento do produto. Había que pensar na automatización das probas, inculcar a responsabilidade persoal (aquí estamos a falar de "aceptar a idea na cabeza", non multas monetarias) do programador polo código lanzado e os erros nel, así como a decisión de liberar/non liberar unha tarefa mediante a implementación automática. 

Eliminando os problemas de calidade, chegamos a dúas decisións importantes: comezamos a realizar probas canarias e introducimos un seguimento automático do fondo de erro cunha resposta automática ao seu exceso. A primeira solución permitiu atopar erros evidentes antes de que o código fose totalmente lanzado en produción, a segunda reduciu o tempo de resposta aos problemas na produción. Os erros, por suposto, ocorren, pero dedicamos a maior parte do noso tempo e esforzo non a corrixilos, senón a minimizalos. 

Equipo de automatización

Actualmente contamos cun persoal de 130 desenvolvedores, e seguimos medrar. O equipo de integración continua e entrega de código (en diante, equipo de Implementación e Integración ou DI) está formado por 7 persoas e traballa en 2 direccións: desenvolvemento da plataforma de automatización Integro e DevOps. 

DevOps é responsable do ambiente Dev/Beta do sitio CIAN, o ambiente Integro, axuda aos desenvolvedores a resolver problemas e desenvolve novos enfoques para escalar ambientes. A dirección de desenvolvemento de Integro trata tanto do propio Integro como dos servizos relacionados, por exemplo, complementos para Jenkins, Jira, Confluence, e tamén desenvolve utilidades e aplicacións auxiliares para equipos de desenvolvemento. 

O equipo de DI traballa en colaboración co equipo da Plataforma, que desenvolve a arquitectura, as bibliotecas e os enfoques de desenvolvemento internamente. Ao mesmo tempo, calquera desenvolvedor de CIAN pode contribuír á automatización, por exemplo, facer micro-automatización para adaptarse ás necesidades do equipo ou compartir unha idea xenial sobre como mellorar aínda máis a automatización.

Torta de capas de automatización no CIAN

Desde scripts á nosa propia plataforma: como automatizamos o desenvolvemento en CIAN

Todos os sistemas implicados na automatización pódense dividir en varias capas:

  1. Sistemas externos (Jira, Bitbucket, etc.). Os equipos de desenvolvemento traballan con eles.
  2. Plataforma Íntegro. Na maioría das veces, os desenvolvedores non traballan con el directamente, pero é o que fai que toda a automatización funcione.
  3. Servizos de entrega, orquestración e descubrimento (por exemplo, Jeknins, Consul, Nomad). Coa súa axuda, implementamos código nos servidores e garantimos que os servizos funcionen entre si.
  4. Capa física (servidores, SO, software relacionado). O noso código funciona neste nivel. Este pode ser un servidor físico ou virtual (LXC, KVM, Docker).

Partindo deste concepto, dividimos áreas de responsabilidade dentro do equipo de DI. Os dous primeiros niveis están na área de responsabilidade da dirección de desenvolvemento Íntegro, e os dous últimos niveis xa están na área de responsabilidade de DevOps. Esta separación permítenos centrarnos nas tarefas e non interfire na interacción, xa que estamos preto uns dos outros e intercambiamos constantemente coñecementos e experiencias.

Intacto

Centrémonos en Íntegro e comecemos coa pila tecnolóxica:

  • CentOS 7
  • Docker + Nomad + Cónsul + Bóveda
  • Java 11 (o antigo monolito Integro permanecerá en Java 8)
  • Spring Boot 2.X + Spring Cloud Config
  • PostgreSql 11
  • CoelloMQ 
  • Apache Ignite
  • Camunda (integrado)
  • Grafana + Grafito + Prometheus + Jaeger + ELK
  • IU web: React (CSR) + MobX
  • SSO: Keycloak

Cumprimos o principio de desenvolvemento de microservizos, aínda que temos un legado en forma de monólito dunha versión inicial de Integro. Cada microservizo execútase no seu propio contedor Docker e os servizos comunícanse entre si mediante solicitudes HTTP e mensaxes RabbitMQ. Os microservizos atópanse a través de Consul e fanlle unha solicitude, pasando a autorización a través de SSO (Keycloak, OAuth 2/OpenID Connect).

Desde scripts á nosa propia plataforma: como automatizamos o desenvolvemento en CIAN

Como exemplo da vida real, considere interactuar con Jenkins, que consta dos seguintes pasos:

  1. O microservizo de xestión de fluxos de traballo (en diante denominado microservizo Flow) quere executar unha compilación en Jenkins. Para iso, usa Consul para atopar o IP:PORT do microservizo para a integración con Jenkins (en diante, o microservizo Jenkins) e envíalle unha solicitude asíncrona para iniciar a compilación en Jenkins.
  2. Despois de recibir unha solicitude, o microservizo Jenkins xera e responde cun ID de traballo, que se pode usar para identificar o resultado do traballo. Ao mesmo tempo, desencadea a compilación en Jenkins mediante unha chamada á API REST.
  3. Jenkins realiza a compilación e, unha vez finalizada, envía un webhook cos resultados da execución ao microservizo Jenkins.
  4. O microservizo de Jenkins, despois de recibir o webhook, xera unha mensaxe sobre a finalización do procesamento de solicitudes e achega os resultados da execución. A mensaxe xerada envíase á cola RabbitMQ.
  5. A través de RabbitMQ, a mensaxe publicada chega ao microservizo Flow, que coñece o resultado do procesamento da súa tarefa facendo coincidir o ID do traballo da solicitude e a mensaxe recibida.

Agora temos uns 30 microservizos, que se poden dividir en varios grupos:

  1. Xestión da configuración.
  2. Información e interacción cos usuarios (mensaxeiros, correo).
  3. Traballar co código fonte.
  4. Integración con ferramentas de despregamento (jenkins, nómade, cónsul, etc.).
  5. Seguimento (lanzamentos, erros, etc.).
  6. Utilidades web (UI para xestionar contornas de proba, recoller estatísticas, etc.).
  7. Integración con rastreadores de tarefas e sistemas similares.
  8. Xestión de fluxos de traballo para diferentes tarefas.

Tarefas de fluxo de traballo

Íntegro automatiza actividades relacionadas co ciclo de vida da tarefa. En termos simplificados, o ciclo de vida dunha tarefa entenderase como o fluxo de traballo dunha tarefa en Jira. Os nosos procesos de desenvolvemento teñen varias variacións de fluxo de traballo dependendo do proxecto, do tipo de tarefa e das opcións seleccionadas nunha tarefa concreta. 

Vexamos o fluxo de traballo que usamos con máis frecuencia:

Desde scripts á nosa propia plataforma: como automatizamos o desenvolvemento en CIAN

No diagrama, a engrenaxe indica que a transición é chamada automaticamente por Integro, mentres que a figura humana indica que a transición é chamada manualmente por unha persoa. Vexamos varios camiños que pode tomar unha tarefa neste fluxo de traballo.

Probas completamente manuais en DEV+BETA sen probas canarias (normalmente é así como lanzamos un monólito):

Desde scripts á nosa propia plataforma: como automatizamos o desenvolvemento en CIAN

Pode haber outras combinacións de transición. Ás veces, o camiño que levará un problema pódese seleccionar mediante opcións en Jira.

Movemento de tarefas

Vexamos os pasos principais que se realizan cando unha tarefa pasa polo fluxo de traballo "Probas DEV + Probas Canarias":

1. O programador ou PM crea a tarefa.

2. O programador leva a tarefa a traballar. Despois de completar, cambia ao estado EN REVISIÓN.

3. Jira envía un Webhook ao microservizo Jira (responsable da integración con Jira).

4. O microservizo Jira envía unha solicitude ao servizo Flow (responsable dos fluxos de traballo internos nos que se realiza o traballo) para iniciar o fluxo de traballo.

5. Dentro do servizo Flow:

  • Os revisores son asignados á tarefa (microservizo de usuarios que sabe todo sobre os usuarios + microservizo Jira).
  • A través do microservizo Fonte (coñece de repositorios e ramas, pero non funciona co propio código), realízase unha busca de repositorios que conteñan unha rama do noso número (para simplificar a busca, o nome da rama coincide co problema). número en Jira). Na maioría das veces, unha tarefa só ten unha rama nun repositorio; isto simplifica a xestión da cola de despregamento e reduce a conectividade entre os depósitos.
  • Para cada rama atopada, realízase a seguinte secuencia de accións:

    i) Actualizando a rama mestra (microservicio Git para traballar con código).
    ii) A rama está bloqueada polos cambios polo programador (microservizo Bitbucket).
    iii) Créase unha solicitude de extracción para esta rama (microservizo Bitbucket).
    iv) Envíase unha mensaxe sobre unha nova solicitude de extracción aos chats de programadores (notificar ao microservizo para traballar con notificacións).
    v) As tarefas de construción, proba e implantación inícianse en DEV (microservicio Jenkins para traballar con Jenkins).
    vi) Se todos os pasos anteriores se completan con éxito, entón Integro coloca a súa aprobación na solicitude de extracción (microservizo Bitbucket).

  • Integro agarda a aprobación na solicitude de extracción dos revisores designados.
  • En canto se reciban todas as aprobacións necesarias (incluídas as probas automatizadas que pasaron positivamente), Integro transfire a tarefa ao estado Test on Dev (microservizo Jira).

6. Os probadores proban a tarefa. Se non hai problemas, a tarefa transfírese ao estado Listo para compilar.

7. Integro "ve" que a tarefa está lista para a súa publicación e comeza a súa implantación en modo canario (microservizo Jenkins). A preparación para o lanzamento está determinada por un conxunto de regras. Por exemplo, a tarefa está no estado requirido, non hai bloqueos noutras tarefas, actualmente non hai cargas activas deste microservizo, etc.

8. A tarefa transfírese ao estado Canario (microservizo Jira).

9. Jenkins lanza unha tarefa de implantación a través de Nomad en modo canario (normalmente 1-3 instancias) e notifica ao servizo de seguimento de lanzamento (microservizo DeployWatch) sobre a implantación.

10. O microservizo DeployWatch recolle o fondo do erro e reacciona ante el, se é necesario. Se se supera o fondo do erro (a norma de fondo calcúlase automaticamente), os desenvolvedores reciben unha notificación a través do microservizo Notificar. Se despois de 5 minutos o programador non respondeu (fai clic en Reverter ou Manter), lánzase unha recuperación automática das instancias canarias. Se non se supera o fondo, o programador debe iniciar manualmente a implementación da tarefa en Production (facendo clic nun botón da IU). Se nun prazo de 60 minutos o programador non iniciou a implementación en Produción, as instancias canarias tamén se revertirán por motivos de seguridade.

11. Despois de iniciar a implantación en Produción:

  • A tarefa transfírese ao estado de produción (microservizo Jira).
  • O microservizo de Jenkins inicia o proceso de implantación e notifica ao microservizo DeployWatch sobre a implantación.
  • O microservizo DeployWatch comproba que todos os contedores en produción se actualizaron (houbo casos nos que non se actualizaron todos).
  • A través do microservizo Notify, envíase a Production unha notificación sobre os resultados da implantación.

12. Os desenvolvedores terán 30 minutos para comezar a revertir unha tarefa desde Produción se se detecta un comportamento incorrecto do microservizo. Pasado este tempo, a tarefa fusionarase automaticamente en mestre (microservizo Git).

13. Despois dunha combinación exitosa co mestre, o estado da tarefa cambiarase a Pechado (microservizo Jira).

O diagrama non pretende ser completamente detallado (en realidade hai aínda máis pasos), pero permite avaliar o grao de integración nos procesos. Non consideramos este esquema ideal e estamos mellorando os procesos de soporte automático de liberación e despregamento.

Que hai a continuación

Temos grandes plans para o desenvolvemento da automatización, por exemplo, eliminar as operacións manuais durante os lanzamentos de monólitos, mellorar a supervisión durante a implantación automática e mellorar a interacción cos desenvolvedores.

Pero detémonos aquí por agora. Cubrimos moitos temas na revisión de automatización de forma superficial, algúns non se trataron en absoluto, polo que estaremos encantados de responder ás preguntas. Estamos á espera de suxestións sobre o que cubrir en detalle, escriba nos comentarios.

Fonte: www.habr.com

Engadir un comentario