Contenidors, microserveis i malles de servei

A Internet un grup articles о malla de servei (malla de servei), i aquí n'hi ha un altre. Hura! Però perquè? Aleshores, vull expressar la meva opinió que hauria estat millor que les malles de servei apareguessin fa 10 anys, abans de l'arribada de plataformes de contenidors com Docker i Kubernetes. No dic que el meu punt de vista sigui millor o pitjor que els altres, però com que les malles de servei són animals força complexos, múltiples punts de vista ajudaran a entendre-les millor.

Parlaré de la plataforma dotCloud, que es va construir amb més d'un centenar de microserveis i va donar suport a milers d'aplicacions en contenidors. Explicaré els reptes que ens hem enfrontat per desenvolupar-lo i llançar-lo, i com les malles de servei podrien (o no) ajudar.

Història de dotCloud

He escrit sobre la història de dotCloud i les opcions d'arquitectura d'aquesta plataforma, però no he parlat gaire de la capa de xarxa. Si no vols submergir-te en la lectura últim article sobre dotCloud, aquí teniu l'essència en poques paraules: és una plataforma PaaS com a servei que permet als clients executar una àmplia gamma d'aplicacions (Java, PHP, Python...), amb suport per a una àmplia gamma de dades. serveis (MongoDB, MySQL, Redis...) i un flux de treball com Heroku: penges el teu codi a la plataforma, construeix imatges de contenidors i les desplega.

Us explicaré com es va dirigir el trànsit a la plataforma dotCloud. No perquè fos especialment genial (tot i que el sistema va funcionar bé per a la seva època!), sinó principalment perquè amb eines modernes, aquest disseny es pot implementar fàcilment en poc temps per un equip modest si necessita una manera d'encaminar el trànsit entre un grup. de microserveis o un munt d'aplicacions. D'aquesta manera, podeu comparar les opcions: què passa si ho desenvolupeu tot vosaltres mateixos o utilitzeu una malla de servei existent. L'opció estàndard és fer-lo tu mateix o comprar-lo.

Encaminament del trànsit per a aplicacions allotjades

Les aplicacions de dotCloud poden exposar punts finals HTTP i TCP.

Punts finals HTTP s'ha afegit dinàmicament a la configuració del clúster de l'equilibrador de càrrega Hipache. Això és similar al que fan els recursos avui dia Ingrés a Kubernetes i un equilibrador de càrrega com Traefik.

Els clients es connecten als punts finals HTTP a través dels dominis adequats, sempre que el nom del domini apunti als equilibradors de càrrega dotCloud. Res especial.

Punts finals TCP associat amb un número de port, que després es passa a tots els contenidors d'aquesta pila mitjançant variables d'entorn.

Els clients poden connectar-se als punts finals TCP mitjançant el nom d'amfitrió adequat (alguna cosa com gateway-X.dotcloud.com) i el número de port.

Aquest nom d'amfitrió es resol amb el clúster de servidors "nats" (no relacionat amb NATS), que encaminarà les connexions TCP entrants al contenidor correcte (o, en el cas dels serveis equilibrats de càrrega, als contenidors correctes).

Si esteu familiaritzat amb Kubernetes, això probablement us recordarà els serveis Port del node.

No hi havia serveis equivalents a la plataforma dotCloud ClústerIP: Per simplificar, s'accedia als serveis de la mateixa manera tant des de dins com des de fora de la plataforma.

Tot estava organitzat de manera senzilla: les implementacions inicials de les xarxes d'encaminament HTTP i TCP probablement eren només uns quants centenars de línies de Python cadascuna. Algorismes senzills (jo diria ingenus) que es van perfeccionar a mesura que la plataforma creixia i apareixien requisits addicionals.

No era necessària una refactorització extensiva del codi existent. En particular, Aplicacions de 12 factors pot utilitzar directament l'adreça obtinguda mitjançant variables d'entorn.

En què és diferent d'una malla de servei moderna?

Limitat visibilitat. No teníem cap mètrica per a la malla d'encaminament TCP. Pel que fa a l'encaminament HTTP, les versions posteriors van introduir mètriques HTTP detallades amb codis d'error i temps de resposta, però les malles de serveis modernes van encara més enllà, proporcionant integració amb sistemes de recollida de mètriques com Prometheus, per exemple.

La visibilitat és important no només des d'una perspectiva operativa (per ajudar a resoldre problemes), sinó també a l'hora de llançar noves funcions. Es tracta de seguretat desplegament blau-verd и desplegament canari.

Eficiència d'encaminament també és limitat. A la malla d'encaminament dotCloud, tot el trànsit havia de passar per un grup de nodes d'encaminament dedicats. Això significava creuar potencialment múltiples límits AZ (zona de disponibilitat) i augmentar significativament la latència. Recordo la resolució de problemes del codi que feia més d'un centenar de consultes SQL per pàgina i obrir una nova connexió al servidor SQL per a cada consulta. Quan s'executa localment, la pàgina es carrega a l'instant, però a dotCloud triga uns quants segons a carregar-se perquè cada connexió TCP (i la consulta SQL posterior) triga desenes de mil·lisegons. En aquest cas particular, les connexions persistents van resoldre el problema.

Les malles de servei modernes són millors per fer front a aquests problemes. En primer lloc, comproven que les connexions estan encaminades a la font. El flux lògic és el mateix: клиент → меш → сервис, però ara la malla funciona localment i no en nodes remots, de manera que la connexió клиент → меш és local i molt ràpid (microssegons en lloc de mil·lisegons).

Les malles de servei modernes també implementen algorismes d'equilibri de càrrega més intel·ligents. En supervisar la salut dels backends, poden enviar més trànsit a backends més ràpids, cosa que resulta en un rendiment general millorat.

Безопасность millor també. La malla d'encaminament dotCloud funcionava completament a EC2 Classic i no xifrava el trànsit (basant-se en el supòsit que si algú aconseguia posar un sniffer al trànsit de la xarxa EC2, ja estaves en grans problemes). Les malles de servei modernes protegeixen de manera transparent tot el nostre trànsit, per exemple, amb l'autenticació TLS mútua i el xifratge posterior.

Encaminament del trànsit per als serveis de la plataforma

D'acord, hem parlat del trànsit entre aplicacions, però què passa amb la pròpia plataforma dotCloud?

La plataforma en si estava formada per un centenar de microserveis responsables de diverses funcions. Alguns van acceptar sol·licituds d'altres, i alguns eren treballadors de fons que es connectaven a altres serveis però no acceptaven connexions per si mateixos. En qualsevol cas, cada servei ha de conèixer els punts finals de les adreces a les quals necessita connectar-se.

Molts serveis d'alt nivell poden utilitzar la malla d'encaminament descrita anteriorment. De fet, molts dels més d'un centenar de microserveis de dotCloud s'han desplegat com a aplicacions habituals a la mateixa plataforma dotCloud. Però un petit nombre de serveis de baix nivell (especialment els que implementen aquesta malla d'encaminament) necessitaven quelcom més senzill, amb menys dependències (ja que no podien dependre d'ells mateixos per funcionar: el problema dels ous i gallines).

Aquests serveis crítics de baix nivell es van implementar executant contenidors directament en uns quants nodes clau. En aquest cas, no es van utilitzar els serveis estàndard de la plataforma: enllaçador, planificador i runner. Si voleu comparar-vos amb les plataformes de contenidors modernes, és com fer servir un avió de control docker run directament als nodes, en lloc de delegar la tasca a Kubernetes. És bastant semblant en concepte mòduls estàtics (pods), que utilitza kubeadm o bootkube en arrencar un clúster autònom.

Aquests serveis es van exposar d'una manera senzilla i crua: un fitxer YAML enumerava els seus noms i adreces; i cada client havia de prendre una còpia d'aquest fitxer YAML per al desplegament.

D'una banda, és extremadament fiable perquè no requereix el suport d'un magatzem de clau/valor extern com Zookeeper (recordeu, etcd o Consul no existien en aquell moment). D'altra banda, dificultava el moviment de serveis. Cada vegada que es feia un moviment, tots els clients rebien un fitxer YAML actualitzat (i potencialment es reiniciaven). No gaire còmode!

Posteriorment, vam començar a implementar un nou esquema, on cada client es connectava a un servidor proxy local. En lloc d'una adreça i un port, només necessita saber el número de port del servei i connectar-se mitjançant localhost. El servidor intermediari local gestiona aquesta connexió i la reenvia al servidor real. Ara, en moure el backend a una altra màquina o escalar, en lloc d'actualitzar tots els clients, només cal que actualitzeu tots aquests servidors intermediaris locals; i un reinici ja no és necessari.

(També es va planificar encapsular el trànsit a les connexions TLS i posar un altre servidor intermediari al costat receptor, així com verificar els certificats TLS sense la participació del servei receptor, que està configurat per acceptar connexions només a localhost. Més sobre això més endavant).

Això és molt semblant a SmartStack d'Airbnb, però la diferència significativa és que SmartStack s'implementa i es desplega en producció, mentre que el sistema d'encaminament intern de dotCloud es va deixar de banda quan dotCloud es va convertir en Docker.

Personalment, considero que SmartStack és un dels predecessors de sistemes com Istio, Linkerd i Consul Connect perquè tots segueixen el mateix patró:

  • Executeu un proxy a cada node.
  • Els clients es connecten al proxy.
  • El pla de control actualitza la configuració del servidor intermediari quan canvien els backends.
  • ... Guany!

Implementació moderna d'una malla de servei

Si haguéssim de implementar una graella similar avui, podríem utilitzar principis similars. Per exemple, configureu una zona DNS interna assignant noms de serveis a adreces de l'espai 127.0.0.0/8. A continuació, executeu HAProxy a cada node del clúster, acceptant connexions a cada adreça de servei (en aquesta subxarxa 127.0.0.0/8) i redirigir/equilibrar la càrrega als backends adequats. La configuració d'HAProxy es pot controlar confd, que us permet emmagatzemar informació de backend a etcd o Consul i enviar automàticament la configuració actualitzada a HAProxy quan sigui necessari.

Així és pràcticament com funciona Istio! Però amb algunes diferències:

  • Usos Proxy enviat en lloc d'HAProxy.
  • Emmagatzema la configuració del backend mitjançant l'API de Kubernetes en lloc d'etcd o Consul.
  • Els serveis s'assignen adreces a la subxarxa interna (adreces IP de Kubernetes Cluster) en lloc de 127.0.0.0/8.
  • Té un component addicional (Citadel) per afegir l'autenticació TLS mútua entre el client i els servidors.
  • Admet noves funcions com ara interrupció de circuits, traçat distribuït, desplegament canari, etc.

Fem una ullada ràpida a algunes de les diferències.

Proxy enviat

Envoy Proxy va ser escrit per Lyft [el competidor d'Uber al mercat de taxis - aprox. carril]. És semblant en molts aspectes a altres servidors intermediaris (per exemple, HAProxy, Nginx, Traefik...), però Lyft va escriure el seu perquè necessitaven funcions que mancaven a altres servidors intermediaris, i semblava més intel·ligent fer-ne un de nou en lloc d'ampliar l'existent.

Envoy es pot utilitzar sol. Si tinc un servei específic que s'ha de connectar a altres serveis, puc configurar-lo per connectar-se a Envoy i, a continuació, configurar i reconfigurar Envoy de manera dinàmica amb la ubicació d'altres serveis, alhora que obteniu moltes funcionalitats addicionals excel·lents, com ara la visibilitat. En lloc d'una biblioteca de client personalitzada o d'injectar traces de trucades al codi, enviem trànsit a Envoy i ens recopila mètriques.

Però Envoy també és capaç de treballar com pla de dades (pla de dades) per a la malla de servei. Això vol dir que Envoy ara està configurat per a aquesta malla de servei pla de control (avió de control).

Pla de control

Per al pla de control, Istio es basa en l'API de Kubernetes. Això no és gaire diferent de l'ús de confd, que es basa en etcd o Consul per veure el conjunt de claus al magatzem de dades. Istio utilitza l'API de Kubernetes per veure un conjunt de recursos de Kubernetes.

Entre això i llavors: Personalment, ho vaig trobar útil Descripció de l'API de Kubernetesque diu:

El servidor API de Kubernetes és un "servidor idiot" que ofereix emmagatzematge, versions, validació, actualització i semàntica per als recursos de l'API.

Istio està dissenyat per treballar amb Kubernetes; i si el voleu utilitzar fora de Kubernetes, haureu d'executar una instància del servidor de l'API de Kubernetes (i el servei d'ajuda etcd).

Adreces de servei

Istio es basa en les adreces IP de Cluster que Kubernetes assigna, de manera que els serveis d'Istio reben una adreça interna (no dins l'interval). 127.0.0.0/8).

El trànsit a l'adreça ClusterIP d'un servei específic en un clúster de Kubernetes sense Istio és interceptat per kube-proxy i enviat al backend d'aquest proxy. Si esteu interessats en els detalls tècnics, kube-proxy configura regles d'iptables (o equilibradors de càrrega IPVS, depenent de com estigui configurat) per reescriure les adreces IP de destinació de les connexions que van a l'adreça IP del Cluster.

Un cop instal·lat Istio en un clúster de Kubernetes, res no canvia fins que s'habilita explícitament per a un determinat consumidor, o fins i tot per a tot l'espai de noms, introduint un contenidor. sidecar en beines personalitzades. Aquest contenidor generarà una instància d'Envoy i configurarà un conjunt de regles d'iptables per interceptar el trànsit que va a altres serveis i redirigir aquest trànsit a Envoy.

Quan s'integra amb Kubernetes DNS, això vol dir que el nostre codi es pot connectar pel nom del servei i tot "funciona". En altres paraules, el nostre codi emet consultes com http://api/v1/users/4242llavors api resoldre la sol·licitud de 10.97.105.48, les regles d'iptables interceptaran les connexions de 10.97.105.48 i les reenviaran al servidor intermediari local d'Envoy, i aquest servidor intermediari local reenviarà la sol·licitud a l'API de fons real. Uf!

Volants addicionals

Istio també proporciona xifratge i autenticació d'extrem a extrem mitjançant mTLS (TLS mutu). Un component anomenat Ciutadella.

També hi ha un component Mesclador, que l'Enviat pot sol·licitar de cadascuna sol·licitud per prendre una decisió especial sobre aquesta sol·licitud en funció de diversos factors com ara les capçaleres, la càrrega del backend, etc. bé com a proxy).

I, per descomptat, hem esmentat la visibilitat: Envoy recull una gran quantitat de mètriques alhora que proporciona un seguiment distribuït. En una arquitectura de microserveis, si una sol·licitud d'API única ha de passar pels microserveis A, B, C i D, després d'iniciar la sessió, el seguiment distribuït afegirà un identificador únic a la sol·licitud i emmagatzemarà aquest identificador a través de les subsol·licituds de tots aquests microserveis, cosa que permetrà totes les trucades associades a capturar, retards, etc.

Desenvolupar o comprar

Istio té fama de complex. En canvi, construir la malla d'encaminament que vaig descriure al principi d'aquesta publicació és relativament senzill utilitzant les eines existents. Aleshores, té sentit crear la vostra pròpia malla de servei?

Si tenim necessitats modestes (no necessitem visibilitat, un interruptor de circuit i altres subtileses), aleshores es pensen a desenvolupar la nostra pròpia eina. Però si fem servir Kubernetes, és possible que ni tan sols sigui necessari perquè Kubernetes ja ofereix eines bàsiques per a la descoberta de serveis i l'equilibri de càrrega.

Però si tenim requisits avançats, llavors "comprar" una malla de servei sembla ser una opció molt millor. (Això no sempre és una "compra" perquè Istio és de codi obert, però encara hem d'invertir temps d'enginyeria per entendre-ho, desplegar-lo i gestionar-lo).

He de triar Istio, Linkerd o Consul Connect?

Fins ara només hem parlat d'Istio, però aquesta no és l'única malla de servei. Alternativa popular - Linkerd, i n'hi ha més Consul Connect.

Què triar?

Sincerament, no ho sé. De moment no em considero prou competent per respondre aquesta pregunta. N'hi ha uns quants interessant articles amb una comparació d'aquestes eines i fins i tot punts de referència.

Un enfocament prometedor és utilitzar una eina com SuperGloo. Implementa una capa d'abstracció per simplificar i unificar les API exposades per les malles de servei. En lloc d'aprendre les API específiques (i, al meu entendre, relativament complexes) de diferents malles de servei, podem utilitzar les construccions més senzilles de SuperGloo i canviar fàcilment d'una a una altra, com si tinguéssim un format de configuració intermedi que descriu interfícies HTTP i backends capaços. de generar la configuració real per a Nginx, HAProxy, Traefik, Apache...

He treballat una mica amb Istio i SuperGloo, i en el següent article vull mostrar com afegir Istio o Linkerd a un clúster existent mitjançant SuperGloo, i com aquest últim fa la feina, és a dir, us permet canviar de una malla de servei a una altra sense sobreescriure les configuracions.

Font: www.habr.com

Afegeix comentari