Likbez lança Istio


Malha de serviço Isstio


Na Namely, estamos usando o Istio há um ano. Ele então acabou de sair. Tivemos uma grande queda no desempenho no cluster Kubernetes, queríamos um rastreamento distribuído e levamos o Istio para executar o Jaeger e descobrir isso. A malha de serviço se encaixa tão bem em nossa infraestrutura que decidimos investir nessa ferramenta.


Eu tive que sofrer, mas nós estudamos isso amplamente. Este é o primeiro post de uma série em que descreverei como o Istio se integra ao Kubernetes e o que aprendemos sobre seu trabalho. Às vezes, vagamos pelas áreas técnicas, mas não muito longe. Além disso, haverá mais posts.


O que é o Istio?


Istio é uma ferramenta de configuração de malha de serviço. Ele lê o estado do cluster Kubernetes e atualiza para proxies L7 (HTTP e gRPC), que são implementados como side-car nos pods do Kubernetes. Esses carros laterais são contêineres Envoy que lêem a configuração da API do Istio Pilot (e o serviço gRPC) e direcionam o tráfego através dela. Com o poderoso proxy L7, podemos usar métricas, traços, lógica de repetição, disjuntor, balanceamento de carga e implantações de canárias.


Vamos começar do começo: Kubernetes


No Kubernetes, criamos usando um deploy ou StatefulSet. Ou pode ser apenas "baunilha" sem um controlador de alto nível. Em seguida, o Kubernetes faz o possível para manter o estado desejado - ele cria pods no cluster no nó, garante que eles sejam iniciados e reiniciados. Quando um under é criado, o Kubernetes passa pelo ciclo de vida da API, garante que cada etapa seja bem-sucedida e só então finalmente cria o under no cluster.


Estágios do ciclo de vida da API:



Obrigado ao Banzai Cloud pela imagem bacana.


Uma das etapas é modificar os webhooks de admissão. Esta é uma parte separada do ciclo de vida no Kubernetes, onde os recursos são personalizados antes de serem confirmados no repositório etcd, a fonte da verdade para a configuração do Kubernetes. E aqui Istio faz sua mágica.


Modificando webhooks de admissão


Quando um sub é criado (via kubectl ou Deployment ), ele passa por esse ciclo de vida e os webhooks modificadores de acesso o alteram antes de liberá-lo para o grande mundo.


Durante a instalação do Istio, o istio-sidecar-injector é adicionado como um recurso de configuração para modificar webhooks:


 $ kubectl get mutatingwebhookconfiguration NAME AGE istio-sidecar-injector 87d 

E a configuração:


 apiVersion: admissionregistration.k8s.io/v1beta1 kind: MutatingWebhookConfiguration metadata: labels: app: istio-sidecar-injector chart: sidecarInjectorWebhook-1.0.4 heritage: Tiller name: istio-sidecar-injector webhooks: - clientConfig: caBundle: redacted service: name: istio-sidecar-injector namespace: istio-system path: /inject failurePolicy: Fail name: sidecar-injector.istio.io namespaceSelector: matchLabels: istio-injection: enabled rules: - apiGroups: - "" apiVersions: - v1 operations: - CREATE resources: - pods 

Ele diz aqui que o Kubernetes deve enviar todos os eventos de criação de lareira para o serviço istio-sidecar-injector no espaço de nome istio-system se o espaço de nome tiver istio-injection=enabled . O injetor inclui mais dois contêineres no PodSpec: um temporário para definir regras de proxy e outro para proxy em si. O injetor de carro lateral insere esses recipientes de acordo com o modelo do mapa de configuração istio-sidecar-injector . Esse processo também é chamado de side-car.


Pods de sidecar


Sidecar são os truques do nosso mágico Istio. O Istio aciona tudo de maneira tão inteligente que, do lado de fora, é apenas mágica, se você não conhece os detalhes. E é útil conhecê-los se você precisar depurar repentinamente solicitações de rede.


Contêineres de inicialização e proxy


O Kubernetes possui contêineres de inicialização únicos temporários que podem ser executados antes dos principais. Eles agrupam recursos, migram bancos de dados ou, como é o caso do Istio, configuram regras de rede.


O Istio usa o Envoy para fazer proxy de todas as solicitações de envio pelas rotas desejadas. Para fazer isso, o Istio cria regras para o iptables e envia o tráfego de entrada e saída diretamente para o Envoy, além de proxies do tráfego para seu destino. O tráfego faz um pequeno desvio, mas você distribuiu o rastreamento, as métricas de consulta e a aplicação de políticas. Neste arquivo, no repositório do Istio, você pode ver como o Istio cria regras para o iptables.


O @jimmysongio desenhou um excelente diagrama de conexão entre as regras do iptables e o proxy Envoy:



Enviado - Tráfego enviado


O Envoy recebe todo o tráfego de entrada e saída; portanto, todo o tráfego geralmente se move dentro do Envoy, como no diagrama. O proxy Istio é outro contêiner que é adicionado a todos os pods modificados pelo injetor lateral do Istio. Nesse contêiner, o processo do Envoy é iniciado, que recebe todo o tráfego da lareira (com algumas exceções, como o tráfego do cluster Kubernetes).


O processo do Envoy descobre todas as rotas por meio da API do Envoy v2, que implementa o Istio.


Enviado e Piloto


O próprio enviado não tem lógica para detectar pods e serviços em um cluster. É um plano de dados e precisa de um plano de controle para guiá-lo. O parâmetro de configuração do Envoy solicita que a porta do host ou serviço receba essa configuração por meio da API do gRPC. O Istio, por meio do serviço Pilot, atende aos requisitos da API do gRPC. O Enviado se conecta a essa API com base em uma configuração de side-car implementada por meio de um webhook de modificação. A API possui todas as regras de tráfego que o Envoy precisa descobrir e rotear para o cluster. Esta é a malha de serviço.



Troca de dados "sob o <-> piloto"


O piloto se conecta ao cluster Kubernetes, lê o status do cluster e aguarda atualizações. Ele monitora os pods, serviços e pontos finais no cluster Kubernetes, para fornecer a configuração correta a todos os carros laterais Envoy conectados ao Pilot. Esta é a ponte entre Kubernetes e Enviado.



De Piloto a Kubernetes


Quando pods, serviços ou pontos de extremidade são criados ou atualizados no Kubernetes, o Pilot aprende sobre ele e envia a configuração necessária para todas as instâncias do Envoy conectadas.


Que configuração está sendo enviada?


Que configuração o Enviado obtém do Istio Pilot?


Por padrão, o Kubernetes resolve seus problemas de rede com um serviço (serviço) que gerencia endpoint . A lista de terminais pode ser aberta com o comando:


 kubectl get endpoints 

Esta é uma lista de todos os IP e portas do cluster e seus endereços (geralmente são pods criados a partir de uma implantação). É importante saber o Istio para configurar e enviar dados de rota para o Envoy.


Serviços, ouvintes e rotas


Ao criar um serviço em um cluster Kubernetes, você inclui atalhos pelos quais todos os pods adequados serão selecionados. Quando você envia tráfego para o IP do serviço, o Kubernetes seleciona o tráfego para esse tráfego. Por exemplo, o comando


 curl my-service.default.svc.cluster.local:3000 

Primeiro, ele encontrará o IP virtual atribuído ao my-service no espaço para nome default e esse IP encaminhará o tráfego para uma sub que corresponda à etiqueta de serviço.


Istio e Envoy mudam ligeiramente essa lógica. O Istio configura o Envoy com base nos serviços e pontos de extremidade no cluster Kubernetes e usa os recursos de roteamento inteligente e balanceamento de carga da Envoy para ignorar o serviço Kubernetes. Em vez de proxy de um IP de cada vez, o Envoy se conecta diretamente à lareira do IP. Para fazer isso, o Istio mapeia a configuração do Kubernetes para a configuração do Envoy .


Os termos Kubernetes, Istio e Enviado são um pouco diferentes, e não está claro o que eles comem imediatamente.


Serviços


Um serviço no Kubernetes é mapeado para um cluster no Envoy. O cluster Envoy contém uma lista de pontos de extremidade , ou seja, o IP (ou nomes de host) das instâncias para o processamento de solicitações. Para ver a lista de clusters configurados no Istio sidecar-pod, execute o istioctl proxy-config cluster < > . Este comando mostra o estado atual das coisas em termos da lareira. Aqui está um exemplo de um de nossos ambientes:


 $ istioctl proxy-config cluster taxparams-6777cf899c-wwhr7 -n applications SERVICE FQDN PORT SUBSET DIRECTION TYPE BlackHoleCluster - - - STATIC accounts-grpc-gw.applications.svc.cluster.local 80 - outbound EDS accounts-grpc-public.applications.svc.cluster.local 50051 - outbound EDS addressvalidator.applications.svc.cluster.local 50051 - outbound EDS 

Todos os mesmos serviços estão neste espaço para nome:


 $ kubectl get services NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) accounts-grpc-gw ClusterIP 10.3.0.91 <none> 80/TCP accounts-grpc-public ClusterIP 10.3.0.202 <none> 50051/TCP addressvalidator ClusterIP 10.3.0.56 <none> 50051/TCP 

Como o Istio sabe qual protocolo usa o serviço? Configura protocolos para manifestos de serviço pelo campo de name na entrada da porta.


 $ kubectl get service accounts-grpc-public -o yaml apiVersion: v1 kind: Service metadata: name: accounts-grpc-public spec: ports: - name: grpc port: 50051 protocol: TCP targetPort: 50051 

Se houver grpc ou o prefixo grpc, o Istio configurará o protocolo HTTP2 para o serviço. Aprendemos por experiência amarga como o Istio usa o nome da porta quando as configurações de proxy estão corrompidas porque não especificaram prefixos http ou grpc ...


Se você usar o kubectl e a página de encaminhamento da porta administrativa no Envoy, poderá ver que os pontos de extremidade account-grpc-public são implementados pelo Pilot como um cluster no Envoy com o protocolo HTTP2. Isso confirma nossas suposições:


 $ kubectl -n applications port-forward otherpod-dc56885ff-dqc6t 15000:15000 & $ curl http://localhost:15000/config_dump | yq r - ... - cluster: circuit_breakers: thresholds: - {} connect_timeout: 1s eds_cluster_config: eds_config: ads: {} service_name: outbound|50051||accounts-grpc-public.applications.svc.cluster.local http2_protocol_options: max_concurrent_streams: 1073741824 name: outbound|50051||accounts-grpc-public.applications.svc.cluster.local type: EDS ... 

A porta 15000 é a página de administração do Envoy , disponível em todos os carros laterais.


Ouvintes


Os ouvintes reconhecem os pontos de extremidade do Kubernetes para passar o tráfego para as lareiras. O serviço de verificação de endereço possui um terminal aqui:


 $ kubectl get ep addressvalidator -o yaml apiVersion: v1 kind: Endpoints metadata: name: addressvalidator subsets: - addresses: - ip: 10.2.26.243 nodeName: ip-10-205-35-230.ec2.internal targetRef: kind: Pod name: addressvalidator-64885ccb76-87l4d namespace: applications ports: - name: grpc port: 50051 protocol: TCP 

Portanto, a lareira de verificação de endereço tem um ouvinte na porta 50051:


 $ kubectl -n applications port-forward addressvalidator-64885ccb76-87l4d 15000:15000 & $ curl http://localhost:15000/config_dump | yq r - ... dynamic_active_listeners: - version_info: 2019-01-13T18:39:43Z/651 listener: name: 10.2.26.243_50051 address: socket_address: address: 10.2.26.243 port_value: 50051 filter_chains: - filter_chain_match: transport_protocol: raw_buffer ... 

Rotas


No Istio, em vez do objeto padrão do Kubernetes Ingress, um recurso personalizado mais abstrato e eficiente é VirtualService - VirtualService . O VirtualService mapeia rotas para clusters upstream, vinculando-os ao gateway. É assim que se usa o Kubernetes Ingress com um controlador do Ingress.


Na Namely, usamos o Istio Ingress-Gateway para todo o tráfego GRPC interno:


 apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: grpc-gateway spec: selector: istio: ingressgateway servers: - hosts: - '*' port: name: http2 number: 80 protocol: HTTP2 --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: grpc-gateway spec: gateways: - grpc-gateway hosts: - '*' http: - match: - uri: prefix: /namely.address_validator.AddressValidator retries: attempts: 3 perTryTimeout: 2s route: - destination: host: addressvalidator port: number: 50051 

À primeira vista, você não entenderá nada no exemplo. Não é visível aqui, mas a implantação do Istio-IngressGateway registra quais pontos de extremidade são necessários com base no istio: ingressgateway . Neste exemplo, o IngressGateway roteia o tráfego para todos os domínios pela porta 80 pelo HTTP2. O VirtualService implementa rotas para esse gateway, corresponde ao prefixo /namely.address_validator.AddressValidator e passa o addressvalidator pela porta 50051 para o serviço upstream addressvalidator uma regra de repetição em dois segundos.


Se redirecionarmos a porta de pod do Istio-IngressGateway e vermos a configuração do Envoy, veremos o que o VirtualService faz:


 $ kubectl -n istio-system port-forward istio-ingressgateway-7477597868-rldb5 15000 ... - match: prefix: /namely.address_validator.AddressValidator route: cluster: outbound|50051||addressvalidator.applications.svc.cluster.local timeout: 0s retry_policy: retry_on: 5xx,connect-failure,refused-stream num_retries: 3 per_try_timeout: 2s max_grpc_timeout: 0s decorator: operation: addressvalidator.applications.svc.cluster.local:50051/namely.address_validator.AddressValidator* ... 

O que pesquisamos no Google enquanto procurávamos no Istio


Ocorre o erro 503 ou 404


Os motivos são diferentes, mas geralmente são:


  • Os aplicativos do side-car não podem entrar em contato com o Pilot (verifique se o Pilot está em execução).
  • O manifesto do serviço Kubernetes tem um protocolo inválido.
  • A configuração do VirtualService / Envoy grava a rota no cluster upstream errado. Comece com o serviço de borda, onde você espera tráfego de entrada, e examine os logs do Envoy. Ou use algo como Jaeger para encontrar erros.

O que significa NR / UH / UF nos logs de proxy do Istio?


  • NR - Sem rota.
  • UH - Upstream Insalubre (upstream inoperável).
  • UF - Falha a montante (falha a montante).

Leia mais no site da Envoy .


Em relação à alta disponibilidade com o Istio


  • Adicione NodeAffinity aos componentes do Istio para distribuir uniformemente lares em diferentes zonas de disponibilidade e aumentar o número mínimo de réplicas.
  • Inicie a nova versão do Kubernetes com o recurso de escalonamento horizontal do pod horizontal. As lareiras mais importantes serão dimensionadas com base na carga.

Por que o cronjob não termina?


Quando a carga de trabalho principal estiver concluída, o contêiner lateral continuará funcionando. Para contornar o problema, desative o sidecar nos cronjobs adicionando a anotação sidecar.istio.io/inject: “false” ao PodSpec.


Como instalar o Istio?


Usamos o Spinnaker para implantações, mas geralmente pegamos os gráficos Helm mais recentes, conjuramos, usamos o helm template -f values.yml e confirmamos os arquivos no Github para ver as alterações antes de aplicá-las via kubectl apply -f - . Isso ocorre para não alterar acidentalmente o CRD ou a API em versões diferentes.


Agradecemos a Bobby Tables e Michael Hamrah por ajudarem a escrever este post.

Source: https://habr.com/ru/post/pt441616/


All Articles