Este blog é o segundo de uma série de blogs que abordam vários aspectos do que foi necessário para construir e operar nosso serviço SaaS :
Em um blog anterior , fornecemos algumas informações básicas sobre nossas necessidades que nos levaram a construir uma nova plataforma. Usando esta plataforma, permitimos que nossos clientes criem um conjunto complexo e diversificado de aplicações — como manufatura inteligente, análise forense de vídeo para segurança pública, negociação algorítmica e redes de telecomunicações 5G.
O primeiro blog desta série abordou um plano de controle distribuído para fornecer vários clusters de aplicativos multilocatários baseados em Kubernetes em nuvem pública, nossos PoPs de rede privada e sites de ponta.
Este blog específico abordará os desafios de conectar e proteger esses clusters de aplicativos distribuídos de três pontos de vista: aplicativo para aplicativo, usuário/máquina para aplicativo e aplicativo para a Internet pública. Apresentaremos um novo caminho de dados L3-L7+ totalmente integrado, um plano de controle distribuído globalmente e nossa rede global de alto desempenho. Essas três coisas se combinam para fornecer um conjunto abrangente de serviços de rede e segurança na ponta, em qualquer VPC na nuvem ou em nossos PoPs de rede global. Depois de operar em produção com mais de 35 clientes por mais de 1 ano, começamos a dimensionar a implantação e sentimos que agora é um bom momento para compartilhar nossa jornada.
Como nossos clientes estão criando aplicativos bastante complexos e de missão crítica — como manufatura inteligente, análise forense de vídeo para segurança pública, negociação algorítmica, transição de telecomunicações para 5G — precisamos oferecer uma experiência sempre ativa, conectada e confiável para os aplicativos e os usuários finais desses aplicativos. Alguns desses aplicativos executam pipelines de dados em clusters dentro de uma nuvem, ou precisam fazer backup de dados em provedores de nuvem, ou precisam de entrega confiável de solicitações de uma base global de usuários para backends de aplicativos, ou precisam de conectividade de alto desempenho de suas máquinas para os backends executados em nuvens centralizadas.
Para atender a essas necessidades, havia dois requisitos da rede física:
R1 — Nuvem para Nuvem — Alto desempenho e conectividade sob demanda de aplicativos em locais de nuvem pública ou privada em todo o mundo
R2 — Internet para a nuvem — Para que dispositivos e máquinas em locais de ponta se conectem de forma confiável ao backend do aplicativo, precisávamos fornecer vários caminhos redundantes, bem como encerrar as conexões o mais próximo possível desses usuários, em vez de fazer o backhaul do tráfego até os backends usando a Internet.
Poderíamos ter resolvido os requisitos R1 e R2 recorrendo a um provedor de serviços de rede como a AT&T, mas eles não poderiam ter nos fornecido uma experiência de rede perfeita e integração com nossos serviços de aplicativos — configuração baseada em API, telemetria de streaming, capacidade de adicionar facilmente nossos próprios prefixos IP ou os de nossos clientes, serviços de rede baseados em API, etc. Além disso, obter cobertura global de provedores de serviços é muito difícil ou proibitivamente caro. Poderíamos ter contratado dois ou três provedores de serviços diferentes para resolver todos esses problemas, mas acabaríamos com uma confusão de sistemas diferentes que se comportam de forma diferente, têm modos de falha diferentes, SLAs diferentes, sistemas de suporte e APIs diferentes (ou nenhuma).
Então acabamos precisando construir nossa própria rede e isso significava que a rede precisava ter três propriedades:
Para atender a essas três propriedades, precisávamos fazer muitas coisas. No entanto, destacaremos cinco grandes itens:
Percebemos muito cedo em nosso desenvolvimento que construir e operar uma rede global exige tempo e habilidades, e precisávamos trazer essa expertise. Após meses de discussões com os fundadores da Acorus Networks (provedora de segurança sediada em Paris, França), acabamos fundindo a Acorus com a Volterra. Isso nos ajudou a resolver o problema da nossa infraestrutura global e implementação de rede. A Acorus havia criado uma ótima rede e uma equipe de operações que automatizou totalmente a configuração da rede física usando o Ansible e construiu APIs externas para telemetria e configuração que poderiam ser usadas por nossa equipe de software.
Agora que tínhamos uma equipe focada em construir uma rede privada global com alta confiabilidade e desempenho, poderíamos resolver facilmente os problemas de tráfego de aplicativo para aplicativo e de usuário/máquina para aplicativo em locais de ponta ou nuvem, conforme mostrado na Figura 1. Além disso, a infraestrutura de computação e armazenamento em nossos PoPs de rede também nos permitiu adicionar terminação de API, segurança de aplicativo e descarregamento de carga de trabalho (da borda ou da nuvem) à nossa rede. Isso nos permitirá continuar a desenvolver nossas capacidades por muito tempo e expandir facilmente nossa cobertura de rede e catálogo de serviços com base na demanda.
Depois que tivemos uma rede global funcional, precisávamos começar a adicionar clusters de aplicativos e nossas cargas de trabalho. Essas cargas de trabalho precisam de seu próprio conjunto de serviços de conectividade e segurança em nível de aplicativo.
Como explicamos em nosso blog anterior , nosso objetivo com a plataforma era melhorar a produtividade e reduzir a complexidade para nossas equipes que gerenciam cargas de trabalho em vários ambientes (nuvem, borda e nossos PoPs de rede).
Cada provedor de nuvem requer configuração, gerenciamento e monitoramento de muitos recursos de nuvem diferentes para conectividade segura com clusters de aplicativos. Por exemplo, no Google Cloud, para configurar um serviço de rede de ponta a ponta para um cluster de aplicativos, será necessário gerenciar muitos conjuntos diferentes de serviços:
Ótimo, embora fosse um grande desafio, poderíamos ter decidido lidar com isso criando uma camada de configuração e operações para harmonizar cada provedor de nuvem. No entanto, sabíamos que isso não resolveria nossos problemas, pois os serviços dos provedores de nuvem não atendiam a todas as nossas necessidades (por exemplo, a capacidade de executar segurança de aplicativos e APIs está ausente nos CSPs), eles não são os melhores da categoria e estão mudando continuamente.
Além disso, o que dizer dos nossos PoPs de rede e sites de ponta? Teríamos que ir e obter recursos semelhantes de fornecedores de hardware ou software e integrá-los todos juntos — por exemplo, Cisco/Juniper para roteamento+nat+vpn, F5 para balanceamento de carga e firewall, etc. Nos locais de ponta, o problema é agravado porque nem os provedores de nuvem nem os grandes fornecedores de rede conseguem resolver os problemas de serviços de rede para um ambiente com recursos limitados. Outra opção potencial para a borda poderia ter sido mover toda a conectividade de aplicativos, segurança e serviços de rede para nossa rede global — mas estava claro que isso não funcionaria, pois o tráfego de aplicativo para aplicativo dentro do mesmo site de borda também precisava de alguns desses serviços.
Depois de muitas discussões e análise do cenário, percebemos que fazer a integração entre muitos provedores de serviços e fornecedores — configuração, telemetria, monitoramento, alertas — e acompanhar seus roteiros e mudanças de API ia contra nossa meta de simplicidade e velocidade.
Outro problema que precisávamos resolver em toda a plataforma era a multilocação — isso era mais fácil de fazer para serviços WAN e roteamento de rede, mas desafiador para serviços de rede de aplicativos, já que a maioria dos balanceadores de carga no mercado não tinha suporte ou tinha suporte muito limitado para multilocação.
Como resultado, decidimos assumir esse desafio e construir um caminho de dados de rede de alto desempenho que não apenas fornece serviço de roteamento de rede, mas também serviços de aplicação, segurança e de longa distância. Isso nos permitiria unificar o portfólio de serviços em nosso ambiente de nuvem híbrida e distribuída e exigiria que dependêssemos de um conjunto mínimo de serviços nativos em nuvens públicas.
Considerando que a equipe tinha boa experiência em rede e que havíamos lidado com esses problemas em nossas vidas anteriores com a Contrail/Juniper, pensamos que a melhor maneira de resolver esse problema seria começar do zero e criar uma solução de rede totalmente nova que integrasse serviços de L3 a L7+ em um único caminho de dados que pudesse ser executado em qualquer nuvem ou edge, sendo gerenciado centralmente por nossa plataforma.
Selecionamos os melhores projetos para iniciar o design deste novo caminho de dados — OpenContrail vRouter (para L3-L4), que já havíamos passado 6 anos desenvolvendo, e Envoy para L7, pois tem grande apoio da comunidade. Dito isso, tivemos que fazer várias alterações e adições para construir um novo caminho de dados L3-L7+ unificado — multilocação para Envoy, dpdk para Envoy, pilha TCP dpdk de espaço de usuário, política de rede, VPNs SSL/IPSec, conexão http, proxy reverso dinâmico, proxy de encaminhamento transparente, gateway de API, política de aplicativo programável, ACLs rápidas para proteção contra DDoS, integração de identidade PKI, mecanismo Chrome v8, segurança de aplicativo e rede, etc.
Este caminho de dados (conforme mostrado na Figura 2 ) é implantado como gateway de entrada/saída para nossos clusters de aplicativos, gateway de software na borda, fornece capacidade de malha de serviços dentro de um cluster ou em vários clusters ou para fornecer serviços de rede de nossos PoPs globais.
Para atender à nossa necessidade de multilocação para a plataforma (abordada no blog anterior), também precisávamos fornecer multilocação completa para esse caminho de dados de rede. Isso foi relativamente simples para a camada de roteamento, pois o vRouter já suportava VRFs . No entanto, tivemos que fazer alterações no Envoy para torná-lo multilocatário. Como não estávamos usando a pilha TCP do kernel, alteramos a interface do soquete TCP do Envoy e adicionamos reconhecimento de VRF.
Esse caminho de dados também precisava executar segurança de API, firewalls de aplicativos e segurança de rede — usamos uma combinação de técnicas algorítmicas e inferência de máquina para isso e abordaremos esse tópico em uma próxima postagem do blog.
Obtivemos muitas vantagens ao construir este caminho de dados de rede:
Capacidades completas de L3 a L7+ com configuração, controle e observabilidade uniformes
O suporte para expansão dentro de um único cluster nos permite oferecer suporte a uma variedade de dispositivos de borda de desempenho muito pequeno e de menor desempenho até 100 Gbps+ ou mais de capacidade em nossa rede ou locais de nuvem pública
Adicione rapidamente novos recursos ao caminho de dados da rede sem depender de nenhum fornecedor de rede e/ou provedor de nuvem
Solução comum com características de falha e desempenho semelhantes em qualquer ambiente — muito importante para nossas equipes de operações.
Agora que podíamos implantar vários clusters de aplicativos com esse novo caminho de dados de rede, era essencial construir um plano de controle distribuído globalmente para configurá-los, controlá-los e monitorá-los.
Há vários problemas que nossa equipe de plataforma teve que resolver como parte da construção de um plano de controle distribuído para gerenciar um grande número de nós de processamento de caminho de dados distribuídos. Como criamos um novo caminho de dados, não havia um plano de controle pronto para uso que pudesse ser aproveitado e tivemos que escrever o nosso próprio: Plano de controle local para gerenciar vários caminhos de dados em execução em um único cluster (nuvem, borda ou PoP de rede). Esses nós de caminho de dados precisavam de um plano de controle local para gerenciar alterações de configuração, atualizações de rotas, executar verificações de integridade, realizar descoberta de serviços, parear com outros dispositivos de rede usando um protocolo como BGP, agregar métricas e logs de acesso, etc. Plano de controle distribuído para gerenciar vários planos de controle locais — Há um plano de controle distribuído em execução em nossos PoPs de rede global ( Figura 3 ) e esse plano de controle é responsável pelo gerenciamento de configuração dos planos de controle locais, pela distribuição do estado operacional em cada um dos planos de controle locais e pela coleta de dados de cada um dos nós. Para distribuir o estado operacional, decidimos usar o BGP, pois ele é consistente e robusto. Como criamos uma implementação de BGP de altíssima escala e multithread como parte do OpenContrail, nós a aproveitamos e adicionamos extensões para balanceamento de carga — distribuição de verificação de integridade, endpoints http/api, propagação de políticas, etc.
Além disso, há um plano de gerenciamento centralizado em execução em duas regiões de nuvem — AWS e Azure — que é usado junto com o plano de controle distribuído para fornecer multilocação, conforme mostrado na Figura 4 . Nossa equipe de SRE pode criar vários locatários e cada locatário fica completamente isolado de outro locatário em nossa rede global. Eles só podem rotear um para o outro usando “rede pública”. Dentro de um locatário, os serviços em namespaces podem se comunicar entre si com base em roteamento http/api/rede e regras de segurança.
Os planos de controle são executados como cargas de trabalho do Kubernetes em um locatário separado e namespace isolado que está sob o controle apenas de nossas equipes de SRE. Como resultado, nenhum desenvolvedor e/ou cliente pode interromper este serviço. Além disso, como o plano de controle é distribuído, uma interrupção de um plano de controle em um local não afeta o serviço geral.
Depois que descobrimos as necessidades de nossa rede global e definimos os requisitos para serviços de rede L3-L7+ para clusters de aplicativos (um novo caminho de dados e plano de controle distribuído), nossa equipe de produtos criou mais requisitos que tornaram nossa vida ainda mais emocionante. Basicamente, eles queriam que entregássemos uma malha de serviços global e intercluster com os seguintes recursos:
Com os requisitos nº 2 e nº 3, o objetivo era melhorar o tempo de resposta e o desempenho da conectividade cliente-servidor, o que é realmente crítico na comunicação entre aplicativos ou em aplicativos SaaS de larga escala. Para atender aos requisitos nº 2 e nº 3, ficou claro que a melhor abordagem seria construir um proxy distribuído (Figura 5) — um proxy de entrada e um proxy de saída e uma rota baseada no endereçamento do aplicativo e não no endereçamento da rede. Além disso, decidimos distribuir a integridade dos endpoints de serviço (ou servidores) aproveitando o plano de controle distribuído e as extensões BGP (conforme descrito anteriormente). Esta implementação do proxy distribuído é mostrada no diagrama abaixo.
Como ele se tornou um proxy distribuído com uma pilha de rede L3-L7+ completa, chamamos isso de gateway de aplicativo distribuído globalmente. Todas as funcionalidades disponíveis em nosso caminho de dados de rede agora estão disponíveis em nossa rede global — por exemplo, balanceamento de carga e roteamento com base em HTTP ou APIs, aplicação de políticas na borda, transformação de API, terminação TLS mais próxima do usuário, etc.
A equipe conseguiu obter vários ganhos com a implementação de um gateway de aplicativo distribuído globalmente. Esses ganhos foram nas áreas de melhorias operacionais, segurança, desempenho e TCO:
Esta série de blogs abordará vários aspectos do que foi necessário para construir e operar nosso serviço SaaS distribuído globalmente com muitos clusters de aplicativos em nuvem pública, nossos PoPs de rede privada e sites de ponta. O próximo é Segurança de Plataforma e Dados …