Des dels scripts fins a la nostra pròpia plataforma: com vam automatitzar el desenvolupament a CIAN

Des dels scripts fins a la nostra pròpia plataforma: com vam automatitzar el desenvolupament a CIAN

A RIT 2019, va fer el nostre company Alexander Korotkov informe sobre l'automatització del desenvolupament a CIAN: per simplificar la vida i el treball, utilitzem la nostra pròpia plataforma Integro. Fa un seguiment del cicle de vida de les tasques, alleuja els desenvolupadors de les operacions rutinàries i redueix significativament el nombre d'errors en producció. En aquesta publicació, complementarem l'informe d'Alexander i us explicarem com vam passar dels scripts simples a la combinació de productes de codi obert mitjançant la nostra pròpia plataforma i què fa el nostre equip d'automatització independent.
 

Nivell zero

"No existeix un nivell zero, no ho sé"
El mestre Shifu de la pel·lícula "Kung Fu Panda"

L'automatització a CIAN va començar 14 anys després de la fundació de l'empresa. En aquell moment hi havia 35 persones a l'equip de desenvolupament. Difícil de creure, oi? Per descomptat, l'automatització existia d'alguna forma, però el 2015 va començar a prendre forma una direcció separada per a la integració contínua i el lliurament de codi. 

En aquell moment, teníem un enorme monòlit de Python, C# i PHP, desplegat als servidors Linux/Windows. Per desplegar aquest monstre, teníem un conjunt de scripts que vam executar manualment. També va haver-hi el muntatge del monòlit, que va comportar dolor i patiment a causa dels conflictes a l'hora de fusionar branques, corregir defectes i reconstruir "amb un conjunt diferent de tasques a la construcció". Un procés simplificat semblava a aquest:

Des dels scripts fins a la nostra pròpia plataforma: com vam automatitzar el desenvolupament a CIAN

No estàvem contents amb això i volíem crear un procés de creació i desplegament repetible, automatitzat i manejable. Per a això, necessitàvem un sistema CI/CD, i vam triar entre la versió gratuïta de Teamcity i la versió gratuïta de Jenkins, ja que vam treballar amb ells i tots dos ens van adaptar pel que fa al conjunt de funcions. Hem escollit Teamcity com a producte més recent. En aquell moment, encara no havíem utilitzat l'arquitectura de microserveis i no esperàvem un gran nombre de tasques i projectes.

Arribem a la idea del nostre propi sistema

La implementació de Teamcity va eliminar només una part del treball manual: el que queda és la creació de Pull Requests, la promoció de problemes per estat a Jira i la selecció de problemes per al seu llançament. El sistema Teamcity ja no podia fer front a això. Calia escollir el camí d'una automatització posterior. Hem considerat opcions per treballar amb scripts a Teamcity o canviar a sistemes d'automatització de tercers. Però al final vam decidir que necessitàvem la màxima flexibilitat, que només la nostra pròpia solució pot proporcionar. Així va aparèixer la primera versió del sistema d'automatització interna anomenat Integro.

Teamcity s'ocupa de l'automatització a nivell de llançament dels processos de creació i desplegament, mentre que Integro es va centrar en l'automatització de primer nivell dels processos de desenvolupament. Va ser necessari combinar el treball amb problemes a Jira amb el processament del codi font associat a Bitbucket. En aquesta etapa, Integro va començar a tenir els seus propis fluxos de treball per treballar amb tasques de diferents tipus. 

A causa de l'augment de l'automatització dels processos empresarials, el nombre de projectes i execucions a Teamcity ha augmentat. Així que va sorgir un nou problema: una instància gratuïta de Teamcity no era suficient (3 agents i 100 projectes), vam afegir una altra instància (3 agents més i 100 projectes), després una altra. Com a resultat, vam acabar amb un sistema de diversos clústers, que era difícil de gestionar:

Des dels scripts fins a la nostra pròpia plataforma: com vam automatitzar el desenvolupament a CIAN

Quan va sorgir la qüestió d'una quarta instància, ens vam adonar que no podíem seguir vivint així, perquè els costos totals de suportar 4 instàncies ja no estaven dins de cap límit. Va sorgir la pregunta sobre comprar Teamcity de pagament o triar Jenkins gratuït. Vam fer càlculs sobre instàncies i plans d'automatització i vam decidir que viurem a Jenkins. Al cap d'un parell de setmanes, vam canviar a Jenkins i vam eliminar part del mal de cap associat amb el manteniment de múltiples instàncies de Teamcity. Per tant, vam poder centrar-nos a desenvolupar Integro i personalitzar Jenkins per nosaltres mateixos.

Amb el creixement de l'automatització bàsica (en forma de creació automàtica de Pull Requests, recollida i publicació de la cobertura del codi i altres comprovacions), hi ha una gran voluntat d'abandonar tant com sigui possible els llançaments manuals i donar aquest treball als robots. A més, l'empresa va començar a passar als microserveis dins de l'empresa, que requerien llançaments freqüents, i per separat els uns dels altres. Així és com a poc a poc vam arribar als llançaments automàtics dels nostres microserveis (actualment estem alliberant el monòlit manualment a causa de la complexitat del procés). Però, com sol passar, va sorgir una nova complexitat. 

Automatitzem les proves

Des dels scripts fins a la nostra pròpia plataforma: com vam automatitzar el desenvolupament a CIAN

A causa de l'automatització dels llançaments, els processos de desenvolupament s'han accelerat, en part a causa de l'omissió d'algunes etapes de proves. I això va provocar una pèrdua temporal de qualitat. Sembla trivial, però juntament amb l'acceleració dels llançaments, va ser necessari canviar la metodologia de desenvolupament del producte. Calia pensar en l'automatització de les proves, inculcar la responsabilitat personal (aquí estem parlant d'"acceptar la idea al cap", no les multes monetàries) del desenvolupador pel codi publicat i els errors que hi ha, així com la decisió de alliberar/no alliberar una tasca mitjançant el desplegament automàtic. 

Eliminant els problemes de qualitat, vam prendre dues decisions importants: vam començar a realitzar proves canàries i vam introduir un seguiment automàtic del fons d'error amb una resposta automàtica al seu excés. La primera solució va permetre trobar errors evidents abans que el codi fos completament llançat en producció, la segona va reduir el temps de resposta als problemes en producció. Els errors, és clar, passen, però dediquem la major part del nostre temps i esforç no a corregir-los, sinó a minimitzar-los. 

Equip d'automatització

Actualment tenim una plantilla de 130 desenvolupadors, i continuem créixer. L'equip d'integració contínua i lliurament de codi (d'ara endavant, equip de Deploy and Integration o DI) està format per 7 persones i treballa en 2 direccions: desenvolupament de la plataforma d'automatització Integro i DevOps. 

DevOps és responsable de l'entorn Dev/Beta del lloc CIAN, l'entorn Integro, ajuda els desenvolupadors a resoldre problemes i desenvolupa nous enfocaments per escalar entorns. La direcció de desenvolupament d'Integro s'ocupa tant del mateix Integro com dels serveis relacionats, per exemple, connectors per a Jenkins, Jira, Confluence, i també desenvolupa utilitats i aplicacions auxiliars per als equips de desenvolupament. 

L'equip de DI treballa en col·laboració amb l'equip de la Plataforma, que desenvolupa l'arquitectura, les biblioteques i els enfocaments de desenvolupament internament. Al mateix temps, qualsevol desenvolupador de CIAN pot contribuir a l'automatització, per exemple, fer que la microautomatització s'adapti a les necessitats de l'equip o compartir una idea fantàstica sobre com millorar encara més l'automatització.

Pastís de capes d'automatització al CIAN

Des dels scripts fins a la nostra pròpia plataforma: com vam automatitzar el desenvolupament a CIAN

Tots els sistemes implicats en l'automatització es poden dividir en diverses capes:

  1. Sistemes externs (Jira, Bitbucket, etc.). Els equips de desenvolupament treballen amb ells.
  2. Plataforma Integro. Molt sovint, els desenvolupadors no hi treballen directament, però és el que manté tota l'automatització en funcionament.
  3. Serveis de lliurament, orquestració i descobriment (per exemple, Jeknins, Cònsol, Nomad). Amb la seva ajuda, despleguem codi als servidors i ens assegurem que els serveis funcionin entre si.
  4. Capa física (servidors, sistema operatiu, programari relacionat). El nostre codi opera en aquest nivell. Pot ser un servidor físic o virtual (LXC, KVM, Docker).

A partir d'aquest concepte, dividim les àrees de responsabilitat dins de l'equip de DI. Els dos primers nivells es troben a l'àrea de responsabilitat de la direcció de desenvolupament Integro, i els dos últims nivells ja són a l'àrea de responsabilitat de DevOps. Aquesta separació ens permet centrar-nos en les tasques i no interfereix en la interacció, ja que estem a prop els uns dels altres i intercanviem constantment coneixements i experiències.

Integro

Centrem-nos a Integro i comencem amb la pila de tecnologia:

  • CentOS 7
  • Docker + Nomad + Cònsol + Volta
  • Java 11 (l'antic monòlit Integro romandrà a Java 8)
  • Spring Boot 2.X + Spring Cloud Config
  • PostgreSql 11
  • ConillMQ 
  • Apache Ignite
  • Camunda (incrustat)
  • Grafana + Grafit + Prometeu + Jaeger + ELK
  • Interfície d'usuari web: React (CSR) + MobX
  • SSO: Keycloak

Ens adherim al principi del desenvolupament de microserveis, tot i que tenim un llegat en forma de monòlit d'una versió primerenca d'Integro. Cada microservei s'executa al seu propi contenidor Docker i els serveis es comuniquen entre ells mitjançant sol·licituds HTTP i missatges RabbitMQ. Els microserveis es troben entre ells a través de Consul i li fan una sol·licitud, passant autorització mitjançant SSO (Keycloak, OAuth 2/OpenID Connect).

Des dels scripts fins a la nostra pròpia plataforma: com vam automatitzar el desenvolupament a CIAN

Com a exemple de la vida real, considereu la possibilitat d'interaccionar amb Jenkins, que consta dels passos següents:

  1. El microservei de gestió del flux de treball (d'ara endavant anomenat microservei Flow) vol executar una compilació a Jenkins. Per fer-ho, utilitza Consul per trobar l'IP:PORT del microservei per a la integració amb Jenkins (d'ara endavant anomenat microservei Jenkins) i li envia una sol·licitud asíncrona per iniciar la compilació a Jenkins.
  2. Després de rebre una sol·licitud, el microservei Jenkins genera i respon amb un ID de treball, que es pot utilitzar per identificar el resultat del treball. Al mateix temps, activa la compilació a Jenkins mitjançant una trucada a l'API REST.
  3. Jenkins realitza la compilació i, un cop finalitzada, envia un webhook amb els resultats de l'execució al microservei Jenkins.
  4. El microservei Jenkins, després d'haver rebut el webhook, genera un missatge sobre la finalització del processament de la sol·licitud i hi adjunta els resultats de l'execució. El missatge generat s'envia a la cua de RabbitMQ.
  5. A través de RabbitMQ, el missatge publicat arriba al microservei Flow, que coneix el resultat del processament de la seva tasca fent coincidir l'ID de la feina de la sol·licitud i el missatge rebut.

Ara tenim uns 30 microserveis, que es poden dividir en diversos grups:

  1. Gestió de la configuració.
  2. Informació i interacció amb els usuaris (missatgers, correu).
  3. Treballant amb codi font.
  4. Integració amb eines de desplegament (jenkins, nòmada, cònsol, etc.).
  5. Seguiment (alliberaments, errors, etc.).
  6. Utilitats web (IU per gestionar entorns de prova, recopilar estadístiques, etc.).
  7. Integració amb rastrejadors de tasques i sistemes similars.
  8. Gestió del flux de treball per a diferents tasques.

Tasques de flux de treball

Integro automatitza activitats relacionades amb el cicle de vida de la tasca. En termes simplificats, el cicle de vida d'una tasca s'entendrà com el flux de treball d'una tasca a Jira. Els nostres processos de desenvolupament tenen diverses variacions de flux de treball segons el projecte, el tipus de tasca i les opcions seleccionades en una tasca concreta. 

Vegem el flux de treball que fem servir més sovint:

Des dels scripts fins a la nostra pròpia plataforma: com vam automatitzar el desenvolupament a CIAN

Al diagrama, l'engranatge indica que Integro anomena automàticament la transició, mentre que la figura humana indica que la transició la crida manualment una persona. Vegem diversos camins que pot prendre una tasca en aquest flux de treball.

Proves completament manuals a DEV+BETA sense proves canàries (normalment així és com alliberem un monòlit):

Des dels scripts fins a la nostra pròpia plataforma: com vam automatitzar el desenvolupament a CIAN

Pot haver-hi altres combinacions de transició. De vegades, el camí que prendrà un problema es pot seleccionar mitjançant les opcions de Jira.

Moviment de la tasca

Vegem els passos principals que es realitzen quan una tasca es mou pel flux de treball "Proves DEV + Proves Canàries":

1. El desenvolupador o PM crea la tasca.

2. El desenvolupador porta la tasca a treballar. Un cop finalitzat, canvia a l'estat EN REVISIÓ.

3. Jira envia un Webhook al microservei Jira (responsable de la integració amb Jira).

4. El microservei Jira envia una sol·licitud al servei Flow (responsable dels fluxos de treball interns en què es treballa) per iniciar el flux de treball.

5. Dins del servei Flow:

  • Els revisors s'assignen a la tasca (microservei d'usuaris que ho sap tot sobre els usuaris + microservei Jira).
  • Mitjançant el microservei Source (coneix repositoris i branques, però no funciona amb el codi en si), es fa una cerca de repositoris que continguin una branca del nostre tema (per simplificar la cerca, el nom de la branca coincideix amb el tema). número a Jira). Molt sovint, una tasca només té una branca en un dipòsit; això simplifica la gestió de la cua de desplegament i redueix la connectivitat entre dipòsits.
  • Per a cada branca trobada, es realitza la següent seqüència d'accions:

    i) Actualització de la branca mestra (microservei Git per treballar amb codi).
    ii) El desenvolupador bloqueja la branca dels canvis (microservei Bitbucket).
    iii) Es crea una sol·licitud d'extracció per a aquesta branca (microservei Bitbucket).
    iv) S'envia un missatge sobre una nova sol·licitud d'extracció als xats de desenvolupadors (notifiqui el microservei per treballar amb notificacions).
    v) Les tasques de creació, prova i desplegament s'inicien a DEV (microservei Jenkins per treballar amb Jenkins).
    vi) Si tots els passos anteriors es completen amb èxit, Integro posa la seva Aprovació a la sol·licitud d'extracció (microservei Bitbucket).

  • Integro espera l'aprovació a la sol·licitud d'extracció dels revisors designats.
  • Tan bon punt s'hagin rebut totes les aprovacions necessàries (incloses les proves automatitzades han passat positivament), Integro transfereix la tasca a l'estat Prova en desenvolupament (microservei Jira).

6. Els verificadors posen a prova la tasca. Si no hi ha problemes, la tasca es transfereix a l'estat A punt per a la creació.

7. Integro "veu" que la tasca està preparada per al seu llançament i comença el seu desplegament en mode canari (microservei Jenkins). La preparació per al llançament està determinada per un conjunt de regles. Per exemple, la tasca es troba en l'estat requerit, no hi ha cap bloqueig en altres tasques, actualment no hi ha càrregues actives d'aquest microservei, etc.

8. La tasca es transfereix a l'estat canari (microservei Jira).

9. Jenkins llança una tasca de desplegament mitjançant Nomad en mode canari (normalment 1-3 instàncies) i notifica al servei de supervisió de llançaments (microservei DeployWatch) sobre el desplegament.

10. El microservei DeployWatch recull el fons d'error i hi reacciona, si cal. Si es supera el fons d'error (la norma de fons es calcula automàticament), els desenvolupadors reben una notificació mitjançant el microservei Notify. Si al cap de 5 minuts el desenvolupador no ha respost (ha fet clic a Revertir o a quedar-se), s'iniciarà una recuperació automàtica de les instàncies canàries. Si no es supera el fons, el desenvolupador ha d'iniciar manualment el desplegament de la tasca a Producció (fent clic a un botó de la interfície d'usuari). Si en un termini de 60 minuts el desenvolupador no ha llançat el desplegament a Production, les instàncies canàries també es tornaran enrere per motius de seguretat.

11. Després d'iniciar el desplegament a Producció:

  • La tasca es transfereix a l'estat de producció (microservei Jira).
  • El microservei Jenkins inicia el procés de desplegament i notifica al microservei DeployWatch sobre el desplegament.
  • El microservei DeployWatch comprova que tots els contenidors de producció s'han actualitzat (hi va haver casos en què no s'havien actualitzat tots).
  • Mitjançant el microservei Notify, s'envia una notificació sobre els resultats del desplegament a Production.

12. Els desenvolupadors tindran 30 minuts per començar a revertir una tasca des de Producció si es detecta un comportament incorrecte del microservei. Passat aquest temps, la tasca es fusionarà automàticament amb el mestre (microservei Git).

13. Després d'una fusió correcta amb el mestre, l'estat de la tasca es canviarà a Tancat (microservei Jira).

El diagrama no pretén ser completament detallat (en realitat encara hi ha més passos), però permet valorar el grau d'integració en els processos. No considerem aquest esquema ideal i estem millorant els processos de llançament automàtic i suport al desplegament.

Què és el següent

Tenim grans plans per al desenvolupament de l'automatització, per exemple, eliminar les operacions manuals durant els llançaments de monòlits, millorar la supervisió durant el desplegament automàtic i millorar la interacció amb els desenvolupadors.

Però aturem-nos aquí de moment. Vam tractar molts temes a la revisió d'automatització de manera superficial, alguns no es van tocar en absolut, així que estarem encantats de respondre les preguntes. Estem esperant suggeriments sobre què tractar en detall, escriviu als comentaris.

Font: www.habr.com

Afegeix comentari