Amazon Athena va Cube.js yordamida Nginx jurnali tahlili

Odatda, Nginx ishlashini kuzatish va tahlil qilish uchun Prometey + Grafana kabi tijorat mahsulotlari yoki tayyor ochiq manbali alternativalardan foydalaniladi. Bu monitoring yoki real vaqtda tahlil qilish uchun yaxshi variant, ammo tarixiy tahlil uchun juda qulay emas. Har qanday mashhur manbada nginx jurnallaridan olingan ma'lumotlar hajmi tez sur'atlar bilan o'sib bormoqda va katta hajmdagi ma'lumotlarni tahlil qilish uchun ko'proq ixtisoslashgan narsadan foydalanish mantiqan to'g'ri keladi.

Ushbu maqolada men sizga qanday qilib foydalanishingiz mumkinligini aytaman Afina jurnallarni tahlil qilish, Nginx-ni misol qilib olish va men ochiq manbali cube.js ramkasidan foydalanib, ushbu ma'lumotlardan tahliliy asboblar panelini qanday yig'ishni ko'rsataman. Mana to'liq yechim arxitekturasi:

Amazon Athena va Cube.js yordamida Nginx jurnali tahlili

TL: DR;
Tayyor boshqaruv paneliga havola.

Ma'lumot to'plash uchun biz foydalanamiz Fluentd, qayta ishlash uchun - AWS Kinesis Data Firehose ΠΈ AWS elim, saqlash uchun - AWS S3. Ushbu to'plamdan foydalanib, siz nafaqat nginx jurnallarini, balki boshqa hodisalarni, shuningdek, boshqa xizmatlar jurnallarini ham saqlashingiz mumkin. Ba'zi qismlarni stekingiz uchun o'xshashlari bilan almashtirishingiz mumkin, masalan, fluentd-ni chetlab o'tib, to'g'ridan-to'g'ri nginx-dan kinesisga jurnallar yozishingiz yoki buning uchun logstash-dan foydalanishingiz mumkin.

Nginx jurnallarini yig'ish

Odatiy bo'lib, Nginx jurnallari quyidagicha ko'rinadi:

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" "-"

Ularni tahlil qilish mumkin, ammo Nginx konfiguratsiyasini JSONda jurnallar yaratish uchun tuzatish ancha oson:

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;

Saqlash uchun S3

Jurnallarni saqlash uchun biz S3 dan foydalanamiz. Bu sizga jurnallarni bir joyda saqlash va tahlil qilish imkonini beradi, chunki Athena bevosita S3-dagi ma'lumotlar bilan ishlashi mumkin. Keyinchalik maqolada men sizga jurnallarni qanday qilib to'g'ri qo'shish va qayta ishlash kerakligini aytaman, lekin avval S3-da toza chelak kerak, unda boshqa hech narsa saqlanmaydi. Qaysi mintaqada chelak yaratishingizni oldindan ko'rib chiqishga arziydi, chunki Afina barcha mintaqalarda mavjud emas.

Afina konsolida sxema yaratish

Keling, Afinada jurnallar uchun jadval yarataylik. Agar siz Kinesis Firehose-dan foydalanishni rejalashtirmoqchi bo'lsangiz, u yozish va o'qish uchun kerak. Athena konsolini oching va jadval yarating:

SQL jadvalini yaratish

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 yaratish

Kinesis Firehose Nginx-dan S3-ga olingan ma'lumotlarni tanlangan formatda yozadi va uni YYYY/MM/DD/HH formatidagi kataloglarga ajratadi. Bu ma'lumotlarni o'qishda foydali bo'ladi. Siz, albatta, fluentd dan to'g'ridan-to'g'ri S3 ga yozishingiz mumkin, ammo bu holda siz JSON yozishingiz kerak bo'ladi va bu fayllarning kattaligi tufayli samarasiz. Bundan tashqari, PrestoDB yoki Athena-dan foydalanganda, JSON eng sekin ma'lumotlar formatidir. Shunday qilib, Kinesis Firehose konsolini oching, "Etkazib berish oqimini yaratish" tugmasini bosing, "etkazib berish" maydonida "to'g'ridan-to'g'ri PUT" ni tanlang:

Amazon Athena va Cube.js yordamida Nginx jurnali tahlili

Keyingi yorliqda "Yozuv formatini o'zgartirish" - "Yoqilgan" ni tanlang va yozish formati sifatida "Apache ORC" ni tanlang. Ba'zi tadqiqotlarga ko'ra Ouen O'Malli, bu PrestoDB va Athena uchun optimal formatdir. Biz yuqorida tuzilgan jadvaldan sxema sifatida foydalanamiz. Shuni esda tutingki, siz kinesisda istalgan S3 manzilini belgilashingiz mumkin, jadvaldan faqat sxema ishlatiladi. Ammo agar siz boshqa S3 manzilini ko'rsatsangiz, ushbu jadvaldan ushbu yozuvlarni o'qiy olmaysiz.

Amazon Athena va Cube.js yordamida Nginx jurnali tahlili

Saqlash uchun S3 ni va avval yaratgan chelakni tanlaymiz. Men biroz keyinroq gaplashadigan Aws Glue Crawler S3 paqiridagi prefikslar bilan ishlay olmaydi, shuning uchun uni bo'sh qoldirish muhimdir.

Amazon Athena va Cube.js yordamida Nginx jurnali tahlili

Qolgan variantlar yukingizga qarab o'zgartirilishi mumkin; Men odatda standart variantlardan foydalanaman. E'tibor bering, S3 siqish mavjud emas, lekin ORC sukut bo'yicha mahalliy siqishni ishlatadi.

Fluentd

Endi biz jurnallarni saqlash va qabul qilishni sozlaganmiz, biz yuborishni sozlashimiz kerak. foydalanamiz Fluentd, chunki men Rubyni yaxshi ko'raman, lekin siz Logstash-dan foydalanishingiz yoki loglarni to'g'ridan-to'g'ri kinesisga yuborishingiz mumkin. Fluentd serveri bir necha usulda ishga tushirilishi mumkin, men sizga docker haqida aytib beraman, chunki u oddiy va qulay.

Birinchidan, bizga fluent.conf konfiguratsiya fayli kerak. Uni yarating va manba qo'shing:

shrift oldinga
24224 porti
bog'lash 0.0.0.0

Endi siz Fluentd serverini ishga tushirishingiz mumkin. Agar sizga yanada rivojlangan konfiguratsiya kerak bo'lsa, o'ting Docker markazi Tasviringizni qanday yig'ishni o'z ichiga olgan batafsil qo'llanma mavjud.

$ 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

Ushbu konfiguratsiya yo'ldan foydalanadi /fluentd/log yuborishdan oldin jurnallarni keshlash uchun. Siz bunisiz ham qilishingiz mumkin, lekin keyin qayta ishga tushirganingizda, orqaga qaytish mehnati bilan keshlangan hamma narsani yo'qotishingiz mumkin. Siz har qanday portdan ham foydalanishingiz mumkin; 24224 standart Fluentd portidir.

Endi bizda Fluentd ishlayotgan bo'lsa, biz u erga Nginx jurnallarini yuborishimiz mumkin. Biz odatda Nginx-ni Docker konteynerida ishga tushiramiz, bu holda Docker-da Fluentd uchun mahalliy jurnal drayveri mavjud:

$ 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

Agar siz Nginx-ni boshqacha ishlatsangiz, log fayllardan foydalanishingiz mumkin, Fluentd bor fayl quyruq plagini.

Keling, Fluent konfiguratsiyasiga yuqorida sozlangan jurnal tahlilini qo'shamiz:

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

Va yordamida Kinesis jurnallarini yuborish kinesis firehose plagini:

<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>

Afina

Agar siz hamma narsani to'g'ri sozlagan bo'lsangiz, bir muncha vaqt o'tgach (sukut bo'yicha, Kinesis olingan ma'lumotlarni har 10 daqiqada bir marta yozib oladi) S3-da jurnal fayllarini ko'rishingiz kerak. Kinesis Firehose-ning "monitoring" menyusida siz S3-da qancha ma'lumot yozilganligini, shuningdek xatolarni ko'rishingiz mumkin. Kinesis roliga S3 paqiriga yozish huquqini berishni unutmang. Agar Kinesis biror narsani tahlil qila olmasa, u xatolarni bir xil paqirga qo'shadi.

Endi siz Afinada ma'lumotlarni ko'rishingiz mumkin. Xatolarni qaytargan so'nggi so'rovlarni topamiz:

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

Har bir so'rov uchun barcha yozuvlarni skanerlash

Endi bizning jurnallarimiz qayta ishlandi va S3 da ORC da saqlangan, siqilgan va tahlilga tayyor. Kinesis Firehose hatto ularni har soat uchun kataloglarga ajratdi. Biroq, jadval bo'linmagan ekan, Athena har bir so'rov bo'yicha barcha vaqt ma'lumotlarini yuklaydi, kamdan-kam holatlar bundan mustasno. Bu ikki sababga ko'ra katta muammo:

  • Ma'lumotlar hajmi doimiy ravishda o'sib bormoqda, so'rovlarni sekinlashtiradi;
  • Afina uchun to'lov skanerlangan ma'lumotlar hajmiga qarab amalga oshiriladi, har bir so'rov uchun kamida 10 MB.

Buni tuzatish uchun biz AWS Glue Crawler-dan foydalanamiz, u S3-dagi ma'lumotlarni tekshiradi va bo'lim ma'lumotlarini Glue Metastore-ga yozadi. Bu bizga Athena so'rovida bo'limlardan filtr sifatida foydalanish imkonini beradi va u faqat so'rovda ko'rsatilgan kataloglarni skanerlaydi.

Amazon Glue Crawler-ni sozlash

Amazon Glue Crawler S3 paqiridagi barcha ma'lumotlarni skanerlaydi va bo'limlar bilan jadvallar yaratadi. AWS Glue konsolidan Glue Crawler yarating va ma'lumotlarni saqlaydigan chelak qo'shing. Bir nechta chelaklar uchun bitta brauzerdan foydalanishingiz mumkin, bu holda u belgilangan ma'lumotlar bazasida chelaklarning nomlariga mos keladigan nomlar bilan jadvallarni yaratadi. Agar siz ushbu ma'lumotlardan muntazam foydalanishni rejalashtirmoqchi bo'lsangiz, Crawler dasturini ishga tushirish jadvalini ehtiyojlaringizga mos ravishda sozlaganingizga ishonch hosil qiling. Biz har soatda ishlaydigan barcha jadvallar uchun bitta Crawlerdan foydalanamiz.

Bo'lingan jadvallar

Brauzerni birinchi ishga tushirgandan so'ng, sozlamalarda ko'rsatilgan ma'lumotlar bazasida har bir skanerlangan chelak uchun jadvallar paydo bo'lishi kerak. Athena konsolini oching va Nginx jurnallari bilan jadvalni toping. Keling, biror narsani o'qishga harakat qilaylik:

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

Bu soΚ»rov 6-yil 7-aprel kuni soat 8 dan 2019 gacha qabul qilingan barcha yozuvlarni tanlaydi. Ammo bu bo'linmagan jadvaldan o'qishdan ko'ra qanchalik samaraliroq? Keling, vaqt tamg'asi bo'yicha filtrlash orqali bir xil yozuvlarni aniqlaymiz va tanlaymiz:

Amazon Athena va Cube.js yordamida Nginx jurnali tahlili

3.59 soniya va 244.34 megabayt ma'lumotlar to'plamida faqat bir haftalik jurnallar mavjud. Keling, bo'lim bo'yicha filtrni sinab ko'raylik:

Amazon Athena va Cube.js yordamida Nginx jurnali tahlili

Bir oz tezroq, lekin eng muhimi - atigi 1.23 megabayt ma'lumot! Narxlarda har bir so'rov uchun kamida 10 megabayt bo'lmasa, bu ancha arzonroq bo'lar edi. Ammo bu hali ham yaxshiroq va katta ma'lumotlar to'plamlarida farq ancha ta'sirli bo'ladi.

Cube.js yordamida asboblar panelini yaratish

Boshqaruv panelini yig'ish uchun biz Cube.js analitik ramkasidan foydalanamiz. U juda ko'p funktsiyalarga ega, ammo bizni ikkitasi qiziqtiradi: bo'lim filtrlarini avtomatik ravishda ishlatish va ma'lumotlarni oldindan yig'ish qobiliyati. U ma'lumotlar sxemasidan foydalanadi ma'lumotlar sxemasi, SQL yaratish va ma'lumotlar bazasi so'rovini bajarish uchun Javascriptda yozilgan. Biz faqat ma'lumotlar sxemasida bo'lim filtridan qanday foydalanishni ko'rsatishimiz kerak.

Keling, yangi Cube.js ilovasini yarataylik. Biz allaqachon AWS stekidan foydalanayotganimiz sababli, joylashtirish uchun Lambda-dan foydalanish mantiqan to'g'ri keladi. Agar siz Cube.js backend-ni Heroku yoki Docker-da joylashtirishni rejalashtirmoqchi bo'lsangiz, avlod uchun ekspress shablondan foydalanishingiz mumkin. Hujjatlar boshqalarni tavsiflaydi hosting usullari.

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

Muhit o'zgaruvchilari cube.js da ma'lumotlar bazasiga kirishni sozlash uchun ishlatiladi. Generator .env faylini yaratadi, unda siz kalitlaringizni belgilashingiz mumkin Afina.

Endi bizga kerak ma'lumotlar sxemasi, unda biz jurnallarimiz qanday saqlanishini aniq ko'rsatamiz. U erda siz asboblar paneli uchun ko'rsatkichlarni qanday hisoblashni ham belgilashingiz mumkin.

Katalogda schema, fayl yarating Logs.js. Mana nginx uchun namuna ma'lumotlar modeli:

Model kodi

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`
    }
  }
});

Bu erda biz o'zgaruvchidan foydalanamiz FILTER_PARAMSbo'lim filtri bilan SQL so'rovini yaratish uchun.

Shuningdek, biz asboblar panelida ko'rsatmoqchi bo'lgan ko'rsatkichlar va parametrlarni o'rnatamiz va oldindan yig'ilishlarni aniqlaymiz. Cube.js oldindan jamlangan ma'lumotlarga ega qo'shimcha jadvallarni yaratadi va ma'lumotlar kelishi bilan avtomatik ravishda yangilanadi. Bu nafaqat so'rovlarni tezlashtiradi, balki Athena'dan foydalanish narxini ham kamaytiradi.

Keling, ushbu ma'lumotni ma'lumotlar sxemasi fayliga qo'shamiz:

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`
      )
    }
  }
}

Biz ushbu modelda foydalanilgan barcha ko'rsatkichlar uchun ma'lumotlarni oldindan jamlash va oylar bo'yicha qismlarga ajratish zarurligini aniqlaymiz. Birlashtirishdan oldin bo'linish ma'lumotlarni yig'ish va yangilashni sezilarli darajada tezlashtirishi mumkin.

Endi biz asboblar panelini yig'ishimiz mumkin!

Cube.js backend taqdim etadi REST API va mashhur front-end ramkalar uchun mijozlar kutubxonalari to'plami. Biz boshqaruv panelini yaratish uchun mijozning React versiyasidan foydalanamiz. Cube.js faqat ma'lumotlarni taqdim etadi, shuning uchun bizga vizualizatsiya kutubxonasi kerak bo'ladi - bu menga yoqadi qayta chizmalar, lekin siz har qanday foydalanishingiz mumkin.

Cube.js serveri so'rovni qabul qiladi JSON formati, bu kerakli ko'rsatkichlarni belgilaydi. Masalan, Nginx kuniga qancha xato qilganligini hisoblash uchun siz quyidagi so'rovni yuborishingiz kerak:

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

NPM orqali Cube.js mijozi va React komponentlar kutubxonasini o'rnatamiz:

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

Biz komponentlarni import qilamiz cubejs ΠΈ QueryRendererma'lumotlarni yuklab olish va asboblar panelini yig'ish uchun:

Boshqaruv paneli kodi

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>
        );
      }}
    />
  )
}

Boshqaruv paneli manbalari bu yerda mavjud kodli sandbox.

Manba: www.habr.com

a Izoh qo'shish