La utilidad Nginx-log-collector de Avito para enviar logs de nginx a Clickhouse

En este artículo se analizará el proyecto nginx-log-collector, que leerá los registros de nginx y los enviará a un clúster de Clickhouse. Normalmente, se utiliza ElasticSearch para los registros. Clickhouse requiere menos recursos (espacio en disco, RAM, CPU). Clickhouse escribe datos más rápido. Clickhouse comprime datos, lo que hace que la información en disco sea aún más compacta. Las ventajas de Clickhouse se evidencian en 2 diapositivas de la presentación. Cómo VK inserta datos en ClickHouse desde decenas de miles de servidores.

La utilidad Nginx-log-collector de Avito para enviar logs de nginx a Clickhouse

La utilidad Nginx-log-collector de Avito para enviar logs de nginx a Clickhouse

Para ver la analítica de los registros, crearemos un dashboard para Grafana.

A los interesados, bienvenidos a continuación.

Instalamos nginx y grafana de manera estándar.

Instalamos un clúster de clickhouse utilizando el ansible-playbook de Denis Proskurin.

Creación de la base de datos y tablas en Clickhouse

En este archivo se describen las consultas SQL para crear la base de datos y tablas para nginx-log-collector en Clickhouse.

Realizamos cada consulta secuencialmente en cada servidor del clúster de Clickhouse.

Nota importante. En esta línea, logs_cluster debe ser reemplazado por el nombre de su clúster del archivo clickhouse_remote_servers.xml entre "remote_servers" y "shard".

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

Instalación y configuración de nginx-log-collector-rpm

Nginx-log-collector no tiene rpm. Aquí https://github.com/patsevanton/nginx-log-collector-rpm creamos su rpm. El rpm se construirá con la ayuda de Fedora Copr

Instalamos el paquete 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 la configuración /etc/nginx-log-collector/config.yaml:

  .......
  upload:
    table: nginx.access_log
    dsn: http://ip-dirección-del-clúster-clickhouse:8123/

- tag: "nginx_error:"
  format: error  # access | error
  buffer_size: 1048576
  upload:
    table: nginx.error_log
    dsn: http://ip-dirección-del-clúster-clickhouse:8123/

Configuración de nginx

Configuración general de nginx:

usuario  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;
}

Un host virtual:

vhost1.conf:

upstream backend {
    server ip-address-of-server-with-stub_http_server:8080;
    server ip-address-of-server-with-stub_http_server:8080;
    server ip-address-of-server-with-stub_http_server:8080;
    server ip-address-of-server-with-stub_http_server:8080;
    server ip-address-of-server-with-stub_http_server:8080;
}

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

Agregamos los hosts virtuales en el archivo /etc/hosts:

ip-address-of-server-with-nginx vhost1

Emulador de servidor HTTP

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

Nodejs-stub-server no tiene rpm. Aquí https://github.com/patsevanton/nodejs-stub-server creamos su rpm. El rpm se construirá con la ayuda de Fedora Copr

Instalamos el paquete rpm nodejs-stub-server en 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

Pruebas de carga

Realizamos pruebas utilizando Apache benchmark.

Lo instalamos:

yum install -y httpd-tools

Iniciamos la prueba con Apache benchmark desde 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

Configuración de Grafana

En el sitio web oficial de Grafana no encontrarás un tablero.

Así que lo haremos manualmente.

Puedes encontrar mi tablero guardado aquí.

También necesitas crear una variable table con el contenido nginx.access_log.
La utilidad Nginx-log-collector de Avito para enviar logs de nginx a Clickhouse

Total de solicitudes de Singlestat:

SELECT
 1 as t,
 count(*) as c
 FROM $table
 WHERE $timeFilter GROUP BY t

La utilidad Nginx-log-collector de Avito para enviar logs de nginx a Clickhouse

Solicitudes fallidas de Singlestat:

SELECT
 1 as t,
 count(*) as c
 FROM $table
 WHERE $timeFilter AND status NOT IN (200, 201, 401) GROUP BY t

La utilidad Nginx-log-collector de Avito para enviar logs de nginx a Clickhouse

Porcentaje de fallos de Singlestat:

SELECT
 1 as t, (sum(status = 500 or status = 499) / sum(status = 200 or status = 201 or status = 401)) * 100 FROM $table
 WHERE $timeFilter GROUP BY t

La utilidad Nginx-log-collector de Avito para enviar logs de nginx a Clickhouse

Tiempo de respuesta promedio de Singlestat:

SELECT
 1, avg(request_time) FROM $table
 WHERE $timeFilter GROUP BY 1

La utilidad Nginx-log-collector de Avito para enviar logs de nginx a Clickhouse

Tiempo de respuesta máximo de Singlestat:

SELECT
 1 as t, max(request_time) as c
 FROM $table
 WHERE $timeFilter GROUP BY t

La utilidad Nginx-log-collector de Avito para enviar logs de nginx a Clickhouse

Contar estado:

$columns(status, count(*) as c) from $table

La utilidad Nginx-log-collector de Avito para enviar logs de nginx a Clickhouse

Para mostrar los datos como un gráfico circular, necesitas instalar el complemento y reiniciar grafana.

grafana-cli plugins install grafana-piechart-panel
service grafana-server restart

Gráfico circular de los 5 principales estados:

SELECT
    1, 
    /* valor de tiempo falso */
    status,
    sum(status) AS Reqs
FROM $table
WHERE $timeFilter
GROUP BY status
ORDER BY Reqs desc
LIMIT 5

La utilidad Nginx-log-collector de Avito para enviar logs de nginx a Clickhouse

A continuación, proporcionaré las consultas sin capturas de pantalla:

Contar http_user_agent:

$columns(http_user_agent, count(*) c) FROM $table

Tasa buena/tasa mala:

$rate(countIf(status = 200) AS good, countIf(status != 200) AS bad) FROM $table

Temporización de respuesta:

$rate(avg(request_time) as request_time) FROM $table

Tiempo de respuesta del upstream (tiempo de respuesta del primer upstream):

$rate(avg(arrayElement(upstream_response_time,1)) as upstream_response_time) FROM $table

Contar estado de tabla para todos los vhosts:

$columns(status, count(*) as c) from $table

Vista general del dashboard

La utilidad Nginx-log-collector de Avito para enviar logs de nginx a Clickhouse

La utilidad Nginx-log-collector de Avito para enviar logs de nginx a Clickhouse

La utilidad Nginx-log-collector de Avito para enviar logs de nginx a Clickhouse

Comparación de avg() y quantile()

avg()
La utilidad Nginx-log-collector de Avito para enviar logs de nginx a Clickhouse
quantile()
La utilidad Nginx-log-collector de Avito para enviar logs de nginx a Clickhouse

Salida:

Espero que la comunidad se una al desarrollo/prueba y uso de nginx-log-collector.
Y alguien que implemente nginx-log-collector cuente cuánto ha ahorrado en disco, RAM, CPU.

Canales de Telegram:

Milisegundos:

A quienes les importan los milisegundos, por favor, escriban o voten en esto problema.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster