Amazon Athena жана Cube.js аркылуу Nginx журналынын аналитикасы

Адатта, Nginxтин иштешин көзөмөлдөө жана талдоо үчүн Prometheus + Grafana сыяктуу коммерциялык продуктылар же даяр ачык булак альтернативалары колдонулат. Бул мониторинг же реалдуу убакыттагы аналитика үчүн жакшы вариант, бирок тарыхый талдоо үчүн абдан ыңгайлуу эмес. Ар кандай популярдуу ресурста nginx журналдарынан алынган маалыматтардын көлөмү тездик менен өсүп жатат жана чоң көлөмдөгү маалыматтарды талдоо үчүн адистештирилген нерсени колдонуу логикага ылайыктуу.

Бул макалада мен сизге кантип колдонсоңуз болорун айтып берем Афина журналдарды талдоо үчүн, мисал катары Nginx алып, жана мен ачык булактуу cube.js алкагын колдонуу менен бул маалыматтардан аналитикалык панелди кантип чогултууну көрсөтөм. Бул жерде толук чечим архитектурасы болуп саналат:

Amazon Athena жана Cube.js аркылуу Nginx журналынын аналитикасы

TL:DR;
Даяр панелге шилтеме.

Маалымат чогултуу үчүн биз колдонобуз эркин, иштетүү үчүн - AWS Kinesis Data Firehose и AWS клей, сактоо үчүн - AWS S3. Бул таңгакты колдонуу менен сиз nginx журналдарын гана эмес, башка окуяларды, ошондой эле башка кызматтардын журналдарын да сактай аласыз. Сиз стекиңиздин кээ бир бөлүктөрүн окшоштору менен алмаштыра аласыз, мисалы, nginxтен кинесиске журналдарды түз жазсаңыз, fluentd-ди кыйгап өтсөңүз болот же бул үчүн logstash колдоно аласыз.

Nginx журналдарын чогултуу

Демейки боюнча, Nginx журналдары төмөнкүдөй көрүнөт:

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

Аларды талданса болот, бирок Nginx конфигурациясын JSONде журналдарды түзө тургандай кылып оңдоо алда канча оңой:

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

Журналдарды сактоо үчүн биз S3 колдонобуз. Бул сизге журналдарды бир жерде сактоого жана анализдөөгө мүмкүндүк берет, анткени Athena S3деги маалыматтар менен түздөн-түз иштей алат. Кийинчерээк макалада мен сизге журналдарды кантип туура кошууну жана иштетүүнү айтып берем, бирок алгач бизге S3 ичинде таза чака керек, анда башка эч нерсе сакталбайт. Чакаңызды кайсы аймакта түзөөрүңүздү алдын ала карап чыгуу керек, анткени Athena бардык аймактарда жеткиликтүү эмес.

Athena консолунда схема түзүү

Келгиле, журналдар үчүн Афинада таблица түзөлү. Эгер сиз Kinesis Firehose колдонууну пландаштырсаңыз, ал жазуу жана окуу үчүн керек. Athena консолун ачып, таблица түзүңүз:

SQL таблицасын түзүү

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 агымын түзүү

Kinesis Firehose тандалган форматта Nginxтен S3ге алынган маалыматтарды жазып, аны ЖЖЖЖ/АА/КК/СС форматындагы каталогдорго бөлөт. Бул маалыматтарды окуп жатканда пайдалуу болот. Сиз, албетте, Fluentdден түз S3ке жаза аласыз, бирок бул учурда сиз JSON жазууга туура келет жана бул файлдардын чоңдугуна байланыштуу натыйжасыз. Мындан тышкары, PrestoDB же Athena колдонуп жатканда, JSON эң жай маалымат форматы. Ошентип, Kinesis Firehose консолун ачып, "Жеткирүү агымын түзүү" баскычын чыкылдатып, "жеткирүү" талаасында "түз PUT" тандаңыз:

Amazon Athena жана Cube.js аркылуу Nginx журналынын аналитикасы

Кийинки өтмөктө "Жазуу форматын өзгөртүү" - "Иштелет" жана жазуу форматы катары "Apache ORC" тандаңыз. Кээ бир изилдөөлөр боюнча Оуэн О'Мэлли, бул PrestoDB жана Athena үчүн оптималдуу формат. Биз жогоруда түзгөн таблицаны схема катары колдонобуз. Сураныч, кинезисте каалаган S3 жайгашкан жерди көрсөтүүгө болорун эске алыңыз; таблицадан схема гана колдонулат. Бирок, эгерде сиз башка S3 жайгашкан жерди көрсөтсөңүз, анда сиз бул таблицадан бул жазууларды окуй албайсыз.

Amazon Athena жана Cube.js аркылуу Nginx журналынын аналитикасы

Сактоо үчүн S3 жана мурда түзүлгөн чаканы тандайбыз. Мен бир аз кийинчерээк сүйлөшө турган Aws Glue Crawler S3 чакасындагы префикстер менен иштей албайт, андыктан аны бош калтыруу маанилүү.

Amazon Athena жана Cube.js аркылуу Nginx журналынын аналитикасы

Калган параметрлерди жүктөөңүзгө жараша өзгөртүүгө болот; Мен адатта демейки опцияларды колдоном. S3 кысуу жеткиликтүү эмес экенин эске алыңыз, бирок ORC демейки боюнча түпнуска кысуу колдонот.

эркин

Эми журналдарды сактоо жана кабыл алуу конфигурациялангандыктан, биз жөнөтүүнү конфигурациялашыбыз керек. Биз колдонобуз эркин, анткени мен Rubyди жакшы көрөм, бирок сиз Logstash колдоно аласыз же журналдарды кинезиске түздөн-түз жөнөтө аласыз. Fluentd серверин бир нече жол менен ишке киргизүүгө болот, мен сизге докер жөнүндө айтып берем, анткени ал жөнөкөй жана ыңгайлуу.

Биринчиден, бизге fluent.conf конфигурация файлы керек. Аны түзүп, булакты кошуңуз:

түрү алдыга
порт 24224
байланыш 0.0.0.0

Эми сиз Fluentd серверин баштасаңыз болот. Эгер сизге өркүндөтүлгөн конфигурация керек болсо, өтүңүз Docker hub Сиздин сүрөттү кантип чогултуу, анын ичинде деталдуу жол бар.

$ 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

Бул конфигурация жолду колдонот /fluentd/log жөнөтүүдөн мурун журналдарды кэштөө үчүн. Сиз ансыз деле кыла аласыз, бирок кайра баштаганда, арткы эмгек менен кэштелген нерселердин баарын жоготуп аласыз. Сиз каалаган портту колдоно аласыз; 24224 демейки Fluentd порту.

Эми бизде Fluentd иштеп жаткандыктан, биз ал жакка Nginx журналдарын жөнөтө алабыз. Биз көбүнчө Nginxти Docker контейнеринде иштетебиз, бул учурда Dockerде Fluentd үчүн жергиликтүү журнал драйвери бар:

$ 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

Эгерде сиз Nginxти башка жол менен иштетсеңиз, анда Fluentd лог файлдарын колдоно аласыз файл куйрук плагини.

Келгиле, Fluent конфигурациясына жогоруда конфигурацияланган журналды талдоону кошолу:

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

Жана колдонуу менен Kinesis журналдарын жөнөтүү kinesis firehose плагини:

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

Афина

Эгер сиз бардыгын туура конфигурациялаган болсоңуз, анда бир аз убакыт өткөндөн кийин (демейки боюнча, Kinesis алынган маалыматтарды 10 мүнөт сайын бир жолу жаздырат) S3 ичинде журнал файлдарын көрүшүңүз керек. Kinesis Firehose'дун "мониторинг" менюсунда S3-те канча маалымат жазылганын, ошондой эле каталарды көрө аласыз. Kinesis ролуна S3 чакага жазуу мүмкүнчүлүгүн берүүнү унутпаңыз. Эгерде Kinesis бир нерсени талдай албаса, анда ал каталарды ошол эле чакага кошот.

Эми сиз Athena маалыматтарды көрө аласыз. Биз каталарды кайтарган акыркы сурамдарды табалы:

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

Ар бир суроо үчүн бардык жазууларды сканерлөө

Азыр биздин журналдар ORCде S3 форматында иштетилип, сакталып, кысылып, анализге даяр. Kinesis Firehose аларды ар бир саат үчүн каталогдорго да уюштурган. Бирок, үстөл бөлүнбөсө, Athena сейрек учурларды эске албаганда, ар бир сурам боюнча бардык убактагы маалыматтарды жүктөйт. Бул эки себеп боюнча чоң көйгөй:

  • Маалыматтын көлөмү тынымсыз өсүүдө, суроо-талаптарды жайлатууда;
  • Athena үчүн эсеп сканерленген берилиштердин көлөмүнө жараша төлөнөт, ар бир суроо үчүн эң аз дегенде 10 МБ.

Муну оңдоо үчүн биз AWS Glue Crawler колдонобуз, ал S3 ичиндеги маалыматтарды сойлоп, Glue Metastore'го бөлүү маалыматын жазат. Бул бизге Athena сурамында бөлүмдөрдү фильтр катары колдонууга мүмкүндүк берет жана ал суроодо көрсөтүлгөн каталогдорду гана сканерлейт.

Amazon Glue Crawler орнотулууда

Amazon Glue Crawler S3 чакасындагы бардык маалыматтарды сканерлейт жана бөлүмдөрү бар таблицаларды түзөт. AWS Glue консолунан Glue Crawler түзүңүз жана дайындарды сактаган чака кошуңуз. Сиз бир жөрмөлөгүчтү бир нече чака үчүн колдоно аласыз, бул учурда ал көрсөтүлгөн маалымат базасында чакалардын атына дал келген аталыштар менен таблицаларды түзөт. Эгер сиз бул маалыматты үзгүлтүксүз колдонууну пландасаңыз, Crawlerin ишке киргизүү графигин сиздин муктаждыктарыңызга ылайыкташтырууну унутпаңыз. Биз бардык таблицалар үчүн бир Crawler колдонобуз, ал саат сайын иштейт.

Бөлүнгөн таблицалар

Crawler биринчи жолу ишке киргизилгенден кийин, ар бир сканерленген чака үчүн таблицалар орнотууларда көрсөтүлгөн маалымат базасында пайда болушу керек. Athena консолун ачып, Nginx журналдары менен таблицаны табыңыз. Келгиле, бир нерсени окуганга аракет кылалы:

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

Бул сурам 6-жылдын 7-апрелинде таңкы саат 8дан 2019ге чейин алынган бардык жазууларды тандайт. Бирок бул бөлүштүрүлбөгөн таблицадан окууга караганда канчалык эффективдүү? Келгиле, ошол эле жазууларды убакыт белгиси боюнча чыпкалап таап, тандап алалы:

Amazon Athena жана Cube.js аркылуу Nginx журналынын аналитикасы

3.59 секунда жана 244.34 мегабайт маалымат топтому бир жумалык гана журналдар менен. Келгиле, бөлүм боюнча чыпкалап көрөлү:

Amazon Athena жана Cube.js аркылуу Nginx журналынын аналитикасы

Бир аз ылдамыраак, бирок эң негизгиси - болгону 1.23 мегабайт маалымат! Баалар боюнча суроо-талапка минималдуу 10 мегабайт болбосо, бул алда канча арзан болмок. Бирок бул дагы эле жакшыраак жана чоң маалымат топтомдорунда айырма алда канча таасирдүү болот.

Cube.js аркылуу башкаруу тактасын түзүү

Куралдар тактасын чогултуу үчүн Cube.js аналитикалык алкагын колдонобуз. Анын бир топ функциялары бар, бирок бизди эки нерсе кызыктырат: бөлүү чыпкаларын автоматтык түрдө колдонуу жана маалыматтарды алдын ала топтоо. Бул маалымат схемасын колдонот маалымат схемасы, SQLди түзүү жана маалымат базасынын суроосун аткаруу үчүн Javascript менен жазылган. Биз маалымат схемасында бөлүү чыпкасын кантип колдонууну гана көрсөтүшүбүз керек.

Келгиле, жаңы Cube.js тиркемесин түзөлү. Биз AWS стекин мурунтан эле колдонуп жаткандыктан, жайылтуу үчүн Lambda колдонуу логикалык. Cube.js серверин Heroku же Докерде жайгаштырууну пландап жатсаңыз, экспресс шаблонду муун үчүн колдоно аласыз. Документтер башкаларды сүрөттөйт хостинг ыкмалары.

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

Курчап турган чөйрө өзгөрмөлөрү cube.js ичинде берилиштер базасына кирүүнү конфигурациялоо үчүн колдонулат. Генератор .env файлын түзөт, анда сиз ачкычтарыңызды белгилей аласыз Афина.

Азыр бизге керек маалымат схемасы, анда биз журналдарыбыз кантип сакталганын так көрсөтөбүз. Ал жерде сиз такталар үчүн көрсөткүчтөрдү кантип эсептөө керектигин да көрсөтө аласыз.

каталогдо schema, файл түзүңүз Logs.js. Бул жерде nginx үчүн үлгү маалымат модели болуп саналат:

Модель коду

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

Бул жерде биз өзгөрмө колдонуп жатабыз FILTER_PARAMSбөлүү чыпкасы менен SQL суроо жаратуу үчүн.

Биз ошондой эле аспаптар тактасында көрсөтүүнү каалаган көрсөткүчтөрдү жана параметрлерди орнотуп, алдын ала топтоолорду белгилейбиз. Cube.js алдын ала топтолгон маалыматтар менен кошумча таблицаларды түзөт жана ал келгенде автоматтык түрдө маалыматтарды жаңыртат. Бул сурамдарды тездетип гана койбостон, Афинаны колдонуунун баасын да азайтат.

Келгиле, бул маалыматты маалымат схемасы файлына кошолу:

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

Биз бул моделде колдонулган бардык көрсөткүчтөр үчүн маалыматтарды алдын ала топтоо жана айлар боюнча бөлүүнү колдонуу керек экенин белгилейбиз. Агрегацияга чейинки бөлүү маалыматтарды чогултууну жана жаңылоону бир кыйла тездете алат.

Эми биз панелди чогулта алабыз!

Cube.js backend камсыз кылат REST API жана популярдуу фронталдык алкактар ​​үчүн кардар китепканаларынын жыйындысы. Куралдар тактасын куруу үчүн кардардын React версиясын колдонобуз. Cube.js дайындарды гана камсыздайт, андыктан бизге визуализация китепканасы керек болот - мага жагат кайра диаграммалар, бирок сиз каалаганын колдоно аласыз.

Cube.js сервери өтүнүчтү кабыл алат JSON форматы, талап кылынган көрсөткүчтөрдү аныктайт. Мисалы, Nginx бир күндө канча ката кетиргенин эсептөө үчүн, төмөнкү сурамды жөнөтүшүңүз керек:

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

Cube.js кардарын жана React компоненттеринин китепканасын NPM аркылуу орнотобуз:

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

Биз компоненттерди импорттоо cubejs и QueryRendererмаалыматтарды жүктөп алуу жана башкаруу тактасын чогултуу үчүн:

Куралдар тактасынын коду

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

Куралдар тактасынын булактары бул жерден жеткиликтүү код кум чөйрөсү.

Source: www.habr.com

Комментарий кошуу