跳至主要内容

将 Vector 集成到 ClickHouse

能够实时分析您的日志对于生产应用程序至关重要。您是否曾经想过 ClickHouse 是否擅长存储和分析日志数据?只需查看 Uber 的经验,了解他们如何将日志基础设施从 ELK 迁移到 ClickHouse。

本指南介绍如何使用流行的数据管道 Vector 尾随 Nginx 日志文件并将其发送到 ClickHouse。以下步骤对于尾随任何类型的日志文件都类似。我们假设您已经启动并运行了 ClickHouse 并且已安装 Vector(但现在无需启动它)。

1. 创建数据库和表

让我们定义一个表来存储日志事件

  1. 我们将从一个名为 **nginxdb** 的新数据库开始。

    CREATE DATABASE IF NOT EXISTS nginxdb
  2. 首先,我们只是将整个日志事件作为单个字符串插入。显然,这不是执行日志数据分析的理想格式,但我们将在下面使用 **_物化视图_** 来解决这个问题。

    CREATE TABLE IF NOT EXISTS  nginxdb.access_logs (
    message String
    )
    ENGINE = MergeTree()
    ORDER BY tuple()
    注意

    目前还没有真正需要主键,这就是为什么 **ORDER BY** 设置为 **tuple()**。

2. 配置 Nginx

我们当然不想花太多时间解释 Nginx,但我们也不想隐藏所有细节,因此在本步骤中,我们将为您提供足够的细节来配置 Nginx 日志记录。

  1. 以下 access_log 属性将日志发送到 ** /var/log/nginx/my_access.log**,格式为 **combined**。此值位于 **nginx.conf** 文件的 http 部分

    http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    access_log /var/log/nginx/my_access.log combined;
    sendfile on;
    keepalive_timeout 65;
    include /etc/nginx/conf.d/*.conf;
    }
  2. 如果您需要修改 **nginx.conf**,请确保重新启动 Nginx。

  3. 通过访问 Web 服务器上的页面在访问日志中生成一些日志事件。**combined** 格式的日志具有以下格式

    192.168.208.1 - - [12/Oct/2021:03:31:44 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"
    192.168.208.1 - - [12/Oct/2021:03:31:44 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "https://127.0.0.1/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"
    192.168.208.1 - - [12/Oct/2021:03:31:49 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"

3. 配置 Vector

Vector 收集、转换和路由日志、指标和跟踪(称为 **源**)到许多不同的供应商(称为 **接收器**),包括 ClickHouse 的开箱即用兼容性。源和接收器在名为 **vector.toml** 的配置文件中定义。

  1. 以下 **vector.toml** 定义了一个类型为 **file** 的 **源**,该源尾随 **my_access.log** 的末尾,并且还将 **接收器** 定义为上面定义的 **access_logs** 表

    [sources.nginx_logs]
    type = "file"
    include = [ "/var/log/nginx/my_access.log" ]
    read_from = "end"

    [sinks.clickhouse]
    type = "clickhouse"
    inputs = ["nginx_logs"]
    endpoint = "http://clickhouse-server:8123"
    database = "nginxdb"
    table = "access_logs"
    skip_unknown_fields = true
  2. 使用上面的配置启动 Vector。有关定义源和接收器的更多详细信息,请访问 Vector 文档

  3. 验证访问日志是否已插入 ClickHouse。运行以下查询,您应该会在表中看到访问日志

    SELECT * FROM nginxdb.access_logs
    View the logs

4. 解析日志

将日志存储在 ClickHouse 中非常棒,但是将每个事件存储为单个字符串并不利于进行数据分析。让我们看看如何使用物化视图解析日志事件。

  1. **物化视图**(简称 MV)是基于现有表的新表,当对现有表进行插入操作时,新数据也会添加到物化视图中。让我们看看如何定义一个 MV,其中包含 **access_logs** 中日志事件的解析表示,换句话说

    192.168.208.1 - - [12/Oct/2021:15:32:43 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"

    ClickHouse 中有各种函数可以解析字符串,但首先让我们看一下 **splitByWhitespace** - 它根据空格解析字符串并将每个标记返回到数组中。为了演示,请运行以下命令

    SELECT splitByWhitespace('192.168.208.1 - - [12/Oct/2021:15:32:43 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"')

    请注意,响应非常接近我们想要的结果!一些字符串有一些额外的字符,并且不需要解析用户代理(浏览器详细信息),但我们将在下一步解决此问题

    ["192.168.208.1","-","-","[12/Oct/2021:15:32:43","+0000]","\"GET","/","HTTP/1.1\"","304","0","\"-\"","\"Mozilla/5.0","(Macintosh;","Intel","Mac","OS","X","10_15_7)","AppleWebKit/537.36","(KHTML,","like","Gecko)","Chrome/93.0.4577.63","Safari/537.36\""]
  2. 与 **splitByWhitespace** 类似,**splitByRegexp** 函数根据正则表达式将字符串拆分为数组。运行以下命令,它将返回两个字符串。

    SELECT splitByRegexp('\S \d+ "([^"]*)"', '192.168.208.1 - - [12/Oct/2021:15:32:43 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"')

    请注意,返回的第二个字符串是成功从日志中解析的用户代理

    ["192.168.208.1 - - [12/Oct/2021:15:32:43 +0000] \"GET / HTTP/1.1\" 30"," \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36\""]
  3. 在查看最终的 **CREATE MATERIALIZED VIEW** 命令之前,让我们查看几个用于清理数据的函数。例如,**RequestMethod** 看起来像 **“GET”**,带有一个不需要的双引号。运行以下 **trim** 函数,它将删除双引号

    SELECT trim(LEADING '"' FROM '"GET')
  4. 时间字符串有一个前导方括号,并且格式不适合 ClickHouse 解析为日期。但是,如果我们将分隔符从冒号(**:**)更改为逗号(**,**),则解析效果很好

    SELECT parseDateTimeBestEffort(replaceOne(trim(LEADING '[' FROM '[12/Oct/2021:15:32:43'), ':', ' '))
  5. 现在我们可以定义物化视图了。我们的定义包括 **POPULATE**,这意味着将立即处理并插入 **access_logs** 中的现有行。运行以下 SQL 语句

    CREATE MATERIALIZED VIEW nginxdb.access_logs_view
    (
    RemoteAddr String,
    Client String,
    RemoteUser String,
    TimeLocal DateTime,
    RequestMethod String,
    Request String,
    HttpVersion String,
    Status Int32,
    BytesSent Int64,
    UserAgent String
    )
    ENGINE = MergeTree()
    ORDER BY RemoteAddr
    POPULATE AS
    WITH
    splitByWhitespace(message) as split,
    splitByRegexp('\S \d+ "([^"]*)"', message) as referer
    SELECT
    split[1] AS RemoteAddr,
    split[2] AS Client,
    split[3] AS RemoteUser,
    parseDateTimeBestEffort(replaceOne(trim(LEADING '[' FROM split[4]), ':', ' ')) AS TimeLocal,
    trim(LEADING '"' FROM split[6]) AS RequestMethod,
    split[7] AS Request,
    trim(TRAILING '"' FROM split[8]) AS HttpVersion,
    split[9] AS Status,
    split[10] AS BytesSent,
    trim(BOTH '"' from referer[2]) AS UserAgent
    FROM
    (SELECT message FROM nginxdb.access_logs)
  6. 现在验证它是否有效。您应该会看到访问日志被很好地解析为列

    SELECT * FROM nginxdb.access_logs_view
    View the logs
    注意

    上面的示例将数据存储在两个表中,但您可以更改初始 **nginxdb.access_logs** 表以使用 **Null** 表引擎 - 解析后的数据仍将存储在 **nginxdb.access_logs_view** 表中,但原始数据将不会存储在表中。

**总结:**通过使用 Vector,它只需要简单的安装和快速配置,我们就可以将 Nginx 服务器上的日志发送到 ClickHouse 中的表。通过使用巧妙的物化视图,我们可以将这些日志解析为列以方便分析。