Na madrugada de 19 de maio (EDT), quay.io caiu. O desastre afetou tanto os consumidores do quay.io quanto os projetos de código aberto usando o quay.io como plataforma para construção e distribuição de software. A Red Hat valoriza a confiança de ambos.
A equipe de engenheiros da SRE imediatamente se intrometeu e tentou estabilizar o serviço de cais o mais rápido possível. No entanto, enquanto faziam isso, os clientes perdiam a capacidade de enviar novas imagens e apenas ocasionalmente conseguiam extrair as existentes. Por algum motivo desconhecido, o banco de dados quay.io foi bloqueado depois que o serviço foi ampliado para sua capacidade total.
“ O que mudou? "- esta é a primeira pergunta que geralmente é feita em tais casos. Percebemos que não muito antes do problema, o cluster OpenShift Dedicated (no qual o quay.io está em execução) começou a ser atualizado para a versão 4.3.19. Como o quay.io é desenvolvido com Red Hat OpenShift Dedicated (OSD), atualizações regulares eram comuns e nunca causaram problemas. Além disso, nos últimos seis meses, atualizamos os clusters do Quay várias vezes sem qualquer interrupção do serviço.
Enquanto tentávamos restaurar o serviço, outros engenheiros começaram a preparar um novo cluster OSD com a versão anterior do software para implantar tudo nele em caso de necessidade.
Análise de causa raiz
O principal sintoma da falha foi uma avalanche de dezenas de milhares de conexões de banco de dados que desativou a instância do MySQL. Isso dificultou o diagnóstico do problema. Estabelecemos um limite para o número máximo de conexões de clientes para ajudar a equipe do SRE a avaliar o problema. Não notamos nenhum tráfego incomum no banco de dados: na verdade, a maioria das solicitações era para leituras e apenas algumas eram para gravações.
Também tentamos identificar um padrão no tráfego do banco de dados que poderia causar essa avalanche. No entanto, não foi possível encontrar nenhum padrão nos logs. Enquanto esperávamos que o novo cluster com OSD 4.3.18 ficasse pronto, continuamos tentando lançar os pods quay.io. Sempre que o cluster estava em capacidade total, o banco de dados travava. Isso significava que era necessário reiniciar a instância RDS, além de todos os pods quay.io.
À noite, estabilizamos o serviço no modo somente leitura e desabilitamos o máximo de funções não essenciais (por exemplo, coleta de lixo no namespace) para reduzir a carga no banco de dados. O travamento parou, mas o motivo nunca foi encontrado . O novo cluster OSD estava pronto e migramos o serviço, conectamos o tráfego e continuamos o monitoramento.
O Quay.io foi executado de forma estável no novo cluster OSD, portanto, voltamos aos logs do banco de dados, mas não encontramos uma correlação que explicasse os bloqueios. Os engenheiros do OpenShift trabalharam conosco para ver se as mudanças no Red Hat OpenShift 4.3.19 podem ter causado problemas no Quay. No entanto, nada foi encontrado e o problema não pôde ser reproduzido em laboratório .
Segunda falha
Em 28 de maio, pouco antes do meio-dia EDT, o quay.io travou novamente com o mesmo sintoma: o banco de dados foi bloqueado. Novamente, colocamos todas as nossas forças na investigação. Em primeiro lugar, era necessário restaurar o serviço. Porém , desta vez, reiniciar o RDS e reiniciar os pods do quay.io não deu em nada : outra avalanche de conexões varreu a base. Mas por que?
O Quay é escrito em Python e cada pod opera como um único contêiner monolítico. Muitas tarefas paralelas são executadas simultaneamente no tempo de execução do contêiner. Estamos usando uma biblioteca
geventemgunicornpara processar solicitações da web. Quando o Quay recebe uma solicitação (por meio de nossa própria API ou da API Docker), um trabalhador gevent é atribuído a ele. Normalmente, esse trabalhador precisa se comunicar com o banco de dados. Após a primeira falha, descobrimos que os funcionários do gevent estavam se conectando ao banco de dados usando as configurações padrão.
Dado o grande número de pods Quay e milhares de solicitações recebidas por segundo, o grande número de conexões de banco de dados poderia teoricamente sobrecarregar a instância do MySQL. Graças ao monitoramento, sabia-se que o Quay processa em média 5 mil requisições por segundo. O número de conexões com o banco de dados era quase o mesmo. 5 mil conexões com uma margem se encaixam nos recursos de nossa instância RDS (o que não pode ser dito sobre dezenas de milhares).Por algum motivo, ocorreram picos inesperados no número de conexões , mas não observamos nenhuma correlação com as solicitações recebidas.
Desta vez, estávamos determinados a encontrar e corrigir a origem do problema, não apenas reinicializar. Foram feitas alterações na base de código do Quay para limitar o número de conexões de banco de dados para cada trabalhador gevent . Esse número se tornou um parâmetro na configuração: tornou-se possível alterá-lo "na hora" sem construir uma nova imagem de contêiner. Para descobrir quantas conexões realmente devem ser tratadas, executamos vários testes com um ambiente de preparação que definiu valores diferentes para ver como isso afetaria os cenários de teste de carga. Como resultado, descobriu-se queO Quay começa a lançar erros 502 quando o número de conexões excede 10 K.
Imediatamente implantamos essa nova versão na produção e começamos a monitorar a programação de conexão do banco de dados. No passado, a base era bloqueada após cerca de 20 minutos. Após 30 minutos sem problemas, tínhamos esperança e, uma hora depois, confiança. Restauramos o tráfego da postagem no site e iniciamos a análise post mortem.
Tendo conseguido contornar o problema que levou ao bloqueio, não descobrimos suas verdadeiras causas . Foi confirmado que não está relacionado a nenhuma mudança no OpenShift 4.3.19, pois o mesmo aconteceu na versão 4.3.18, que anteriormente funcionava com o Quay sem problemas.
Claramente havia algo mais escondido no aglomerado.
Estudo detalhado
Quay.io tem usado as configurações padrão para se conectar ao banco de dados por seis anos sem problemas. O que mudou? É claro que o tráfego para quay.io tem crescido constantemente todo esse tempo. No nosso caso, tudo parecia como se um determinado valor limite tivesse sido atingido, o que serviu de gatilho para uma avalanche de conexões. Continuamos a examinar os logs do banco de dados após a segunda falha, mas não encontramos nenhum padrão ou relacionamento óbvio.
Nesse ínterim, a equipe SRE tem trabalhado em melhorias na capacidade de observação da consulta do Quay e na integridade geral do serviço. Novas métricas e painéis foram implantados mostrando quais partes de Quay são mais solicitadas pelos clientes.
O Quay.io funcionou bem até 9 de junho. Pela manhã (via EDT), novamente testemunhamos um aumento significativo no número de conexões de banco de dados. Desta vez, não houve tempo de inatividade , pois o novo parâmetro limitou seu número e não permitiu que a taxa de transferência do MySQL fosse excedida. No entanto, por cerca de meia hora, muitos usuários perceberam que o quay.io estava lento. Coletamos rapidamente todos os dados possíveis usando as ferramentas de monitoramento adicionadas. Um padrão surgiu de repente.
Pouco antes do salto nas conexões, um grande número de solicitações foi enviado para a API do App Registry... O App Registry é um recurso pouco conhecido do quay.io. Ele permite que você armazene coisas como gráficos e contêineres do Helm ricos em metadados. A maioria dos usuários do quay.io não usa esse recurso, mas o Red Hat OpenShift o está usando ativamente. OperatorHub dentro do OpenShift armazena todos os operadores no App Registry. Essas operadoras formam a base do ecossistema de carga de trabalho OpenShift e do modelo operacional centrado no parceiro (nas operações do segundo dia).
Cada cluster OpenShift 4 usa operadores do OperatorHub integrado para publicar um catálogo de operadores disponíveis para instalação e fornecer atualizações para aqueles já instalados. Com a crescente popularidade do OpenShift 4, o número de clusters em todo o mundo também aumentou. Cada um desses clusters carrega o conteúdo do operador para executar o OperatorHub integrado, usando o App Registry dentro do quay.io como back-end. Procurando a origem do problema, perdemos o fato de que, conforme a popularidade do OpenShift aumentava gradualmente, também aumentava a carga em uma das funções quay.io raramente usadas .
Fizemos algumas análises do tráfego de solicitações do App Registry e analisamos o código do registro. Imediatamente, falhas foram reveladas, devido às quais as consultas ao banco de dados foram formadas de forma subótima. Eles não causaram problemas sob carga leve, mas quando aumentaram, tornaram-se uma fonte de problemas. O App Registry revelou ter dois endpoints problemáticos que não reagiram bem a um aumento na carga: o primeiro forneceu uma lista de todos os pacotes no repositório, o segundo retornou todos os blobs de um pacote.
Eliminação de causas
Na semana seguinte, otimizamos o código do próprio App Registry e seu ambiente. Obviamente, as consultas SQL ineficientes foram retrabalhadas, as chamadas desnecessárias ao comando foram eliminadas
tar(ele era executado sempre que um blob era obtido) e o armazenamento em cache era adicionado sempre que possível. Em seguida, conduzimos testes extensivos de desempenho e comparamos o desempenho do App Registry antes e depois das alterações.
As solicitações de API que costumavam levar até meio minuto agora foram concluídas em milissegundos . Implementamos as mudanças na produção na semana seguinte e quay.io está estável desde então. Durante esse tempo, houve vários picos de tráfego no endpoint do App Registry, mas as melhorias feitas evitaram interrupções no banco de dados.
O que aprendemos?
É claro que qualquer serviço tenta evitar o tempo de inatividade. Em nosso caso, acreditamos que os acidentes recentes ajudaram a tornar o quay.io melhor. Para nós mesmos, tiramos várias lições principais que queremos compartilhar:
- Dados sobre quem usa seu serviço e como não são supérfluos . Como o Quay “simplesmente funcionou”, nunca tivemos que perder tempo otimizando o tráfego e gerenciando a carga. Tudo isso criou uma falsa sensação de segurança de que o serviço poderia escalar indefinidamente.
- , — . Quay , . , — , .
- Avalie o impacto de cada uma das funções de serviço . Os clientes raramente usavam o App Registry, por isso não era uma prioridade para nossa equipe. Quando alguns recursos do produto mal são usados, seus bugs raramente "aparecem" e os desenvolvedores param de monitorar o código. É fácil cair na ilusão de que é assim que deveria ser - até que, de repente, esse recurso está no centro de um grande incidente.
Qual é o próximo?
O trabalho para garantir a estabilidade do serviço nunca para e estamos em constante aprimoramento. Os volumes de tráfego na quay.io continuam a crescer e reconhecemos que temos a responsabilidade de fazer o nosso melhor para corresponder à confiança dos nossos clientes. Portanto, estamos trabalhando atualmente nas seguintes tarefas:
- , RDS.
- RDS. . , ( ); .
- . , .
- firewall’ - (WAF), , quay.io.
- , Red Hat OpenShift App Registry (Operator Catalogs), , quay.io.
- O suporte para as especificações de artefato da Open Container Initiative (OCI) pode ser uma substituição de longo prazo para o App Registry. Atualmente está sendo implementado como funcionalidade nativa do Quay e estará disponível para os usuários quando a própria especificação for finalizada.
Todos os itens acima são parte do investimento contínuo da Red Hat em quay.io à medida que passamos de uma pequena equipe semelhante a uma startup para uma plataforma madura conduzida por SRE. Sabemos que muitos de nossos clientes confiam no quay.io para seu trabalho diário (Red Hat incluído!) E tentam ser o mais aberto possível sobre interrupções recentes e esforços contínuos para melhorar.
PS do tradutor
Leia também em nosso blog: