
, diseñado para recopilar, transformar y enviar datos de logs, métricas y eventos.
→
Escrita en Rust, se distingue por su alto rendimiento y bajo consumo de memoria en comparación con sus similares. Además, se presta especial atención a las funcionalidades relacionadas con la corrección, en particular, las capacidades de almacenar eventos no enviados en el búfer del disco y la rotación de archivos.
Arquitectónicamente, Vector es un enrutador de eventos, que recibe mensajes de uno o más fuentes, aplicando opcionalmente transformaciones sobre esos mensajes transformaciones, y enviándolos a uno o más destinos.
Vector es un reemplazo de filebeat y logstash, puede desempeñar ambos roles (recibir y enviar logs), más detalles en su .
Si en Logstash la cadena se construye como input → filter → output, en Vector es → →
Puedes ver ejemplos en la documentación.
Esta guía es una versión revisada de la . En la guía original hay un manejo de geoip. Durante mis pruebas con geoip desde la red interna, Vector arrojó un error.
Aug 05 06:25:31.889 DEBUG transform{name=nginx_parse_rename_fields type=rename_fields}: vector::transforms::rename_fields: El campo no existía field=«geoip.country_name» rate_limit_secs=30Si alguien necesita manejar geoip, consulte la guía original de .
Configuraremos la cadena Nginx (Access logs) → Vector (Client | Filebeat) → Vector (Server | Logstash) → por separado en Clickhouse y por separado en Elasticsearch. Instalaremos 4 servidores. Aunque se puede hacer con 3 servidores.

El esquema es aproximadamente así.
Desactivamos Selinux en todos sus servidores
sed -i 's\/^SELINUX=.*\/SELINUX=disabled\/g' \/etc\/selinux\/config
rebootEn todos los servidores instalamos un emulador de servidor HTTP + utilidades
Usaremos como emulador de servidor HTTP desde
Nodejs-stub-server no tiene rpm. creamos su rpm. El rpm se construirá con la ayuda de
Añadimos el repositorio antonpatsev\/nodejs-stub-server
yum -y install yum-plugin-copr epel-release
yes | yum copr enable antonpatsev\/nodejs-stub-serverInstalamos nodejs-stub-server, Apache benchmark y el multiplexor de terminal screen en todos los servidores
yum -y install stub_http_server screen mc httpd-tools screenCorregí en el archivo \/var\/lib\/stub_http_server\/stub_http_server.js el tiempo de respuesta del stub_http_server para que haya más logs.
var max_sleep = 10;Iniciaremos stub_http_server.
systemctl start stub_http_server
systemctl enable stub_http_serveren el tercer servidor
ClickHouse utiliza un conjunto de instrucciones SSE 4.2, por lo que, a menos que se indique lo contrario, su soporte en el procesador utilizado es un requisito adicional para el sistema. Este es el comando para verificar si el procesador actual soporta SSE 4.2:
grep -q sse4_2 \/proc\/cpuinfo && echo "SSE 4.2 supported" || echo "SSE 4.2 not supported"Primero, necesitamos conectar el repositorio oficial:
sudo yum install -y yum-utils
sudo rpm --import https:\/\/repo.clickhouse.tech\/CLICKHOUSE-KEY.GPG
sudo yum-config-manager --add-repo https:\/\/repo.clickhouse.tech\/rpm\/stable\/x86_64Para instalar los paquetes, es necesario ejecutar los siguientes comandos:
sudo yum install -y clickhouse-server clickhouse-clientPermitimos que clickhouse-server escuche en la tarjeta de red en el archivo \/etc\/clickhouse-server\/config.xml
0.0.0.0Cambiamos el nivel de logging de trace a debug
debug
Las configuraciones de compresión son las estándar:
min_compress_block_size 65536
max_compress_block_size 1048576Para activar la compresión Zstd, se recomienda no tocar la configuración, sino aplicar DDL.

No encontré cómo aplicar la compresión zstd a través de DDL en Google. Por lo tanto, lo dejé como está.
Colegas, quienes usan la compresión zstd en Clickhouse, por favor compartan las instrucciones.
Para iniciar el servidor como un demonio, ejecute:
service clickhouse-server startAhora pasemos a la configuración de Clickhouse.
Accedemos a Clickhouse.
clickhouse-client -h 172.26.10.109 -m172.26.10.109 — IP del servidor donde está instalado Clickhouse.
Creamos la base de datos vector.
CREATE DATABASE vector;Verificamos que la base de datos exista.
show databases;Creamos la tabla vector.logs.
/* Это таблица где хранятся логи как есть */
CREATE TABLE vector.logs
(
`node_name` String,
`timestamp` DateTime,
`server_name` String,
`user_id` String,
`request_full` String,
`request_user_agent` String,
`request_http_host` String,
`request_uri` String,
`request_scheme` String,
`request_method` String,
`request_length` UInt64,
`request_time` Float32,
`request_referrer` String,
`response_status` UInt16,
`response_body_bytes_sent` UInt64,
`response_content_type` String,
`remote_addr` IPv4,
`remote_port` UInt32,
`remote_user` String,
`upstream_addr` IPv4,
`upstream_port` UInt32,
`upstream_bytes_received` UInt64,
`upstream_bytes_sent` UInt64,
`upstream_cache_status` String,
`upstream_connect_time` Float32,
`upstream_header_time` Float32,
`upstream_response_length` UInt64,
`upstream_response_time` Float32,
`upstream_status` UInt16,
`upstream_content_type` String,
INDEX idx_http_host request_http_host TYPE set(0) GRANULARITY 1
)
ENGINE = MergeTree()
PARTITION BY toYYYYMMDD(timestamp)
ORDER BY timestamp
TTL timestamp + toIntervalMonth(1)
SETTINGS index_granularity = 8192;Verificamos que se hayan creado las tablas. Ejecutamos clickhouse-client y hacemos la consulta.
Pasamos a la base de datos vector.
use vector;
Ok.
0 rows in set. Elapsed: 0.001 sec.Observamos las tablas.
show tables;
┌─name────────────────┐
│ logs │
└─────────────────────┘Instalación de elasticsearch en el cuarto servidor para enviar los mismos datos a Elasticsearch para comparación con Clickhouse.
Agregamos la clave RPM pública.
rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearchCreamos 2 repositorios:
/etc/yum.repos.d/elasticsearch.repo
[elasticsearch]
name=Repositorio de Elasticsearch para paquetes 7.x
baseurl=https://artifacts.elastic.co/packages/7.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=0
autorefresh=1
type=rpm-md/etc/yum.repos.d/kibana.repo
[kibana-7.x]
name=Repositorio de Kibana para paquetes 7.x
baseurl=https://artifacts.elastic.co/packages/7.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-mdInstalaremos elasticsearch y kibana.
yum install -y kibana elasticsearchDado que será en una sola instancia, necesitamos agregar en el archivo /etc/elasticsearch/elasticsearch.yml:
discovery.type: single-nodePara que vector pueda enviar datos a Elasticsearch desde otro servidor, cambiamos network.host.
network.host: 0.0.0.0Para conectarnos a kibana, cambiamos el parámetro server.host en el archivo /etc/kibana/kibana.yml.
server.host: "0.0.0.0"Iniciamos y habilitamos elasticsearch para que se inicie automáticamente.
systemctl enable elasticsearch
systemctl start elasticsearchy kibana.
systemctl enable kibana
systemctl start kibanaConfiguración de Elasticsearch para modo single-node, 1 shard, 0 replicas. Seguramente ustedes tendrán un clúster con muchos servidores y no necesitan hacer esto.
Para futuros índices, actualizamos la plantilla por defecto:
curl -X PUT http://localhost:9200/_template/default -H 'Content-Type: application/json' -d '{"index_patterns": ["*"],"order": -1,"settings": {"number_of_shards": "1","number_of_replicas": "0"}}' Instalación como reemplazo de Logstash en el segundo servidor.
yum install -y https://packages.timber.io/vector/0.9.X/vector-x86_64.rpm mc httpd-tools screenConfiguramos Vector como reemplazo de Logstash. Editamos el archivo /etc/vector/vector.toml.
# /etc/vector/vector.toml
data_dir = "/var/lib/vector"
[sources.nginx_input_vector]
# General
type = "vector"
address = "0.0.0.0:9876"
shutdown_timeout_secs = 30
[transforms.nginx_parse_json]
inputs = [ "nginx_input_vector" ]
type = "json_parser"
[transforms.nginx_parse_add_defaults]
inputs = [ "nginx_parse_json" ]
type = "lua"
version = "2"
hooks.process = """
function (event, emit)
function split_first(s, delimiter)
result = {};
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
table.insert(result, match);
end
return result[1];
end
function split_last(s, delimiter)
result = {};
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
table.insert(result, match);
end
return result[#result];
end
event.log.upstream_addr = split_first(split_last(event.log.upstream_addr, ', '), ':')
event.log.upstream_bytes_received = split_last(event.log.upstream_bytes_received, ', ')
event.log.upstream_bytes_sent = split_last(event.log.upstream_bytes_sent, ', ')
event.log.upstream_connect_time = split_last(event.log.upstream_connect_time, ', ')
event.log.upstream_header_time = split_last(event.log.upstream_header_time, ', ')
event.log.upstream_response_length = split_last(event.log.upstream_response_length, ', ')
event.log.upstream_response_time = split_last(event.log.upstream_response_time, ', ')
event.log.upstream_status = split_last(event.log.upstream_status, ', ')
if event.log.upstream_addr == "" then
event.log.upstream_addr = "127.0.0.1"
end
if (event.log.upstream_bytes_received == "-" or event.log.upstream_bytes_received == "") then
event.log.upstream_bytes_received = "0"
end
if (event.log.upstream_bytes_sent == "-" or event.log.upstream_bytes_sent == "") then
event.log.upstream_bytes_sent = "0"
end
if event.log.upstream_cache_status == "" then
event.log.upstream_cache_status = "DISABLED"
end
if (event.log.upstream_connect_time == "-" or event.log.upstream_connect_time == "") then
event.log.upstream_connect_time = "0"
end
if (event.log.upstream_header_time == "-" or event.log.upstream_header_time == "") then
event.log.upstream_header_time = "0"
end
if (event.log.upstream_response_length == "-" or event.log.upstream_response_length == "") then
event.log.upstream_response_length = "0"
end
if (event.log.upstream_response_time == "-" or event.log.upstream_response_time == "") then
event.log.upstream_response_time = "0"
end
if (event.log.upstream_status == "-" or event.log.upstream_status == "") then
event.log.upstream_status = "0"
end
emit(event)
end
"""
[transforms.nginx_parse_remove_fields]
inputs = [ "nginx_parse_add_defaults" ]
type = "remove_fields"
fields = ["data", "file", "host", "source_type"]
[transforms.nginx_parse_coercer]
type = "coercer"
inputs = ["nginx_parse_remove_fields"]
types.request_length = "int"
types.request_time = "float"
types.response_status = "int"
types.response_body_bytes_sent = "int"
types.remote_port = "int"
types.upstream_bytes_received = "int"
types.upstream_bytes_send = "int"
types.upstream_connect_time = "float"
types.upstream_header_time = "float"
types.upstream_response_length = "int"
types.upstream_response_time = "float"
types.upstream_status = "int"
types.timestamp = "timestamp"
[sinks.nginx_output_clickhouse]
inputs = ["nginx_parse_coercer"]
type = "clickhouse"
database = "vector"
healthcheck = true
host = "http://172.26.10.109:8123" # Адрес Clickhouse
table = "logs"
encoding.timestamp_format = "unix"
buffer.type = "disk"
buffer.max_size = 104900000
buffer.when_full = "block"
request.in_flight_limit = 20
[sinks.elasticsearch]
type = "elasticsearch"
inputs = ["nginx_parse_coercer"]
compression = "none"
healthcheck = true
# 172.26.10.116 - сервер где установен elasticsearch
host = "http://172.26.10.116:9200"
index = "vector-%Y-%m-%d"Puedes ajustar la sección transforms.nginx_parse_add_defaults.
Dado que usa estas configuraciones para un pequeño CDN y en upstream_* pueden recibir varios valores.
Por ejemplo:
"upstream_addr": "128.66.0.10:443, 128.66.0.11:443, 128.66.0.12:443"
"upstream_bytes_received": "-, -, 123"
"upstream_status": "502, 502, 200"Si esta no es su situación, esta sección se puede simplificar.
Crearemos la configuración del servicio para systemd /etc/systemd/system/vector.service
# /etc/systemd/system/vector.service
[Unit]
Description=Vector
After=network-online.target
Requires=network-online.target
[Service]
User=vector
Group=vector
ExecStart=/usr/bin/vector
ExecReload=/bin/kill -HUP $MAINPID
Restart=no
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=vector
[Install]
WantedBy=multi-user.targetDespués de crear las tablas, se puede iniciar Vector.
systemctl enable vector
systemctl start vectorLos registros de vector se pueden ver así:
journalctl -f -u vectorEn los registros deben aparecer estas entradas:
INFO vector::topology::builder: Healthcheck: Pasó.
INFO vector::topology::builder: Healthcheck: Pasó.En el cliente (Servidor Web) — primer servidor.
En el servidor con nginx, es necesario desactivar ipv6, ya que en la tabla logs en clickhouse se utiliza el campo upstream_addr IPv4, ya que no utilizo ipv6 dentro de la red. Si no se desactiva ipv6, habrá errores:
DB::Exception: Valor IPv4 inválido.: (al leer el valor de la clave upstream_addr)Es posible que los lectores deban agregar soporte para ipv6.
Creamos el archivo /etc/sysctl.d/98-disable-ipv6.conf
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1Aplicamos la configuración.
sysctl --systemInstalaremos nginx.
Agregué el archivo del repositorio nginx /etc/yum.repos.d/nginx.repo
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=trueInstalaremos el paquete nginx.
yum install -y nginxPara empezar, necesitamos configurar el formato de los registros en Nginx en el archivo /etc/nginx/nginx.conf
usuario nginx;
# Debes establecer los procesos de trabajo según tus núcleos de CPU, nginx no se beneficia de configurar más de eso
worker_processes auto; # algunas versiones recientes lo calculan automáticamente
# número de descriptores de archivo utilizados por nginx
# el límite para el máximo de FDs en el servidor suele ser establecido por el SO.
# si no estableces FD, se utilizarán las configuraciones del SO que por defecto son 2000
worker_rlimit_nofile 100000;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
# proporciona el contexto del archivo de configuración en el que se especifican las directivas que afectan al procesamiento de conexiones.
events {
# determina cuántos clientes serán atendidos por trabajador
# max clientes = worker_connections * worker_processes
# el número máximo de clientes también está limitado por la cantidad de conexiones de socket disponibles en el sistema (~64k)
worker_connections 4000;
# optimizado para atender a muchos clientes con cada hilo, esencial para linux -- para entorno de prueba
use epoll;
# acepta tantas conexiones como sea posible, puede inundar las conexiones del trabajador si se establece demasiado bajo -- para entorno de prueba
multi_accept on;
}
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 vector escape=json
'{'
'"node_name":"nginx-vector",'
'"timestamp":"$time_iso8601",'
'"server_name":"$server_name",'
'"request_full": "$request",'
'"request_user_agent":"$http_user_agent",'
'"request_http_host":"$http_host",'
'"request_uri":"$request_uri",'
'"request_scheme": "$scheme",'
'"request_method":"$request_method",'
'"request_length":"$request_length",'
'"request_time": "$request_time",'
'"request_referrer":"$http_referer",'
'"response_status": "$status",'
'"response_body_bytes_sent":"$body_bytes_sent",'
'"response_content_type":"$sent_http_content_type",'
'"remote_addr": "$remote_addr",'
'"remote_port": "$remote_port",'
'"remote_user": "$remote_user",'
'"upstream_addr": "$upstream_addr",'
'"upstream_bytes_received": "$upstream_bytes_received",'
'"upstream_bytes_sent": "$upstream_bytes_sent",'
'"upstream_cache_status":"$upstream_cache_status",'
'"upstream_connect_time":"$upstream_connect_time",'
'"upstream_header_time":"$upstream_header_time",'
'"upstream_response_length":"$upstream_response_length",'
'"upstream_response_time":"$upstream_response_time",'
'"upstream_status": "$upstream_status",'
'"upstream_content_type":"$upstream_http_content_type"'
'}';
access_log /var/log/nginx/access.log main;
access_log /var/log/nginx/access.json.log vector; # Nuevo log en formato json
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}Para no romper tu configuración actual, Nginx permite tener múltiples directivas access_log
access_log /var/log/nginx/access.log main; # Log estándar
access_log /var/log/nginx/access.json.log vector; # Nuevo log en formato jsonNo olvides añadir una regla en logrotate para los nuevos logs (si el archivo de log no termina en .log)
Eliminamos default.conf de /etc/nginx/conf.d/
rm -f /etc/nginx/conf.d/default.confAñadimos un host virtual /etc/nginx/conf.d/vhost1.conf
server {
listen 80;
server_name vhost1;
location / {
proxy_pass http://172.26.10.106:8080;
}
}Añadimos el host virtual /etc/nginx/conf.d/vhost2.conf
server {
listen 80;
server_name vhost2;
location / {
proxy_pass http://172.26.10.108:8080;
}
}Añadimos el host virtual /etc/nginx/conf.d/vhost3.conf
server {
listen 80;
server_name vhost3;
location / {
proxy_pass http://172.26.10.109:8080;
}
}Añadimos el host virtual /etc/nginx/conf.d/vhost4.conf
server {
listen 80;
server_name vhost4;
location / {
proxy_pass http://172.26.10.116:8080;
}
}Añadimos en el archivo /etc/hosts los hosts virtuales (172.26.10.106 es la IP del servidor donde está instalado nginx) en todos los servidores:
172.26.10.106 vhost1
172.26.10.106 vhost2
172.26.10.106 vhost3
172.26.10.106 vhost4Y si todo está listo, entonces
nginx -t
systemctl restart nginxAhora instalaremos el mismo
yum install -y https://packages.timber.io/vector/0.9.X/vector-x86_64.rpmCrearemos el archivo de configuración para systemd /etc/systemd/system/vector.service
[Unit]
Description=Vector
After=network-online.target
Requires=network-online.target
[Service]
User=vector
Group=vector
ExecStart=/usr/bin/vector
ExecReload=/bin/kill -HUP $MAINPID
Restart=no
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=vector
[Install]
WantedBy=multi-user.targetY configuraremos la sustitución de Filebeat en la configuración /etc/vector/vector.toml. La dirección IP 172.26.10.108 es la dirección IP del servidor de logs (Vector-Server)
data_dir = "/var/lib/vector"
[sources.nginx_file]
type = "file"
include = [ "/var/log/nginx/access.json.log" ]
start_at_beginning = false
fingerprinting.strategy = "device_and_inode"
[sinks.nginx_output_vector]
type = "vector"
inputs = [ "nginx_file" ]
address = "172.26.10.108:9876"No olvides agregar el usuario vector al grupo necesario para que pueda leer los archivos de log. Por ejemplo, nginx en centos crea logs con permisos del grupo adm.
usermod -a -G adm vectorIniciaremos el servicio vector
systemctl enable vector
systemctl start vectorLos registros de vector se pueden ver así:
journalctl -f -u vectorEn los logs debería aparecer este registro
INFO vector::topology::builder: Healthcheck: Passed.Pruebas de carga
Realizamos pruebas utilizando Apache benchmark.
El paquete httpd-tools ha sido instalado en todos los servidores
Empezamos la prueba usando Apache benchmark desde 4 servidores diferentes en screen. Primero lanzamos el multiplexor de terminal screen, y luego iniciamos la prueba con Apache benchmark. Cómo trabajar con screen puedes encontrar en .
Desde el primer servidor
while true; do ab -H "User-Agent: 1server" -c 100 -n 10 -t 10 http://vhost1/; sleep 1; doneDesde el segundo servidor
while true; do ab -H "User-Agent: 2server" -c 100 -n 10 -t 10 http://vhost2/; sleep 1; doneDesde el tercer servidor
while true; do ab -H "User-Agent: 3server" -c 100 -n 10 -t 10 http://vhost3/; sleep 1; doneDesde el cuarto servidor
while true; do ab -H "User-Agent: 4server" -c 100 -n 10 -t 10 http://vhost4/; sleep 1; doneVerificaremos los datos en Clickhouse
Accedemos a Clickhouse.
clickhouse-client -h 172.26.10.109 -mHacemos una consulta SQL
SELECCIONAR * DE vector.logs;
┌─node_name────┬───────────timestamp─┬─server_name─┬─user_id─┬─request_full───┬─request_user_agent─┬─request_http_host─┬─request_uri─┬─request_scheme─┬─request_method─┬─request_length─┬─request_time─┬─request_referrer─┬─response_status─┬─response_body_bytes_sent─┬─response_content_type─┬───remote_addr─┬─remote_port─┬─remote_user─┬─upstream_addr─┬─upstream_port─┬─upstream_bytes_received─┬─upstream_bytes_sent─┬─upstream_cache_status─┬─upstream_connect_time─┬─upstream_header_time─┬─upstream_response_length─┬─upstream_response_time─┬─upstream_status─┬─upstream_content_type─┐
│ nginx-vector │ 2020-08-07 04:32:42 │ vhost1 │ │ GET / HTTP/1.0 │ 1server │ vhost1 │ / │ http │ GET │ 66 │ 0.028 │ │ 404 │ 27 │ │ 172.26.10.106 │ 45886 │ │ 172.26.10.106 │ 0 │ 109 │ 97 │ DISABLEADO │ 0 │ 0.025 │ 27 │ 0.029 │ 404 │ │
└──────────────┴─────────────────────┴─────────────┴─────────┴────────────────┴────────────────────┴───────────────────┴─────────────┴────────────────┴────────────────┴────────────────┴──────────────┴──────────────────┴─────────────────┴──────────────────────────┴───────────────────────┴───────────────┴─────────────┴─────────────┴───────────────┴───────────────┴─────────────────────────┴─────────────────────┴───────────────────────┴───────────────────────┴──────────────────────┴──────────────────────────┴────────────────────────┴─────────────────┴───────────────────────Conocemos el tamaño de las tablas en Clickhouse
SELECT concat(database, '.', table) AS tabla,
formatReadableSize(sum(bytes)) AS tamaño,
sum(rows) AS filas,
max(modification_time) AS última_modificación,
sum(bytes) AS tamaño_bytes,
any(engine) AS motor,
formatReadableSize(sum(primary_key_bytes_in_memory)) AS tamaño_claves_principales
FROM system.parts
WHERE active
GROUP BY database, table
ORDER BY tamaño_bytes DESC;Verificamos cuánto espacio ocupan los logs en Clickhouse.

El tamaño de la tabla logs es de 857.19 MB.

El tamaño de los mismos datos en el índice de Elasticsearch ocupa 4.5 GB.
Si no se especifica vector en los parámetros de Clickhouse, los datos ocupan 4500/857.19 = 5.24 veces menos que en Elasticsearch.
En vector, el campo compression se utiliza por defecto.
Chat de Telegram sobre
Chat de Telegram sobre
Chat de Telegram sobre ""
Fuente: habr.com
