Alerta para horários divergentes no Linux – Usando Chrony e AWS Cloudwatch

Observabilidade - Alerta de horários

Introdução

O horário correto e exato no Linux é de extrema importância em servidores de produção por várias razões cruciais. Em primeiro lugar, muitas aplicações e serviços dependem de sincronização precisa de tempo para funcionar corretamente, evitando falhas e inconsistências. Além disso, registros e logs são frequentemente usados para solucionar problemas e analisar o desempenho, e um horário preciso é essencial para rastrear eventos com precisão.

Nesse cenário, a observabilidade desempenha um papel fundamental. Ao adotar ferramentas de observabilidade, como monitoramento em tempo real e análise de registros, os administradores do sistema podem identificar rapidamente qualquer desvio no horário do servidor. Além disso, sistemas de alerta podem ser configurados para notificar imediatamente sobre problemas relacionados ao horário.

Através da observabilidade, é possível garantir que o horário do servidor permaneça sincronizado e, em caso de desvios, permitir uma resposta rápida para corrigir o problema antes que possa causar impacto negativo nas operações do servidor e, consequentemente, nos serviços prestados aos usuários.

No post abaixo, eu expliquei sobre a instalação e o funcionamento do Chrony, que iremos usar neste artigo:

Problema

As instâncias de máquinas virtuais que fazem parte do Auto Scaling geralmente são criadas a partir de imagens pré-configuradas. Essas imagens podem incluir um horário de referência incorreto ou desatualizado. Além disso, o processo de inicialização pode levar algum tempo, o que pode resultar em uma defasagem entre o horário configurado na imagem e o horário real de inicialização.

Quando múltiplas instâncias são implantadas e desativadas dinamicamente em resposta à carga do sistema, a correção manual do horário em cada instância pode ser uma tarefa difícil e propensa a erros. O descompasso do horário entre as máquinas pode causar problemas de sincronização em serviços distribuídos, logs inconsistentes e dificuldades no rastreamento de eventos em todo o ambiente.

A natureza dinâmica das máquinas no Auto Scaling apresenta desafios significativos em relação à observabilidade do ambiente. À medida que novas instâncias são criadas e outras são desligadas, a topologia da infraestrutura muda constantemente. Isso pode levar a problemas na descoberta e no monitoramento das instâncias em tempo real.

Outra questão é a coleta centralizada de logs, métricas e eventos de diferentes instâncias que estão sendo adicionadas e removidas continuamente. Lidar com a variedade de endereços IP, identificadores e outros atributos atribuídos dinamicamente às máquinas pode dificultar a configuração de um sistema de monitoramento eficiente.

Solução para EC2 em ASG(Auto Scaling Group)

Fluxo - Observabilidade - Alerta de horários

Para as máquinas que trabalham no Auto Scaling e necessitam um horário correto com toda garantia, foi necessário configurar o Chrony e uma estrutura para alarmar caso ocorra alguma divergência no horário, baseado em métricas que foram geradas dinamicamente.

A estrutura é explicada na imagem acima, que pode ser resumida em:

  • Durante o start da EC2, é feita a criação do alarme com base na configuração existente no caminho /etc/rc.local
  • Configuração na Crontab publica a métrica no AWS Cloudwatch.
  • Antes do terminate da EC2, ocorre a deleção do alarme, com base no “aa-run-before-shutdown.service”.

Todo este processo é necessário para que os alarmes sejam criados dinamicamente para cada EC2 que nasce no Auto Scaling e não fiquem sujeiras no Cloudwatch.

Etapas

Ponto de atenção

As configurações que serão demonstradas no artigo, são realizadas num servidor Linux que é servido de base para o Auto Scaling, ao final do processo é indicado que seja feita criação de uma AMI e as devidas configurações no Launch Template e no Auto Scaling. Não irei detalhar estes passos para o artigo não ficar muito extenso.

Também precisamos de um tópico no AWS SNS, para o envio de emails via alertas do AWS Cloudwatch.

Script cria alarme

Precisamos criar o alarme no Cloudwatch toda vez que a EC2 nascer, para isto vamos deixar configurado o /etc/rc.local

O arquivo /etc/rc.local é um arquivo de script que é executado durante o processo de inicialização do sistema Linux. No entanto, o uso do rc.local para inicializar serviços ou executar scripts pode variar dependendo da distribuição Linux que você está usando. A partir do Debian 9 (Stretch) e sistemas mais recentes, o rc.local é desativado por padrão e requer algumas configurações extras para ser usado.

Editar o arquivo /etc/rc.local e adicionar o seguinte conteúdo:

#!/bin/bash

INSTANCE_ID=`curl -s http://169.254.169.254/latest/meta-data/instance-id`

aws cloudwatch put-metric-alarm \
    --alarm-name "alerta-horario-ec2-$INSTANCE_ID" \
    --alarm-description "Alarme sobre problemas no horario da EC2 $INSTANCE_ID devido ClockErrorBound acima de 1ms" \
    --metric-name ClockErrorBound \
    --namespace TimeDrift \
    --statistic Average \
    --period 300 \
    --threshold 1 \
    --comparison-operator GreaterThanThreshold  \
    --dimensions "Name=Instance,Value=$INSTANCE_ID" \
    --evaluation-periods 3 \
    --datapoints-to-alarm 3 \
    --region sa-east-1 \
    --alarm-actions arn:aws:sns:sa-east-1:123456789:alertas-devops-mind

Esse script é um script de shell (Bash) que cria um alarme de métricas no Amazon CloudWatch para monitorar o desvio de tempo (time drift) em uma instância EC2 específica.

Vamos analisar cada parte do script:

  1. #!/bin/bash: Especifica o interpretador de comandos a ser usado, neste caso, o Bash.
  2. INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id): Usa o comando curl para obter o ID da instância EC2 atual em que o script está sendo executado. O endereço IP 169.254.169.254 é uma interface de metadados especial para instâncias EC2 na AWS, e /latest/meta-data/instance-id é um endpoint que retorna o ID da instância.
  3. aws cloudwatch put-metric-alarm ...: Usa o AWS CLI (Command Line Interface) para criar um alarme de métricas no Amazon CloudWatch com os seguintes parâmetros:
    • --alarm-name "alerta-horario-ec2-$INSTANCE_ID": Define o nome do alarme, que inclui o ID da instância para torná-lo exclusivo.
    • --alarm-description "Alarme sobre problemas no horario da EC2 $INSTANCE_ID devido ClockErrorBound acima de 1ms": Fornece uma descrição para o alarme, explicando sua finalidade.
    • --metric-name ClockErrorBound: Especifica o nome da métrica a ser monitorada (neste caso, “ClockErrorBound”).
    • --namespace TimeDrift: Define o namespace da métrica. O namespace é um “container” para métricas relacionadas.
    • --statistic Average: Define a estatística a ser usada para a avaliação do alarme (neste caso, a média).
    • --period 300: Especifica o período de tempo, em segundos, em que a métrica é avaliada (neste caso, 300 segundos ou 5 minutos).
    • --threshold 1: Define o valor do limite para o qual o alarme será acionado (neste caso, 1, indicando um desvio de tempo acima de 1 milissegundo).
    • --comparison-operator GreaterThanThreshold: Define o operador de comparação a ser usado para avaliar a métrica em relação ao limite especificado (neste caso, “maior que”).
    • --dimensions "Name=Instance,Value=$INSTANCE_ID": Especifica as dimensões da métrica para identificar a instância específica que será monitorada (usando o ID da instância obtido anteriormente).
    • --evaluation-periods 3: Define o número de períodos de avaliação consecutivos em que a métrica deve estar acima do limite para o alarme ser acionado (neste caso, 3 períodos de 5 minutos cada, totalizando 15 minutos).
    • --datapoints-to-alarm 3: Especifica o número de pontos de dados que devem atender à condição do alarme para que ele seja acionado (neste caso, 3 pontos de dados consecutivos).
    • --region sa-east-1: Define a região da AWS onde o alarme será criado (neste caso, “sa-east-1”).
    • --alarm-actions arn:aws:sns:sa-east-1:123456789:alertas-devops-mind: Especifica a ação a ser executada quando o alarme for acionado. Neste caso, o alarme enviará uma notificação para o tópico SNS (Simple Notification Service) “alertas-devops-mind” na região “sa-east-1” com o número da conta AWS “123456789”.

Portanto, esse script cria um alarme de métricas no Amazon CloudWatch para monitorar o desvio de tempo (“ClockErrorBound”) em uma instância EC2 específica e envia uma notificação para um tópico SNS quando o desvio de tempo ultrapassa 1 milissegundo em três períodos de avaliação consecutivos. Isso permite identificar problemas de sincronização de horário na instância e tomar ações corretivas em resposta aos alarmes acionados.

Script cria métrica

Também precisamos criar um Shell Script que forma a métrica baseado na saída do Chrony.

O instance-id da EC2 é obtido via endpoint do meta-data.

O valor gerado a partir do Chrony é enviado ao AWS Cloudwatch usando aws-cli.

Salvar o script no caminho /devops/scripts/alerta-horario/timepublisher.sh , com o conteúdo abaixo:

#!/bin/bash

SYSTEM_TIME=""
ROOT_DELAY=""
ROOT_DISPERSION=""
INSTANCE_ID=`curl -s http://169.254.169.254/latest/meta-data/instance-id`

output=$(chronyc tracking)

while read -r line; do 
# look for "System time", "Root delay", "Root dispersion".

 if [[ $line == "System time"* ]]
 then
 SYSTEM_TIME=`echo $line | cut -f2 -d":" | cut -f2 -d" "`
 elif [[ $line == "Root delay"* ]]
 then
 ROOT_DELAY=`echo $line | cut -f2 -d":" | cut -f2 -d" " `
 elif [[ $line == "Root dispersion"* ]]
 then
 ROOT_DISPERSION=`echo $line | cut -f2 -d":" | cut -f2 -d" " `
 fi
done <<< "$output"

CLOCK_ERROR_BOUND=`echo "($SYSTEM_TIME + (.5 * $ROOT_DELAY) + $ROOT_DISPERSION) * 1000" | bc `

# create or update a custom metric in CW.
aws cloudwatch put-metric-data \
    --metric-name ClockErrorBound \
    --dimensions Instance=$INSTANCE_ID \
    --namespace "TimeDrift" \
    --region sa-east-1 \
    --value $CLOCK_ERROR_BOUND

Este script de shell (Bash) tem o objetivo de obter informações sobre o desvio de tempo (time drift) da instância EC2 em que está sendo executado e, em seguida, enviar esse valor como uma métrica personalizada (ClockErrorBound) para o Amazon CloudWatch.

Vamos analisar passo a passo o que o script faz:

  1. #!/bin/bash: Especifica o interpretador de comandos a ser usado, neste caso, o Bash.
  2. Definição das variáveis: SYSTEM_TIME, ROOT_DELAY, ROOT_DISPERSION, INSTANCE_ID. Essas variáveis serão usadas para armazenar as informações sobre o desvio de tempo e o ID da instância EC2.
  3. INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id): Usa o comando curl para obter o ID da instância EC2 atual em que o script está sendo executado. O endereço IP 169.254.169.254 é uma interface de metadados especial para instâncias EC2 na AWS, e /latest/meta-data/instance-id é um endpoint que retorna o ID da instância.
  4. output=$(chronyc tracking): Executa o comando chronyc tracking para obter informações sobre o estado de sincronização do cliente Chrony, que é responsável pelo ajuste de tempo na instância.
  5. while read -r line; do: Inicia um loop para iterar sobre cada linha de saída do comando chronyc tracking.
  6. if [[ $line == "System time"* ]]: Verifica se a linha contém a informação “System time” (hora do sistema) e, em caso afirmativo, extrai o valor da hora do sistema e armazena na variável SYSTEM_TIME.
  7. elif [[ $line == "Root delay"* ]]: Verifica se a linha contém a informação “Root delay” (atraso da raiz) e, em caso afirmativo, extrai o valor do atraso da raiz e armazena na variável ROOT_DELAY.
  8. elif [[ $line == "Root dispersion"* ]]: Verifica se a linha contém a informação “Root dispersion” (dispersão da raiz) e, em caso afirmativo, extrai o valor da dispersão da raiz e armazena na variável ROOT_DISPERSION.
  9. done <<< "$output": Encerra o loop após processar todas as linhas de saída do comando chronyc tracking.
  10. CLOCK_ERROR_BOUND=...: Calcula o valor do desvio de tempo usando as variáveis SYSTEM_TIME, ROOT_DELAY e ROOT_DISPERSION. O cálculo é baseado em uma fórmula específica que utiliza esses valores para determinar o limite do erro de relógio (ClockErrorBound).
  11. aws cloudwatch put-metric-data ...: Usa o AWS CLI (Command Line Interface) para criar ou atualizar uma métrica personalizada no Amazon CloudWatch com os seguintes parâmetros:
    • --metric-name ClockErrorBound: Define o nome da métrica personalizada como “ClockErrorBound”.
    • --dimensions Instance=$INSTANCE_ID: Especifica as dimensões da métrica para identificar a instância específica que está sendo monitorada (usando o ID da instância obtido anteriormente).
    • --namespace "TimeDrift": Define o namespace da métrica personalizada como “TimeDrift”.
    • --region sa-east-1: Define a região da AWS onde a métrica será criada ou atualizada (neste caso, “sa-east-1”).
    • --value $CLOCK_ERROR_BOUND: Define o valor da métrica personalizada como o resultado do cálculo do desvio de tempo realizado anteriormente.

Portanto, este script coleta informações sobre o desvio de tempo da instância EC2 usando o cliente Chrony, calcula um limite de erro de relógio com base nessas informações e envia o valor resultante como uma métrica personalizada para o Amazon CloudWatch. Esse processo pode ser útil para monitorar a precisão da sincronização de tempo da instância e tomar medidas corretivas se o desvio de tempo estiver além do limite definido.

Ajustar crontab

Para editar a crontab, você pode usar o comando crontab. A crontab é o arquivo que contém as entradas cron, e cada usuário pode ter sua própria crontab personalizada. Para editar a crontab do usuário atual, siga os passos abaixo:

  1. Abra um terminal no Linux.
  2. Digite o seguinte comando para abrir a crontab do usuário atual no editor padrão definido pelo sistema (geralmente o nano, vim ou vi):
crontab -e
  1. Se esta é a primeira vez que você está editando a crontab para esse usuário, o sistema pode solicitar que você escolha o editor padrão. Selecione o editor desejado, se solicitado.
  2. Uma vez dentro do editor, você verá as entradas cron existentes ou um arquivo em branco, caso ainda não tenha entradas.
  3. Adicione, edite ou remova as linhas de cron conforme necessário. Cada linha representa uma tarefa agendada, seguindo o formato cron padrão.
  4. Salve as alterações e feche o editor. Por exemplo, no editor nano, você pode salvar pressionando Ctrl + O e sair pressionando Ctrl + X.

Para ajustar a crontab para utilizar o script que cria métrica num período especifico, adicione o seguinte conteúdo:

*/5 * * * * /devops/scripts/alerta-horario/timepublisher.sh >/dev/null 2>&1

Essa linha especifica que o script /devops/scripts/alerta-horario/timepublisher.sh será executado a cada 5 minutos.

Vamos analisar a cron em detalhes:

  • */5: O primeiro campo indica os minutos em que o script será executado. */5 significa “a cada 5 minutos”. Ou seja, o script será executado quando o minuto for 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50 ou 55.
  • *: O segundo campo indica as horas em que o script será executado. O asterisco (*) significa que o script será executado em todas as horas, não importa qual.
  • *: O terceiro campo indica o dia do mês em que o script será executado. O asterisco (*) significa que o script será executado em todos os dias do mês, não importa qual.
  • *: O quarto campo indica o mês em que o script será executado. O asterisco (*) significa que o script será executado em todos os meses, não importa qual.
  • *: O quinto campo indica o dia da semana em que o script será executado. O asterisco (*) significa que o script será executado em todos os dias da semana (de domingo a sábado), não importa qual.
  • /devops/scripts/alerta-horario/timepublisher.sh: Este é o caminho para o script timepublisher.sh que será executado.
  • >/dev/null 2>&1: Esta parte redireciona a saída (stdout e stderr) do script para /dev/null, que é um dispositivo especial no sistema Linux que descarta todos os dados enviados a ele. Isso significa que qualquer saída gerada pelo script não será exibida no terminal ou armazenada em arquivos de log.

Em resumo, essa cron é usada para agendar a execução do script timepublisher.sh localizado em /devops/scripts/alerta-horario/ a cada 5 minutos. Qualquer saída gerada pelo script será descartada e não será exibida ou registrada em logs.

Script deleta alarme – Pré terminate

Precisamos garantir a configuração do SystemD na EC2, para que durante o terminate seja efetuado o delete do alarme, para não ficarem sujeiras no AWS Cloudwatch, sobre EC2 mortas do ASG.

Caminho onde deve ficar o script que deve rodar antes do terminate:

/devops/scripts/alerta-horario/delete-alarm-alerta-horario.sh

Script necessário:

#!/bin/bash

INSTANCE_ID=`curl -s http://169.254.169.254/latest/meta-data/instance-id`

aws cloudwatch delete-alarms \
    --region sa-east-1 \
    --alarm-names "alerta-horario-ec2-$INSTANCE_ID"

Caminho onde deve ser configurado o script pré terminate:

/etc/systemd/system/aa-run-before-shutdown.service 

Script deve ser executado durante shutdown(pré-terminate):

[Unit]
Description=Deletar alarme
Requires=network-online.target
After=network.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStop=/devops/scripts/alerta-horario/delete-alarm-alerta-horario.sh


[Install]
WantedBy=network.target

Aqui está a explicação do que cada seção do arquivo faz:

1. [Unit]: Essa seção define informações sobre a unidade de serviço, incluindo sua descrição e as dependências que ele requer e as ordens de inicialização ou encerramento. As principais diretivas usadas são:

  • Description: Uma descrição do serviço, que é usada para fins de documentação.
  • Requires=network-online.target: Indica que o serviço requer a disponibilidade da rede (conexão de rede online) para ser iniciado.
  • After=network.target: Define que o serviço deve ser iniciado após a inicialização do serviço de rede.

2. [Service]: Nesta seção, são especificados os detalhes da execução do serviço. As principais diretivas usadas são:

  • Type=oneshot: Indica que é um serviço que é executado uma única vez e, em seguida, sai. Geralmente usado para tarefas que não são serviços contínuos, mas sim tarefas pontuais.
  • RemainAfterExit=yes: Essa diretiva informa ao systemd que, após a execução bem-sucedida do serviço, ele deve considerar o serviço como “ativo” mesmo após a sua finalização.
  • ExecStop=/devops/scripts/alerta-horario/delete-alarm-alerta-horario.sh: Especifica o comando ou script a ser executado quando o serviço é parado (nesse caso, antes do shutdown). O caminho “/devops/scripts/alerta-horario/delete-alarm-alerta-horario.sh” aponta para o script que será executado.

3. [Install]: Essa seção determina como o serviço será instalado e ativado. A principal diretiva usada é:

  • WantedBy=network.target: Indica que o serviço será ativado como parte do processo de inicialização quando a rede estiver disponível (network.target).

Resumindo, o arquivo de serviço “/etc/systemd/system/aa-run-before-shutdown.service” define um serviço do systemd que será executado antes do desligamento (shutdown) do sistema. Esse serviço é do tipo “oneshot” e, após ser executado, permanecerá como “ativo” mesmo após o término. A execução do serviço envolve a chamada do script “delete-alarm-alerta-horario.sh” localizado em “/devops/scripts/alerta-horario/”.

Observação

Nos scripts que forneci, a região utilizada foi São Paulo(sa-east-1), lembre de ajustar conforme o seu ambiente.

Solução para EC2 Standalone

Nas máquinas que não fazem parte de um ASG(Auto Scaling Group), o procedimento é mais simples.

Etapas

  1. Criar alarme no AWS Cloudwatch.
  2. Criar Shell Script que cria a métrica baseado no chronyc.
  3. Ajustar crontab para utilizar o script de métrica no tempo definido.

Material de apoio

Chrony – FAQ

AWS Cloudwatch – Documentação

Conclusão

Espero que as informações aqui apresentadas tenham sido úteis e tenham fornecido insights valiosos sobre a importância da observabilidade e do monitoramento de divergências de horários no Linux usando o Chrony e o AWS CloudWatch.

O monitoramento proativo das instâncias EC2 é essencial para garantir a estabilidade e a eficiência de seus serviços em ambientes em nuvem. Ao detectar e alertar sobre potenciais problemas de sincronização de tempo, podemos agir rapidamente para evitar impactos negativos nos sistemas.

A AWS oferece uma variedade de ferramentas poderosas para o monitoramento e gerenciamento de recursos em nuvem, e a combinação do Chrony com o AWS CloudWatch proporciona uma solução confiável para lidar com divergências de horários.

Se ficarem com alguma dúvida sobre a solução, não hesitem em perguntar!

Fernando Müller Junior
Fernando Müller Junior

Eu sou o Fernando Müller, um Tech Lead SRE com 16 anos de experiência em TI, atualmente eu trabalho na Appmax, uma fintech localizada no Brasil. Apaixonado por trabalhar com arquiteturas e aplicações Cloud Native, ferramentas Open Source e tudo que existe no mundo SRE, sempre procurando se desenvolver e aprender constantemente(Lifelong learning), atuando em projetos inovadores!

Artigos: 28

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *