Que sabemos dos microservizos

Ola! Chámome Vadim Madison, dirixo o desenvolvemento da plataforma do sistema Avito. Xa se dixo máis dunha vez como nós na empresa estamos pasando dunha arquitectura monolítica a unha de microservizos. É hora de compartir como transformamos a nosa infraestrutura para sacar o máximo proveito dos microservizos e evitar que nos perdamos neles. Como nos axuda PaaS aquí, como simplificamos a implantación e reducimos a creación dun microservizo a un só clic - segue lendo. Non todo o que escribo a continuación está totalmente implementado en Avito, parte é como desenvolvemos a nosa plataforma.

(E ao final deste artigo, falarei sobre a oportunidade de asistir a un seminario de tres días do experto en arquitectura de microservizos Chris Richardson).

Que sabemos dos microservizos

Como chegamos aos microservizos

Avito é un dos sitios clasificados máis grandes do mundo; publícanse máis de 15 millóns de novos anuncios ao día. O noso backend acepta máis de 20 mil solicitudes por segundo. Actualmente temos varios centos de microservizos.

Levamos varios anos construíndo unha arquitectura de microservizos. Como exactamente - os nosos compañeiros en detalle contou na nosa sección en RIT++ 2017. No CodeFest 2017 (ver. video), Sergey Orlov e Mikhail Prokopchuk explicaron en detalle por que necesitabamos a transición aos microservizos e que papel xogou aquí Kubernetes. Ben, agora estamos facendo todo para minimizar os custos de escalado que son inherentes a tal arquitectura.

Inicialmente, non creamos un ecosistema que nos axudara a desenvolver e lanzar microservizos de forma integral. Simplemente recolleron solucións sensatas de código aberto, lanzáronas na casa e invitaron ao programador a tratar con elas. Como resultado, foi a unha ducia de lugares (paneleiros, servizos internos), despois de que se fixo máis forte no seu desexo de cortar o código á antiga, nun monólito. A cor verde dos diagramas a continuación indica o que o desenvolvedor fai dunha maneira ou doutra coas súas propias mans, e a cor amarela indica automatización.

Que sabemos dos microservizos

Agora, na utilidade PaaS CLI, créase un novo servizo cun comando e engádese unha nova base de datos con dous máis e implantarase en Stage.

Que sabemos dos microservizos

Como superar a era da "fragmentación de microservizos"

Cunha arquitectura monolítica, por mor da coherencia dos cambios no produto, os desenvolvedores víronse obrigados a descubrir o que estaba a pasar cos seus veciños. Cando se traballa na nova arquitectura, os contextos de servizo xa non dependen uns dos outros.

Ademais, para que unha arquitectura de microservizos sexa efectiva, é necesario establecer moitos procesos, a saber:

• rexistro;
• solicitude de rastrexo (Jaeger);
• agregación de erros (Sentry);
• estados, mensaxes, eventos de Kubernetes (Procesamento de fluxos de eventos);
• límite de carreira / interruptor de circuito (podes usar Hystrix);
• control da conectividade do servizo (usamos Netramesh);
• seguimento (Grafana);
• montaxe (TeamCity);
• comunicación e notificación (Slack, correo electrónico);
• seguimento de tarefas; (Xira)
• elaboración da documentación.

Para garantir que o sistema non perda a súa integridade e siga sendo efectivo mentres se escala, repensamos a organización dos microservizos en Avito.

Como xestionamos os microservizos

O seguinte axuda a implementar unha "política de partido" unificada entre moitos microservizos de Avito:

  • dividir a infraestrutura en capas;
  • Concepto de plataforma como servizo (PaaS);
  • supervisando todo o que ocorre cos microservizos.

As capas de abstracción de infraestrutura inclúen tres capas. Imos de arriba a abaixo.

A. Top - malla de servizo. Ao principio probamos Istio, pero resultou que utiliza demasiados recursos, o que é demasiado caro para os nosos volumes. Polo tanto, o enxeñeiro senior do equipo de arquitectura Alexander Lukyanchenko desenvolveu a súa propia solución: Netramesh (dispoñible en Código Aberto), que utilizamos actualmente na produción e que consume varias veces menos recursos que Istio (pero non fai todo o que Istio pode presumir).
B. Medio - Kubernetes. Implementamos e operamos microservizos nel.
C. Fondo - metal nu. Non usamos nubes ou cousas como OpenStack, pero confiamos enteiramente no bare metal.

Todas as capas están combinadas por PaaS. E esta plataforma, á súa vez, consta de tres partes.

I. Xeradores, controlado mediante unha utilidade CLI. É ela quen axuda ao programador a crear un microservizo da forma correcta e cun mínimo esforzo.

II. Colector consolidado con control de todas as ferramentas a través dun panel común.

III. Almacenamento. Conecta con programadores que configuran automaticamente activadores para accións significativas. Grazas a este sistema, non se perde unha soa tarefa só porque alguén se esqueceu de configurar unha tarefa en Jira. Para iso utilizamos unha ferramenta interna chamada Atlas.

Que sabemos dos microservizos

A implantación dos microservizos en Avito tamén se realiza segundo un esquema único, o que simplifica o control dos mesmos en cada fase de desenvolvemento e lanzamento.

Como funciona unha canalización estándar de desenvolvemento de microservizos?

En xeral, a cadea de creación de microservizos ten o seguinte aspecto:

CLI-push → Integración continua → Bake → Implementar → Probas artificiais → Probas Canary → Squeeze Testing → Produción → Mantemento.

Imos pasar por iso exactamente nesta orde.

CLI-push

• Creación dun microservizo.
Loitamos durante moito tempo para ensinar a todos os desenvolvedores a facer microservizos. Isto incluíu escribir instrucións detalladas en Confluence. Pero os esquemas cambiaron e foron complementados. O resultado é que apareceu un pescozo de botella ao comezo da viaxe: o lanzamento dos microservizos levou moito máis tempo e aínda así xurdiron problemas durante a súa creación.

Ao final, creamos unha sinxela utilidade CLI que automatiza os pasos básicos ao crear un microservizo. De feito, substitúe o primeiro git push. Aquí tes o que fai exactamente ela.

— Crea un servizo segundo un modelo — paso a paso, en modo “asistente”. Temos modelos para as principais linguaxes de programación no backend de Avito: PHP, Golang e Python.

- Un comando á vez desprega un ambiente para o desenvolvemento local nunha máquina específica - Lanzase Minikube, os gráficos Helm xéranse e lánzanse automaticamente en kubernetes locais.

— Conecta a base de datos requirida. O programador non necesita coñecer a IP, o inicio de sesión e o contrasinal para acceder á base de datos que precisa, xa sexa localmente, no escenario ou en produción. Ademais, a base de datos desprégase inmediatamente nunha configuración tolerante a fallos e con equilibrio.

— Realiza a propia montaxe en directo. Digamos que un programador corrixiu algo nun microservizo a través do seu IDE. A utilidade ve cambios no sistema de ficheiros e, baseándose neles, reconstruí a aplicación (para Golang) e reinicia. Para PHP, simplemente reenviamos o directorio dentro do cubo e alí obtense a recarga en directo "automáticamente".

- Xera autotests. En forma de espazos en branco, pero bastante axeitado para o seu uso.

• Implantación de microservizos.

A implantación dun microservizo adoitaba ser unha tarefa para nós. Foron necesarios os seguintes:

I. Dockerfile.

II. Config.
III. Gráfico de timón, que en si mesmo é engorroso e inclúe:

- os propios gráficos;
- modelos;
— Valores específicos tendo en conta diferentes ambientes.

Eliminamos a dor de reelaborar os manifestos de Kubernetes polo que agora se xeran automaticamente. Pero o máis importante é que simplificaron o despregamento ata o límite. A partir de agora temos un Dockerfile e o programador escribe toda a configuración nun único ficheiro app.toml curto.

Que sabemos dos microservizos

Si, e no propio app.toml non hai nada que facer durante un minuto. Especificamos onde e cantas copias do servizo hai que levantar (no servidor de desenvolvemento, na posta en escena, en produción) e indicamos as súas dependencias. Observe o tamaño da liña = "pequeno" no bloque [motor]. Este é o límite que se asignará ao servizo a través de Kubernetes.

Despois, en función da configuración, xéranse automaticamente todos os gráficos Helm necesarios e créanse conexións coas bases de datos.

• Validación básica. Estes controis tamén están automatizados.
É necesario rastrexar:
— hai un Dockerfile;
— hai app.toml;
- ¿Hai documentación dispoñible?
- ¿Está en orde a dependencia?
— se se estableceron regras de alerta.
Ata o último punto: o propio propietario do servizo determina que métricas do produto monitorizar.

• Elaboración de documentación.
Aínda é unha zona problemática. Parece o máis obvio, pero ao mesmo tempo tamén é un rexistro “moitas veces esquecido”, e polo tanto un elo vulnerable da cadea.
É necesario que exista documentación para cada microservizo. Inclúe os seguintes bloques.

I. Breve descrición do servizo. Literalmente algunhas frases sobre o que fai e por que é necesario.

II. Enlace diagrama de arquitectura. É importante que, cunha ollada rápida, sexa fácil comprender, por exemplo, se está a usar Redis para almacenar na caché ou como o principal almacén de datos en modo persistente. En Avito por agora esta é unha ligazón a Confluencia.

III. Runbook. Unha pequena guía sobre o inicio do servizo e as complexidades de manexalo.

IV. FAQ, onde sería bo anticiparse aos problemas que poidan atopar os seus compañeiros ao traballar co servizo.

V. Descrición dos puntos finais para a API. Se de súpeto non especificaches os destinos, os compañeiros cuxos microservizos estean relacionados cos teus, case seguro que o pagarán. Agora usamos Swagger e a nosa solución chamada breve para iso.

VI. Etiquetas. Ou marcadores que mostran a que produto, funcionalidade ou división estrutural da empresa pertence o servizo. Axúdanche a comprender rapidamente, por exemplo, se estás recortando a funcionalidade que os teus compañeiros lanzaron para a mesma unidade de negocio hai unha semana.

VII. Propietario ou titulares do servizo. Na maioría dos casos, pode determinarse automaticamente usando PaaS, pero para estar seguro, esiximos que o programador os especifique manualmente.

Finalmente, é unha boa práctica revisar a documentación, de xeito similar á revisión de código.

Integración continua

  • Preparación de repositorios.
  • Creando unha canalización en TeamCity.
  • Establecemento de dereitos.
  • Busca propietarios de servizos. Aquí hai un esquema híbrido: marcado manual e automatización mínima de PaaS. Un esquema totalmente automático falla cando se transfiren os servizos para obter soporte a outro equipo de desenvolvemento ou, por exemplo, se o desenvolvedor do servizo sae.
  • Rexistrar un servizo en Atlas (ver arriba). Con todos os seus donos e dependencias.
  • Comprobación de migracións. Comprobamos se algún deles é potencialmente perigoso. Por exemplo, nun deles aparece unha táboa de modificación ou outra cousa que pode romper a compatibilidade do esquema de datos entre as diferentes versións do servizo. A continuación, a migración non se realiza, senón que se coloca nunha subscrición: o PaaS debe indicarlle ao propietario do servizo cando sexa seguro usalo.

Ás

A seguinte etapa son os servizos de empaquetado antes da implantación.

  • Construíndo a aplicación. Segundo os clásicos, nunha imaxe de Docker.
  • Xeración de gráficos Helm para o propio servizo e recursos relacionados. Incluíndo para bases de datos e caché. Créanse automaticamente de acordo coa configuración do app.toml que se xerou na fase CLI-push.
  • Creando tickets para administradores para abrir portos (cando sexa necesario).
  • Realización de probas unitarias e cálculo de cobertura de código. Se a cobertura do código está por debaixo do limiar especificado, o máis probable é que o servizo non vaia máis lonxe, ata a implantación. Se está a piques de ser aceptable, o servizo asignaráselle un coeficiente "pesimizador": entón, se non hai melloras no indicador ao longo do tempo, o desenvolvedor recibirá unha notificación de que non hai avances nas probas ( e hai que facer algo ao respecto).
  • Contabilidade das limitacións de memoria e CPU. Principalmente escribimos microservizos en Golang e executámolos en Kubernetes. De aí unha sutileza asociada á peculiaridade da linguaxe Golang: de forma predeterminada, ao iniciarse, utilízanse todos os núcleos da máquina, se non estableces explícitamente a variable GOMAXPROCS e cando se inician varios destes servizos na mesma máquina, comezan. competir polos recursos, interferindo entre si. Os gráficos que aparecen a continuación mostran como cambia o tempo de execución se executas a aplicación sen contención e no modo de carreira polos recursos. (As fontes dos gráficos son aquí).

Que sabemos dos microservizos

Tempo de execución, menos é mellor. Máximo: 643 ms, mínimo: 42 ms. A foto pódese facer clic.

Que sabemos dos microservizos

Tempo para a cirurxía, menos é mellor. Máximo: 14091 ns, mínimo: 151 ns. A foto pódese facer clic.

Na fase de preparación da montaxe, pode establecer esta variable de forma explícita ou pode utilizar a biblioteca automaxprocs dos mozos de Uber.

Implantar

• Comprobación de convencións. Antes de comezar a entregar conxuntos de servizo aos ambientes previstos, cómpre comprobar o seguinte:
- Puntos finais da API.
— Cumprimento das respostas dos puntos finais da API co esquema.
- Formato de rexistro.
— Establecer cabeceiras para as solicitudes ao servizo (actualmente faino netramesh)
— Configurar o token do propietario ao enviar mensaxes ao bus de eventos. Isto é necesario para rastrexar a conectividade dos servizos a través do autobús. Podes enviar tanto datos idempotentes ao bus, que non aumentan a conectividade dos servizos (que é bo), como datos comerciais que reforzan a conectividade dos servizos (que é moi malo!). E no momento no que esta conectividade se converte nun problema, entender quen escribe e le o autobús axuda a separar correctamente os servizos.

Aínda non hai moitas convencións en Avito, pero a súa piscina vaise ampliando. Canto máis estes acordos estean dispoñibles nunha forma que o equipo poida entender e comprender, máis fácil será manter a coherencia entre os microservizos.

Ensaios sintéticos

• Probas de bucle pechado. Para iso agora estamos a usar código aberto Hoverfly.io. En primeiro lugar, rexistra a carga real do servizo e despois, só nun bucle pechado, emula.

• Probas de esforzo. Intentamos levar todos os servizos a un rendemento óptimo. E todas as versións de cada servizo deben estar suxeitas a probas de carga; deste xeito podemos entender o rendemento actual do servizo e a diferenza coas versións anteriores do mesmo servizo. Se, despois dunha actualización do servizo, o seu rendemento diminuíu unha vez e media, este é un sinal claro para os seus propietarios: cómpre investigar o código e corrixir a situación.
Usamos os datos recollidos, por exemplo, para implementar correctamente o escalado automático e, ao final, entender en xeral o escalable que é o servizo.

Durante a proba de carga, comprobamos se o consumo de recursos cumpre os límites establecidos. E centrámonos principalmente nos extremos.

a) Observamos a carga total.
- Demasiado pequeno - o máis probable é que algo non funcione en absoluto se a carga baixa de súpeto varias veces.
- Demasiado grande: é necesario optimizar.

b) Observamos o corte segundo RPS.
Aquí observamos a diferenza entre a versión actual e a anterior e a cantidade total. Por exemplo, se un servizo produce 100 rps, entón está mal escrito ou esta é a súa especificidade, pero en calquera caso, esta é unha razón para mirar o servizo moi de preto.
Se, pola contra, hai demasiados RPS, quizais haxa algún tipo de erro e algúns dos puntos finais deixaron de executar a carga útil e simplemente se activa algún outro. return true;

Probas canarias

Despois de pasar as probas sintéticas, probamos o microservizo nun pequeno número de usuarios. Comezamos con coidado, cunha pequena parte da audiencia prevista do servizo: menos do 0,1 %. Nesta fase, é moi importante que as métricas técnicas e de produto correctas se inclúan no seguimento para que mostren o problema no servizo o máis rápido posible. O tempo mínimo para unha proba canaria é de 5 minutos, o principal é de 2 horas. Para servizos complexos, configuramos a hora manualmente.
Analicemos:
— métricas específicas do idioma, en particular, traballadores php-fpm;
— erros en Sentry;
- estados de resposta;
— tempo de resposta, exacto e medio;
- latencia;
— excepcións, procesadas e non tratadas;
- métricas do produto.

Proba de apretamento

As probas de espremer tamén se denominan probas de "espremer". O nome da técnica foi introducido en Netflix. A súa esencia é que primeiro enchemos unha instancia con tráfico real ata o punto de falla e establecemos así o seu límite. Despois engadimos outra instancia e cargamos este par, de novo ao máximo; vemos o seu teito e o seu delta co primeiro "espremer". E así conectamos unha instancia á vez e calculamos o patrón de cambios.
Os datos das probas mediante o "espremer" tamén flúen nunha base de datos de métricas común, onde enriquecemos os resultados da carga artificial con eles ou mesmo substituímos os "sintéticos" con eles.

Produción

• Escalado. Cando lanzamos un servizo á produción, supervisamos como se escala. Na nosa experiencia, supervisar só os indicadores da CPU é ineficaz. O escalado automático con benchmarking RPS na súa forma pura funciona, pero só para determinados servizos, como a transmisión en liña. Entón, miramos primeiro as métricas de produtos específicas da aplicación.

Como resultado, ao escalar analizamos:
- Indicadores de CPU e RAM,
- o número de solicitudes na cola,
- tempo de resposta,
— Previsión baseada en datos históricos acumulados.

Ao escalar un servizo, tamén é importante supervisar as súas dependencias para non escalar o primeiro servizo da cadea, e aqueles aos que accede fallan baixo carga. Para establecer unha carga aceptable para todo o conxunto de servizos, observamos os datos históricos do servizo dependente "máis próximo" (baseado nunha combinación de indicadores de CPU e RAM, xunto con métricas específicas da aplicación) e comparamos cos datos históricos. do servizo de inicialización, e así sucesivamente ao longo da "cadea de dependencias"", de arriba abaixo.

Servizo

Despois de poñer en funcionamento o microservizo, podemos engadirlle disparadores.

Aquí están as situacións típicas nas que se producen desencadenantes.
— Migracións potencialmente perigosas detectadas.
— Publicáronse actualizacións de seguranza.
— Hai moito tempo que non se actualiza o servizo en si.
— A carga do servizo diminuíu notablemente ou algunhas das súas métricas do produto están fóra do intervalo normal.
— O servizo xa non cumpre os requisitos da nova plataforma.

Algúns dos disparadores son responsables da estabilidade do funcionamento, algúns -en función do mantemento do sistema-, por exemplo, algún servizo non se implantou durante moito tempo e a súa imaxe base deixou de pasar as comprobacións de seguridade.

Panel de control

En resumo, o panel de control é o panel de control de todo o noso PaaS.

  • Un único punto de información sobre o servizo, con datos sobre a súa cobertura de probas, o número das súas imaxes, o número de copias de produción, versións, etc.
  • Unha ferramenta para filtrar datos por servizos e etiquetas (marcadores de pertenza a unidades de negocio, funcionalidade do produto, etc.)
  • Unha ferramenta para a integración con ferramentas de infraestrutura para o rastrexo, rexistro e seguimento.
  • Documentación dun único punto de servizo.
  • Un único punto de vista de todos os eventos en todos os servizos.

Que sabemos dos microservizos
Que sabemos dos microservizos
Que sabemos dos microservizos
Que sabemos dos microservizos

En total

Antes de introducir PaaS, un novo desenvolvedor podería pasar varias semanas entendendo todas as ferramentas necesarias para lanzar un microservizo en produción: Kubernetes, Helm, as nosas funcións internas de TeamCity, configurar conexións a bases de datos e cachés de forma tolerante a fallos, etc. leva un par de horas ler o inicio rápido e crear o propio servizo.

Dei un informe sobre este tema para HighLoad++ 2018, podes velo video и presentación.

Bonus track para os que len ata o final

En Avito estamos organizando unha formación interna de tres días para desenvolvedores de Chris Richardson, un experto en arquitectura de microservizos. Queremos darlle a oportunidade de participar nel a un dos lectores deste post. Aquí Publicouse o programa de formación.

A formación terá lugar do 5 ao 7 de agosto en Moscova. Son días laborables que estarán totalmente ocupados. O xantar e a formación serán na nosa oficina, e o participante seleccionado pagará por si mesmo o desprazamento e o aloxamento.

Podes solicitar a participación neste formulario de google. De ti - a resposta á pregunta por que precisas asistir á formación e información sobre como contactar contigo. Contesta en inglés, porque Chris elixirá o propio participante que asistirá á formación.
Anunciaremos o nome do participante na formación nunha actualización desta publicación e nas redes sociais Avito para desenvolvedores (AvitoTech en Фейсбуке, VKontakte, Twitter) antes do 19 de xullo.

Fonte: www.habr.com

Engadir un comentario