Utilitário Nginx-log-collector do Avito para enviar logs do nginx ao Clickhouse

Este artigo abordará o projeto nginx-log-collector, que irá ler os logs do nginx e enviá-los para um cluster Clickhouse. Normalmente, para logs, utiliza-se o ElasticSearch. O Clickhouse requer menos recursos (espaço em disco, RAM, CPU). O Clickhouse grava dados mais rapidamente. O Clickhouse comprime os dados, tornando-os ainda mais compactos no disco. As vantagens do Clickhouse são evidentes em 2 slides da apresentação Como o VK insere dados no ClickHouse de dezenas de milhares de servidores.

Utilitário Nginx-log-collector do Avito para enviar logs do nginx ao Clickhouse

Utilitário Nginx-log-collector do Avito para enviar logs do nginx ao Clickhouse

Para visualizar a análise dos logs, criaremos um dashboard para o Grafana.

Quem tiver interesse, sinta-se à vontade para continuar.

Instalamos o nginx e o grafana da maneira padrão.

Instalamos o cluster Clickhouse usando o ansible-playbook de Denis Proskurin.

Criação de banco de dados e tabelas no Clickhouse

Neste arquivo estão descritas as consultas SQL para criar o banco de dados e tabelas para o nginx-log-collector no Clickhouse.

Executamos cada consulta sequencialmente em cada servidor do cluster Clickhouse.

Importante observar: nesta linha, logs_cluster deve ser substituído pelo nome do seu cluster do arquivo clickhouse_remote_servers.xml entre "remote_servers" e "shard".

ENGINE = Distributed('logs_cluster', 'nginx', 'access_log_shard', rand())

Instalação e configuração do nginx-log-collector-rpm

Nginx-log-collector não possui rpm. Aqui https://github.com/patsevanton/nginx-log-collector-rpm Criamos o rpm para ele. O rpm será construído com o auxílio de Fedora Copr

Instalamos o pacote rpm nginx-log-collector-rpm

yum -y install yum-plugin-copr
yum copr enable antonpatsev/nginx-log-collector-rpm
yum -y install nginx-log-collector
systemctl start nginx-log-collector

Editamos o config /etc/nginx-log-collector/config.yaml:

  .......
  upload:
    table: nginx.access_log
    dsn: http://ip-endereço-do-cluster-clickhouse:8123/

- tag: "nginx_error:"
  format: error  # access | error
  buffer_size: 1048576
  upload:
    table: nginx.error_log
    dsn: http://ip-endereço-do-cluster-clickhouse:8123/

Configuração do nginx

Configuração geral do nginx:

user  nginx;
worker_processes  auto;

#error_log  \/var\/log\/nginx\/error.log warn;
pid        \/var\/run\/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       \/etc\/nginx\/mime.types;
    default_type  application\/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    log_format avito_json escape=json
                     '{'
                     '"event_datetime": "$time_iso8601", '
                     '"server_name": "$server_name", '
                     '"remote_addr": "$remote_addr", '
                     '"remote_user": "$remote_user", '
                     '"http_x_real_ip": "$http_x_real_ip", '
                     '"status": "$status", '
                     '"scheme": "$scheme", '
                     '"request_method": "$request_method", '
                     '"request_uri": "$request_uri", '
                     '"server_protocol": "$server_protocol", '
                     '"body_bytes_sent": $body_bytes_sent, '
                     '"http_referer": "$http_referer", '
                     '"http_user_agent": "$http_user_agent", '
                     '"request_bytes": "$request_length", '
                     '"request_time": "$request_time", '
                     '"upstream_addr": "$upstream_addr", '
                     '"upstream_response_time": "$upstream_response_time", '
                     '"hostname": "$hostname", '
                     '"host": "$host"'
                     '}';

    access_log     syslog_server=unix:\/var\/run\/nginx_log.sock,nohostname,tag=nginx avito_json; #ClickHouse
    error_log      syslog_server=unix:\/var\/run\/nginx_log.sock,nohostname,tag=nginx_error; #ClickHouse

    #access_log  \/var\/log\/nginx\/access.log  main;

    proxy_ignore_client_abort on;
    sendfile        on;
    keepalive_timeout  65;
    include \/etc\/nginx\/conf.d\/*.conf;
}

Um host virtual:

vhost1.conf:

upstream backend {
    server ip-endereço-do-servidor-com-stub_http_server:8080;
    server ip-endereço-do-servidor-com-stub_http_server:8080;
    server ip-endereço-do-servidor-com-stub_http_server:8080;
    server ip-endereço-do-servidor-com-stub_http_server:8080;
    server ip-endereço-do-servidor-com-stub_http_server:8080;
}

server {
    listen   80;
    server_name vhost1;
    location \/ {
        proxy_pass http:\/\/backend;
    }
}

Adicionando hosts virtuais ao arquivo \/etc\/hosts:

ip-endereço-do-servidor-com-nginx vhost1

Emulador de servidor HTTP

Usaremos como emulador de servidor HTTP nodejs-stub-server de Maxim Ignatenko

Nodejs-stub-server não possui rpm. Aqui https://github.com/patsevanton/nodejs-stub-server Criamos o rpm para ele. O rpm será construído com o auxílio de Fedora Copr

Instalando o pacote rpm nodejs-stub-server no upstream nginx

yum -y install yum-plugin-copr
yum copr enable antonpatsev\/nodejs-stub-server
yum -y install stub_http_server
systemctl start stub_http_server

Testes de carga

Os testes são realizados com a ajuda do Apache benchmark.

Instalando-o:

yum install -y httpd-tools

Iniciando o teste com Apache benchmark em 5 servidores diferentes:

while true; do ab -H "User-Agent: 1server" -c 10 -n 10 -t 10 http:\/\/vhost1\/.; sleep 1; done
while true; do ab -H "User-Agent: 2server" -c 10 -n 10 -t 10 http:\/\/vhost1\/.; sleep 1; done
while true; do ab -H "User-Agent: 3server" -c 10 -n 10 -t 10 http:\/\/vhost1\/.; sleep 1; done
while true; do ab -H "User-Agent: 4server" -c 10 -n 10 -t 10 http:\/\/vhost1\/.; sleep 1; done
while true; do ab -H "User-Agent: 5server" -c 10 -n 10 -t 10 http:\/\/vhost1\/.; sleep 1; done

Configuração do Grafana

No site oficial do Grafana, você não encontrará um painel.

Portanto, faremos isso manualmente.

Meu painel salvo pode ser encontrado aqui.

Você também precisa criar uma variável table com o conteúdo nginx.access_log.
Utilitário Nginx-log-collector do Avito para enviar logs do nginx ao Clickhouse

Singlestat Total Requests:

SELECIONE
 1 como t,
 contagem(*) como c
 DE $table
 ONDE $timeFilter AGRUPAR POR t

Utilitário Nginx-log-collector do Avito para enviar logs do nginx ao Clickhouse

Solicitações com Falha de Singlestat:

SELECIONE
 1 como t,
 contagem(*) como c
 DE $table
 ONDE $timeFilter E status NÃO IN (200, 201, 401) AGRUPAR POR t

Utilitário Nginx-log-collector do Avito para enviar logs do nginx ao Clickhouse

Percentual de Falhas de Singlestat:

SELECIONE
 1 como t, (soma(status = 500 ou status = 499) / soma(status = 200 ou status = 201 ou status = 401)) * 100 DE $table
 ONDE $timeFilter AGRUPAR POR t

Utilitário Nginx-log-collector do Avito para enviar logs do nginx ao Clickhouse

Tempo Médio de Resposta de Singlestat:

SELECIONE
 1, média(tempo_de_resposta) DE $table
 ONDE $timeFilter AGRUPAR POR 1

Utilitário Nginx-log-collector do Avito para enviar logs do nginx ao Clickhouse

Tempo Máximo de Resposta de Singlestat:

SELECIONE
 1 como t, max(tempo_de_resposta) como c
 DE $table
 ONDE $timeFilter AGRUPAR POR t

Utilitário Nginx-log-collector do Avito para enviar logs do nginx ao Clickhouse

Contagem de Status:

$columns(status, contagem(*) como c) DE $table

Utilitário Nginx-log-collector do Avito para enviar logs do nginx ao Clickhouse

Para exibir dados como gráfico de pizza, é necessário instalar o plugin e reiniciar o grafana.

grafana-cli plugins install grafana-piechart-panel
serviço grafana-server restart

Top 5 Status de Pizza:

SELECIONE
    1, 
    status,
    soma(status) COMO Reqs
DE $table
ONDE $timeFilter
AGRUPAR POR status
ORDER BY Reqs desc
LIMIT 5

Utilitário Nginx-log-collector do Avito para enviar logs do nginx ao Clickhouse

A partir daqui, apresentarei as consultas sem capturas de tela:

Contagem de http_user_agent:

$columns(http_user_agent, contagem(*) c) DE $table

Taxa Boa/Taxa Ruim:

$rate(contagemSe(status = 200) COMO boa, contagemSe(status != 200) COMO ruim) DE $table

Tempo de Resposta:

$rate(média(tempo_de_resposta) como tempo_de_resposta) DE $table

Tempo de resposta do upstream (tempo de resposta do primeiro upstream):

$rate(média(elemento_array(tempo_de_resposta_upstream, 1)) como tempo_de_resposta_upstream) DE $table

Contagem de Status da Tabela para todos os vhosts:

$columns(status, contagem(*) como c) DE $table

Visão geral do painel

Utilitário Nginx-log-collector do Avito para enviar logs do nginx ao Clickhouse

Utilitário Nginx-log-collector do Avito para enviar logs do nginx ao Clickhouse

Utilitário Nginx-log-collector do Avito para enviar logs do nginx ao Clickhouse

Comparação entre avg() e quantile()

avg()
Utilitário Nginx-log-collector do Avito para enviar logs do nginx ao Clickhouse
quantile()
Utilitário Nginx-log-collector do Avito para enviar logs do nginx ao Clickhouse

Conclusão:

Espero que a comunidade se junte ao desenvolvimento/teste e uso do nginx-log-collector.
E quem implementar o nginx-log-collector vai contar quanto economizou de disco, RAM, CPU.

Canais do Telegram:

Milissegundos:

Para quem se importa com milissegundos, por favor, escreva ou vote neste issue.

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster