As arquiteturas de software da Uber consistem em milhares de microsserviços que permitem que as equipes façam uma iteração rápida e suportem o crescimento global de nossa empresa.
Esses microsserviços suportam uma variedade de soluções, como aplicativos móveis, serviços internos e de infraestrutura, e produtos, além de configurações complexas que afetam esses produtos nos níveis de cidade e sub-cidade.
Para manter nosso crescimento e arquitetura, a equipe de observabilidade da Uber construiu um pipeline robusto e escalonável de métricas e alertas, responsável por detectar, mitigar e notificar os engenheiros sobre problemas com seus serviços assim que eles ocorrem.
Especificamente, construímos dois sistemas de alerta no data center, chamados uMonitor e Neris, que fluem para o mesmo pipeline de notificação e alerta. O uMonitor é o nosso sistema de alerta baseado em métricas que executa verificações em nosso banco de dados de métricas M3, enquanto o Neris procura principalmente por alertas na infraestrutura no nível do host.
Tanto o Neris quanto o uMonitor utilizam um pipeline comum para enviar notificações e deduplicação. Vamos mergulhar nesses sistemas, juntamente com uma discussão sobre nosso esforço em direção a mais ações de mitigação, nossa nova plataforma de deduplicação de alertas chamada Origami, e os desafios na criação de alertas com alta relação sinal-ruído.
Além disso, desenvolvemos um sistema de alerta de caixa preta que detecta interrupções de alto nível de fora do data center nos casos em que nossos sistemas internos falham ou temos interrupções completas do data center. Um artigo futuro falará sobre essa configuração.
Alertas na Uber
Na escala da Uber, o monitoramento e o alerta exigem pensar fora das soluções tradicionais disponíveis no mercado. O alerta na Uber começou com o Nagios, emitindo verificações de limite do Graphite contra métricas de carbon usando scripts controlados por fonte. Devido a problemas de escalabilidade com nosso cluster de métricas do Carbon, decidimos construir nossa própria plataforma de métricas de grande escala, M3.
Para melhorar a usabilidade de nosso sistema de alertas, desenvolvemos o uMonitor, nosso sistema de alerta baseado em métricas de séries temporais, interno, para métricas armazenadas no M3. Para as métricas não armazenadas no M3, criamos o Neris para realizar verificações de alerta no nível do host.
O uMonitor foi construído com flexibilidade e usa a diversidade de casos em mente. Alguns alertas são gerados automaticamente em métricas padrão, como erros de endpoint e consumo de CPU/memória.
Outros alertas são criados por equipes individuais relacionadas a métricas específicas de suas necessidades. Nós construímos o uMonitor como uma plataforma para lidar com esses variados casos de uso, especificamente:
- Fácil gerenciamento de alertas: iteração para determinar as funções e limites apropriados para um alerta.
- Ações flexíveis: notificações como paginação, e-mails e bate-papo. Suporte para mitigações automatizadas, como reverter implementações e alterações de configuração.
- Lidar com alta cardinalidade: ser capaz de alertar em resposta aos menores problemas críticos do escopo, mas não inundar as equipes com notificações sobre interrupções maiores.
Alerta baseado em métricas com uMonitor
O uMonitor tem três componentes separados: um serviço de armazenamento que possui APIs de gerenciamento de alertas e envolve nossos alertas e armazenamentos de estado do Cassandra; um agendador que rastreia todos os alertas e despacha as tarefas de verificação de alerta para workers a cada minuto para cada alerta; e workers que executam verificações de alerta em relação às métricas subjacentes definidas pelo alerta.
Os workers mantêm o estado de verificação de alerta em uma loja Cassandra e asseguram que as notificações sejam enviadas pelo menos uma vez por meio de um mecanismo de repetição agressivo.
Os workers também são responsáveis por re-alertar de vez em quando (geralmente a cada hora) sobre alertas que continuam sendo disparados. Atualmente, o uMonitor tem 125.000 configurações de alerta que verificam 700 milhões de pontos de dados em mais de 1,4 milhão de séries temporais a cada segundo.
Uma definição de alerta tem uma consulta M3 (Graphite ou M3QL) e limites que determinam se o alerta está violando limites ou não. Uma consulta retorna uma ou mais séries temporais a partir do M3, e os limites são aplicados a cada uma das séries subjacentes. As ações de alerta são enviadas se a consulta violar os limites.
O worker com assistência de estado armazenado no Cassandra mantém uma máquina de estado que garante que as notificações sejam enviadas pelo menos uma vez quando um alerta for acionado, reenviadas periodicamente enquanto o alerta permanecer acionado, e resolvido quando o problema for mitigado.
Existem dois tipos de limites: estáticos e de anomalia. Para métricas que têm um estado estacionário específico ou podemos construir consultas que retornam valores consistentes calculando valores como porcentagens de sucesso/falha, geralmente usamos limites estáticos. Para métricas cíclicas como a contagem de viagens por cidade e outras métricas de negócios, o uMonitor faz uso do Argos, nossa plataforma de detecção de anomalias, para gerar limites dinâmicos para o que representaria valores anômalos de métricas com base em dados históricos.
Alertas de host com Neris
O Neris é o nosso sistema interno de alerta baseado em host, construído para alta resolução de alta cardinalidade por métrica de host não disponível em nosso sistema de métricas M3. A razão para as métricas de host não estarem no M3 é dupla.
Primeiro, verificar cada uma das 1,5 milhão de métricas de host geradas por minuto em 40.000 hosts por data center é mais eficiente no host, em vez de consultar um armazenamento central de métricas; como tal, a sobrecarga de ingerir e armazenar as métricas é desnecessária.
Segundo, até recentemente, a política de retenção do M3 fazia com que as métricas de dez segundos fossem armazenadas por 48 horas e as métricas de um minuto por 30 dias, e não havia necessidade de armazenar métricas do host com essa retenção e resolução.
Como o Nagios exigia que o código fosse escrito e implementado para cada verificação, o que não aumentava à medida que nossa infraestrutura crescia, decidimos construir um sistema internamente.
O Neris tem um agente que é executado em todos os hosts em nossos data centers e periodicamente (a cada minuto) executa verificações de alerta no próprio host.
Em seguida, o agente envia os resultados das verificações para uma camada de agregação que, por sua vez, envia os resultados agregados para o Origami.
O Origami é responsável por decidir quais alertas enviar com base nas regras que analisam o número de alertas com falha e a importância dos alertas subjacentes.
Aproveitando o Origami, o Neris executa aproximadamente 1,5 milhão de verificações por minuto em toda a nossa frota de hosts em cada data center.
Na inicialização do agente em cada host, o Neris extrai informações de definição de alerta para o host a partir de um armazenamento de configuração central chamado Object Config que é amplamente usado pelos serviços de infraestrutura de baixo nível na Uber. Determinar quais alertas serão executados em um determinado host depende de sua função.
Por exemplo, o host que executa o Cassandra terá verificações relacionadas ao status do Cassandra, uso do disco e outras métricas. A maioria dessas verificações de nível de host é criada e mantida pelas equipes da plataforma de infraestrutura.
Lidando com a cardinalidade
A alta cardinalidade sempre foi o maior desafio para nossa plataforma de alerta. Tradicionalmente, isso era feito por meio de uma consulta de alerta que retorna várias séries e permite que regras simples em torno de um alerta sejam acionadas somente se mais de uma determinada porcentagem das séries violar o limite.
O uMonitor também permite que os usuários definam um alerta como dependente de outros alertas – um alerta rastreando um problema com mais escopo depende de um alerta de escopo maior e, se o alerta de escopo maior estiver sendo disparado, o alerta dependente será suprimido.
As técnicas acima funcionaram bem, desde que as consultas retornassem um número limitado de séries e as dependências pudessem ser facilmente definidas.
No entanto, com a Uber crescendo para operar muitas linhas de produtos diferentes em centenas de cidades, os desafios de cardinalidade exigiram uma solução mais genérica. Nós usamos o Origami para ajudar a lidar com a alta cardinalidade.
O Origami é usado pelo Neris como seu principal mecanismo de deduplicação e notificação, e permite notificações consolidadas para alertas do uMonitor.
Para métricas de negócios, o Origami é útil quando precisamos alertar por cidade, por produto e por versão de aplicativo. O Origami permite que os usuários criem alertas/verificações subjacentes para combinações de cidade, produto e versão do aplicativo e alertem sobre políticas agregadas para receber notificações em uma base por cidade/produto/versão do aplicativo.
No caso de interrupções maiores (por exemplo, quando muitas cidades estão tendo problemas ao mesmo tempo), o Origami enviará notificações acumuladas indicando a lista de alertas subjacentes disparados.
No cenário de alerta do host, o Origami nos dá a capacidade de enviar gravidades variadas de notificações com base no estado agregado de um alerta. Vamos considerar um exemplo de uso do espaço em disco em um cluster do Cassandra.
Nesse exemplo, uma política de notificação do Origami para isso poderia ser semelhante a:
- Enviar uma notificação por e-mail se menos de três hosts tiverem 70% de uso de disco.
- Enviar uma página se mais de três hosts tiverem 70% de uso de disco.
- Enviar uma página se um ou mais hosts tiverem 90% de uso de disco.
Notificações de alerta
Notificações de alerta úteis são o principal desafio em escalar nosso sistema de alertas. As ações de alerta começaram principalmente com notificações como a paginação de um engenheiro de plantão para problemas de alta prioridade e o uso de bate-papo ou e-mail para problemas informacionais.
Nosso foco agora se voltou para a construção de ações de mitigação. A maioria dos incidentes e interrupções ocorre devido a alterações ou implementações de configuração. O uMonitor fornece suporte de primeira classe para ações de mitigação que revertem alterações de configuração e implementações recentes.
Para equipes que têm runbooks de mitigação mais complexos, temos suporte para webhooks que fazem uma chamada POST em um endpoint com contexto completo sobre o alerta, que por sua vez pode executar runbooks de mitigação. Além disso, aproveitando nosso pipeline de deduplicação no Origami, podemos suprimir notificações de granularidade mais detalhadas em interrupções maiores.
Além do exposto acima, temos trabalhado para tornar as notificações mais relevantes e direcioná-las para os indivíduos certos. Um esforço recente envolveu a identificação de proprietários de mudanças de configuração/implementação e o contato direto com eles quando um alerta é acionado para um serviço que eles modificaram.
Fizemos esforços adicionais para fornecer mais contexto em notificações de alerta para falhas de serviço relacionadas, combinando informações de rastreamento do Jaeger com informações de alertas.
Gerenciamento de alertas
Como mencionado anteriormente, nós nos esforçamos para construir o uMonitor como uma plataforma em que outras equipes podem construir para casos de uso específicos.
A configuração e o gerenciamento de alertas do host geralmente são especializados principalmente para equipes que mantêm seu próprio hardware dedicado e para equipes que estão construindo plataformas de infraestrutura para a empresa, incluindo armazenamento, métricas e soluções de computação, entre outros. Os alertas são configurados nos repositórios git individuais das equipes, que são sincronizados com o Object Config.
Em alto nível, o uMonitor tem três classes de alertas:
- Alertas gerados automaticamente em métricas padrão relacionadas a CPU, uso de disco e estatísticas de RPC para todos os serviços.
- Alertas únicos criados por meio da interface do usuário para detectar problemas específicos.
- Alertas criados e gerenciados através de scripts e sistemas de configuração externos em camadas no topo do uMonitor.
Temos visto um maior crescimento na última classe de alertas, pois as equipes trabalham para detectar problemas que podem ser alertados com a melhor granularidade possível. A necessidade dessa granularidade se resume ao crescimento global da Uber.
Mudanças de código nos serviços que suportam os aplicativos móveis da Uber geralmente são lançadas em grupos específicos de cidades durante um período de algumas horas.
É muito importante monitorar a saúde de nossa plataforma no nível da cidade para descobrir problemas antes que eles se tornem mais difundidos.
Além disso, os parâmetros de configuração, controlados por equipes de engenharia e operações locais, são diferentes para cada cidade.
Por exemplo, as coletas de passageiros de uma cidade podem estar bloqueadas em uma rua devido a um evento em andamento, como um desfile, ou outros eventos podem causar alterações no tráfego.
Muitas equipes criaram soluções de geração de alertas no uMonitor que lidam com esses casos de uso. Alguns dos desafios que essas ferramentas resolvem são:
- 1 – Iterar e gerar alertas em várias dimensões.
- 2 – Determinar agendas de alerta com base em informações específicas de negócios, como feriados em países/cidades específicos, e configurar essas informações no uMonitor para evitar alertas falsos.
- 3 – Nos casos em que os limites de anomalia estática ou atual não funcionam, determinar limites com base em dados passados ou consultas complexas em métricas subjacentes que se aplicam a linhas de negócios específicas (mais sobre isso abaixo, em Consultas de alerta).
Além disso, muitas dessas soluções geram painéis que estão em sincronia com os alertas gerados.
O uMonitor também fornece uma poderosa UI de edição e causa raiz. O aspecto de edição e experimentação da UI é essencial, pois a maioria das métricas não pode ser usada para alertas como ocorre devido a variações e picos. A equipe de observabilidade fornece orientações sobre como criar consultas mais ideais para alertas.
Consultas de alerta
A linguagem de consulta Graphite e o M3QL fornecem uma infinidade de funções que permitem soluções mais personalizadas. Abaixo, descrevemos alguns exemplos de como fazer com que uma consulta retorne valores mais consistentes para tornar as métricas mais alertáveis:
- 1 – Alertar para dizer movingAverage de métricas ao longo de alguns minutos para suavizar quaisquer picos nas métricas.
- 2 – Combinar o exposto acima com períodos de sustentação para enviar notificações somente se uma violação de limite persistir por um período de tempo definido.
- 3 – Para métricas que possuem padrões para cima e para baixo, use funções derivativas para garantir que os picos em qualquer direção não sejam muito repentinos.
- 4 – Alertar sobre porcentagens/taxas para que a métrica não seja suscetível a mudanças na magnitude das métricas.
Planos futuros
Acabamos de iniciar o caminho para escalar nossos sistemas com a capacidade de detectar pequenos problemas e exibir apenas as informações apropriadas para o usuário, suprimindo alertas desnecessários.
Trabalhamos para permitir que isso esteja em andamento em todas as partes do pipeline, incluindo coleta mais eficiente de métricas, dimensionamento e simplificação da infraestrutura de execução de alertas e criação de interfaces de usuário e interfaces que permitam correlacionar informações em várias origens.
Se qualquer um dos itens acima fizer com que você deseje enfrentar os desafios de observabilidade em escala, considere a possibilidade de se inscrever para um cargo em nossa equipe.
***
Este artigo é do Uber Engineering. Ele foi escrito por Shreyas Srivatsan. A tradução foi feita pela Redação iMasters com autorização. Você pode conferir o original em: https://eng.uber.com/observability-at-scale/