ΠΠ±ΠΈΡΠ½ΠΎ, ΠΊΠΎΠΌΠ΅ΡΡΠΈΡΠ°Π»Π½ΠΈΡΠ΅ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈ ΠΈΠ»ΠΈ Π³ΠΎΡΠΎΠ²ΠΈ Π°Π»ΡΠ΅ΡΠ½Π°ΡΠΈΠ²ΠΈ ΡΠΎ ΠΎΡΠ²ΠΎΡΠ΅Π½ ΠΊΠΎΠ΄, ΠΊΠ°ΠΊΠΎ ΡΡΠΎ ΡΠ΅ Prometheus + Grafana, ΡΠ΅ ΠΊΠΎΡΠΈΡΡΠ°Ρ Π·Π° ΡΠ»Π΅Π΄Π΅ΡΠ΅ ΠΈ Π°Π½Π°Π»ΠΈΠ·Π° Π½Π° ΡΠ°Π±ΠΎΡΠ°ΡΠ° Π½Π° Nginx. ΠΠ²Π° Π΅ Π΄ΠΎΠ±ΡΠ° ΠΎΠΏΡΠΈΡΠ° Π·Π° ΡΠ»Π΅Π΄Π΅ΡΠ΅ ΠΈΠ»ΠΈ Π°Π½Π°Π»ΠΈΡΠΈΠΊΠ° Π²ΠΎ ΡΠ΅Π°Π»Π½ΠΎ Π²ΡΠ΅ΠΌΠ΅, Π½ΠΎ Π½Π΅ Π΅ ΠΌΠ½ΠΎΠ³Ρ ΠΏΠΎΠ³ΠΎΠ΄Π½Π° Π·Π° ΠΈΡΡΠΎΡΠΈΡΠΊΠ° Π°Π½Π°Π»ΠΈΠ·Π°. ΠΠ° ΠΊΠΎΡ Π±ΠΈΠ»ΠΎ ΠΏΠΎΠΏΡΠ»Π°ΡΠ΅Π½ ΡΠ΅ΡΡΡΡ, ΠΎΠ±Π΅ΠΌΠΎΡ Π½Π° ΠΏΠΎΠ΄Π°ΡΠΎΡΠΈ ΠΎΠ΄ Π΄Π½Π΅Π²Π½ΠΈΡΠΈΡΠ΅ Π½Π° nginx ΡΠ°ΠΏΠΈΠ΄Π½ΠΎ ΡΠ°ΡΡΠ΅, Π° Π·Π° Π΄Π° ΡΠ΅ Π°Π½Π°Π»ΠΈΠ·ΠΈΡΠ° Π³ΠΎΠ»Π΅ΠΌΠ° ΠΊΠΎΠ»ΠΈΡΠΈΠ½Π° Π½Π° ΠΏΠΎΠ΄Π°ΡΠΎΡΠΈ, Π»ΠΎΠ³ΠΈΡΠ½ΠΎ Π΅ Π΄Π° ΡΠ΅ ΠΊΠΎΡΠΈΡΡΠΈ Π½Π΅ΡΡΠΎ ΠΏΠΎΡΠΏΠ΅ΡΠΈΡΠ°Π»ΠΈΠ·ΠΈΡΠ°Π½ΠΎ.
ΠΠΎ ΠΎΠ²Π°Π° ΡΡΠ°ΡΠΈΡΠ° ΡΠ΅ Π²ΠΈ ΠΊΠ°ΠΆΠ°ΠΌ ΠΊΠ°ΠΊΠΎ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π΄Π° Π³ΠΎ ΠΊΠΎΡΠΈΡΡΠΈΡΠ΅
TL:DR;
ΠΠ° ΡΠΎΠ±ΠΈΡΠ°ΡΠ΅ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ ΡΡΠΎ Π³ΠΈ ΠΊΠΎΡΠΈΡΡΠΈΠΌΠ΅
Π‘ΠΎΠ±ΠΈΡΠ°ΡΠ΅ Π½Π° Π»ΠΎΠ³ΠΎΠ²ΠΈ Π½Π° 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 Π½Π΅ Π΅ Π΄ΠΎΡΡΠ°ΠΏΠ½Π° Π²ΠΎ ΡΠΈΡΠ΅ ΡΠ΅Π³ΠΈΠΎΠ½ΠΈ.
Π‘ΠΎΠ·Π΄Π°Π²Π°ΡΠ΅ ΠΊΠΎΠ»ΠΎ Π²ΠΎ ΠΊΠΎΠ½Π·ΠΎΠ»Π°ΡΠ° ΠΡΠΈΠ½Π°
ΠΡΠ΄Π΅ Π΄Π° ΡΠΎΠ·Π΄Π°Π΄Π΅ΠΌΠ΅ ΡΠ°Π±Π΅Π»Π° Π²ΠΎ ΠΡΠΈΠ½Π° Π·Π° Π΄Π½Π΅Π²Π½ΠΈΡΠΈ. ΠΠΎΡΡΠ΅Π±Π΅Π½ Π΅ ΠΈ Π·Π° ΠΏΠΈΡΡΠ²Π°ΡΠ΅ ΠΈ Π·Π° ΡΠΈΡΠ°ΡΠ΅ Π΄ΠΎΠΊΠΎΠ»ΠΊΡ ΠΏΠ»Π°Π½ΠΈΡΠ°ΡΠ΅ Π΄Π° ΠΊΠΎΡΠΈΡΡΠΈΡΠ΅ 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 Π²ΠΎ ΠΈΠ·Π±ΡΠ°Π½ΠΈΠΎΡ ΡΠΎΡΠΌΠ°Ρ, ΠΏΠΎΠ΄Π΅Π»ΡΠ²Π°ΡΡΠΈ Π³ΠΈ Π²ΠΎ Π΄ΠΈΡΠ΅ΠΊΡΠΎΡΠΈΡΠΌΠΈ Π²ΠΎ ΡΠΎΡΠΌΠ°Ρ ΠΠΠΠ/ΠΠ/ΠΠ/ΠΠ. ΠΠ²Π° ΡΠ΅ Π²ΠΈ ΠΏΠΎΠΌΠΎΠ³Π½Π΅ ΠΏΡΠΈ ΡΠΈΡΠ°ΡΠ΅ ΠΏΠΎΠ΄Π°ΡΠΎΡΠΈ. Π‘Π΅ ΡΠ°Π·Π±ΠΈΡΠ°, ΠΌΠΎΠΆΠ΅ΡΠ΅ Π΄Π° ΠΏΠΈΡΡΠ²Π°ΡΠ΅ Π΄ΠΈΡΠ΅ΠΊΡΠ½ΠΎ Π½Π° S3 ΠΎΠ΄ fluentd, Π½ΠΎ Π²ΠΎ ΠΎΠ²ΠΎΡ ΡΠ»ΡΡΠ°Ρ ΡΠ΅ ΠΌΠΎΡΠ° Π΄Π° Π½Π°ΠΏΠΈΡΠ΅ΡΠ΅ JSON, Π° ΡΠΎΠ° Π΅ Π½Π΅Π΅ΡΠΈΠΊΠ°ΡΠ½ΠΎ ΠΏΠΎΡΠ°Π΄ΠΈ Π³ΠΎΠ»Π΅ΠΌΠ°ΡΠ° Π³ΠΎΠ»Π΅ΠΌΠΈΠ½Π° Π½Π° Π΄Π°ΡΠΎΡΠ΅ΠΊΠΈΡΠ΅. ΠΠΎΠΏΠΎΠ»Π½ΠΈΡΠ΅Π»Π½ΠΎ, ΠΊΠΎΠ³Π° ΠΊΠΎΡΠΈΡΡΠΈΡΠ΅ PrestoDB ΠΈΠ»ΠΈ Athena, JSON Π΅ Π½Π°ΡΠ±Π°Π²Π½ΠΈΠΎΡ ΡΠΎΡΠΌΠ°Ρ Π½Π° ΠΏΠΎΠ΄Π°ΡΠΎΡΠΈ. ΠΠ°ΡΠΎΠ°, ΠΎΡΠ²ΠΎΡΠ΅ΡΠ΅ ΡΠ° ΠΊΠΎΠ½Π·ΠΎΠ»Π°ΡΠ° Kinesis Firehose, ΠΊΠ»ΠΈΠΊΠ½Π΅ΡΠ΅ βΠΡΠ΅ΠΈΡΠ°Ρ ΠΏΠΎΡΠΎΠΊ Π·Π° ΠΈΡΠΏΠΎΡΠ°ΠΊΠ°β, ΠΈΠ·Π±Π΅ΡΠ΅ΡΠ΅ βΠ΄ΠΈΡΠ΅ΠΊΡΠ½ΠΎ PUTβ Π²ΠΎ ΠΏΠΎΠ»Π΅ΡΠΎ βΠΈΡΠΏΠΎΡΠ°ΠΊΠ°β:
ΠΠΎ ΡΠ»Π΅Π΄Π½ΠΎΡΠΎ ΡΠ°Π·ΠΈΡΠ΅, ΠΈΠ·Π±Π΅ΡΠ΅ΡΠ΅ βΠΠΎΠ½Π²Π΅ΡΠ·ΠΈΡΠ° Π½Π° ΡΠΎΡΠΌΠ°Ρ Π·Π° ΡΠ½ΠΈΠΌΠ°ΡΠ΅β - βΠΠ²ΠΎΠ·ΠΌΠΎΠΆΠ΅Π½ΠΎβ ΠΈ ΠΈΠ·Π±Π΅ΡΠ΅ΡΠ΅ βApache ORCβ ΠΊΠ°ΠΊΠΎ ΡΠΎΡΠΌΠ°Ρ Π·Π° ΡΠ½ΠΈΠΌΠ°ΡΠ΅. Π‘ΠΏΠΎΡΠ΅Π΄ Π½Π΅ΠΊΠΎΠΈ ΠΈΡΡΡΠ°ΠΆΡΠ²Π°ΡΠ°
ΠΠ·Π±ΠΈΡΠ°ΠΌΠ΅ S3 Π·Π° ΡΠΊΠ»Π°Π΄ΠΈΡΠ°ΡΠ΅ ΠΈ ΠΊΠΎΡΠΏΠ°ΡΠ° ΡΡΠΎ ΡΠ° ΡΠΎΠ·Π΄Π°Π΄ΠΎΠ²ΠΌΠ΅ ΠΏΡΠ΅ΡΡ ΠΎΠ΄Π½ΠΎ. Aws Glue Crawler, Π·Π° ΠΊΠΎΡ ΡΠ΅ Π·Π±ΠΎΡΡΠ²Π°ΠΌ ΠΌΠ°Π»ΠΊΡ ΠΏΠΎΠ΄ΠΎΡΠ½Π°, Π½Π΅ ΠΌΠΎΠΆΠ΅ Π΄Π° ΡΠ°Π±ΠΎΡΠΈ ΡΠΎ ΠΏΡΠ΅ΡΠΈΠΊΡΠΈ Π²ΠΎ ΠΊΠΎΡΠ° S3, ΠΏΠ° Π·Π°ΡΠΎΠ° Π΅ Π²Π°ΠΆΠ½ΠΎ Π΄Π° Π³ΠΎ ΠΎΡΡΠ°Π²ΠΈΡΠ΅ ΠΏΡΠ°Π·Π΅Π½.
ΠΡΡΠ°Π½Π°ΡΠΈΡΠ΅ ΠΎΠΏΡΠΈΠΈ ΠΌΠΎΠΆΠ΅ Π΄Π° ΡΠ΅ ΠΏΡΠΎΠΌΠ΅Π½Π°Ρ Π²ΠΎ Π·Π°Π²ΠΈΡΠ½ΠΎΡΡ ΠΎΠ΄ Π²Π°ΡΠ΅ΡΠΎ ΠΎΠΏΡΠΎΠ²Π°ΡΡΠ²Π°ΡΠ΅; ΡΠ°Ρ ΠΎΠ±ΠΈΡΠ½ΠΎ Π³ΠΈ ΠΊΠΎΡΠΈΡΡΠ°ΠΌ ΡΡΠ°Π½Π΄Π°ΡΠ΄Π½ΠΈΡΠ΅. ΠΠ°Π±Π΅Π»Π΅ΠΆΠ΅ΡΠ΅ Π΄Π΅ΠΊΠ° ΠΊΠΎΠΌΠΏΡΠ΅ΡΠΈΡΠ°ΡΠ° S3 Π½Π΅ Π΅ Π΄ΠΎΡΡΠ°ΠΏΠ½Π°, Π½ΠΎ ORC ΡΡΠ°Π½Π΄Π°ΡΠ΄Π½ΠΎ ΠΊΠΎΡΠΈΡΡΠΈ ΠΌΠ°ΡΡΠΈΠ½ ΠΊΠΎΠΌΠΏΡΠ΅ΡΠΈΡΠ°.
Π’Π΅ΡΠ½ΠΎ
Π‘Π΅Π³Π° ΠΊΠΎΠ³Π° ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠΈΡΠ°Π²ΠΌΠ΅ ΡΠΊΠ»Π°Π΄ΠΈΡΠ°ΡΠ΅ ΠΈ ΠΏΡΠΈΠΌΠ°ΡΠ΅ Π΄Π½Π΅Π²Π½ΠΈΡΠΈ, ΡΡΠ΅Π±Π° Π΄Π° Π³ΠΎ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠΈΡΠ°ΠΌΠ΅ ΠΈΡΠΏΡΠ°ΡΠ°ΡΠ΅ΡΠΎ. ΠΠΈΠ΅ ΡΠ΅ ΠΊΠΎΡΠΈΡΡΠΈΠΌΠ΅
ΠΡΠ²ΠΎ, ΠΏΠΎΡΡΠ΅Π±Π½Π° Π½ΠΈ Π΅ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡΠΊΠ°ΡΠ° Π΄Π°ΡΠΎΡΠ΅ΠΊΠ° 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 ΠΊΠΎΡΠΈΡΡΠ΅ΡΡΠΈ
<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 ΡΠ΅ Π²ΡΠΈΡΠ° ΠΏΠΎΠ΄Π°ΡΠΎΡΠΈ Π·Π° ΡΠΈΡΠ΅ Π²ΡΠ΅ΠΌΠΈΡΠ° Π½Π° ΡΠ΅ΠΊΠΎΠ΅ Π±Π°ΡΠ°ΡΠ΅, ΡΠΎ ΡΠ΅ΡΠΊΠΈ ΠΈΡΠΊΠ»ΡΡΠΎΡΠΈ. ΠΠ²Π° Π΅ Π³ΠΎΠ»Π΅ΠΌ ΠΏΡΠΎΠ±Π»Π΅ΠΌ ΠΎΠ΄ Π΄Π²Π΅ ΠΏΡΠΈΡΠΈΠ½ΠΈ:
- ΠΠ±Π΅ΠΌΠΎΡ Π½Π° ΠΏΠΎΠ΄Π°ΡΠΎΡΠΈ ΠΏΠΎΡΡΠΎΡΠ°Π½ΠΎ ΡΠ°ΡΡΠ΅, Π·Π°Π±Π°Π²ΡΠ²Π°ΡΡΠΈ Π³ΠΈ Π±Π°ΡΠ°ΡΠ°ΡΠ°;
- ΠΡΠΈΠ½Π° ΡΠ΅ Π½Π°ΠΏΠ»Π°ΡΠ° Π²ΡΠ· ΠΎΡΠ½ΠΎΠ²Π° Π½Π° ΠΎΠ±Π΅ΠΌΠΎΡ Π½Π° ΡΠΊΠ΅Π½ΠΈΡΠ°Π½ΠΈ ΠΏΠΎΠ΄Π°ΡΠΎΡΠΈ, ΡΠΎ ΠΌΠΈΠ½ΠΈΠΌΡΠΌ 10 MB ΠΏΠΎ Π±Π°ΡΠ°ΡΠ΅.
ΠΠ° Π΄Π° Π³ΠΎ ΠΏΠΎΠΏΡΠ°Π²ΠΈΠΌΠ΅ ΠΎΠ²Π°, ΠΊΠΎΡΠΈΡΡΠΈΠΌΠ΅ AWS Glue Crawler, ΠΊΠΎΡ ΡΠ΅ Π³ΠΈ ΠΈΠ½Π΄Π΅ΠΊΡΠΈΡΠ° ΠΏΠΎΠ΄Π°ΡΠΎΡΠΈΡΠ΅ Π²ΠΎ S3 ΠΈ ΡΠ΅ Π³ΠΈ Π·Π°ΠΏΠΈΡΠ΅ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈΡΠ΅ Π·Π° ΠΏΠ°ΡΡΠΈΡΠΈΡΠ°ΡΠ° Π²ΠΎ Glue Metastore. ΠΠ²Π° ΡΠ΅ Π½ΠΈ ΠΎΠ²ΠΎΠ·ΠΌΠΎΠΆΠΈ Π΄Π° ΠΊΠΎΡΠΈΡΡΠΈΠΌΠ΅ ΠΏΠ°ΡΡΠΈΡΠΈΠΈ ΠΊΠ°ΠΊΠΎ ΡΠΈΠ»ΡΠ΅Ρ ΠΏΡΠΈ Π±Π°ΡΠ°ΡΠ΅ΡΠΎ Π½Π° Athena ΠΈ ΡΠ΅ Π³ΠΈ ΡΠΊΠ΅Π½ΠΈΡΠ° ΡΠ°ΠΌΠΎ Π΄ΠΈΡΠ΅ΠΊΡΠΎΡΠΈΡΠΌΠΈΡΠ΅ Π½Π°Π²Π΅Π΄Π΅Π½ΠΈ Π²ΠΎ Π±Π°ΡΠ°ΡΠ΅ΡΠΎ.
ΠΠΎΡΡΠ°Π²ΡΠ²Π°ΡΠ΅ Π½Π° Amazon Glue Crawler
Amazon Glue Crawler Π³ΠΈ ΡΠΊΠ΅Π½ΠΈΡΠ° ΡΠΈΡΠ΅ ΠΏΠΎΠ΄Π°ΡΠΎΡΠΈ Π²ΠΎ ΠΊΠΎΡΠΏΠ°ΡΠ° S3 ΠΈ ΡΠΎΠ·Π΄Π°Π²Π° ΡΠ°Π±Π΅Π»ΠΈ ΡΠΎ ΠΏΠ°ΡΡΠΈΡΠΈΠΈ. Π‘ΠΎΠ·Π΄Π°Π΄Π΅ΡΠ΅ Π»Π΅ΠΏΠ°ΠΊ Crawler ΠΎΠ΄ ΠΊΠΎΠ½Π·ΠΎΠ»Π°ΡΠ° AWS 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 Π³ΠΎΠ΄ΠΈΠ½Π°. ΠΠΎ, ΠΊΠΎΠ»ΠΊΡ Π΅ ΠΎΠ²Π° ΠΏΠΎΠ΅ΡΠΈΠΊΠ°ΡΠ½ΠΎ ΠΎΠ΄ ΡΠ°ΠΌΠΎ ΡΠΈΡΠ°ΡΠ΅ ΠΎΠ΄ Π½Π΅ΠΏΠ°ΡΡΠΈΡΠΈΠΎΠ½ΠΈΡΠ°Π½Π° ΡΠ°Π±Π΅Π»Π°? ΠΡΠ΄Π΅ Π΄Π° Π΄ΠΎΠ·Π½Π°Π΅ΠΌΠ΅ ΠΈ Π΄Π° Π³ΠΈ ΠΈΠ·Π±Π΅ΡΠ΅ΠΌΠ΅ ΠΈΡΡΠΈΡΠ΅ Π·Π°ΠΏΠΈΡΠΈ, ΡΠΈΠ»ΡΡΠΈΡΠ°ΡΡΠΈ Π³ΠΈ ΠΏΠΎ Π²ΡΠ΅ΠΌΠ΅Π½ΡΠΊΠΈ ΠΏΠ΅ΡΠ°Ρ:
3.59 ΡΠ΅ΠΊΡΠ½Π΄ΠΈ ΠΈ 244.34 ΠΌΠ΅Π³Π°Π±Π°ΡΡΠΈ ΠΏΠΎΠ΄Π°ΡΠΎΡΠΈ Π½Π° Π±Π°Π·Π° Π½Π° ΠΏΠΎΠ΄Π°ΡΠΎΡΠΈ ΡΠΎ ΡΠ°ΠΌΠΎ Π΅Π΄Π½Π° Π½Π΅Π΄Π΅Π»Π° Π΄Π½Π΅Π²Π½ΠΈΡΠΈ. ΠΡΠ΄Π΅ Π΄Π° ΠΏΡΠΎΠ±Π°ΠΌΠ΅ ΡΠΈΠ»ΡΠ΅Ρ ΠΏΠΎ ΠΏΠ°ΡΡΠΈΡΠΈΡΠ°:
ΠΠ°Π»ΠΊΡ ΠΏΠΎΠ±ΡΠ·ΠΎ, Π½ΠΎ Π½Π°ΡΠ²Π°ΠΆΠ½ΠΎ - ΡΠ°ΠΌΠΎ 1.23 ΠΌΠ΅Π³Π°Π±Π°ΡΡΠΈ ΠΏΠΎΠ΄Π°ΡΠΎΡΠΈ! Π’ΠΎΠ° Π±ΠΈ Π±ΠΈΠ»ΠΎ ΠΌΠ½ΠΎΠ³Ρ ΠΏΠΎΠ΅Π²ΡΠΈΠ½ΠΎ Π°ΠΊΠΎ Π½Π΅ Π·Π° ΠΌΠΈΠ½ΠΈΠΌΠ°Π»Π½ΠΈΡΠ΅ 10 ΠΌΠ΅Π³Π°Π±Π°ΡΡΠΈ ΠΏΠΎ Π±Π°ΡΠ°ΡΠ΅ Π²ΠΎ ΡΠ΅Π½Π°ΡΠ°. ΠΠΎ, ΡΠ΅ΠΏΠ°ΠΊ Π΅ ΠΌΠ½ΠΎΠ³Ρ ΠΏΠΎΠ΄ΠΎΠ±ΡΠΎ, Π° ΠΊΠ°Ρ Π³ΠΎΠ»Π΅ΠΌΠΈΡΠ΅ Π·Π±ΠΈΡΠΊΠΈ Π½Π° ΠΏΠΎΠ΄Π°ΡΠΎΡΠΈ ΡΠ°Π·Π»ΠΈΠΊΠ°ΡΠ° ΡΠ΅ Π±ΠΈΠ΄Π΅ ΠΌΠ½ΠΎΠ³Ρ ΠΏΠΎΠΈΠΌΠΏΡΠ΅ΡΠΈΠ²Π½Π°.
ΠΠ·Π³ΡΠ°Π΄Π±Π° Π½Π° ΠΊΠΎΠ½ΡΡΠΎΠ»Π½Π° ΡΠ°Π±Π»Π° ΠΊΠΎΡΠΈΡΡΠ΅ΡΡΠΈ Cube.js
ΠΠ° Π΄Π° ΡΠ° ΡΠΎΡΡΠ°Π²ΠΈΠΌΠ΅ ΠΊΠΎΠ½ΡΡΠΎΠ»Π½Π°ΡΠ° ΡΠ°Π±Π»Π°, ΡΠ° ΠΊΠΎΡΠΈΡΡΠΈΠΌΠ΅ Π°Π½Π°Π»ΠΈΡΠΈΡΠΊΠ°ΡΠ° ΡΠ°ΠΌΠΊΠ° Cube.js. ΠΠΌΠ° Π΄ΠΎΡΡΠ° ΡΡΠ½ΠΊΡΠΈΠΈ, Π½ΠΎ Π½ΠΈΠ΅ ΡΠΌΠ΅ Π·Π°ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠΈΡΠ°Π½ΠΈ Π·Π° Π΄Π²Π΅: ΡΠΏΠΎΡΠΎΠ±Π½ΠΎΡΡ Π·Π° Π°Π²ΡΠΎΠΌΠ°ΡΡΠΊΠΎ ΠΊΠΎΡΠΈΡΡΠ΅ΡΠ΅ Π½Π° ΡΠΈΠ»ΡΡΠΈ Π·Π° ΠΏΠ°ΡΡΠΈΡΠΈΠΈ ΠΈ ΠΏΡΠ΅Π΄-Π°Π³ΡΠ΅Π³Π°ΡΠΈΡΠ° Π½Π° ΠΏΠΎΠ΄Π°ΡΠΎΡΠΈ. Π’Π°Π° ΠΊΠΎΡΠΈΡΡΠΈ ΠΏΠΎΠ΄Π°ΡΠΎΡΠ½Π° ΡΠ΅ΠΌΠ°
ΠΡΠ΄Π΅ Π΄Π° ΡΠΎΠ·Π΄Π°Π΄Π΅ΠΌΠ΅ Π½ΠΎΠ²Π° Π°ΠΏΠ»ΠΈΠΊΠ°ΡΠΈΡΠ° 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`
}
}
});
ΠΠ²Π΄Π΅ ΡΠ° ΠΊΠΎΡΠΈΡΡΠΈΠΌΠ΅ ΠΏΡΠΎΠΌΠ΅Π½Π»ΠΈΠ²Π°ΡΠ°
ΠΠΈ ΠΏΠΎΡΡΠ°Π²ΡΠ²Π°ΠΌΠ΅ ΠΈ ΠΌΠ΅ΡΡΠΈΠΊΠΈΡΠ΅ ΠΈ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΈΡΠ΅ ΡΡΠΎ ΡΠ°ΠΊΠ°ΠΌΠ΅ Π΄Π° Π³ΠΈ ΠΏΡΠΈΠΊΠ°ΠΆΠ΅ΠΌΠ΅ Π½Π° ΠΊΠΎΠ½ΡΡΠΎΠ»Π½Π°ΡΠ° ΡΠ°Π±Π»Π° ΠΈ Π³ΠΈ ΡΠΏΠ΅ΡΠΈΡΠΈΡΠΈΡΠ°ΠΌΠ΅ ΠΏΡΠ΅Π΄-Π°Π³ΡΠ΅Π³Π°ΡΠΈΠΈΡΠ΅. 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 backend ΠΎΠ±Π΅Π·Π±Π΅Π΄ΡΠ²Π°
Π‘Π΅ΡΠ²Π΅ΡΠΎΡ Cube.js Π³ΠΎ ΠΏΡΠΈΡΠ°ΡΠ° Π±Π°ΡΠ°ΡΠ΅ΡΠΎ Π²ΠΎ
{
"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>
);
}}
/>
)
}
ΠΠ·Π²ΠΎΡΠΈΡΠ΅ Π½Π° ΠΊΠΎΠ½ΡΡΠΎΠ»Π½Π°ΡΠ° ΡΠ°Π±Π»Π° ΡΠ΅ Π΄ΠΎΡΡΠ°ΠΏΠ½ΠΈ Π½Π°
ΠΠ·Π²ΠΎΡ: www.habr.com