BLOG

Gerenciando milhares de clusters Edge Kubernetes com GitOps

Miniatura F5
F5
Publicado em 18 de dezembro de 2019

Na Volterra, o trabalho da equipe de SRE é operar uma plataforma de ponta global baseada em SaaS . Temos que resolver vários desafios no gerenciamento de um grande número de clusters de aplicativos em vários estados (ou seja, online, offline, inatividade do administrador, etc.) e fazemos isso aproveitando o ecossistema e as ferramentas do Kubernetes com um modelo declarativo baseado em pull usando o GitOps.

Neste blog, descreveremos:

Usando o GitOps para gerenciar e monitorar com eficiência uma grande frota de infraestrutura (hosts físicos ou em nuvem) e clusters K8s

  1. As ferramentas que construímos para resolver problemas em torno de CI/CD
  2. Orquestração de objetos e gerenciamento de configuração
  3. Observabilidade em toda a frota de aglomerados Infra e K8s

Vamos nos aprofundar nas lições aprendidas em escala (3.000 sites de ponta), o que também foi abordado na minha palestra recente no Cloud Native Rejekts em San Diego.

TL;DR (Resumo)

  1. Não conseguimos encontrar uma solução simples e pronta para uso que pudesse ser usada para implantar e operar milhares (e potencialmente milhões) de clusters de aplicativos e infraestrutura em nuvens públicas, locais locais ou nômades.
     
  2. Esta solução precisava fornecer o gerenciamento do ciclo de vida para hosts (físicos ou na nuvem), plano de controle do Kubernetes, cargas de trabalho de aplicativos e a configuração contínua de vários serviços. Além disso, a solução precisava atender aos nossos requisitos de design de SRE — definição declarativa, ciclo de vida imutável, gitops e nenhum acesso direto aos clusters.
     
  3. Depois de avaliar vários projetos de código aberto — por exemplo, Kubespray+Ansible (para implantação do Kubernetes) ou Helm/Spinnaker (para gerenciamento de carga de trabalho) — chegamos à conclusão de que nenhuma dessas soluções poderia atender aos nossos requisitos acima sem adicionar um inchaço significativo de software em cada site de ponta. Como resultado, decidimos construir nosso próprio daemon de software baseado em Golang que executava o gerenciamento do ciclo de vida do host (físico ou na nuvem), do plano de controle do Kubernetes e das cargas de trabalho do aplicativo.
     
  4. À medida que dimensionamos o sistema para 3.000 clusters e mais (dentro de um único locatário), todas as nossas suposições sobre nossos provedores de nuvem pública, escalabilidade de nossos daemons de software, ferramentas de operações e infraestrutura de observabilidade foram sistematicamente quebradas. Cada um deles exigiu uma reestrutura de alguns dos nossos componentes de software para superar esses desafios.

Definição de Edge

  • Customer Edge (CE) — São locais de clientes na nuvem (como AWS, Azure, GCP ou nuvem privada), locais no local (como chão de fábrica, instalação de petróleo/gás etc.) ou locais nômades (como automotivo, robótica etc.). O CE é gerenciado pela equipe de SRE da Volterra, mas também pode ser implantado pelos clientes sob demanda em locais de sua escolha.
  • Bordas Regionais (RE) — São pontos de presença (PoP) da Volterra em instalações de colocation nos principais mercados metropolitanos que são interconectados com nosso próprio backbone privado altamente interligado. Esses sites de ponta regionais também são usados para interconectar com segurança os locais de ponta do cliente (CE) e/ou expor serviços de aplicativos à Internet pública. Os sites de RE são totalmente gerenciados e de propriedade da equipe de operações de infraestrutura da Volterra (Infra SRE).
gerenciar01
Figura 1: Visão geral do sistema

O diagrama de arquitetura (Figura 1) acima mostra a conectividade lógica entre nossos REs e CEs, onde cada CE estabelece conexões redundantes (IPSec ou SSL VPN) com o RE mais próximo.

Requisitos para gerenciamento de borda

Quando começamos a projetar nossa plataforma há cerca de 2 anos, nossa equipe de produto nos pediu para resolver os seguintes desafios:

  1. Escalabilidade do sistema — nossos clientes precisavam que demos suporte a milhares (eventualmente milhões) de sites de ponta do cliente, e isso é muito diferente de executar um punhado de clusters Kubernetes em regiões de nuvem. Por exemplo, um dos nossos clientes tem cerca de 17 mil lojas de conveniência e outro opera mais de 20 mil estações de carregamento. Essa escala significava que tínhamos que construir nossas ferramentas de forma muito diferente do que lidaríamos com alguns clusters.
  2. Implantação Zero-Touch — já que qualquer pessoa deve ser capaz de implantar um novo site sem muito conhecimento de hardware, software ou Kubernetes. O site de ponta precisava se comportar como uma caixa preta que liga, liga para casa e fica online.
  3. Gerenciamento de frotas — gerenciamento simplificado de milhares de sites e cargas de trabalho sem a necessidade de tratá-los individualmente. Qualquer site pode ficar offline ou indisponível no momento de uma alteração ou atualização solicitada. Como resultado, os sites precisam buscar atualizações quando ficam online.
  4. Tolerância a falhas — os sites de ponta precisam estar operacionais mesmo após a falha de qualquer componente. Tudo deve ser gerenciado remotamente e fornecer recursos como redefinição de fábrica ou reconstrução do site em caso de falha. Tivemos que assumir que não há acesso físico ao local.

Princípios de design (sem Kubectl! Não Ansible! Sem Pacotes!)

Considerando nossos requisitos e desafios de operar um sistema altamente distribuído, decidimos definir vários princípios que precisávamos que nossa equipe de SRE seguisse para reduzir problemas posteriores:

  1. Definição Declarativa — todo o sistema deve ser descrito declarativamente, pois isso nos permite criar um modelo de abstração fácil e realizar a validação em relação ao modelo.
     
  2. Gerenciamento de ciclo de vida imutável — no passado, trabalhávamos em grandes instalações de nuvem privada usando ferramentas LCM mutáveis, como Ansible, Salt ou Puppet. Desta vez, queríamos manter o sistema operacional base bem simples e tentar enviar tudo como um contêiner, sem gerenciamento de pacotes ou necessidade de ferramentas de gerenciamento de configuração.
     
  3. GitOps — fornece um modelo operacional padrão para gerenciar clusters Kubernetes. Ele também nos ajuda a obter aprovações, auditorias e fluxos de trabalho de alterações prontos para uso, sem precisar criar um sistema extra de gerenciamento de fluxo de trabalho. Portanto, decidimos que tudo deveria passar pelo git.
     
  4. Sem kubectl — Este foi um dos princípios mais importantes, pois ninguém tem permissão para acesso direto aos clusters de aplicativos. Como resultado, removemos a capacidade de executar o kubectl dentro de clusters de borda individuais ou usar scripts executados em locais centrais, incluindo sistemas de CD centralizados. Sistemas de CD centralizados com um método push são bons para dezenas de clusters, mas certamente não são adequados para milhares sem garantia de 100% de disponibilidade de rede.
     
  5. Nenhuma tecnologia (ou ferramentas) exagerada — nossa experiência passada mostrou que muitas das ferramentas populares de código aberto não correspondem ao que prometem. Enquanto avaliávamos vários projetos da comunidade para fornecer infraestrutura, K8s e cargas de trabalho de aplicativos (como Helm, Spinnaker e Terraform), acabamos usando apenas o Terraform para a parte de infraestrutura virtual e desenvolvemos um código personalizado que descreveremos nas próximas partes deste blog.

Gerenciamento do ciclo de vida do site

Como parte do gerenciamento do ciclo de vida do site de ponta, tivemos que resolver como provisionar o sistema operacional host, fazer configurações básicas (por exemplo, gerenciamento de usuários, autoridade de certificação, hugepages, etc.), ativar K8s, implantar cargas de trabalho e gerenciar alterações de configuração contínuas.

Uma das opções que consideramos, mas eventualmente rejeitamos, foi usar KubeSpray+Ansible (para gerenciar o sistema operacional e implantar K8s) e Helm/Spinnaker (para implantar cargas de trabalho). O motivo pelo qual rejeitamos isso foi que isso exigiria que gerenciássemos 2 a 3 ferramentas de código aberto e, então, fizéssemos modificações significativas para atender aos nossos requisitos, que continuaram a crescer à medida que adicionávamos mais recursos, como dimensionamento automático de clusters de borda, suporte para módulos TPM seguros, atualizações diferenciais, etc.

Como nosso objetivo era manter a simplicidade e minimizar o número de componentes em execução diretamente no sistema operacional (fora do Kubernetes), decidimos escrever um daemon Golang leve chamado Volterra Platform Manager (VPM). Este é o único contêiner Docker do systemd no sistema operacional e atua como um canivete suíço que executa muitas funções:

Ciclo de vida do host

O VPM é responsável por gerenciar o ciclo de vida do sistema operacional host, incluindo instalação, atualizações, patches, configuração, etc. Há muitos aspectos que precisam ser configurados (por exemplo, alocação de hugepages, /etc/hosts, etc)

  1. Gerenciamento de atualização do sistema operacional — infelizmente, a vantagem não diz respeito apenas ao Kubernetes e temos que gerenciar a versão do kernel e do sistema operacional em geral. Nossa vantagem é baseada no CoreOS (ou CentOS, dependendo da necessidade do cliente) com uma partição ativa e passiva. As atualizações são sempre baixadas para a partição passiva quando uma atualização é agendada. Uma reinicialização é a última etapa da atualização, onde as partições ativa e passiva são trocadas. A única parte sensível é a estratégia de reinicialização (para um cluster de vários nós), porque todos os nós não podem ser reinicializados ao mesmo tempo. Implementamos nosso próprio bloqueio de reinicialização etcd (no VPM), onde os nós em um cluster são reinicializados um por um.
  2. Gerenciamento de acesso do usuário ao sistema operacional — nossa necessidade é restringir os usuários e seu acesso ao ssh e ao console remotamente. O VPM realiza todas essas operações, como rotações de CA SSH.
  3. Como desenvolvemos nosso próprio caminho de dados L3-L7 , isso exige que configuremos 2M ou 1G hugepages no sistema operacional host com base no tipo de hardware (ou VM na nuvem)

Ciclo de vida do Kubernetes

Gerenciamento para fornecer ciclo de vida para manifesto do Kubernetes. Em vez de usar o Helm, decidimos usar a biblioteca K8s client-go, que integramos ao VPM e usamos vários recursos desta biblioteca:

  1. Implantação otimista versus pessimista — esse recurso nos permite categorizar aplicativos nos quais precisamos esperar até que estejam íntegros. Ele simplesmente observa as anotações no manifesto do K8s ves.io/deploy: positive .

    Otimista = crie recursos e não espere pelo status. É muito semelhante ao comando apply do kubernetes , onde você não sabe se os pods reais são iniciados com sucesso.

    Pessimista = aguardar o estado do recurso do Kubernetes. Por exemplo, a implantação aguarda até que todos os pods estejam prontos. Isso é semelhante ao novo comando kubectl wait .

  2. Ações de pré-atualização, como pré-extração — às vezes não é possível confiar nas atualizações contínuas do K8, especialmente quando o plano de dados da rede é enviado. O motivo é que o pod antigo é destruído e então o novo pod é retirado. Entretanto, no caso do plano de dados, você perde a conectividade de rede. Portanto, a nova imagem do contêiner não pode ser extraída e o pod nunca será iniciado. A anotação de metadados ves.io/prepull com uma lista de imagens aciona a ação de extração antes que o manifesto K8s seja aplicado.
  3. Tentativas e reversões em caso de falhas aplicadas. Esta é uma situação muito comum quando um servidor K8s API tem algumas interrupções intermitentes.

Configuração em andamento

Além das configurações relacionadas aos manifestos do K8s, também precisamos configurar vários serviços do Volterra por meio de suas APIs. Um exemplo são as configurações de VPN IPsec/SSL — o VPM recebe a configuração do nosso plano de controle global e as programa em nós individuais.

Redefinição de fábrica

Esse recurso nos permite redefinir uma caixa remotamente para o estado original e fazer todo o processo de instalação e registro novamente. É um recurso muito crítico para recuperar um site que precisa de acesso físico/de console.

Embora o gerenciamento do ciclo de vida do K8 possa parecer um grande tópico de discussão para muitas pessoas, para nossa equipe ele provavelmente representa apenas 40–50% do volume geral de trabalho.

Provisionamento Zero-Touch

O provisionamento zero-touch do site de ponta em qualquer local (nuvem, local ou ponta nômade) é uma funcionalidade crítica, pois não podemos esperar ter acesso a sites individuais nem queremos contratar tantos especialistas em Kubernetes para instalar e gerenciar sites individuais. Simplesmente não é possível dimensionar para milhares.

O diagrama a seguir (Figura 2) mostra como o VPM está envolvido no processo de registro de um novo site:

gerenciar02
Figura 2 — Fluxo de provisionamento Zero-Touch
  1. Uma vez ligado, o VPM em execução no CE (representado pela caixa verde) apresentará um token de registro ao nosso plano de controle global (GC) para criar um novo registro. O token de registro fornecido como parte do cloud-init da VM na nuvem e pode ser uma chave inserida por um humano durante o processo de inicialização ou programada no TPM para hardware de ponta.
  2. O GC recebe a solicitação com token, o que lhe permite criar um novo registro sob um locatário (codificado no token). O operador do cliente pode ver imediatamente o novo local de ponta no mapa e aprová-lo, podendo inserir o nome e outros parâmetros de configuração.
  3. O VP-Controller dentro do GC gera configuração (por exemplo, decide quem é o mestre, o lacaio, etc. do K8s) e certificados para etcd, K8s e VPM.
  4. O VPM inicia o bootstrapping do site, incluindo o download de imagens do docker, a configuração de hugepages, a instalação do cluster K8s e a inicialização dos serviços do plano de controle do Volterra.
  5. O VPM configura túneis redundantes (IPSec/SSL VPN) para os dois sites Regionais Edge mais próximos que serão usados para tráfego de dados e interconectividade entre sites e rede pública.

Como você pode ver, todo o processo é totalmente automatizado e o usuário não precisa saber nada sobre configuração detalhada ou executar nenhuma etapa manual. Leva cerca de 5 minutos para que todo o dispositivo fique online e pronto para atender aos aplicativos e solicitações dos clientes.

Atualizações de software de infraestrutura

A atualização é uma das coisas mais complicadas que tivemos que resolver. Vamos definir o que está sendo atualizado em sites de ponta: 

  • Atualizações do sistema operacional — abrange o kernel e todos os pacotes do sistema. Qualquer pessoa que tenha operado uma distribuição padrão do sistema operacional Linux está ciente da dificuldade de atualizar versões secundárias (por exemplo, do Ubuntu 16.04.x para o 16.04.y) e da dificuldade muito maior de atualizar versões principais (por exemplo, do Ubuntu 16.04 para o 18.04). No caso de milhares de sites, as atualizações precisam ser determinísticas e não podem se comportar de forma diferente entre os sites. Portanto, escolhemos o CoreOS e o CentOS Atomic com a capacidade de seguir atualizações A/B com 2 partições e sistema de arquivos somente leitura nos caminhos. Isso nos dá a capacidade de reverter imediatamente, alternando as partições da ordem de inicialização e mantendo o sistema operacional consistente sem precisar manter os pacotes do sistema operacional. Entretanto, não podemos mais atualizar componentes individuais no sistema, por exemplo, o servidor openssh, apenas instalando um novo pacote. Alterações em componentes individuais precisam ser lançadas como uma nova versão imutável do sistema operacional.
     
  • Atualizações de software — abrange serviços de controle VPM, etcd, Kubernetes e Volterra executados como cargas de trabalho do K8s. Como já mencionei, nosso objetivo é ter tudo dentro do K8s rodando como um contêiner systemd. Felizmente, conseguimos converter tudo como cargas de trabalho do K8s, exceto 3 serviços: VPM, etcd e kubelet.

Existem dois métodos conhecidos que podem ser usados para fornecer atualizações para sites de ponta: 

  1. Baseado em push — um método push geralmente é feito por uma ferramenta de CD (entrega contínua) centralizada, como Spinnaker, Jenkins ou CM baseado em Ansible. Nesse caso, uma ferramenta central precisaria ter acesso ao site ou cluster de destino e estar disponível para executar a ação.
  2. Baseado em pull — um método baseado em pull busca informações de atualização de forma independente, sem nenhum mecanismo de entrega centralizado. Ele escala melhor e também elimina a necessidade de armazenar credenciais de todos os sites em um único lugar.

Nosso objetivo com a atualização era maximizar a simplicidade e a confiabilidade — semelhante às atualizações padrão de celulares. Além disso, há outras considerações que a estratégia de atualização precisa satisfazer: o contexto de atualização pode ser apenas com o operador do site, ou o dispositivo pode ficar offline ou indisponível por algum tempo devido a problemas de conectividade, etc. Esses requisitos poderiam ser mais facilmente atendidos com o método pull e, portanto, decidimos adotá-lo para atender às nossas necessidades.

GitOps

Além disso, escolhemos o GitOps porque ele facilitou o fornecimento de um modelo operacional padrão para gerenciar clusters, fluxos de trabalho e alterações de auditoria do Kubernetes para nossa equipe de SRE.

Para resolver os problemas de dimensionamento de milhares de sites, criamos a arquitetura para SRE mostrada na Figura 3:

gerenciar03
Figura 3 — Fluxo do GitOps

Primeiro, quero enfatizar que não usamos o Git apenas para armazenar estados ou manifestos. O motivo é que nossa plataforma precisa não apenas lidar com manifestos do K8s, mas também com configurações contínuas de API, versões do K8s, etc. No nosso caso, os manifestos do K8s representam cerca de 60% de toda a configuração declarativa. Por esse motivo, tivemos que criar nossa própria abstração DSL sobre ele, que é armazenada no git. Além disso, como o git não fornece uma API ou qualquer recurso de mesclagem de parâmetros, tivemos que desenvolver daemons Golang adicionais para SRE: API de configuração, Executor e Controlador VP.

Vamos analisar o fluxo de trabalho de lançamento de uma nova versão de software na ponta do cliente usando nossa plataforma SaaS: 

  1. O operador decide lançar uma nova versão e abre uma solicitação de mesclagem (MR) contra o modelo git
     
  2. Depois que esse MR é aprovado e mesclado, o CI aciona a ação para carregar uma configuração de modelo git em nosso daemon SRE Config-API. Este daemon tem várias APIs para mesclagem de parâmetros, configuração interna de DNS, etc.
     
  3. O Config-API é monitorado pelo daemon Executor; imediatamente após as alterações do git serem carregadas, ele começa a renderizar os manifestos finais do K8s com a versão na anotação. Esses manifestos são então carregados no armazenamento de artefatos (como S3) no caminho ce01-site// .yml
     
  4. Depois que a nova versão é renderizada e carregada no armazenamento de artefatos, o Executor produz um novo status com uma versão disponível para a API do cliente; isso é muito semelhante à nova versão disponível em telefones celulares
     
  5. O cliente (ou operador) pode agendar uma atualização do seu site para a versão mais recente e essa informação é repassada ao VP-Controller. VP-Controller é o daemon responsável pelo gerenciamento do site, incluindo provisionamento, descomissionamento ou migração para um local diferente. Isso já foi parcialmente explicado no provisionamento zero-touch e é responsável por atualizar sites de ponta por meio da API mTLS
     
  6. A última etapa do diagrama acontece no site de ponta — uma vez que a conexão VPN IPSec/SSL esteja ativa, o VP-Controller notifica o VPM na ponta para baixar atualizações com a nova versão; no entanto, se a conectividade estiver quebrada ou tiver problemas intermitentes, o VPM pesquisa por uma atualização a cada 5 minutos
     
  7. Novos manifestos e configurações do K8s são buscados e implantados no K8s. Usando o recurso de implantação pessimista descrito na seção anterior, o VPM aguarda até que todos os pods estejam prontos
     
  8. Como última etapa, o VPM envia o status da atualização de volta ao VP Controller e ele é enviado como status para a API do cliente.

Você pode assistir a uma demonstração de todo o fluxo de trabalho aqui:

Lições aprendidas com testes de 3000 sites de ponta

Nas seções anteriores, descrevemos como nossas ferramentas são usadas para implantar e gerenciar o ciclo de vida de sites de ponta. Para validar nosso design, decidimos construir um ambiente de grande escala com três mil sites de ponta do cliente (conforme mostrado na Figura 4)

gerenciar04
Figura 4 — 3000 sites de ponta do cliente

Usamos o Terraform para provisionar 3.000 VMs na AWS, Azure, Google e nossa própria nuvem bare metal local para simular escala. Todas essas VMs eram CEs (sites de ponta do cliente) independentes que estabeleciam túneis redundantes para nossos sites de ponta regionais (também conhecidos como PoPs).

A captura de tela abaixo é do nosso painel SRE e mostra números de arestas em locais representados pelo tamanho do círculo. No momento em que fizemos a captura de tela, tínhamos cerca de 2.711 sites de ponta saudáveis e 356 não saudáveis.

gerenciar05
Figura 5–3000 Implantação do Customer Edge Site

Principais conclusões:Operações

Como parte do dimensionamento, encontramos alguns problemas na configuração e no lado operacional que exigiram que fizéssemos modificações em nossos daemons de software. Além disso, tivemos muitos problemas com um provedor de nuvem que levaram à abertura de vários tíquetes de suporte — por exemplo, latência de resposta da API, incapacidade de obter mais de 500 VMs em uma única região, etc. 

  1. Otimizar o VP-Controller — inicialmente, processamos o registro em série e cada processo levava cerca de dois minutos, pois precisávamos criar vários certificados para etcd, kubernetes e VPM. Otimizamos esse tempo por meio de chaves pré-geradas usando maior entropia e paralelização com um número maior de trabalhadores. Isso nos permitiu processar o registro de 100 sites em menos de 20 segundos. Conseguimos atender a todos os 3.000 sites de ponta consumindo apenas uma vCPU e 2 GB de RAM em nosso VP-Controller.
     
  2. Otimize a entrega de imagens do Docker — quando começamos a escalar, percebemos que a quantidade de dados transmitidos para sites de ponta é enorme. Cada borda baixou cerca de 600 MB (multiplicado por 3.000), totalizando 1,8 TB de dados transmitidos. Além disso, reconstruímos sites de ponta diversas vezes durante nossos testes, então esse número seria muito maior. Como resultado, tivemos que otimizar o tamanho das nossas imagens do Docker e criar imagens de nuvem e iso com imagens do Docker pré-extraídas para reduzir o download. Embora ainda estejamos usando um serviço de registro de contêineres em nuvem pública, estamos trabalhando ativamente em um design para distribuir nosso registro de contêineres por meio de REs (PoPs) e executar atualizações incrementais (binary-diff).
     
  3. Otimize as operações do banco de dados de controle global — todos os nossos serviços de controle Volterra são baseados na estrutura de serviço Golang que usa o ETCD como banco de dados. Cada site é representado como um objeto de configuração. Cada objeto de configuração do site tem alguns StatusObjects, como software-upgrade, hardware-info ou ipsec-status. Esses StatusObjects são produzidos por vários componentes de plataforma e todos são referenciados em uma API de configuração global. Quando atingimos 3000 sites, tivemos que fazer certas otimizações em nosso esquema de objetos. Por exemplo, limite o número de tipos de StatusObjects aceitos pela API de configuração global ou decidimos movê-los para uma instância ETCD dedicada para reduzir o risco de sobrecarregar o banco de dados de objetos de configuração. Isso nos permite fornecer melhor disponibilidade e tempo de resposta para o banco de dados de configuração e também nos permite reconstruir o banco de dados de status em caso de falhas. Outro exemplo de otimização foi parar de fazer operações desnecessárias de lista de objetos do site em todos os locatários ou introduzir índices secundários para reduzir a carga no banco de dados.

Principais conclusões:observabilidade

A observabilidade em um sistema distribuído apresentou um conjunto muito maior de desafios à medida que dimensionávamos o sistema.

Inicialmente, para métricas, começamos com a federação do Prometheus — o Prometheus central no controle global federando o Promethei em bordas regionais (REs), que extrai suas métricas de serviço e federa métricas de seus CEs conectados. O Prometheus de nível superior avaliou alertas e serviu como uma fonte de métricas para análises posteriores. Atingimos rapidamente os limites dessa abordagem (cerca de 1000 CEs) e tentamos minimizar o impacto do número crescente de CEs. Começamos a gerar séries pré-calculadas para histogramas e outras métricas de alta cardinalidade. Isso nos poupou um ou dois dias e depois tivemos que empregar listas de permissões para métricas. No final, conseguimos reduzir o número de métricas de séries temporais de cerca de 60.000 para 2.000 para cada local de CE.

Por fim, após continuar expandindo para mais de 3.000 locais CE e permanecer em produção por muitos dias, ficou claro que isso não era escalável e tivemos que repensar nossa infraestrutura de monitoramento. Decidimos abandonar o Prometheus de nível superior (no controle global) e dividir o Prometheus em cada RE em duas instâncias separadas. Um é responsável por coletar métricas de serviços locais e o segundo por federar métricas de CE. Ambos geram alertas e enviam métricas para o armazenamento central no Cortex. O Cortex é usado para fonte analítica e de visualização e não faz parte do fluxo de alerta de monitoramento principal. Testamos diversas soluções de métricas centralizadas, como Thanos e M3db, e descobrimos que o Cortex era o mais adequado às nossas necessidades.

gerenciar06
Figura 6 — Arquitetura de coleta de métricas

A captura de tela a seguir (Figura 7) mostra o consumo de memória da extração de prometheus-cef no momento de 3.000 endpoints. O interessante é que os 29,7 GB de RAM foram consumidos, o que não é tanto considerando o tamanho do sistema. Ele pode ser otimizado ainda mais dividindo a raspagem em várias delas ou movendo a gravação remota para o Cortex diretamente na própria borda.

gerenciar07
Figura 7 — Utilização de recursos no local de RE

A próxima captura de tela (Figura 8) mostra quanta memória e recursos de CPU precisamos para ingestores Cortex (máx. 19 GB de RAM) e distribuidores nessa escala. A maior vantagem do Cortex é o dimensionamento horizontal, que nos permite adicionar mais réplicas em comparação ao Prometheus, onde o dimensionamento precisa ocorrer verticalmente.

gerenciar08
Figura 8 — Utilização de recursos no controle global

Para infraestrutura de registro em CEs e REs, usamos serviços Fluentbit por nó, que coletam os eventos de registro de serviço e sistema e os encaminham para o Fluentd no RE conectado. O Fluentd encaminha os dados para o ElasticSearch presente no RE. Os dados do ElasticSearch são avaliados pelo Elastalert e regras são definidas para criar alertas do Alertmanager. Estamos usando nossa integração personalizada do Elastalert com o Alertmananger para produzir alertas com os mesmos rótulos produzidos pelo Prometheus.

Os pontos-chave em nossa jornada de monitoramento:

  • Usando novos filtros de federação do Prometheus para descartar métricas e rótulos não utilizados

    - Inicialmente tínhamos cerca de 50.000 séries temporais por CE com uma média de 15 rótulos

    - Nós otimizamos para 2000 por CE em média

    Listas while simples para nomes de métricas e listas negras para nomes de rótulos

  • Migração da federação global Prometheus para o cluster Cortex

    - O Prometheus centralizado removeu todos os Prometheus REs e CEs

    - Em 1000 EC, tornou-se insustentável gerir a quantidade de métricas

    - Atualmente temos Prometheus em cada RE (federando-se aos Promethei dos CEs conectados) com RW para Cortex

  • Clusters e logs do Elasticsearch

    - Arquitetura de registro descentralizada

    - Fluentbit como coletor em cada nó encaminha logs para Fluentd (agregador) em RE

    - O ElasticSearch é implantado em cada RE usando pesquisa de cluster remoto para consultar logs de uma única instância do Kibana

Resumo

Espero que este blog lhe dê uma ideia de tudo o que precisa ser considerado para gerenciar milhares de sites e clusters de ponta implantados ao redor do mundo. Embora tenhamos conseguido atender e validar a maioria dos nossos requisitos iniciais de design, ainda há muitas melhorias pela frente…