Az Nginx naplóelemzést végez az Amazon Athena és a Cube.js használatával

Általában kereskedelmi termékeket vagy kész nyílt forráskódú alternatívákat, például Prometheus + Grafana-t használnak az Nginx működésének megfigyelésére és elemzésére. Ez egy jó lehetőség figyeléshez vagy valós idejű elemzéshez, de nem túl kényelmes történelmi elemzéshez. Bármely népszerű erőforráson az nginx naplókból származó adatok mennyisége gyorsan növekszik, és nagy mennyiségű adat elemzéséhez logikus, hogy valami speciálisabbat használjunk.

Ebben a cikkben elmondom, hogyan használhatod Athéné a naplók elemzéséhez, az Nginx példájával, és megmutatom, hogyan lehet ezekből az adatokból analitikai irányítópultot összeállítani a nyílt forráskódú cube.js keretrendszer használatával. Íme a teljes megoldás architektúrája:

Az Nginx naplóelemzést végez az Amazon Athena és a Cube.js használatával

TL:DR;
Link a kész műszerfalhoz.

Az általunk használt információk gyűjtésére Fluentd, feldolgozásra - AWS Kinesis Data Firehose и AWS ragasztó, tárolásra - AWS S3. Ezzel a csomaggal nem csak nginx naplókat tárolhat, hanem más eseményeket, valamint más szolgáltatások naplóit is. Néhány alkatrészt lecserélhet hasonlókra a veremben, például naplókat írhat a kinesisbe közvetlenül az nginx-ből, a fluentd megkerülésével, vagy használhatja a logstash-t.

Nginx naplók gyűjtése

Alapértelmezés szerint az Nginx naplók valahogy így néznek ki:

4/9/2019 12:58:17 PM1.1.1.1 - - [09/Apr/2019:09:58:17 +0000] "GET /sign-up HTTP/2.0" 200 9168 "https://example.com/sign-in" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" "-"
4/9/2019 12:58:17 PM1.1.1.1 - - [09/Apr/2019:09:58:17 +0000] "GET /sign-in HTTP/2.0" 200 9168 "https://example.com/sign-up" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" "-"

Elemezhetők, de sokkal könnyebb kijavítani az Nginx konfigurációt úgy, hogy az JSON-ban készítsen naplókat:

log_format json_combined escape=json '{ "created_at": "$msec", '
            '"remote_addr": "$remote_addr", '
            '"remote_user": "$remote_user", '
            '"request": "$request", '
            '"status": $status, '
            '"bytes_sent": $bytes_sent, '
            '"request_length": $request_length, '
            '"request_time": $request_time, '
            '"http_referrer": "$http_referer", '
            '"http_x_forwarded_for": "$http_x_forwarded_for", '
            '"http_user_agent": "$http_user_agent" }';

access_log  /var/log/nginx/access.log  json_combined;

S3 tároláshoz

A naplók tárolásához az S3-at fogjuk használni. Ez lehetővé teszi a naplók egy helyen történő tárolását és elemzését, mivel az Athena közvetlenül tud dolgozni az S3-ban lévő adatokkal. A cikk későbbi részében elmondom, hogyan kell helyesen hozzáadni és feldolgozni a naplókat, de először szükségünk van egy tiszta vödörre az S3-ban, amelyben semmi más nem kerül tárolásra. Érdemes előre megfontolni, hogy melyik régióban hozza létre a gyűjtőhelyet, mert az Athena nem érhető el minden régióban.

Áramkör létrehozása az Athena konzolban

Hozzunk létre egy táblázatot az Athénában a naplókhoz. Íráshoz és olvasáshoz egyaránt szükséges, ha a Kinesis Firehose használatát tervezi. Nyissa meg az Athena konzolt, és hozzon létre egy táblázatot:

SQL tábla létrehozása

CREATE EXTERNAL TABLE `kinesis_logs_nginx`(
  `created_at` double, 
  `remote_addr` string, 
  `remote_user` string, 
  `request` string, 
  `status` int, 
  `bytes_sent` int, 
  `request_length` int, 
  `request_time` double, 
  `http_referrer` string, 
  `http_x_forwarded_for` string, 
  `http_user_agent` string)
ROW FORMAT SERDE 
  'org.apache.hadoop.hive.ql.io.orc.OrcSerde' 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.orc.OrcInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat'
LOCATION
  's3://<YOUR-S3-BUCKET>'
TBLPROPERTIES ('has_encrypted_data'='false');

Kinesis Firehose Stream létrehozása

A Kinesis Firehose az Nginxtől kapott adatokat az S3-ba a kiválasztott formátumban írja, YYYY/MM/DD/ÓH formátumú könyvtárakra osztva. Ez hasznos lesz az adatok olvasásakor. A fluentd-ből természetesen közvetlenül S3-ba írhat, de ebben az esetben JSON-t kell írnia, és ez a fájlok nagy mérete miatt nem hatékony. Ezenkívül a PrestoDB vagy az Athena használatakor a JSON a leglassabb adatformátum. Tehát nyissa meg a Kinesis Firehose konzolt, kattintson a „Kézbesítési adatfolyam létrehozása” elemre, és válassza ki a „közvetlen PUT” lehetőséget a „szállítás” mezőben:

Az Nginx naplóelemzést végez az Amazon Athena és a Cube.js használatával

A következő lapon válassza a „Record format conversion” - „Enabled” lehetőséget, és válassza ki az „Apache ORC” felvételi formátumot. Egyes kutatások szerint Owen O'Malley, ez az optimális formátum a PrestoDB és az Athena számára. A fent elkészített táblázatot használjuk sémaként. Kérjük, vegye figyelembe, hogy a kinezisben tetszőleges S3 helyet megadhat, a táblázatból csak a séma kerül felhasználásra. De ha más S3 helyet ad meg, akkor nem fogja tudni kiolvasni ezeket a rekordokat ebből a táblázatból.

Az Nginx naplóelemzést végez az Amazon Athena és a Cube.js használatával

Tároláshoz az S3-at és a korábban létrehozott vödröt választjuk. Az Aws Glue Crawler, amelyről egy kicsit később fogok beszélni, nem tud működni az S3-as vödörben lévő előtagokkal, ezért fontos, hogy üresen hagyja.

Az Nginx naplóelemzést végez az Amazon Athena és a Cube.js használatával

A többi opció a terheléstől függően változtatható, én általában az alapértelmezetteket használom. Vegye figyelembe, hogy az S3 tömörítés nem elérhető, de az ORC alapértelmezés szerint natív tömörítést használ.

Fluentd

Most, hogy beállítottuk a naplók tárolását és fogadását, konfigurálnunk kell a küldést. Használni fogjuk Fluentd, mert szeretem a Rubyt, de használhatsz Logstash-t vagy küldhetsz naplókat közvetlenül a kinesisnek. A Fluentd szerver többféleképpen indítható, a dockerről mesélek, mert egyszerű és kényelmes.

Először is szükségünk van a fluent.conf konfigurációs fájlra. Hozd létre és add hozzá a forrást:

típus előre
port 24224
köt 0.0.0.0

Most elindíthatja a Fluentd szervert. Ha fejlettebb konfigurációra van szüksége, látogasson el ide Docker hub Van egy részletes útmutató, amely tartalmazza a kép összeállítását.

$ docker run 
  -d 
  -p 24224:24224 
  -p 24224:24224/udp 
  -v /data:/fluentd/log 
  -v <PATH-TO-FLUENT-CONF>:/fluentd/etc fluentd 
  -c /fluentd/etc/fluent.conf
  fluent/fluentd:stable

Ez a konfiguráció az elérési utat használja /fluentd/log a naplók gyorsítótárazásához küldés előtt. E nélkül is megteheti, de újraindításkor mindent elveszíthet, ami gyorsítótárban van a visszatörő munkával. Bármilyen portot is használhat; a 24224 az alapértelmezett Fluentd port.

Most, hogy a Fluentd fut, Nginx naplókat küldhetünk oda. Az Nginx-et általában Docker-tárolóban futtatjuk, ebben az esetben a Docker rendelkezik egy natív naplózási illesztőprogrammal a Fluentd számára:

$ docker run 
--log-driver=fluentd 
--log-opt fluentd-address=<FLUENTD-SERVER-ADDRESS>
--log-opt tag="{{.Name}}" 
-v /some/content:/usr/share/nginx/html:ro 
-d 
nginx

Ha az Nginxet másként futtatja, használhat naplófájlokat, a Fluentd ezt teszi file tail plugin.

Adjuk hozzá a fent konfigurált naplóelemzést a Fluent konfigurációhoz:

<filter YOUR-NGINX-TAG.*>
  @type parser
  key_name log
  emit_invalid_record_to_error false
  <parse>
    @type json
  </parse>
</filter>

És naplók küldése a Kinesisnek segítségével kinesis firehose plugin:

<match YOUR-NGINX-TAG.*>
    @type kinesis_firehose
    region region
    delivery_stream_name <YOUR-KINESIS-STREAM-NAME>
    aws_key_id <YOUR-AWS-KEY-ID>
    aws_sec_key <YOUR_AWS-SEC_KEY>
</match>

Athéné

Ha mindent helyesen konfigurált, akkor egy idő után (a Kinesis alapértelmezés szerint 10 percenként rögzíti a kapott adatokat) naplófájlokat kell látnia az S3-ban. A Kinesis Firehose „monitoring” menüjében láthatja, hogy mennyi adatot rögzít az S3, valamint a hibákat. Ne felejtsen el írási hozzáférést adni az S3 tárolóhoz a Kinesis szerepkör számára. Ha a Kinesis nem tudott valamit elemezni, akkor a hibákat ugyanabba a gyűjtőhelyre teszi.

Most megtekintheti az adatokat az Athénában. Nézzük meg a legutóbbi kéréseket, amelyeknél hibát adtunk vissza:

SELECT * FROM "db_name"."table_name" WHERE status > 499 ORDER BY created_at DESC limit 10;

Az összes rekord szkennelése minden kérés esetén

Mostanra naplóinkat feldolgoztuk és az S3-ban tároltuk ORC-ben, tömörítve és elemzésre készen. A Kinesis Firehose még könyvtárakba is rendezte őket óránként. Mindaddig azonban, amíg a tábla nincs particionálva, az Athena ritka kivételektől eltekintve minden kérésre minden idők adatait betölti. Ez két okból is nagy probléma:

  • Az adatok mennyisége folyamatosan növekszik, lassítva a lekérdezéseket;
  • Az Athena számlázása a beolvasott adatok mennyisége alapján történik, kérésenként legalább 10 MB-tal.

Ennek kijavításához az AWS Glue Crawlert használjuk, amely feltérképezi az S3-ban lévő adatokat, és beírja a partíciós információkat a Glue Metastore-ba. Ez lehetővé teszi számunkra, hogy a partíciókat szűrőként használjuk az Athena lekérdezésekor, és csak a lekérdezésben megadott könyvtárakat vizsgálja.

Az Amazon Glue Crawler beállítása

Az Amazon Glue Crawler átvizsgálja az S3 vödörben lévő összes adatot, és partíciókat tartalmazó táblázatokat hoz létre. Hozzon létre egy Glue Crawlert az AWS Glue konzolból, és adjon hozzá egy tárolót, ahol tárolja az adatokat. Egy bejáró több gyűjtőcsoporthoz is használható, ebben az esetben a megadott adatbázisban táblákat hoz létre, amelyek neve megegyezik a gyűjtőzónák nevével. Ha rendszeresen szeretné használni ezeket az adatokat, ügyeljen arra, hogy igényeinek megfelelően konfigurálja a Crawler indítási ütemezését. Minden asztalhoz egy bejárót használunk, amely óránként fut.

Partícionált táblák

A bejáró első indítása után a beállításokban megadott adatbázisban minden beolvasott gyűjtőcsoporthoz tartozó táblázatoknak meg kell jelenniük. Nyissa meg az Athena konzolt, és keresse meg az Nginx-naplókat tartalmazó táblázatot. Próbáljunk meg olvasni valamit:

SELECT * FROM "default"."part_demo_kinesis_bucket"
WHERE(
  partition_0 = '2019' AND
  partition_1 = '04' AND
  partition_2 = '08' AND
  partition_3 = '06'
  );

Ez a lekérdezés az összes rekordot kiválasztja, amely 6. április 7-án reggel 8 és reggel 2019 óra között érkezett. De mennyivel hatékonyabb ez, mint egy nem particionált táblából olvasni? Nézzük meg és jelöljük ki ugyanazokat a rekordokat, időbélyeg szerint szűrve őket:

Az Nginx naplóelemzést végez az Amazon Athena és a Cube.js használatával

3.59 másodperc és 244.34 megabájt adat egy adatkészleten, amely mindössze egy hét naplózást tartalmaz. Próbáljunk meg partíciónkénti szűrést:

Az Nginx naplóelemzést végez az Amazon Athena és a Cube.js használatával

Kicsit gyorsabb, de ami a legfontosabb - csak 1.23 megabájt adat! Sokkal olcsóbb lenne, ha nem a minimum 10 megabájt kérésenként az árazásban. De még mindig sokkal jobb, és a nagy adatkészleteken a különbség sokkal lenyűgözőbb lesz.

Irányítópult készítése a Cube.js használatával

Az irányítópult összeállításához a Cube.js elemzési keretrendszert használjuk. Elég sok funkciója van, de minket kettő érdekel: a partíciószűrők automatikus használatának lehetősége és az adatok előzetes összesítése. Adatsémát használ adatséma, Javascriptben írva SQL generálására és adatbázis-lekérdezés végrehajtására. Csak azt kell jeleznünk, hogyan használjuk a partíciószűrőt az adatsémában.

Hozzon létre egy új Cube.js alkalmazást. Mivel már az AWS-vermet használjuk, logikus a Lambda használata a telepítéshez. Használhatja az expressz sablont a generáláshoz, ha a Cube.js háttérprogramot Herokuban vagy Dockerben kívánja tárolni. A dokumentáció másokat is leír fogadási módszerek.

$ npm install -g cubejs-cli
$ cubejs create nginx-log-analytics -t serverless -d athena

A környezeti változók segítségével konfigurálható az adatbázis-hozzáférés a cube.js fájlban. A generátor létrehoz egy .env fájlt, amelyben megadhatja a kulcsait Athéné.

Most szükségünk van adatséma, amelyben pontosan jelezni fogjuk a naplóink ​​tárolásának módját. Itt megadhatja azt is, hogyan számítsa ki az irányítópultok mérőszámait.

Könyvtárban schema, hozzon létre egy fájlt Logs.js. Íme egy példa adatmodell az nginx számára:

Modell kód

const partitionFilter = (from, to) => `
    date(from_iso8601_timestamp(${from})) <= date_parse(partition_0 || partition_1 || partition_2, '%Y%m%d') AND
    date(from_iso8601_timestamp(${to})) >= date_parse(partition_0 || partition_1 || partition_2, '%Y%m%d')
    `

cube(`Logs`, {
  sql: `
  select * from part_demo_kinesis_bucket
  WHERE ${FILTER_PARAMS.Logs.createdAt.filter(partitionFilter)}
  `,

  measures: {
    count: {
      type: `count`,
    },

    errorCount: {
      type: `count`,
      filters: [
        { sql: `${CUBE.isError} = 'Yes'` }
      ]
    },

    errorRate: {
      type: `number`,
      sql: `100.0 * ${errorCount} / ${count}`,
      format: `percent`
    }
  },

  dimensions: {
    status: {
      sql: `status`,
      type: `number`
    },

    isError: {
      type: `string`,
      case: {
        when: [{
          sql: `${CUBE}.status >= 400`, label: `Yes`
        }],
        else: { label: `No` }
      }
    },

    createdAt: {
      sql: `from_unixtime(created_at)`,
      type: `time`
    }
  }
});

Itt a változót használjuk FILTER_PARAMSSQL lekérdezés generálásához partíciószűrővel.

Ezenkívül beállítjuk azokat a mérőszámokat és paramétereket, amelyeket meg szeretnénk jeleníteni a műszerfalon, és megadjuk az előzetes összesítéseket. A Cube.js további táblákat hoz létre előre összesített adatokkal, és automatikusan frissíti az adatokat, amint megérkeznek. Ez nemcsak felgyorsítja a lekérdezéseket, hanem csökkenti az Athena használatának költségeit is.

Adjuk hozzá ezt az információt az adatsémafájlhoz:

preAggregations: {
  main: {
    type: `rollup`,
    measureReferences: [count, errorCount],
    dimensionReferences: [isError, status],
    timeDimensionReference: createdAt,
    granularity: `day`,
    partitionGranularity: `month`,
    refreshKey: {
      sql: FILTER_PARAMS.Logs.createdAt.filter((from, to) => 
        `select
           CASE WHEN from_iso8601_timestamp(${to}) + interval '3' day > now()
           THEN date_trunc('hour', now()) END`
      )
    }
  }
}

Ebben a modellben megadjuk, hogy az összes használt metrika adatait előre összesíteni kell, és havi particionálást kell használni. Összesítés előtti particionálás jelentősen felgyorsíthatja az adatgyűjtést és frissítést.

Most összeállíthatjuk a műszerfalat!

A Cube.js háttérprogramja biztosítja REST API és klienskönyvtárak készlete a népszerű front-end keretrendszerekhez. Az irányítópult felépítéséhez az ügyfél React verzióját fogjuk használni. A Cube.js csak adatokat szolgáltat, ezért szükségünk lesz egy vizualizációs könyvtárra – tetszik újragrafikonok, de bármelyiket használhatod.

A Cube.js szerver elfogadja a kérést JSON formátum, amely megadja a szükséges mutatókat. Például annak kiszámításához, hogy az Nginx hány hibát adott naponta, el kell küldenie a következő kérést:

{
  "measures": ["Logs.errorCount"],
  "timeDimensions": [
    {
      "dimension": "Logs.createdAt",
      "dateRange": ["2019-01-01", "2019-01-07"],
      "granularity": "day"
    }
  ]
}

Telepítsük a Cube.js klienst és a React komponens könyvtárat NPM-en keresztül:

$ npm i --save @cubejs-client/core @cubejs-client/react

Alkatrészeket importálunk cubejs и QueryRendereraz adatok letöltéséhez és az irányítópult összegyűjtéséhez:

Irányítópult kódja

import React from 'react';
import { LineChart, Line, XAxis, YAxis } from 'recharts';
import cubejs from '@cubejs-client/core';
import { QueryRenderer } from '@cubejs-client/react';

const cubejsApi = cubejs(
  'YOUR-CUBEJS-API-TOKEN',
  { apiUrl: 'http://localhost:4000/cubejs-api/v1' },
);

export default () => {
  return (
    <QueryRenderer
      query={{
        measures: ['Logs.errorCount'],
        timeDimensions: [{
            dimension: 'Logs.createdAt',
            dateRange: ['2019-01-01', '2019-01-07'],
            granularity: 'day'
        }]
      }}
      cubejsApi={cubejsApi}
      render={({ resultSet }) => {
        if (!resultSet) {
          return 'Loading...';
        }

        return (
          <LineChart data={resultSet.rawData()}>
            <XAxis dataKey="Logs.createdAt"/>
            <YAxis/>
            <Line type="monotone" dataKey="Logs.errorCount" stroke="#8884d8"/>
          </LineChart>
        );
      }}
    />
  )
}

Az irányítópult forrásai a címen érhetők el kód homokozó.

Forrás: will.com

Hozzászólás