Nginx log analytics gamit ang Amazon Athena ug Cube.js

Kasagaran, ang mga komersyal nga produkto o andam nga gihimo nga open-source nga mga alternatibo, sama sa Prometheus + Grafana, gigamit sa pagmonitor ug pag-analisar sa operasyon sa Nginx. Kini usa ka maayong kapilian alang sa pag-monitor o real-time nga analytics, apan dili kaayo kombenyente alang sa pagtuki sa kasaysayan. Sa bisan unsang sikat nga kapanguhaan, ang gidaghanon sa datos gikan sa nginx logs kusog nga mitubo, ug sa pag-analisar sa daghang gidaghanon sa datos, makatarunganon nga gamiton ang usa ka butang nga mas espesyal.

Niini nga artikulo isulti ko kanimo kung giunsa nimo magamit Athena sa pag-analisar sa mga troso, pagkuha sa Nginx isip usa ka pananglitan, ug akong ipakita kung unsaon pag-assemble ang usa ka analytical dashboard gikan niini nga datos gamit ang open-source cube.js framework. Ania ang kompleto nga arkitektura sa solusyon:

Nginx log analytics gamit ang Amazon Athena ug Cube.js

TL:DR;
I-link sa nahuman nga dashboard.

Aron makolekta ang kasayuran nga among gigamit larinod, alang sa pagproseso - AWS Kinesis Data Firehose ΠΈ AWS Pandikit, alang sa pagtipig - AWS S3. Gamit kini nga bundle, mahimo nimong tipigan dili lamang ang nginx logs, kondili usab ang uban pang mga panghitabo, ingon man ang mga log sa ubang mga serbisyo. Mahimo nimong ilisan ang pipila ka mga bahin nga parehas sa imong stack, pananglitan, mahimo nimong isulat ang mga troso sa kinesis direkta gikan sa nginx, pag-bypass sa fluentd, o paggamit sa logstash alang niini.

Pagkolekta sa mga log sa Nginx

Sa kasagaran, ang mga log sa Nginx ingon niini:

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

Mahimo kini nga ma-parse, apan mas sayon ​​​​ang pagtul-id sa configuration sa Nginx aron kini makahimo og mga log sa 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 alang sa pagtipig

Sa pagtipig sa mga troso, atong gamiton ang S3. Gitugotan ka niini sa pagtipig ug pag-analisar sa mga troso sa usa ka lugar, tungod kay mahimo’g molihok si Athena sa data sa S3 direkta. Sa ulahi sa artikulo isulti ko kanimo kung giunsa ang husto nga pagdugang ug pagproseso sa mga troso, apan kinahanglan una namon ang usa ka limpyo nga balde sa S3, diin wala’y lain nga matipigan. Angayan nga hunahunaon daan kung unsang rehiyon ang imong himuon nga balde, tungod kay ang Athena dili magamit sa tanan nga mga rehiyon.

Paghimo og sirkito sa Athena console

Magbuhat ta ug lamesa sa Athena para sa mga troso. Kinahanglan kini alang sa pagsulat ug pagbasa kung plano nimo nga gamiton ang Kinesis Firehose. Ablihi ang Athena console ug paghimo og lamesa:

Paghimo sa SQL table

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');

Paghimo sa Kinesis Firehose Stream

Isulat sa Kinesis Firehose ang datos nga nadawat gikan sa Nginx hangtod sa S3 sa napili nga format, gibahin kini sa mga direktoryo sa format nga YYYY/MM/DD/HH. Magamit kini kung magbasa sa datos. Mahimo nimo, siyempre, pagsulat direkta sa S3 gikan sa fluentd, apan sa kini nga kaso kinahanglan nimo nga isulat ang JSON, ug kini dili epektibo tungod sa kadako sa mga file. Dugang pa, kung gamiton ang PrestoDB o Athena, ang JSON mao ang pinakahinay nga format sa datos. Busa ablihi ang Kinesis Firehose console, i-klik ang "Create delivery stream", pilia ang "direct PUT" sa "delivery" field:

Nginx log analytics gamit ang Amazon Athena ug Cube.js

Sa sunod nga tab, pilia ang "Record format conversion" - "Enabled" ug pilia ang "Apache ORC" isip format sa pagrekord. Sumala sa pipila ka panukiduki Owen O'Malley, kini ang labing maayo nga format alang sa PrestoDB ug Athena. Gigamit namo ang lamesa nga among gibuhat sa ibabaw isip schema. Palihug timan-i nga mahimo nimong ipiho ang bisan unsang lokasyon sa S3 sa kinesis; ang eskema lamang ang gigamit gikan sa lamesa. Apan kung imong gipiho ang lahi nga lokasyon sa S3, nan dili nimo mabasa kini nga mga rekord gikan sa kini nga lamesa.

Nginx log analytics gamit ang Amazon Athena ug Cube.js

Gipili namo ang S3 alang sa pagtipig ug ang balde nga among gibuhat kaniadto. Ang Aws Glue Crawler, nga akong hisgutan sa ulahi, dili makagana sa mga prefix sa usa ka S3 nga balde, busa importante nga biyaan kini nga walay sulod.

Nginx log analytics gamit ang Amazon Athena ug Cube.js

Ang nahabilin nga mga kapilian mahimong usbon depende sa imong load; kasagaran akong gigamit ang mga default. Timan-i nga ang S3 compression dili magamit, apan ang ORC naggamit sa native compression pinaagi sa default.

larinod

Karon nga na-configure na namo ang pagtipig ug pagdawat og mga troso, kinahanglan namong i-configure ang pagpadala. Atong gamiton larinod, tungod kay gihigugma ko si Ruby, apan mahimo nimong gamiton ang Logstash o ipadala ang mga troso direkta sa kinesis. Ang Fluentd server mahimong ilunsad sa daghang mga paagi, isulti ko kanimo ang bahin sa docker tungod kay kini yano ug dali.

Una, kinahanglan nato ang fluent.conf configuration file. Paghimo niini ug idugang ang tinubdan:

matang sa unahan
port 24224
higot 0.0.0.0

Karon mahimo nimong sugdan ang Fluentd server. Kung kinahanglan nimo ang usa ka mas abante nga pag-configure, adto sa Hub sa pantalan Adunay usa ka detalyado nga giya, lakip ang kung giunsa pag-assemble ang imong imahe.

$ 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

Kini nga configuration naggamit sa dalan /fluentd/log sa cache logs sa dili pa ipadala. Mahimo nimo nga wala kini, apan kung mag-restart ka, mahimo nimong mawad-an ang tanan nga na-cache nga adunay back-breaking labor. Mahimo usab nimo gamiton ang bisan unsang pantalan; ang 24224 mao ang default nga Fluentd port.

Karon nga kami adunay Fluentd nga nagdagan, mahimo namon ipadala ang mga log sa Nginx didto. Kanunay namong gipadagan ang Nginx sa usa ka sudlanan sa Docker, diin ang Docker adunay lumad nga drayber sa pag-log alang sa 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

Kung lahi ang imong pagpadagan sa Nginx, mahimo nimong gamiton ang mga file sa log, adunay Fluentd file tail plugin.

Atong idugang ang log parsing nga gi-configure sa ibabaw sa Fluent configuration:

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

Ug pagpadala mga troso sa Kinesis gamit 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>

Athena

Kung na-configure nimo ang tanan sa husto, unya pagkahuman sa usa ka panahon (sa default, ang mga rekord sa Kinesis nakadawat data kausa matag 10 minuto) kinahanglan nimo nga makita ang mga file sa log sa S3. Sa "monitoring" nga menu sa Kinesis Firehose imong makita kung unsa ka daghang data ang natala sa S3, ingon man mga sayup. Ayaw kalimti ang paghatag ug access sa pagsulat sa S3 bucket sa papel sa Kinesis. Kung dili ma-parse sa Kinesis ang usa ka butang, idugang niini ang mga sayup sa parehas nga balde.

Karon mahimo nimong tan-awon ang datos sa Athena. Atong pangitaon ang pinakabag-o nga mga hangyo diin gibalik namo ang mga sayop:

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

Pag-scan sa tanan nga mga rekord alang sa matag hangyo

Karon ang among mga troso giproseso ug gitipigan sa S3 sa ORC, gi-compress ug andam na alang sa pagtuki. Ang Kinesis Firehose nag-organisar pa gani kanila ngadto sa mga direktoryo sa matag oras. Bisan pa, basta ang lamesa dili mabahin, si Athena mag-load sa tanan nga oras nga datos sa matag hangyo, nga adunay talagsaon nga mga eksepsiyon. Kini usa ka dako nga problema tungod sa duha ka hinungdan:

  • Ang gidaghanon sa datos kanunay nga nagtubo, nagpahinay sa mga pangutana;
  • Gisingil si Athena base sa gidaghanon sa datos nga na-scan, nga adunay minimum nga 10 MB matag hangyo.

Aron ayuhon kini, gigamit namo ang AWS Glue Crawler, nga mo-crawl sa datos sa S3 ug isulat ang impormasyon sa partition sa Glue Metastore. Kini magtugot kanamo sa paggamit sa mga partisyon ingon nga usa ka filter kung mangutana sa Athena, ug kini mag-scan lamang sa mga direktoryo nga gipiho sa pangutana.

Pag-set up sa Amazon Glue Crawler

Ang Amazon Glue Crawler nag-scan sa tanang datos sa S3 bucket ug nagmugna og mga lamesa nga adunay mga partisyon. Paghimo og Glue Crawler gikan sa AWS Glue console ug pagdugang og balde diin imong gitipigan ang datos. Mahimo nimong gamiton ang usa ka crawler alang sa daghang mga balde, diin maghimo kini nga mga lamesa sa piho nga database nga adunay mga ngalan nga katumbas sa mga ngalan sa mga balde. Kung nagplano ka nga gamiton kini nga data kanunay, siguruha nga i-configure ang iskedyul sa paglansad sa Crawler aron mohaum sa imong mga panginahanglan. Gigamit namon ang usa ka Crawler alang sa tanan nga mga lamesa, nga nagdagan matag oras.

Gibahin nga mga lamesa

Pagkahuman sa una nga paglansad sa crawler, ang mga lamesa alang sa matag gi-scan nga balde kinahanglan nga makita sa database nga gitakda sa mga setting. Ablihi ang Athena console ug pangitaa ang lamesa nga adunay mga log sa Nginx. Atong sulayan ang pagbasa og usa ka butang:

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

Kini nga pangutana mopili sa tanang mga rekord nga nadawat tali sa alas 6 sa buntag ug alas 7 sa buntag sa Abril 8, 2019. Apan unsa pa ka labi ka episyente kini kaysa pagbasa lamang gikan sa usa ka dili partitioned nga lamesa? Atong hibal-an ug pilion ang parehas nga mga rekord, pagsala niini pinaagi sa timestamp:

Nginx log analytics gamit ang Amazon Athena ug Cube.js

3.59 segundos ug 244.34 megabytes nga datos sa usa ka dataset nga adunay usa lang ka semana nga mga log. Atong sulayan ang usa ka filter pinaagi sa partisyon:

Nginx log analytics gamit ang Amazon Athena ug Cube.js

Mas paspas, apan labing hinungdanon - 1.23 megabytes ra nga datos! Mas barato kini kung dili sa minimum nga 10 megabytes matag hangyo sa presyo. Apan mas maayo pa kini, ug sa dagkong mga dataset ang kalainan mahimong mas impresibo.

Paghimo ug dashboard gamit ang Cube.js

Aron ma-assemble ang dashboard, among gigamit ang Cube.js analytical framework. Kini adunay daghan nga mga gimbuhaton, apan kami interesado sa duha: ang abilidad sa awtomatikong paggamit sa partition filter ug data pre-aggregation. Gigamit niini ang data schema eskema sa datos, gisulat sa Javascript aron makamugna og SQL ug ipatuman ang usa ka pangutana sa database. Kinahanglan lang nato nga ipakita kung unsaon paggamit ang partition filter sa data schema.

Magbuhat ta ug bag-ong aplikasyon sa Cube.js. Tungod kay gigamit na namo ang AWS stack, makatarunganon nga gamiton ang Lambda alang sa pag-deploy. Mahimo nimong gamiton ang express template alang sa henerasyon kung plano nimo nga i-host ang Cube.js backend sa Heroku o Docker. Ang dokumentasyon naghulagway sa uban mga pamaagi sa pag-host.

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

Ang mga variable sa palibot gigamit aron ma-configure ang pag-access sa database sa cube.js. Ang generator maghimo usa ka .env file diin mahimo nimong ipiho ang imong mga yawe Athena.

Karon atong gikinahanglan eskema sa datos, diin among ipakita kung giunsa ang among mga troso gitipigan. Didto mahimo nimong ipiho kung giunsa ang pagkalkulo sa mga sukatan alang sa mga dashboard.

Sa direktoryo schema, paghimo og file Logs.js. Ania ang usa ka pananglitan nga modelo sa datos alang sa nginx:

Model code

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

Dinhi atong gigamit ang variable FILTER_PARAMSaron makamugna og SQL query nga adunay partition filter.

Gibutang usab namo ang mga sukatan ug mga parameter nga gusto namong ipakita sa dashboard ug ipiho ang mga pre-aggregations. Ang Cube.js maghimo og dugang nga mga lamesa nga adunay pre-aggregated nga datos ug awtomatiko nga i-update ang datos sa pag-abot niini. Dili lamang kini makapadali sa mga pangutana, apan makapamenos usab sa gasto sa paggamit sa Athena.

Atong idugang kini nga impormasyon sa data schema file:

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

Among gipiho niini nga modelo nga gikinahanglan ang pag-pre-aggregate sa datos alang sa tanang metrics nga gigamit, ug paggamit sa partitioning kada bulan. Pre-aggregation partitioning makapadali sa pagkolekta ug pag-update sa datos.

Karon mahimo na namon ma-assemble ang dashboard!

Naghatag ang Cube.js backend PAHULAY API ug usa ka hugpong sa mga librarya sa kliyente alang sa sikat nga front-end frameworks. Gamiton namo ang React nga bersyon sa kliyente sa paghimo sa dashboard. Naghatag lang ang Cube.js og datos, mao nga magkinahanglan kami og visualization library - ganahan ko niini recharts, apan mahimo nimong gamiton ang bisan unsa.

Gidawat sa server sa Cube.js ang hangyo sa JSON nga format, nga nagtino sa gikinahanglan nga metrics. Pananglitan, aron makalkulo kung pila ang mga sayup nga gihatag sa Nginx sa adlaw, kinahanglan nimo ipadala ang mosunud nga hangyo:

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

Atong i-install ang Cube.js client ug ang React component library pinaagi sa NPM:

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

Nag-import kami og mga sangkap cubejs ΠΈ QueryRendereraron i-download ang datos, ug kolektahon ang dashboard:

Dashboard code

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

Ang mga tinubdan sa dashboard anaa sa code sandbox.

Source: www.habr.com

Idugang sa usa ka comment