Configuração de logs do Amazon ECS para obtenção de alto throughput - Amazon Elastic Container Service

Configuração de logs do Amazon ECS para obtenção de alto throughput

Para cenários com alto throughput de logs, recomendamos o uso do driver de log awsfirelens com o FireLens e o Fluent Bit. O Fluent Bit é um processador de log leve que é eficiente com recursos e capaz de lidar com milhões de registros de log. Porém, atingir o desempenho ideal em grande escala requer ajustar sua configuração.

Esta seção aborda técnicas avançadas de otimização de Fluent Bit para lidar com a alto throughput de logs e, ao mesmo tempo, manter a estabilidade do sistema e garantir que não haja perda de dados.

Para obter informações sobre como usar arquivos de configuração personalizados com o FireLens, consulte Uso de um arquivo de configuração personalizado. Para receber exemplos adicionais, consulte Exemplos do FireLens do Amazon ECS no GitHub.

nota

Algumas opções de configuração nesta seção, como workers e threaded, exigem o AWS para Fluent Bit versão 3 ou posterior. Para obter informações sobre versões disponíveis, consulte Versões do AWS para Fluent Bit.

Usar o processo de buffer do sistema de arquivos

Por padrão, o Fluent Bit armazena em buffer todos os dados na memória. Quando os dados são inseridos mais rapidamente do que podem ser enviados para as saídas, o buffer fica cheio. Uma vez cheio, o plug-in de entrada faz uma pausa até que haja espaço disponível no buffer, o que pode causar contrapressão e tornar sua aplicação mais lenta.

Para cenários de alto throughput, recomendamos o uso do processo de buffer do sistema de arquivos. Para obter mais informações sobre como o Fluent Bit gerencia o processo de buffer e o armazenamento, consulte Processo de buffer e armazenamento na documentação do Fluent Bit.

O processo de buffer do sistema de arquivos oferece as seguintes vantagens:

  • Maior capacidade de buffer: o espaço em disco geralmente é mais abundante do que a memória.

  • Persistência: os dados armazenados em buffer persistem após as reinicializações do Fluent Bit.

  • Degradação normal: durante falhas de saída, os dados se acumulam no disco em vez de causarem esgotamento da memória.

Para habilitar o buffer do sistema de arquivos, forneça um arquivo de configuração personalizado do Fluent Bit. O exemplo a seguir mostra a configuração recomendada:

[SERVICE] # Flush logs every 1 second Flush 1 # Wait 120 seconds during shutdown to flush remaining logs Grace 120 # Directory for filesystem buffering storage.path /var/log/flb-storage/ # Limit chunks stored 'up' in memory (reduce for memory-constrained environments) storage.max_chunks_up 32 # Flush backlog chunks to destinations during shutdown (prevents log loss) storage.backlog.flush_on_shutdown On [INPUT] Name forward unix_path /var/run/fluent.sock # Run input in separate thread to prevent blocking threaded true # Enable filesystem buffering for persistence storage.type filesystem [OUTPUT] Name cloudwatch_logs Match * region us-west-2 log_group_name /aws/ecs/my-app log_stream_name $(ecs_task_id) # Use multiple workers for parallel processing workers 2 # Retry failed flushes up to 15 times retry_limit 15 # Maximum disk space for buffered data for this output storage.total_limit_size 10G

Principais parâmetros de configuração:

storage.path

O diretório em que o Fluent Bit armazena os blocos armazenados em buffer no disco.

storage.backlog.flush_on_shutdown

Quando habilitado, o Fluent Bit tenta liberar todos os blocos do sistema de arquivos de backlog para seus respectivos destinos durante o desligamento. Isso ajuda a garantir a entrega dos dados antes que o Fluent Bit pare, mas pode prolongar o tempo de desligamento.

storage.max_chunks_up

O número de blocos que permanecem na memória. O padrão é 128 blocos, que podem consumir mais de 500 MB de memória, pois cada bloco pode usar até 4–5 MB. Em ambientes com memória limitada, reduza esse valor. Por exemplo, se você tiver 50 MB disponíveis para buffer, defina isso como 8–10 blocos.

storage.type filesystem

Habilita o armazenamento do sistema de arquivos para o plug-in de entrada. Apesar do nome, o Fluent Bit usa mmap para mapear fragmentos na memória e no disco, fornecendo persistência sem sacrificar o desempenho.

threaded true

Executa a entrada em seu próprio thread, separado do loop de eventos principal do Fluent Bit. Isso evita que entradas lentas bloqueiem todo o pipeline.

Otimizar a configuração de saída

Problemas de rede, interrupções no serviço e controle de utilização de destino podem impedir a entrega dos logs. A configuração adequada da saída garante resiliência sem perda de dados.

Quando uma descarga de saída falha, o Fluent Bit pode repetir a operação. Os seguintes parâmetros controlam o comportamento de novas tentativas:

retry_limit

O número máximo de novas tentativas antes de descartar registros. O padrão é 1. Para ambientes de produção, recomendamos 15 ou mais, o que cobre vários minutos de interrupção com recuo exponencial.

scheduler.base

O mínimo de segundos entre novas tentativas. Recomendamos 10 segundos.

scheduler.cap

O máximo de segundos entre novas tentativas ao usar o recuo exponencial. Recomendamos 60 segundos.

workers

O número de threads para processamento de saída paralelo. Vários operadores permitem descargas simultâneas, melhorando o throughput ao processar muitos blocos.

O parâmetro Grace na seção [SERVICE] define o tempo de espera do Fluent Bit durante o desligamento para limpar os dados em buffer. O período de Grace deve ser coordenado com o stopTimeout do contêiner. Certifique-se de que stopTimeout exceda o período de Grace para permitir que o Fluent Bit faça a descarga completa antes de receber SIGKILL. Por exemplo, se Grace for 120 segundos, defina stopTimeout como 150 segundos.

O exemplo a seguir mostra uma configuração completa de Fluent Bit com todas as definições recomendadas para cenários de alto throughput:

[SERVICE] # Flush logs every 1 second Flush 1 # Wait 120 seconds during shutdown to flush remaining logs Grace 120 # Directory for filesystem buffering storage.path /var/log/flb-storage/ # Limit chunks stored 'up' in memory (reduce for memory-constrained environments) storage.max_chunks_up 32 # Flush backlog chunks to destinations during shutdown (prevents log loss) storage.backlog.flush_on_shutdown On # Minimum seconds between retries scheduler.base 10 # Maximum seconds between retries (exponential backoff cap) scheduler.cap 60 [INPUT] Name forward unix_path /var/run/fluent.sock # Run input in separate thread to prevent blocking threaded true # Enable filesystem buffering for persistence storage.type filesystem [OUTPUT] Name cloudwatch_logs Match * region us-west-2 log_group_name /aws/ecs/my-app log_stream_name $(ecs_task_id) # Use multiple workers for parallel processing workers 2 # Retry failed flushes up to 15 times retry_limit 15 # Maximum disk space for buffered data for this output storage.total_limit_size 10G

Usar o registro em log em vários destinos para confiabilidade

O envio de logs para vários destinos elimina pontos únicos de falha. Por exemplo, se o Amazon CloudWatch Logs sofrer uma interrupção, os logs ainda chegarão ao Amazon S3.

O registro em log em vários destinos oferece os seguintes benefícios. O plug-in de saída do Amazon S3 também oferece suporte a opções de compactação, como gzip e formato Parquet, que podem reduzir os custos de armazenamento. Para mais informações, consulte Compactação do S3 na documentação do Fluent Bit.

O registro em log em vários destinos pode oferecer os seguintes benefícios:

  • Redundância: se um destino falhar, os logs ainda chegarão ao outro.

  • Recuperação: reconstrua lacunas em um sistema a partir do outro.

  • Durabilidade: arquive logs no Amazon S3 para retenção de longo prazo.

  • Otimização de custos: mantenha logs recentes em um serviço de consulta rápida, como o Amazon CloudWatch Logs, com retenção mais curta, enquanto arquiva todos os logs no armazenamento do Amazon S3 de baixo custo para retenção de longo prazo.

A configuração do Fluent Bit a seguir envia logs ao Amazon CloudWatch Logs e ao Amazon S3:

[OUTPUT] Name cloudwatch_logs Match * region us-west-2 log_group_name /aws/ecs/my-app log_stream_name $(ecs_task_id) workers 2 retry_limit 15 [OUTPUT] Name s3 Match * bucket my-logs-bucket region us-west-2 total_file_size 100M s3_key_format /fluent-bit-logs/$(ecs_task_id)/%Y%m%d/%H/%M/$UUID upload_timeout 10m # Maximum disk space for buffered data for this output storage.total_limit_size 5G

Como ambas as saídas usam o mesmo padrão Match *, todos os registros são enviados aos dois destinos de maneira independente. Durante uma interrupção em um destino, os logs continuam fluindo para o outro, enquanto as descargas com falha se acumulam no buffer do sistema de arquivos para uma nova tentativa posterior.

Usar o log com base em arquivo com o plug-in de entrada tail

Para cenários de alto throughput em que a perda de logs é uma preocupação crítica, é possível usar uma abordagem alternativa: fazer com que sua aplicação grave os logs em arquivos no disco e configurar o Fluent Bit para lê-los usando o plug-in de entrada tail. Essa abordagem ignora completamente a camada do driver de log do Docker.

O registro em log baseado em arquivo com o plug-in tail oferece os seguintes benefícios:

  • Rastreamento de deslocamento: o plug-in tail pode armazenar deslocamentos de arquivos em um arquivo de banco de dados (usando a opção DB), proporcionando durabilidade entre reinicializações do Fluent Bit. Isso ajuda a evitar a perda de logs durante a reinicialização do contêiner.

  • Processo de buffer em nível de entrada: é possível configurar limites de buffer de memória diretamente no plug-in de entrada usando Mem_Buf_Limit, fornecendo um controle mais granular sobre o uso da memória.

  • Evita a sobrecarga do Docker: os logs vão diretamente do arquivo para o Fluent Bit sem passarem pelos buffers de log do Docker.

Para usar essa abordagem, sua aplicação deve gravar logs em arquivos em vez de stdout. Tanto o contêiner da aplicação quanto o contêiner do Fluent Bit montam um volume compartilhado onde os arquivos de log são armazenados.

O exemplo a seguir mostra uma configuração de entrada de cauda com práticas recomendadas:

[INPUT] Name tail # File path or glob pattern to tail Path /var/log/app.log # Database file for storing file offsets (enables resuming after restart) DB /var/log/flb_tail.db # when true, controls that only fluent-bit will access the database (improves performance) DB.locking true # Skip long lines instead of skipping the entire file Skip_Long_Lines On # How often (in seconds) to check for new files matching the glob pattern Refresh_Interval 10 # Extra seconds to monitor a file after rotation to account for pending flush Rotate_Wait 30 # Maximum size of the buffer for a single line Buffer_Max_Size 10MB # Initial allocation size for reading file data Buffer_Chunk_Size 1MB # Maximum memory buffer size (tail pauses when full) Mem_Buf_Limit 75MB

Ao usar o plug-in de entrada tail, considere o seguinte:

  • Implemente a alternância de logs para os logs da sua aplicação para evitar o esgotamento do disco. Monitore as métricas de volume subjacentes para avaliar a performance.

  • Considere configurações como Ignore_Older, Read_from_Head e analisadores multilinha com base no formato do seu log.

Para mais informações, consulte Tail na documentação do Fluent Bit. Para conhecer as práticas recomendadas, consulte Configuração do Tail com práticas recomendadas, no guia de solução de problemas da AWS para Fluent Bit.

Registrar em log diretamente no FireLens

Quando o driver de log awsfirelens for especificado em uma definição de tarefa, o agente do Amazon ECS injetará as seguintes variáveis de ambiente no contêiner:

FLUENT_HOST

O endereço IP atribuído ao contêiner do FireLens.

nota

Se você estiver usando o EC2 com o modo de rede bridge, a variável de ambiente FLUENT_HOST no contêiner da aplicação poderá se tornar imprecisa após uma reinicialização do contêiner do roteador de log do FireLens (o contêiner com o objeto firelensConfiguration na definição de contêiner). Isso ocorre porque o FLUENT_HOST é um endereço IP dinâmico e pode mudar após uma reinicialização. O registro em log diretamente do contêiner da aplicação para o endereço IP do FLUENT_HOST pode começar a falhar após a alteração do endereço. Para obter mais informações sobre como reiniciar contêineres individuais, consulte Reiniciar contêineres individuais em tarefas do Amazon ECS com políticas de reinicialização de contêineres.

FLUENT_PORT

A porta em que o protocolo Fluent Forward está escutando.

Você pode usar essas variáveis de ambiente para registrar diretamente no roteador de log do Fluent Bit a partir do código da sua aplicação usando o protocolo Fluent Forward, em vez de gravar no stdout. Essa abordagem ignora a camada do driver de log do Docker, o que oferece os seguintes benefícios:

  • Menor latência: os logs vão diretamente para o Fluent Bit sem passarem pela infraestrutura de registro em log do Docker.

  • Registro em log estruturado: envie dados de log estruturados de maneira nativa sem sobrecarga de codificação JSON.

  • Melhor controle: sua aplicação pode implementar sua própria lógica de buffer e tratamento de erros.

As seguintes bibliotecas de agente de log Fluent são compatíveis com o protocolo Fluent Forward e podem ser usadas para enviar logs diretamente para o Fluent Bit:

Configurar o limite de buffer do Docker

Ao criar uma definição de tarefa, você pode especificar o número de linhas de log que são armazenadas em buffer na memória especificando o valor em log-driver-buffer-limit. Isso controla o buffer entre o Docker e o Fluent Bit. Para obter mais informações, consulte Driver de registro do Fluentd na documentação do Docker.

Use essa opção quando houver throughput alto, porque o Docker pode ficar sem memória buffer e descartar mensagens de buffer para que ele possa adicionar novas mensagens.

Considere o seguinte ao usar essa opção:

  • Essa opção é compatível com o tipo EC2 e Fargate com a versão da plataforma 1.4.0 ou posterior.

  • A opção só é válida quando logDriver estiver definido como awsfirelens.

  • O limite de buffer padrão é de 1048576 linhas de log.

  • O limite de buffer deve ser maior ou igual a 0 ou menor que as linhas de log 536870912.

  • A quantidade máxima de memória usada para esse buffer é o produto do tamanho de cada linha de log pelo tamanho do buffer. Por exemplo, se as linhas de log da aplicação tivessem em média 2 KiB, um limite de buffer de 4096 usaria no máximo 8 MiB. A quantidade total de memória alocada no nível da tarefa deve ser maior que a quantidade de memória alocada para todos os contêineres, mais o limite de buffer da memória.

A seguinte definição de tarefa mostra como configurar log-driver-buffer-limit:

{ "containerDefinitions": [ { "name": "my_service_log_router", "image": "public.ecr.aws/aws-observability/aws-for-fluent-bit:3", "cpu": 0, "memoryReservation": 51, "essential": true, "firelensConfiguration": { "type": "fluentbit" } }, { "essential": true, "image": "public.ecr.aws/docker/library/httpd:latest", "name": "app", "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "firehose", "region": "us-west-2", "delivery_stream": "my-stream", "log-driver-buffer-limit": "52428800" } }, "dependsOn": [ { "containerName": "my_service_log_router", "condition": "START" } ], "memoryReservation": 100 } ] }