Contedores, microservizos e mallas de servizos

En internet un grupo artigos о malla de servizo (malla de servizo), e aquí tes outro. Hurra! Pero por qué? Entón, quero expresar a miña opinión de que sería mellor que as mallas de servizo aparecesen hai 10 anos, antes da aparición de plataformas de contedores como Docker e Kubernetes. Non digo que o meu punto de vista sexa mellor ou peor que outros, pero como as mallas de servizo son animais bastante complexos, varios puntos de vista axudarán a entendelos mellor.

Falarei da plataforma dotCloud, que se construíu con máis de cen microservizos e admitiu miles de aplicacións en contedores. Explicarei os retos aos que nos enfrontamos ao desenvolvelo e lanzalo, e como as mallas de servizo poderían (ou non) axudar.

Historia de dotCloud

Escribín sobre a historia de dotCloud e as opcións de arquitectura para esta plataforma, pero non falei moito sobre a capa de rede. Se non queres mergullarte na lectura último artigo sobre dotCloud, aquí tes a esencia en poucas palabras: é unha plataforma PaaS como servizo que permite aos clientes executar unha ampla gama de aplicacións (Java, PHP, Python...), con soporte para unha ampla gama de datos. servizos (MongoDB, MySQL, Redis...) e un fluxo de traballo como Heroku: Cargas o teu código á plataforma, constrúe imaxes de contedores e desprégaas.

Vouche dicir como se dirixiu o tráfico á plataforma dotCloud. Non porque fose especialmente xenial (aínda que o sistema funcionou ben para a súa época!), senón principalmente porque con ferramentas modernas, un equipo modesto pode implementar un deseño deste tipo facilmente en pouco tempo se necesitan unha forma de encamiñar o tráfico entre un grupo. de microservizos ou unha morea de aplicacións. Deste xeito, podes comparar as opcións: que pasa se desenvolves todo ti mesmo ou utilizas unha malla de servizo existente. A opción estándar é facelo vostede mesmo ou mercalo.

Enrutamento de tráfico para aplicacións aloxadas

As aplicacións en dotCloud poden expoñer puntos finais HTTP e TCP.

Puntos finais HTTP engadido dinámicamente á configuración do clúster do equilibrador de carga Hipache. Isto é semellante ao que fan hoxe os recursos Ingreso en Kubernetes e un equilibrador de carga como Traefik.

Os clientes conéctanse aos extremos HTTP a través dos dominios axeitados, sempre que o nome de dominio apunte aos equilibradores de carga dotCloud. Nada en especial.

Puntos finais TCP asociado cun número de porto, que despois se pasa a todos os contedores desa pila mediante variables de ambiente.

Os clientes poden conectarse a puntos finais TCP usando o nome de host apropiado (algo como gateway-X.dotcloud.com) e o número de porto.

Este nome de host resolve-se no clúster de servidores "nats" (non relacionado con NATS), que enrutará as conexións TCP entrantes ao contedor correcto (ou, no caso dos servizos de carga equilibrada, aos contedores correctos).

Se estás familiarizado con Kubernetes, isto probablemente che recordará aos Servizos NodePort.

Non había servizos equivalentes na plataforma dotCloud ClústerIP: Para simplificar, accedíase aos servizos do mesmo xeito tanto desde dentro como desde fóra da plataforma.

Todo estaba organizado de forma moi sinxela: as implementacións iniciais das redes de enrutamento HTTP e TCP eran probablemente só uns centos de liñas de Python cada unha. Algoritmos sinxelos (diría que inxenuos) que foron perfeccionando a medida que a plataforma medraba e aparecían requisitos adicionais.

Non foi necesaria unha refactorización extensa do código existente. En particular, Aplicacións de 12 factores pode utilizar directamente o enderezo obtido mediante variables de ambiente.

En que se diferencia isto dunha malla de servizo moderna?

Limitado visibilidade. Non tiñamos ningunha métrica para a malla de enrutamento TCP. No que se refire ao enrutamento HTTP, versións posteriores introduciron métricas HTTP detalladas con códigos de erro e tempos de resposta, pero as mallas de servizos modernas van aínda máis alá, proporcionando integración con sistemas de recollida de métricas como Prometheus, por exemplo.

A visibilidade é importante non só desde unha perspectiva operativa (para axudar a solucionar problemas), senón tamén ao lanzar novas funcións. Trátase de seguro despregamento azul-verde и despregamento canario.

Eficiencia do enrutamento tamén é limitada. Na malla de enrutamento dotCloud, todo o tráfico tiña que pasar por un grupo de nodos de enrutamento dedicados. Isto significaba potencialmente cruzar varios límites AZ (zona de dispoñibilidade) e aumentar significativamente a latencia. Recordo resolver problemas de código que facía máis de cen consultas SQL por páxina e abrir unha nova conexión co servidor SQL para cada consulta. Cando se executa localmente, a páxina cárgase ao instante, pero en dotCloud tarda uns segundos en cargarse porque cada conexión TCP (e a consulta SQL posterior) leva decenas de milisegundos. Neste caso particular, as conexións persistentes resolveron o problema.

As mallas de servizo modernas son mellores para xestionar estes problemas. En primeiro lugar, comproban que as conexións están encamiñadas na fonte. O fluxo lóxico é o mesmo: клиент → меш → сервис, pero agora a malla funciona localmente e non en nós remotos, polo que a conexión клиент → меш é local e moi rápido (microssegundos en lugar de milisegundos).

As mallas de servizo modernas tamén implementan algoritmos de equilibrio de carga máis intelixentes. Ao supervisar o estado dos backends, poden enviar máis tráfico a backends máis rápidos, o que resulta en un rendemento xeral mellorado.

Безопасность mellor tamén. A malla de enrutamento dotCloud funcionou totalmente en EC2 Classic e non cifraba o tráfico (baseándose no suposto de que se alguén conseguía poñer un sniffer no tráfico da rede EC2, xa tiñas grandes problemas). As mallas de servizo modernas protexen de forma transparente todo o noso tráfico, por exemplo, coa autenticación TLS mutua e o posterior cifrado.

Enrutamento de tráfico para servizos de plataforma

Está ben, falamos do tráfico entre aplicacións, pero que pasa coa propia plataforma dotCloud?

A propia plataforma estaba formada por preto dun centenar de microservizos responsables de diversas funcións. Algúns aceptaron solicitudes doutros e algúns eran traballadores en segundo plano que se conectaron a outros servizos pero que non aceptaron as conexións por si mesmos. En calquera caso, cada servizo debe coñecer os extremos dos enderezos aos que precisa conectarse.

Moitos servizos de alto nivel poden usar a malla de enrutamento descrita anteriormente. De feito, moitos dos máis de cen microservizos de dotCloud implantáronse como aplicacións habituais na propia plataforma dotCloud. Pero un pequeno número de servizos de baixo nivel (especialmente aqueles que implementan esta malla de enrutamento) necesitaban algo máis sinxelo, con menos dependencias (xa que non podían depender de si mesmos para funcionar: o problema do bo vello e dos ovos).

Estes servizos de baixo nivel e de misión crítica despregáronse mediante a execución de contedores directamente nuns poucos nodos clave. Neste caso, non se utilizaron servizos de plataforma estándar: enlazador, planificador e runner. Se queres comparar coas plataformas de contedores modernas, é como executar un avión de control docker run directamente nos nodos, en lugar de delegar a tarefa en Kubernetes. É bastante semellante no concepto módulos estáticos (pods), que utiliza kubeadm ou bootkube ao iniciar un clúster autónomo.

Estes servizos expuxéronse dun xeito sinxelo e tosco: un ficheiro YAML enumeraba os seus nomes e enderezos; e cada cliente tivo que levar unha copia deste ficheiro YAML para a súa implantación.

Por unha banda, é extremadamente fiable porque non precisa do soporte dunha tenda de claves/valores externas como Zookeeper (lembre que, etcd ou Consul non existían nese momento). Por outra banda, dificultaba o desprazamento dos servizos. Cada vez que se realizaba un movemento, todos os clientes recibirían un ficheiro YAML actualizado (e potencialmente reiniciaríanse). Non moi cómodo!

Posteriormente, comezamos a implementar un novo esquema, onde cada cliente se conectaba a un servidor proxy local. En lugar dun enderezo e un porto, só precisa coñecer o número de porto do servizo e conectarse mediante localhost. O proxy local xestiona esta conexión e reenvíaa ao servidor real. Agora, ao mover o backend a outra máquina ou escalar, en lugar de actualizar todos os clientes, só precisa actualizar todos estes proxies locais; e xa non é necesario reiniciar.

(Tamén se planeou encapsular o tráfico nas conexións TLS e poñer outro servidor proxy no lado receptor, así como verificar os certificados TLS sen a participación do servizo receptor, que está configurado para aceptar conexións só en localhost. Máis sobre isto máis tarde).

Isto é moi semellante a SmartStack de Airbnb, pero a diferenza significativa é que SmartStack está implementado e despregado en produción, mentres que o sistema de enrutamento interno de dotCloud foi abandonado cando dotCloud se converteu en Docker.

Persoalmente, considero que SmartStack é un dos predecesores de sistemas como Istio, Linkerd e Consul Connect porque todos seguen o mesmo patrón:

  • Executar un proxy en cada nodo.
  • Os clientes conéctanse ao proxy.
  • O plano de control actualiza a configuración do proxy cando cambian os backends.
  • ... Beneficio!

Implementación moderna dunha malla de servizo

Se hoxe necesitásemos implementar unha grella similar, poderiamos usar principios similares. Por exemplo, configure unha zona DNS interna asignando nomes de servizos a enderezos no espazo 127.0.0.0/8. A continuación, execute HAProxy en cada nodo do clúster, aceptando conexións en cada enderezo de servizo (nesa subrede 127.0.0.0/8) e redirixindo/equilibrando a carga aos backends axeitados. Pódese controlar a configuración de HAProxy confd, o que lle permite almacenar información do backend en etcd ou Consul e enviar automaticamente a configuración actualizada a HAProxy cando sexa necesario.

Isto é practicamente como funciona Istio! Pero con algunhas diferenzas:

  • Usos Envoy Proxy en lugar de HAProxy.
  • Almacena a configuración do backend a través da API de Kubernetes en lugar de etcd ou Consul.
  • Os servizos son enderezos asignados na subrede interna (enderezos IP de Kubernetes Cluster) en lugar de 127.0.0.0/8.
  • Ten un compoñente adicional (Citadel) para engadir a autenticación TLS mutua entre o cliente e os servidores.
  • Admite novas funcións como corte de circuítos, rastrexo distribuído, despregamento canario, etc.

Vexamos rapidamente algunhas das diferenzas.

Envoy Proxy

Envoy Proxy foi escrito por Lyft [competidor de Uber no mercado de taxis - aprox. carril]. É parecido en moitos aspectos a outros proxies (por exemplo, HAProxy, Nginx, Traefik...), pero Lyft escribiu o seu porque necesitaba funcións das que carecían outros proxies, e parecía máis intelixente facer un novo en lugar de ampliar o existente.

Envoy pódese usar por si só. Se teño un servizo específico que precisa conectarse a outros servizos, podo configuralo para conectarse a Envoy e, a continuación, configurar e reconfigurar Envoy de forma dinámica coa localización doutros servizos, ao tempo que consigo unha gran cantidade de funcións adicionais, como visibilidade. En lugar dunha biblioteca de cliente personalizada ou de inxectar trazos de chamadas no código, enviamos tráfico a Envoy e recompila métricas para nós.

Pero Envoy tamén é capaz de traballar como plano de datos (plano de datos) para a malla de servizo. Isto significa que Envoy agora está configurado para esta malla de servizo plano de control (plano de control).

Plano de control

Para o plano de control, Istio confía na API de Kubernetes. Isto non é moi diferente de usar confd, que depende de etcd ou Consul para ver o conxunto de claves no almacén de datos. Istio usa a API de Kubernetes para ver un conxunto de recursos de Kubernetes.

Entre isto e entón: Persoalmente pareceume útil Descrición da API de Kubernetesque reza:

O servidor de API de Kubernetes é un "servidor tonto" que ofrece almacenamento, versións, validación, actualización e semántica para os recursos da API.

Istio está deseñado para traballar con Kubernetes; e se queres usalo fóra de Kubernetes, debes executar unha instancia do servidor da API de Kubernetes (e do servizo auxiliar etcd).

Enderezos de servizo

Istio confía nos enderezos ClusterIP que Kubernetes asigna, polo que os servizos de Istio reciben un enderezo interno (non dentro do intervalo). 127.0.0.0/8).

Kube-proxy intercepta o tráfico ao enderezo IP do clúster dun servizo específico nun clúster de Kubernetes sen Istio e envíase ao backend dese proxy. Se estás interesado nos detalles técnicos, kube-proxy configura regras de iptables (ou equilibradores de carga IPVS, dependendo de como estea configurado) para reescribir os enderezos IP de destino das conexións que van ao enderezo IP do Cluster.

Unha vez que Istio está instalado nun clúster de Kubernetes, nada cambia ata que se habilita explícitamente para un determinado consumidor, ou incluso para todo o espazo de nomes, introducindo un contedor. sidecar en vainas personalizadas. Este contedor xerará unha instancia de Envoy e configurará un conxunto de regras de iptables para interceptar o tráfico que vai a outros servizos e redirixilo a Envoy.

Cando se integra co DNS de Kubernetes, isto significa que o noso código pode conectarse polo nome do servizo e todo "simplemente funciona". Noutras palabras, o noso código emite consultas como http://api/v1/users/4242entón api resolver a solicitude de 10.97.105.48, as regras de iptables interceptarán as conexións desde 10.97.105.48 e as reenviarán ao proxy Envoy local, e ese proxy local reenviará a solicitude á API do backend real. Uf!

Volantes adicionais

Istio tamén ofrece cifrado e autenticación de extremo a extremo mediante mTLS (TLS mutuo). Un compoñente chamado Citadela.

Tamén hai un compoñente Batedeira, que o Envoy pode solicitar de cada un solicitude para tomar unha decisión especial sobre esa solicitude dependendo de varios factores, como cabeceiras, carga de back-end, etc... (non te preocupes: hai moitas formas de manter o Mixer funcionando, e aínda que falle, Envoy seguirá traballando). ben como apoderado).

E, por suposto, mencionamos a visibilidade: Envoy recolle unha gran cantidade de métricas ao tempo que proporciona un rastrexo distribuído. Nunha arquitectura de microservizos, se unha única solicitude de API debe pasar polos microservizos A, B, C e D, despois de iniciar sesión, o rastrexo distribuído engadirá un identificador único á solicitude e almacenará este identificador mediante subsolicitudes a todos estes microservizos, o que permitirá todas as chamadas relacionadas deben ser capturadas, atrasos, etc.

Desenvolver ou mercar

Istio ten fama de complexo. En cambio, construír a malla de enrutamento que describín ao comezo desta publicación é relativamente sinxelo usando ferramentas existentes. Entón, ten sentido crear a súa propia malla de servizo?

Se temos necesidades modestas (non necesitamos visibilidade, un interruptor de circuito e outras sutilezas), entón os pensamentos chegan a desenvolver a nosa propia ferramenta. Pero se usamos Kubernetes, quizais nin sequera sexa necesario porque Kubernetes xa ofrece ferramentas básicas para o descubrimento de servizos e o equilibrio de carga.

Pero se temos requisitos avanzados, entón "comprar" unha malla de servizo parece ser unha opción moito mellor. (Isto non sempre é unha "compra" porque Istio é de código aberto, pero aínda necesitamos investir tempo de enxeñería para entendelo, implementalo e xestionalo).

¿Debo escoller Istio, Linkerd ou Consul Connect?

Ata agora só falamos de Istio, pero esta non é a única malla de servizo. Alternativa popular - Linkerd, e hai máis Consul Connect.

Que escoller?

Sinceramente, non o sei. Polo momento non me considero suficientemente competente para responder a esta pregunta. Hai uns cantos interesante artigos cunha comparación destas ferramentas e mesmo puntos de referencia.

Un enfoque prometedor é utilizar unha ferramenta como SuperGloo. Implementa unha capa de abstracción para simplificar e unificar as API expostas polas mallas de servizo. En lugar de aprender as API específicas (e, na miña opinión, relativamente complexas) de diferentes mallas de servizo, podemos usar as construcións máis sinxelas de SuperGloo e cambiar facilmente dunha a outra, coma se tivésemos un formato de configuración intermedio que describa interfaces HTTP e backends capaces de xerar a configuración real para Nginx, HAProxy, Traefik, Apache...

Entreguei un pouco con Istio e SuperGloo, e no seguinte artigo quero mostrar como engadir Istio ou Linkerd a un clúster existente usando SuperGloo, e como este último fai o traballo, é dicir, permíteche cambiar de unha malla de servizo a outra sen sobrescribir as configuracións.

Fonte: www.habr.com

Engadir un comentario