Como criar pipelines do Logstash de forma a facilitar a manutenção e a reutilização
O Logstash é um pipeline open source de processamento de dados que ingere eventos de uma ou mais entradas, os transforma e depois envia cada evento para uma ou mais saídas. Algumas implementações do Logstash podem ter muitas linhas de código e podem processar eventos de várias fontes de entrada. Para facilitar a manutenção dessas implementações, vou mostrar como aumentar a reutilização do código criando pipelines a partir de componentes modulares.
Motivação
Geralmente, é necessário que o Logstash aplique um subconjunto comum de lógica a eventos de várias fontes de entrada. Isso geralmente é conseguido de uma das seguintes maneiras:
- Processar eventos de diversas fontes de entrada diferentes em um único pipeline para que a lógica comum possa ser aplicada facilmente a todos os eventos de todas as fontes. Nessas implementações, além da lógica comum, geralmente há uma quantidade significativa de lógica condicional. Portanto, essa abordagem pode resultar em implementações do Logstash complicadas e difíceis de entender.
- Executar um pipeline exclusivo para processar eventos de cada fonte de entrada exclusiva. Para essa abordagem, é necessário duplicar e copiar funcionalidades comuns em cada pipeline, o que dificulta a manutenção das partes comuns do código.
A técnica apresentada neste blog elimina as deficiências das abordagens acima armazenando componentes modulares do pipeline em arquivos diferentes e depois construindo pipelines combinando esses componentes. Essa técnica pode reduzir a complexidade do pipeline e eliminar a duplicação do código.
Construção modular do pipeline
Um arquivo de configuração do Logstash consiste em entradas, filtros e saídas que são executados por um pipeline do Logstash. Em configurações mais avançadas, é comum ter uma instância do Logstash executando vários pipelines. Por padrão, quando você inicia o Logstash sem argumentos, ele lê um arquivo chamado pipelines.yml
e instancia os pipelines especificados.
As entradas, filtros e saídas do logstash podem ser armazenados em vários arquivos que podem ser selecionados para inclusão em um pipeline por meio da especificação de uma expressão glob. Os arquivos que corresponderem a uma expressão glob serão combinados em ordem alfabética. Como a ordem de execução dos filtros geralmente é importante, pode ser útil incluir identificadores numéricos nos nomes dos arquivos para garantir que os arquivos sejam combinados na ordem desejada.
Definiremos abaixo dois pipelines exclusivos que são uma combinação de diversos componentes modulares do Logstash. Nós armazenamos nossos componentes do Logstash nos seguintes arquivos:
- Declarações de entrada:
01_in.cfg
,02_in.cfg
- Declarações de filtro:
01_filter.cfg
,02_filter.cfg
,03_filter.cfg
- Declarações de saída:
01_out.cfg
Usando expressões glob, definimos os pipelines no arquivo pipelines.yml
para conterem os componentes desejados da seguinte maneira:
- pipeline.id: my-pipeline_1 path.config: "<path>/{01_in,01_filter,02_filter,01_out}.cfg" - pipeline.id: my-pipeline_2 path.config: "<path>/{02_in,02_filter,03_filter,01_out}.cfg"
Na configuração dos pipelines acima, o arquivo 02_filter.cfg
está presente nos dois pipelines, o que demonstra como o código comum a ambos os pipelines pode ser definido e mantido em um único arquivo e também ser executado por vários pipelines.
Teste dos pipelines
Nesta seção, fornecemos um exemplo concreto dos arquivos que serão combinados nos pipelines exclusivos definidos no arquivo pipelines.yml
acima. Então, executamos o Logstash com esses arquivos e apresentamos a saída gerada.
Arquivos de configuração
Arquivo de entrada: 01_in.cfg
Esse arquivo define uma entrada que é um gerador. A entrada do gerador foi projetada para testar o Logstash e, nesse caso, gerará um único evento.
input { generator { lines => ["Generated line"] count => 1 } }
Arquivo de entrada: 02_in.cfg
Esse arquivo define uma entrada do Logstash que escuta no stdin.
input { stdin {} }
Arquivo de filtro: 01_filter.cfg
filter { mutate { add_field => { "filter_name" => "Filter 01" } } }
Arquivo de filtro: 02_filter.cfg
filter { mutate { add_field => { "filter_name" => "Filter 02" } } }
Arquivo de filtro: 03_filter.cfg
filter { mutate { add_field => { "filter_name" => "Filter 03" } } }
Arquivo de saída: 01_out.cfg
output { stdout { codec => "rubydebug" } }
Executar o pipeline
A inicialização do Logstash sem nenhuma opção executará o arquivo pipelines.yml
que definimos anteriormente. Execute o Logstash da seguinte maneira:
./bin/logstash
Como o pipeline my-pipeline_1
está executando um gerador para simular um evento de entrada, deveremos ver a saída a seguir assim que a inicialização do Logstash terminar. Isso mostra que o conteúdo de 01_filter.cfg
e 02_filter.cfg
é executado por esse pipeline conforme o esperado.
{ "sequence" => 0, "host" => "alexandersmbp2.lan", "message" => "Generated line", "@timestamp" => 2020-02-05T22:10:09.495Z, "@version" => "1", "filter_name" => [ [0] "Filter 01", [1] "Filter 02" ] }
Como o outro pipeline chamado my-pipeline_2
está aguardando entrada no stdin, ainda não vimos nenhum evento processado por esse pipeline. Digite algo no terminal no qual o Logstash está sendo executado e pressione Return para criar um evento para esse pipeline. Depois de fazer isso, você deverá ver algo assim:
{ "filter_name" => [ [0] "Filter 02", [1] "Filter 03" ], "host" => "alexandersmbp2.lan", "message" => "I’m testing my-pipeline_2", "@timestamp" => 2020-02-05T22:20:43.250Z, "@version" => "1" }
Podemos ver acima que a lógica de 02_filter.cfg
e 03_filter.cfg
é aplicada conforme o esperado.
Ordem de execução
Esteja ciente de que o Logstash não presta atenção à ordem dos arquivos na expressão glob. Ele usa apenas a expressão glob para determinar quais arquivos incluir e os ordena alfabeticamente. Ou seja, mesmo se alterássemos a definição de my-pipeline_2
para que 03_filter.cfg
aparecesse na expressão glob antes de 02_filter.cfg
, cada evento passaria pelo filtro em 02_filter.cfg
antes do filtro definido em 03_filter.cfg
.
Conclusão
O uso de expressões glob permite que os pipelines do Logstash sejam formados com componentes modulares, que são armazenados como arquivos individuais. Isso pode melhorar a manutenção, a reutilização e a legibilidade do código.
Apenas a título de observação, além da técnica documentada neste blog, a comunicação de um pipeline a outro também deve ser considerada para avaliar se ela pode melhorar a modularidade da implementação do Logstash.