Uchambuzi wa kumbukumbu wa Nginx kwa kutumia Amazon Athena na Cube.js

Kwa kawaida, bidhaa za kibiashara au njia mbadala za chanzo-wazi zilizotengenezwa tayari, kama vile Prometheus + Grafana, hutumiwa kufuatilia na kuchambua utendakazi wa Nginx. Hili ni chaguo zuri kwa ufuatiliaji au uchanganuzi wa wakati halisi, lakini sio rahisi sana kwa uchambuzi wa kihistoria. Kwenye rasilimali yoyote maarufu, kiasi cha data kutoka kwa kumbukumbu za nginx kinakua kwa kasi, na kuchambua kiasi kikubwa cha data, ni busara kutumia kitu maalum zaidi.

Katika makala hii nitakuambia jinsi unaweza kutumia Athena kuchambua kumbukumbu, nikichukua Nginx kama mfano, na nitaonyesha jinsi ya kukusanya dashibodi ya uchanganuzi kutoka kwa data hii kwa kutumia mfumo wa chanzo huria wa cube.js. Hapa kuna usanifu kamili wa suluhisho:

Uchambuzi wa kumbukumbu wa Nginx kwa kutumia Amazon Athena na Cube.js

TL:DR;
Unganisha kwa dashibodi iliyokamilika.

Kukusanya taarifa tunazotumia mwenye ufasaha, kwa usindikaji - AWS Kinesis Data Firehose ΠΈ Gundi ya AWS, kwa kuhifadhi - AWS S3. Kutumia kifungu hiki, unaweza kuhifadhi sio kumbukumbu za nginx tu, bali pia matukio mengine, pamoja na kumbukumbu za huduma nyingine. Unaweza kubadilisha baadhi ya sehemu na zinazofanana kwa rafu yako, kwa mfano, unaweza kuandika kumbukumbu kwa kinesis moja kwa moja kutoka kwa nginx, kupita kwa ufasaha, au kutumia logstash kwa hili.

Kukusanya kumbukumbu za Nginx

Kwa msingi, magogo ya Nginx yanaonekana kama hii:

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

Zinaweza kuchanganuliwa, lakini ni rahisi zaidi kusahihisha usanidi wa Nginx ili itoe magogo katika 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 kwa uhifadhi

Ili kuhifadhi kumbukumbu, tutatumia S3. Hii inakuwezesha kuhifadhi na kuchambua kumbukumbu katika sehemu moja, kwani Athena inaweza kufanya kazi na data katika S3 moja kwa moja. Baadaye katika makala nitakuambia jinsi ya kuongeza na kusindika magogo kwa usahihi, lakini kwanza tunahitaji ndoo safi katika S3, ambayo hakuna kitu kingine kitakachohifadhiwa. Inafaa kuzingatia mapema ni eneo gani utatengeneza ndoo yako, kwa sababu Athena haipatikani katika maeneo yote.

Kuunda mzunguko katika koni ya Athena

Wacha tuunda meza huko Athena kwa kumbukumbu. Inahitajika kwa kuandika na kusoma ikiwa unapanga kutumia Kinesis Firehose. Fungua koni ya Athena na uunda meza:

Uundaji wa jedwali la 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');

Kuunda Mtiririko wa Kinesis Firehose

Kinesis Firehose itaandika data iliyopokelewa kutoka kwa Nginx hadi S3 katika umbizo lililochaguliwa, na kuigawanya katika saraka katika umbizo la YYYY/MM/DD/HH. Hii itakuwa muhimu wakati wa kusoma data. Unaweza, kwa kweli, kuandika moja kwa moja kwa S3 kutoka kwa ufasaha, lakini katika kesi hii utalazimika kuandika JSON, na hii haifai kwa sababu ya saizi kubwa ya faili. Zaidi ya hayo, unapotumia PrestoDB au Athena, JSON ndiyo umbizo la data polepole zaidi. Kwa hivyo fungua kiweko cha Kinesis Firehose, bofya "Unda mtiririko wa uwasilishaji", chagua "PUT moja kwa moja" katika sehemu ya "utoaji":

Uchambuzi wa kumbukumbu wa Nginx kwa kutumia Amazon Athena na Cube.js

Katika kichupo kinachofuata, chagua "Ubadilishaji wa umbizo la Rekodi" - "Imewezeshwa" na uchague "Apache ORC" kama umbizo la kurekodi. Kulingana na utafiti fulani Owen O'Malley, huu ndio umbizo bora zaidi la PrestoDB na Athena. Tunatumia jedwali tulilounda hapo juu kama schema. Tafadhali kumbuka kuwa unaweza kubainisha eneo lolote la S3 katika kinesis; ni schema pekee inayotumika kutoka kwenye jedwali. Lakini ukibainisha eneo tofauti la S3, basi hutaweza kusoma rekodi hizi kutoka kwa jedwali hili.

Uchambuzi wa kumbukumbu wa Nginx kwa kutumia Amazon Athena na Cube.js

Tunachagua S3 kwa kuhifadhi na ndoo ambayo tumeunda hapo awali. Aws Glue Crawler, ambayo nitazungumzia baadaye kidogo, haiwezi kufanya kazi na viambishi awali kwenye ndoo ya S3, kwa hiyo ni muhimu kuiacha tupu.

Uchambuzi wa kumbukumbu wa Nginx kwa kutumia Amazon Athena na Cube.js

Chaguzi zilizobaki zinaweza kubadilishwa kulingana na mzigo wako; mimi hutumia chaguo-msingi. Kumbuka kuwa mfinyazo wa S3 haupatikani, lakini ORC hutumia mbano asilia kwa chaguo-msingi.

mwenye ufasaha

Sasa kwa kuwa tumesanidi kuhifadhi na kupokea kumbukumbu, tunahitaji kusanidi kutuma. Tutatumia mwenye ufasaha, kwa sababu ninampenda Ruby, lakini unaweza kutumia Logstash au kutuma kumbukumbu kwa kinesis moja kwa moja. Seva ya Fluentd inaweza kuzinduliwa kwa njia kadhaa, nitakuambia kuhusu docker kwa sababu ni rahisi na rahisi.

Kwanza, tunahitaji faili ya usanidi ya fluent.conf. Unda na uongeze chanzo:

aina mbele
bandari 24224
funga 0.0.0.0

Sasa unaweza kuanza seva ya Fluentd. Ikiwa unahitaji usanidi wa hali ya juu zaidi, nenda kwa Kitovu cha Docker Kuna mwongozo wa kina, ikiwa ni pamoja na jinsi ya kuunganisha picha yako.

$ 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

Usanidi huu hutumia njia /fluentd/log kuweka kumbukumbu kabla ya kutuma. Unaweza kufanya bila hii, lakini kisha unapoanzisha upya, unaweza kupoteza kila kitu kilichohifadhiwa na kazi ya kuvunja nyuma. Unaweza pia kutumia mlango wowote; 24224 ndio mlango chaguomsingi wa Fluentd.

Sasa kwa kuwa tuna Fluentd inayoendesha, tunaweza kutuma kumbukumbu za Nginx huko. Kawaida tunaendesha Nginx kwenye kontena ya Docker, ambapo Docker ina dereva wa asili wa ukataji miti wa 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

Ikiwa unaendesha Nginx tofauti, unaweza kutumia faili za kumbukumbu, Fluentd ina programu-jalizi ya mkia wa faili.

Wacha tuongeze uchanganuzi wa logi uliosanidiwa hapo juu kwa usanidi wa Fasaha:

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

Na kutuma magogo kwa Kinesis kwa kutumia kinesis firehose programu-jalizi:

<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

Ikiwa umesanidi kila kitu kwa usahihi, basi baada ya muda (kwa default, rekodi za Kinesis zilipokea data mara moja kila baada ya dakika 10) unapaswa kuona faili za logi katika S3. Katika menyu ya "ufuatiliaji" ya Kinesis Firehose unaweza kuona ni data ngapi iliyorekodiwa katika S3, pamoja na makosa. Usisahau kutoa ufikiaji wa maandishi kwa ndoo ya S3 kwa jukumu la Kinesis. Ikiwa Kinesis haikuweza kuchanganua kitu, itaongeza makosa kwenye ndoo sawa.

Sasa unaweza kuona data katika Athena. Wacha tupate maombi ya hivi punde ambayo tulirudisha makosa:

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

Inachanganua rekodi zote kwa kila ombi

Sasa kumbukumbu zetu zimechakatwa na kuhifadhiwa katika S3 katika ORC, zimebanwa na ziko tayari kwa uchambuzi. Kinesis Firehose hata ilizipanga katika saraka kwa kila saa. Walakini, mradi jedwali halijagawanywa, Athena atapakia data ya wakati wote kwa kila ombi, isipokuwa nadra. Hili ni tatizo kubwa kwa sababu mbili:

  • Kiasi cha data kinakua kila wakati, na kupunguza kasi ya maswali;
  • Athena inatozwa kulingana na kiasi cha data iliyochanganuliwa, na kiwango cha chini cha MB 10 kwa kila ombi.

Ili kurekebisha hili, tunatumia AWS Glue Crawler, ambayo itatambaa data katika S3 na kuandika maelezo ya kizigeu kwenye Glue Metastore. Hii itaturuhusu kutumia partitions kama kichujio wakati wa kuuliza Athena, na itachanganua saraka zilizobainishwa kwenye hoja.

Inasanidi Kitambaa cha Gundi cha Amazon

Amazon Glue Crawler huchanganua data yote kwenye ndoo ya S3 na kuunda majedwali yaliyo na sehemu. Unda Kitambaa cha Gundi kutoka kwa kiweko cha Glue cha AWS na uongeze ndoo ambapo unahifadhi data. Unaweza kutumia kitambazaji kimoja kwa ndoo kadhaa, ambapo kitaunda majedwali katika hifadhidata iliyobainishwa na majina yanayolingana na majina ya ndoo. Ikiwa unapanga kutumia data hii mara kwa mara, hakikisha kuwa umeweka ratiba ya uzinduzi wa Crawler ili kukidhi mahitaji yako. Tunatumia Crawler moja kwa jedwali zote, ambayo hufanya kazi kila saa.

Meza zilizogawanywa

Baada ya uzinduzi wa kwanza wa kutambaa, majedwali kwa kila ndoo iliyochanganuliwa yanapaswa kuonekana katika hifadhidata iliyobainishwa katika mipangilio. Fungua koni ya Athena na utafute jedwali na magogo ya Nginx. Hebu jaribu kusoma kitu:

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

Hoja hii itachagua rekodi zote zilizopokelewa kati ya 6 asubuhi na 7 asubuhi tarehe 8 Aprili 2019. Lakini hii ina ufanisi kiasi gani kuliko kusoma tu kutoka kwa jedwali ambalo halijagawanywa? Wacha tujue na uchague rekodi sawa, tukizichuja kwa muhuri wa saa:

Uchambuzi wa kumbukumbu wa Nginx kwa kutumia Amazon Athena na Cube.js

Sekunde 3.59 na megabaiti 244.34 za data kwenye mkusanyiko wa data wenye kumbukumbu za wiki moja pekee. Wacha tujaribu kichungi kwa kizigeu:

Uchambuzi wa kumbukumbu wa Nginx kwa kutumia Amazon Athena na Cube.js

Kwa kasi kidogo, lakini muhimu zaidi - megabytes 1.23 tu ya data! Itakuwa nafuu zaidi ikiwa si kwa kiwango cha chini cha megabaiti 10 kwa kila ombi katika bei. Lakini bado ni bora zaidi, na kwenye hifadhidata kubwa tofauti itakuwa ya kuvutia zaidi.

Kuunda dashibodi kwa kutumia Cube.js

Ili kuunganisha dashibodi, tunatumia mfumo wa uchanganuzi wa Cube.js. Ina kazi nyingi sana, lakini tunavutiwa na mbili: uwezo wa kutumia vichungi vya kuhesabu kiotomatiki na ujumuishaji wa mapema wa data. Inatumia schema ya data schema ya data, iliyoandikwa kwa Javascript kutoa SQL na kutekeleza hoja ya hifadhidata. Tunahitaji tu kuashiria jinsi ya kutumia kichujio cha kuhesabu kwenye schema ya data.

Hebu tuunde programu mpya ya Cube.js. Kwa kuwa tayari tunatumia mrundikano wa AWS, ni jambo la busara kutumia Lambda kwa kupelekwa. Unaweza kutumia kiolezo cha moja kwa moja cha kizazi ikiwa unapanga kupangisha mazingira ya nyuma ya Cube.js katika Heroku au Docker. Nyaraka zinaelezea wengine njia za mwenyeji.

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

Vigezo vya mazingira vinatumika kusanidi ufikiaji wa hifadhidata katika cube.js. Jenereta itaunda faili ya .env ambayo unaweza kubainisha funguo zako Athena.

Sasa tunahitaji schema ya data, ambayo tutaonyesha hasa jinsi magogo yetu yanahifadhiwa. Huko unaweza pia kubainisha jinsi ya kukokotoa vipimo vya dashibodi.

Katika saraka schema, tengeneza faili Logs.js. Hapa kuna mfano wa mfano wa data wa nginx:

Msimbo wa mfano

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

Hapa tunatumia kutofautisha FILTER_PARAMSili kutoa swali la SQL na kichungi cha kuhesabu.

Pia tunaweka vipimo na vigezo ambavyo tunataka kuonyesha kwenye dashibodi na kubainisha majumuisho ya awali. Cube.js itaunda majedwali ya ziada yenye data iliyokusanywa mapema na itasasisha data kiotomatiki inapofika. Hii sio tu kuongeza kasi ya maswali, lakini pia inapunguza gharama ya kutumia Athena.

Wacha tuongeze habari hii kwenye faili ya schema ya data:

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

Tunabainisha katika muundo huu kwamba ni muhimu kujumlisha mapema data ya vipimo vyote vinavyotumika, na kutumia kugawanya kwa mwezi. Kugawanya kabla ya kujumlisha inaweza kuongeza kasi ya kukusanya na kusasisha data.

Sasa tunaweza kukusanya dashibodi!

Cube.js backend hutoa API YA REST na seti ya maktaba za mteja kwa mifumo maarufu ya mbele. Tutatumia toleo la React la mteja kuunda dashibodi. Cube.js hutoa data pekee, kwa hivyo tutahitaji maktaba ya taswira - ninaipenda recharts, lakini unaweza kutumia yoyote.

Seva ya Cube.js inakubali ombi ndani Umbizo la JSON, ambayo hubainisha vipimo vinavyohitajika. Kwa mfano, kuhesabu ni makosa ngapi ambayo Nginx alitoa kwa siku, unahitaji kutuma ombi lifuatalo:

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

Wacha tusakinishe mteja wa Cube.js na maktaba ya sehemu ya React kupitia NPM:

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

Tunaagiza vipengele cubejs ΠΈ QueryRendererkupakua data, na kukusanya dashibodi:

Msimbo wa dashibodi

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

Vyanzo vya Dashibodi vinapatikana kwa code sandbox.

Chanzo: mapenzi.com

Kuongeza maoni