Таҳлили гузориши Nginx бо истифода аз Amazon Athena ва Cube.js

Одатан, маҳсулоти тиҷоратӣ ё алтернативаҳои кушодаи кушода, ба монанди Prometheus + Grafana, барои назорат ва таҳлили кори Nginx истифода мешаванд. Ин як варианти хуб барои мониторинг ё таҳлили вақти воқеӣ аст, аммо барои таҳлили таърихӣ чандон қулай нест. Дар ҳама гуна манбаи маъмул, ҳаҷми маълумот аз гузоришҳои nginx босуръат меафзояд ва барои таҳлили миқдори зиёди маълумот, истифодаи чизи махсусгардонидашуда мантиқист.

Дар ин мақола ман ба шумо мегӯям, ки чӣ тавр шумо метавонед истифода баред Афина барои таҳлили гузоришҳо, бо назардошти Nginx ҳамчун намуна ва ман нишон медиҳам, ки чӣ гуна панели аналитикиро аз ин маълумот бо истифода аз чаҳорчӯбаи кушодаи cube.js ҷамъ кунем. Ин аст меъмории пурраи ҳалли:

Таҳлили гузориши Nginx бо истифода аз Amazon Athena ва Cube.js

TL: DR;
Истинод ба панели анҷомёфта.

Барои ҷамъоварии маълумот мо истифода мебарем равон, барои коркард - AWS Kinesis Data Firehose и AWS ширеш, барои нигоҳдорӣ - AWS S3. Бо истифода аз ин баста, шумо метавонед на танҳо гузоришҳои nginx, балки рӯйдодҳои дигар, инчунин гузоришҳои хидматҳои дигарро захира кунед. Шумо метавонед баъзе қисмҳоро бо қисмҳои шабеҳи стеки худ иваз кунед, масалан, шумо метавонед ба kinesis мустақиман аз 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 Stream

Kinesis Firehose маълумоти аз Nginx ба S3 гирифташударо дар формати интихобшуда менависад ва онро ба директорияҳо дар формати YYYY/MM/DD/HH тақсим мекунад. Ин ҳангоми хондани маълумот муфид хоҳад буд. Шумо, албатта, метавонед мустақиман ба S3 аз fluentd нависед, аммо дар ин ҳолат шумо бояд JSON нависед ва ин аз сабаби андозаи калони файлҳо бесамар аст. Илова бар ин, ҳангоми истифодаи PrestoDB ё Athena, JSON формати сусттарини додаҳо мебошад. Пас, консоли Kinesis Firehose -ро кушоед, "Эҷод кардани ҷараёни интиқол" -ро клик кунед, дар майдони "расмкунӣ" "ПУСи мустақим" -ро интихоб кунед:

Таҳлили гузориши Nginx бо истифода аз Amazon Athena ва Cube.js

Дар ҷадвали навбатӣ, "Табдилдиҳии формати сабт" - "Фаъол" -ро интихоб кунед ва ҳамчун формати сабт "Apache ORC" -ро интихоб кунед. Мувофики баъзе тадкикотхо Оуэн О'Мэлли, ин формати оптималии PrestoDB ва Athena мебошад. Мо ҷадвали дар боло сохтаамонро ҳамчун схема истифода мебарем. Лутфан қайд кунед, ки шумо метавонед ягон макони S3-ро дар кинезис муайян кунед; танҳо схема аз ҷадвал истифода мешавад. Аммо агар шумо макони дигари S3-ро муайян кунед, пас шумо ин сабтҳоро аз ин ҷадвал хонда наметавонед.

Таҳлили гузориши Nginx бо истифода аз Amazon Athena ва Cube.js

Мо S3-ро барои нигоҳдорӣ ва сатил, ки қаблан сохта будем, интихоб мекунем. Aws Glue Crawler, ки ман каме дертар дар бораи он сӯҳбат хоҳам кард, наметавонад бо префиксҳо дар сатили S3 кор кунад, аз ин рӯ муҳим аст, ки онро холӣ гузоред.

Таҳлили гузориши Nginx бо истифода аз Amazon Athena ва Cube.js

Вариантҳои боқимонда метавонанд вобаста ба сарбории шумо тағир дода шаванд; Ман одатан имконоти пешфарзро истифода мебарам. Дар хотир доред, ки фишурдани S3 дастрас нест, аммо ORC бо нобаёнӣ фишурдани маҳаллиро истифода мебарад.

равон

Акнун, ки мо сабт ва қабули сабтҳоро танзим кардем, мо бояд ирсолро танзим кунем. истифода мебарем равон, зеро ман Ruby-ро дӯст медорам, аммо шумо метавонед Logstash-ро истифода баред ё гузоришҳоро мустақиман ба kinesis фиристед. Сервери Fluentd-ро бо чанд роҳ оғоз кардан мумкин аст, ман ба шумо дар бораи докер нақл мекунам, зеро он оддӣ ва қулай аст.

Аввалан, ба мо файли конфигуратсияи fluent.conf лозим аст. Онро эҷод кунед ва манбаъ илова кунед:

намуди пештар
нуқтаи 24224
баста 0.0.0.0

Акнун шумо метавонед сервери Fluentd-ро оғоз кунед. Агар ба шумо конфигуратсияи пешрафта лозим бошад, ба он равед Маркази докерӣ Роҳнамои муфассал мавҷуд аст, аз он ҷумла чӣ гуна тасвири худро ҷамъ кардан.

$ 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 сабт шудааст ва инчунин хатогиҳо. Фаромӯш накунед, ки дастрасии навиштан ба сатили S3 ба нақши Kinesis. Агар Kinesis чизеро таҳлил карда натавонад, он хатогиҳоро ба ҳамон сатил илова мекунад.

Акнун шумо метавонед маълумотро дар Афина дидан кунед. Биёед дархостҳои охиринро пайдо кунем, ки мо хатогиҳоро баргардонидем:

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

Скан кардани ҳама сабтҳо барои ҳар як дархост

Ҳоло гузоришҳои мо дар S3 дар ORC коркард ва нигоҳ дошта шудаанд, фишурда шудаанд ва барои таҳлил омодаанд. Kinesis Firehose ҳатто онҳоро ба феҳристҳо барои ҳар соат ташкил дод. Аммо, то он даме, ки ҷадвал тақсим карда нашавад, Athena маълумоти ҳама вақтро дар ҳар дархост, ба истиснои истисноҳои нодир бор мекунад. Ин як мушкили бузург барои ду сабаб аст:

  • Ҳаҷми маълумот пайваста афзоиш ёфта, дархостҳоро суст мекунад;
  • Athena дар асоси ҳаҷми маълумоти сканшуда бо ҳадди ақал 10 МБ барои як дархост ҳисоб карда мешавад.

Барои ислоҳи ин, мо AWS Glue Crawler-ро истифода мебарем, ки он маълумотро дар S3 ҷустуҷӯ мекунад ва маълумоти тақсимотро ба Glue Metastore менависад. Ин ба мо имкон медиҳад, ки қисмҳоро ҳамчун филтр ҳангоми дархости Athena истифода барем ва он танҳо директорияҳои дар дархост нишондодашударо скан мекунад.

Насб кардани Amazon Glue Crawler

Amazon Glue Crawler тамоми маълумотро дар сатили S3 скан мекунад ва ҷадвалҳо бо қисмҳо эҷод мекунад. Аз консоли AWS Glue Crawler Glue эҷод кунед ва сатилеро илова кунед, ки шумо маълумотро нигоҳ медоред. Шумо метавонед як браузерро барои якчанд сатил истифода баред, дар ин ҳолат он дар базаи додашуда ҷадвалҳоро бо номҳое месозад, ки ба номи сатилҳо мувофиқат мекунанд. Агар шумо нақшаи истифодаи мунтазами ин маълумотро дошта бошед, боварӣ ҳосил кунед, ки ҷадвали оғози 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 гирифташударо интихоб мекунад. Аммо ин аз хондан аз ҷадвали тақсимнашуда то чӣ андоза самараноктар аст? Биёед ҳамон сабтҳоро фаҳмем ва интихоб кунем ва онҳоро аз рӯи тамғаи вақт филтр кунем:

Таҳлили гузориши Nginx бо истифода аз Amazon Athena ва Cube.js

3.59 сония ва 244.34 мегабайт маълумот дар маҷмӯи додаҳо бо танҳо як ҳафтаи гузоришҳо. Биёед филтрро аз рӯи қисмат санҷем:

Таҳлили гузориши Nginx бо истифода аз Amazon Athena ва Cube.js

Каме тезтар, вале муҳимтар аз ҳама - танҳо 1.23 мегабайт маълумот! Ин хеле арзонтар мебуд, агар на камтар аз 10 мегабайт барои як дархост дар нархгузорӣ. Аммо он ҳоло ҳам хеле беҳтар аст ва дар маҷмӯи додаҳои калон фарқият хеле таъсирбахштар хоҳад буд.

Сохтани панели идоракунӣ бо истифода аз Cube.js

Барои ҷамъ кардани панели идоракунӣ, мо чаҳорчӯбаи таҳлилии Cube.js -ро истифода мебарем. Он дорои функсияҳои хеле зиёд аст, аммо мо ба ду чиз таваҷҷӯҳ дорем: қобилияти ба таври худкор истифода бурдани филтрҳои тақсимот ва ҷамъоварии пешакии додаҳо. Он схемаи маълумотро истифода мебарад схемаи маълумот, навишта шудааст дар Javascript барои тавлиди SQL ва иҷрои дархости пойгоҳи додаҳо. Мо танҳо бояд нишон диҳем, ки чӣ тавр истифода бурдани филтри тақсимот дар схемаи додаҳо.

Биёед як барномаи нави Cube.js эҷод кунем. Азбаски мо аллакай стеки AWS-ро истифода мебарем, истифодаи Lambda барои ҷойгиркунӣ мантиқист. Шумо метавонед қолаби экспрессро барои насл истифода баред, агар шумо ният доред, ки пуштибонии Cube.js-ро дар Heroku ё Docker ҷойгир кунед. Ҳуҷҷатҳо дигаронро тавсиф мекунанд усулҳои хостинг.

$ 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 бо маълумоти пешакӣ ҷамъшуда ҷадвалҳои иловагӣ эҷод мекунад ва ҳангоми расидан ба таври худкор маълумотро нав мекунад. Ин на танҳо дархостҳоро суръат мебахшад, балки арзиши истифодаи Athena-ро коҳиш медиҳад.

Биёед ин маълумотро ба файли схемаи додаҳо илова кунем:

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 пуштибониро таъмин мекунад оромии 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>
        );
      }}
    />
  )
}

Сарчашмаҳои панели дастрас дар қуттии код.

Манбаъ: will.com

Илова Эзоҳ