DevOps Mind
Imagine acordar um dia e descobrir que todos os relógios da sua casa estão marcando horas diferentes. Confuso, não é? Agora, pense nisso acontecendo com os servidores da sua empresa. Em um ambiente de nuvem dinâmico, onde máquinas são criadas e destruídas constantemente, manter todos os horários sincronizados é crucial. É aí que entra nossa solução: usar o alarme no CloudWatch para detectar horários incorretos em máquinas Linux do Auto Scaling Group.
Neste post, vamos explorar como configurar esse sistema de alerta eficiente. Você aprenderá a garantir que todas as suas instâncias Linux estejam sempre “no mesmo horário”, evitando os problemas que discrepâncias nos horários podem causar em logs, transações e na segurança do seu ambiente AWS.
Tópicos
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 horários incorretos no Linux ou desatualizados. 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)
Para as máquinas que trabalham no Auto Scaling e necessitam evitar horários incorretos no linux, 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 no cloudwatch, 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 de horários incorretos no linux - 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 AWS CloudWatch para monitorar o desvio de tempo (time drift) em uma instância EC2 específica.
Vamos analisar cada parte do script:
#!/bin/bash
: Especifica o interpretador de comandos a ser usado, neste caso, o Bash.INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
: Usa o comandocurl
para obter o ID da instância EC2 atual em que o script está sendo executado. O endereço IP169.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.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 – AWS Cloudwatch
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:
#!/bin/bash
: Especifica o interpretador de comandos a ser usado, neste caso, o Bash.- 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. INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
: Usa o comandocurl
para obter o ID da instância EC2 atual em que o script está sendo executado. O endereço IP169.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.output=$(chronyc tracking)
: Executa o comandochronyc tracking
para obter informações sobre o estado de sincronização do cliente Chrony, que é responsável pelo ajuste de tempo na instância.while read -r line; do
: Inicia um loop para iterar sobre cada linha de saída do comandochronyc tracking
.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ávelSYSTEM_TIME
.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ávelROOT_DELAY
.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ávelROOT_DISPERSION
.done <<< "$output"
: Encerra o loop após processar todas as linhas de saída do comandochronyc tracking
.CLOCK_ERROR_BOUND=...
: Calcula o valor do desvio de tempo usando as variáveisSYSTEM_TIME
,ROOT_DELAY
eROOT_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).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:
- Abra um terminal no Linux.
- Digite o seguinte comando para abrir a crontab do usuário atual no editor padrão definido pelo sistema (geralmente o
nano
,vim
ouvi
):
crontab -e
- 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.
- Uma vez dentro do editor, você verá as entradas cron existentes ou um arquivo em branco, caso ainda não tenha entradas.
- Adicione, edite ou remova as linhas de cron conforme necessário. Cada linha representa uma tarefa agendada, seguindo o formato cron padrão.
- Salve as alterações e feche o editor. Por exemplo, no editor
nano
, você pode salvar pressionandoCtrl + O
e sair pressionandoCtrl + 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 scripttimepublisher.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
- Criar alarme no AWS Cloudwatch.
- Criar Shell Script que cria a métrica baseado no chronyc.
- Ajustar crontab para utilizar o script de métrica no tempo definido.
Material de apoio
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 / horários incorretos no linux.
Se ficarem com alguma dúvida sobre a solução, não hesitem em perguntar!