Elastic
O ElasticSearch é usado para guardar o log das conversas e o log da avaliação das conversas, de cada chatbot.
Cada chatbot contém pelo menos 3 índices por ambiente (d + p):
- Índice de Log: cada documento tem o UUID da conversa, a pergunta, a resposta, qual componente forneceu a resposta, dentre outros detalhes daquela interação.
- Índice de Vote: cada documento tem o UUID da conversa e o resultado da avaliação da conversa sendo 0 negativa e 1 positiva.
- Índice de Billing: guarda o consumo de recursos bilhetados do chatbot, incluindo mensagens, conversas e tokens de IA Generativa usados na conversação e na ingestão de documentos das bases de conhecimento.
Ademais, os motores Perguntas & Respotas e GPT, se incluídos, criam índices no qual são feitas buscas para conseguir achar as respostas/chunks candidatos.
Há também um índice global que guarda o log de auditoria do uso da própria plataforma, retendo informação sobre quais usuários executaram os casos de uso do Manager que criam/atualizam dados.
Dados quantitivos sobre as mensagens e conversas de um cahtbot ao longo do tempo, para fins de bilhetagem por exemplo, são obtidos fazendo consultas no Elastic.
Na Produção o Elastic funciona em modo cluster.
Problema de quantidade máxima de shards abertos
O Elastic fatia um índice em pedaços distribuídos (shards), e espalha réplicas deles para dar mais disponibilidade e desempenho. O Elastic não recomenda ter muitos shards abertos, pois isso prejudica sua performance e a gestão dos recursos distribuídos.
Diferente de outras aplicações que possuem um conjunto estável de índices e um crescimento controlado, o Serprobots faz um uso dinâmico e imprevisível do cluster Elastic, já que chatbots novos podem ser criados a qualquer momento em diferentes contas.
Cada chatbot demanda a criação de 6 índices: log-d, log-p, vote-d, vote-p, billing-d e billing-p. Cada índice desse tem 2 shards (redudância distribuida do elastic). Inicialmente os indices de log e billing estavam configurados com 2 réplicas, e o de billing 1 réplica. Dessa forma, no total estavam sendo gerados 20 shards abertos por chatbot no cluster.
Nos Elastic não produtivos, havia um limite de 1000 shards abertos. Já no de produção o limite estava em 3000. Quando esse limite é atingido, quando o integrador tenta criar os índices de um chatbot novo o erro abaixo acontece:
{"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"Validation Failed: 1: this action would add [2] shards, but this cluster currently has [3000]/[3000] maximum normal shards open;"}],"type":"illegal_argument_exception","reason":"Validation Failed: 1: this action would add [2] shards, but this cluster currently has [3000]/[3000] maximum normal shards open;"},"status":400}
Quando isso ocorre, é preciso tomar medidas seja para reduzir a quantidade de shards abertos, seja para aumentar o limite. No entanto, uma revisão geral sobre a governança desses índices e até sobre o modelo de uso do cluster é necessária, pois deixar o limite "inifinito" poderá incorrer em baixo desempenho.
Em Maio de 2024, esbarramos no teto de 3000 shards na Produção. Emergencialmente, aumentamos o limite de shards abertos para 6000 com o comando abaixo:
PUT _cluster/settings
{
"persistent": {
"cluster.routing.allocation.total_shards_per_node" : 6000,
"cluster.max_shards_per_node" : 6000
},
"transient": {
"cluster.routing.allocation.total_shards_per_node" : 6000,
"cluster.max_shards_per_node" : 6000
}
}
Para listar os shards abertos, use o comando:
GET /_cat/shards
Para contar quantos shards estão abertos, use o comando:
GET _cluster/stats?filter_path=indices.shards.total
Em Julho de 2024 foi feito um acompanhamento e verificou-se que existiam 4035 shards abertos. Algumas medidas novas foram feitas para gerenciar melhor a situação.
O número de replicas dos índices de log e vote foram abaixadas de 2 para 1. Com isso, a quantide de shards abertos passou para 2888.
Os comandos que abaixaram a quantidade de réplicas foi:
PUT /vote-*/_settings
{ "number_of_replicas": 1 }
PUT /log-*/_settings
{ "number_of_replicas": 1 }
TO-DO #1: revisar o Integrador, pois, apesar da quantidade de réplicas estar parametrizada em 1, os índices parecem estar sendo criados com 2 réplicas mesmo assim. Garantir que os 2 índices (log, vote e billing) são criados com 2 shards + 1 réplica.
TO-DO #2: lá no início a plataforma fora pensada para ter uma quantidade máxima de chatbots suportada por uma "célula" de infraestrutura. O cluster Elastic suporta uma quantidade grande de chatbots, possivelmente o limite é mais pautado pela disponibilidade de CPU e memória RAM do Estaleiro. Mas é preciso ainda sim estabelecer um limite ainda que teórico apra o Elastic, já que seu uso ficou mais "caixa-preta" quando virou PAAS.
TO-DO #3: avaliar o que pode ser liberado de recurso do Elastic quando o chatbot é arquivado/excluído. Pelo que foi já foi visto, fechar o índice preserva os dados em disco mas torna ele indisponível para leitura, o que traria impacto nos dashboards que vão nele. O dashboard analítico não tem problema ficar indisponível, pois o chatbot foi arquivado. Já o gerencial, seria benéfico se realmente fosse feito um esquema de ETL, levando os fatos consolidados do chatbot para outro índice periodicamente, permitindo de fato arquivar os índices brutos de log/vote/billing depois dessa consolidação.
Proposta de remodelagens dos índices
Algumas mudanças na modelagem dos índices seriam benéficas, dentre elas:
- Unificar os índices dos 2 ambientes num só, colocar o nome do ambiente num campo. Na stack e no gateway, ignorar o sufixo
-dou-pdo nome do chatbot acessado. - Passar a gravar o mnemônico do chatbot em cada registro, pois hoje naõ tem esse campo, temos apenas o metadado do nome do índice (
_index) que impões algumas limitações. - TALVEZ unificar log e vote. O índice de log é orientado por troca de mensagem, e o índice de vote é orientado por conversa. No log temos todo o histórico de mensagens recebidas, dentre as a avaliação e o feedback, que ocorrem em mensagens diferentes. No vote o resultado da avaliação fica consolidade pela chave da conversa. Para deixar de ter o índice de vote, teriamos que ter campos que permitissem chegar assertivamente no valor da avaliação e no texto do feeback. Uma idéia é um campo "message_type", que poderia ser "answer/assessment/feedback", então bastaria filtrar o log por ele para chegar em todas as avaliações e em todos os feedbacks.
Para fazer qualquer mudança de modelagem, o gateway e a stack precisam passar a gravar essa informação da forma atualizada. Então ela só vai existir para registros novos, logo, pode ser interessante pensar em atualizar os dados legados para que os paineis não fiquem com ausência de informações mais críticas.
O Elastic não atualiza os registros. O que você faz é ler os registros de um índice A e gravá-los num índice B com modificações. Como nossos índices utilizam alias, fica possível fazer isso à quente.
Um passo a passo para reindexação do log-bot-fulano-p seria:
- Garantir que a stack/gateway já estejam escrevendo da forma "nova" no índice corrente (
log-bot-fulano-p-2023.12.12.) - Criar um índice novo, sem o alias ainda. Usar o sufixo de data do dia corrente, ex:
log-bot-fulano-2024.07.17 - Fazer o reindex lendo do índice antigo (
log-bot-fulano-p-2023.12.12)para o novo (log-bot-fulano-2024.07.17). O reindex pode ser feito disparando um job no próprio Elastic se a regra de reindaxação for simples. Se for algo mais complexo, pode ser preciso escrever um programa que leia de um ponto e escreve em outro. - Depois que finalizar o reindex, remover o alias do índice velho e adicionar o alias no índice novo. Há uma operação de API que consegue fazer as duas operações atomicamente (vide https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html). Assim, as novas escritas passarão a ir pra ele.
- Conferir se ficou tudo ok e excluir o índice antigo.
Esse processo pode demorar. O ideal seria fechar a plataforma para criação de novos chatbots numa janela de "semi-indisponibilidade", fazer toda a migração de stack, fazer rodas as reindexações, e depois liberar para continuar sendo utilizada.
Dump do Elastic
Se for necessário extrair um dump do Elastic, pode ser utilizada a ferramenta elasticdump para Node (https://www.npmjs.com/package/elasticdump). Ela permite copiar índices de uma fonte para um alvo, inclusive arquivo JSON.
elasticdump --maxRows 50000 --params '{"size":1000}' --noRefresh --sourceOnly --input=https://22144239827:Serpro123\!@elastic-h-serprobots.estaleiro.serpro.gov.br/log-hbot-demoaraguaina-p*/ --output=out/demoaraguaina-log-p.json --type=data
O script vai informar quantos objetos ele leu/escreveu, e você pode confrontar isso com a quantidade de objetos indexados usando um _count no Elastic.
https://elastic-h-serprobots.estaleiro.serpro.gov.br/log-hbot-demoaraguaina-d*/_count
O Elastic de onde será extraído o dump, precisa estar exposto de alguma forma, através de um endereço IP ou URL. Lembre-se de não deixar elastic de ambientes produtivos expostos após o uso.
Kibana
Todos os ambientes possuem Kibana acessível e usuários com permissão de administração para a própria equipe Serprobots no space Default.
Quando um chatbot é criado, o Integrador chama a API do Elastic e do Kibana para criar usuário de acesso ao Kibana para aquele chatbot (vide o menu Acompanhar do Serprobots onde informa usuário e senha do Kiaban daquele cahtbot). Esses usuários criados tem acesso somente leitura e apenas ao space Curadoria que contém dashboards específicos para acompanhar as conversas e mensagens de um determinado chatbot.
Estratégia para Mudança no Modelos de Dados
- O mais problemático é migrar todas as publicações de stack e garantir que estão funcionando.
- Tentar uma estratégia que permita conviver dados novos + velhos e ir migrando os bots sem muita criticidade, no fluxo normal de uso das equipes.
- Avaliar impactos durante transição.
ETAPA 1: mudanças globais no código
-
Alterar integrador: criar um template de índice único para ambos ambientes.
- Deve ficar de forma que seja criado um índice físico único (sem o -d ou -p) com sufixo de data com o alias sem data (ex:
log-bot-fulano-2024.08.12->log-bot-fulano) - Vale pro
loge probilling
- Deve ficar de forma que seja criado um índice físico único (sem o -d ou -p) com sufixo de data com o alias sem data (ex:
-
Alterar stack para retornar o message_type (importante para recuperar voto e feedback do assessment), e para não gravar voto no índice vote. Indice de billing gravar "ambiente" e "mnemonico".
-
Alterar gateway para gravar logs em
log-bot-fulanotanto vindo de d quanto de p, com os campos novos de log (mnemonico + ambiente + message_type). -
Criar um dashboard que enxergue o índice único, filtre por ambiente e não vá no índice "vote".
- Avaliação da conversa: agregar por
message_type == assessment - Feedback da conversa: filtrar por
message_type == feedback
- Avaliação da conversa: agregar por
-
Alterar a interface única para apresentar apenas o dashboard único.
- Alterar menu no frontend
- Mudar URL no backend
OBSERVAÇÕES IMPORTANTES: - Stack/gateway já podem gravar dado novo o quanto antes, mesmo nos indices antigos - Dashboard novo pode ser feito antes e ficar pronto, mas vai precisar da presença de dados no novo formato para conseguir implementar/testar. Fazer algum esforço manual para desimpedir isso. - Gateway/integrador/interface-unica só podem subir quando for rolar a virada. Implementar em branches. Ideal seria fazer merge e subir pra produção isolado de outras mudanças impactantes.
ETAPA 2: Criar scripts de migração de dados e de stack
-
Criar script batch para criar indices unicos prots bots logados (vide ETAPA 2)
-
Criar script batch para reindexar dados dos 2 indices separados no novo índice único
-
Criar script de subida de stack global (alterar stack no banco + clonar imagem + republicar)
ETAPA 3: Bloquear plataforma e implantar mudanças
Migrar D num dia, migrar H no outro e P na data combinada (fim de semana?).
-
Bloquear serprobots para criação de novos chatbots
- Pode ocorrer qualquer dia, mas pode ser num sábado pra também não impactar se outras coisas respingarem.
-
Rodar um batch para cada chatbot existente:
- Criar um alias sem ambiente (
log-bot-fulano,billing-bot-fulano) - Criar um indice fisico para ser indice de escrita (
log-bot-fulano-2024.08.12) - Dar alias aos 2 indices legados
- Criar um alias sem ambiente (
-
Publicar gateway, integrador, backend + frontend da interface unica.
-
Liberar ambiente para criar novos chatbots.
-
Deixar dashboards indisponíveis até terminar de migrar stacks e reindexar dados antigos.
- Prever forma de ligar/desligar dashboard.
OBSERVAÇÕES IMPORTANTES:
- Gateway "novo" irá gravar no indice unico tão loga seja publicado
- Stack "velha" continuará gravando em vote-d/p e billing-d/p.
- Stack "nova" não gravá nada no vote (retornará message_type pro gateway gravar no log) e passará a gravar num billing único.
- Dashboard trará a união dos 3 índices (unico + legado-d + legado-p)
- Dashboard unico estará errado temporariamente (dados antigos não terão campos "ambiente", "mnemonico" e "message_type"), mas ficará corrigido depois de rodar o "reindex".
- Quantidades de conversas/mensagens não estará errada
- Soma de tokens total não estará errada
- Votos e feedbacks da avaliação só vão enxergar formato novo
- Se filtrar por ambiente é que ficará com contagens erradas nos dados antigos
- SOLUÇÃO DA EQUIPE: Dashboard fica indisponível até reindexarmos todo mundo.
ETAPA 4: migrar chatbots legados de stack e reindexar dados antigos
-
Após migrar um bot de stack (ambas as publicações devem estar na stack nova, d e p), rodar script para reindexar os dados antigos no formato novo:
- INDICE LOG: Gerar os campos "ambiente" e "mnemonico" e "message_type". Usar os valores guardados no índice de "log" e "vote" para entender o "message_type" das mensagens antigas.
- Ex: o índice
log-bot-fulano-d-2024.01.01vai ser reindexado nolog-bot-fulano-2024.08.12com ambiente d e mnemonico log-bot-fulano. - o índice reindexado poderá ser excluído depois.
- INDICE BILLING: Gerar os campos "ambiente" e "mnemonico".
- Deixar algum critério para se precisar apagar registros e reindexar de novo
-
Deixar os índices antigos somente leitura e programar para apagá-los no mês seguinte (margem de segurança se precisar reindexar de novo).
Resumo da estratégia:
- Alterar código da plataforma (1 semana).
- Criar e testar scripts de migração (1 semana).
- Bloquear plataforma temporariamente para que não sejam criados novos bots (sábado?)
- Rodar script para criar índice unico pros bots legados (durante o bloqueio).
- Subir nova versão da plataforma que passará a escrever no índice único (durante o bloqueio)
- Disponibilizar plataforma novamente com Dashboard de acompanhamento indisponível.
- Rodar scripts para atualizar versão dos chatbots (stack) e reindexar dados legados no índice novo (se tudo der certo, logo após migração, eventuais problemas podem ser tratados na semana seguinte).
- Reativar dashboard após todos estarem migrados (no máximo 1 semana).
Gerenciar migração por contas: o script de migração é acionado pra 1 chatbot. Acionar as migrações em conjuntos de bots de uma mesma conta para controlar quais contas já estão 100% migradas.
IaC
Sempre que houver necessidade de criar um novo ambiente para o Elastic dentro do IaC, seguir os seguintes passos.
Arquitetura
Alterar o arquivo arquitetura.yml do repositório do IaC do Serprobots para incluir um novo Elasticsearch. Criar a issue no IaC para deploy de ambiente. Aguarde até que a instância do Elasticsearch esteja criada.
Configuração
A issue no Git de demandas do IaC será atualizado, contendo o IP do servidor. Será necessário conectar neste servidor usando seu CPF e senha do Rundeck. Caso ainda não tenha uma senha no Rundeck, acessar https://rundeck.cd.serpro/user/login. A senha deste ambiente é diferente da senha do LDAP. Caso ainda não tenha uma senha, crie ou recupere sua senha antiga.
Conecte via ssh ao IP do servidor: ssh -l <cpf> <ip>. Logo que conectar, execute sudo su para obter acesso de root. Todos os comandos precisarão de permissões de root, senão, dará erro de "Permission denied".
Agora, precisamos trocar a senha do usuário padrão, chamado "elastic". Para tal, precisamos ir ao diretório de instalação do Elastic, que fica em /usr/share/elasticsearch/bin. Nesta pasta, execute o comando ./elasticsearch-reset-password -u elastic -i.
Logo após trocar a senha, precisamos alterar o arquivo /etc/elasticsearch/elasticsearch.yml. Edite este arquivo com o vim/nano e encontre a linha que contém #network.host: 192.168.0.1. Primeiro, descomente essa linha e troque o IP para o IP da máquina que você está conectado. Logo abaixo, você verá uma linha com #http.host 9200. Apenas descomente essa linha. Procure também a linha que contém #action.destructive_requires_name: false e descomente-a.
Adicione a linha a seguir em qualquer parte deste arquivo: discovery.type: single-node. Agora, procure a seguinte linha:
xpack.security.http.ssl:
enabled: false
keystore.path: certs/http.p12
Apenas altere a linha enabled: true para enabled: false. Procure agora a linha abaixo:
xpack.security.transport.ssl:
enabled: true
verification_mode: certificate
keystore.path: certs/transport.p12
truststore.path: certs/transport.p12
Apenas altere a linha enabled: true para enabled: false. Agora, preocure a linha que contém cluster.initial_master_nodes e comente-a.
Agora, reinicie o servidor do Elasticsearch com systemctl restart elasticsearch.