Slide 1

Slide 1 text

‹#› Ingest Node is powered by (re)indexing and enriching documents within Elasticsearch David Pilato Developer | Evangelist @dadoonet

Slide 2

Slide 2 text

2

Slide 3

Slide 3 text

Elastic Subscriptions: Product, Experience, & Support 3 Open Source Elasticsearch Kibana Logstash Beats Elastic Stack Expertise and Support Elasticsearch as a Service (Found) Development Production Plugins Security (Shield) Alerting (Watcher) Monitoring (Marvel) Technical Guidance • Architecture (hardware/software) • Cluster management (tuning) • Index / shard design • Query optimization • Integration with other products • Backup and HA strategy • Dev to production migration / upgrades • Best practices Troubleshooting & Support • Dedicated, hands-on SLA-based support • Analysis of internal logs • Proactively monitoring of clusters • Escalation to engineering team

Slide 4

Slide 4 text

‹#› Why ingest node?

Slide 5

Slide 5 text

‹#› I just want to tail a file.

Slide 6

Slide 6 text

Logstash: collect, enrich & transport 6 grok date mutate input output Filters The file Elasticsearch

Slide 7

Slide 7 text

Logstash common setup 7 127.0.0.1 - - [19/Apr/2016:12:00:00 +0200] "GET /robots.txt HTTP/1.1" 200 68 127.0.0.1 - - [19/Apr/2016:12:00:01 +0200] "GET /cgi-bin/try/ HTTP/1.1" 200 3395 127.0.0.1 - - [19/Apr/2016:12:00:04 +0200] "GET / HTTP/1.1" 200 24 127.0.0.1 - - [19/Apr/2016:12:00:07 +0200] "GET /not_found/ HTTP/1.1" 404 7218 127.0.0.1 - - [19/Apr/2016:12:00:09 +2000] "GET /favicon.ico HTTP/1.1" 200 3638 127.0.0.1 - - [19/Apr/2016:12:00:15 +0200] "GET / HTTP/1.1" 200 24 127.0.0.1 - - [19/Apr/2016:12:00:18 +2000] "GET /favicon.ico HTTP/1.1" 200 3638 127.0.0.1 - - [19/Apr/2016:12:00:00 +0200] "GET /robots.txt HTTP/1.1" 200 68 127.0.0.1 - - [19/Apr/2016:12:00:01 +0200] "GET /cgi-bin/try/ HTTP/1.1" 200 3395 127.0.0.1 - - [19/Apr/2016:12:00:04 +0200] "GET / HTTP/1.1" 200 24 127.0.0.1 - - [19/Apr/2016:12:00:07 +0200] "GET /not_found/ HTTP/1.1" 404 7218 127.0.0.1 - - [19/Apr/2016:12:00:09 +2000] "GET /favicon.ico HTTP/1.1" 200 3638 127.0.0.1 - - [19/Apr/2016:12:00:15 +0200] "GET / HTTP/1.1" 200 24 127.0.0.1 - - [19/Apr/2016:12:00:18 +2000] "GET /favicon.ico HTTP/1.1" 200 3638 127.0.0.1 - - [19/Apr/2016:12:00:00 +0200] "GET /robots.txt HTTP/1.1" 200 68 127.0.0.1 - - [19/Apr/2016:12:00:04 +0200] "GET / HTTP/1.1" 200 24 127.0.0.1 - - [19/Apr/2016:12:00:07 +0200] "GET /not_found/ HTTP/1.1" 404 7218 127.0.0.1 - - [19/Apr/2016:12:00:09 +2000] "GET /favicon.ico HTTP/1.1" 200 3638 127.0.0.1 - - [19/Apr/2016:12:00:15 +0200] "GET / HTTP/1.1" 200 24 message

Slide 8

Slide 8 text

Ingest node setup 8 127.0.0.1 - - [19/Apr/2016:12:00:00 +0200] "GET /robots.txt HTTP/1.1" 200 68 127.0.0.1 - - [19/Apr/2016:12:00:01 +0200] "GET /cgi-bin/try/ HTTP/1.1" 200 3395 127.0.0.1 - - [19/Apr/2016:12:00:04 +0200] "GET / HTTP/1.1" 200 24 127.0.0.1 - - [19/Apr/2016:12:00:07 +0200] "GET /not_found/ HTTP/1.1" 404 7218 127.0.0.1 - - [19/Apr/2016:12:00:09 +2000] "GET /favicon.ico HTTP/1.1" 200 3638 127.0.0.1 - - [19/Apr/2016:12:00:15 +0200] "GET / HTTP/1.1" 200 24 127.0.0.1 - - [19/Apr/2016:12:00:18 +2000] "GET /favicon.ico HTTP/1.1" 200 3638 127.0.0.1 - - [19/Apr/2016:12:00:00 +0200] "GET /robots.txt HTTP/1.1" 200 68 127.0.0.1 - - [19/Apr/2016:12:00:01 +0200] "GET /cgi-bin/try/ HTTP/1.1" 200 3395 127.0.0.1 - - [19/Apr/2016:12:00:04 +0200] "GET / HTTP/1.1" 200 24 127.0.0.1 - - [19/Apr/2016:12:00:07 +0200] "GET /not_found/ HTTP/1.1" 404 7218 127.0.0.1 - - [19/Apr/2016:12:00:09 +2000] "GET /favicon.ico HTTP/1.1" 200 3638 127.0.0.1 - - [19/Apr/2016:12:00:15 +0200] "GET / HTTP/1.1" 200 24 127.0.0.1 - - [19/Apr/2016:12:00:18 +2000] "GET /favicon.ico HTTP/1.1" 200 3638 127.0.0.1 - - [19/Apr/2016:12:00:00 +0200] "GET /robots.txt HTTP/1.1" 200 68 127.0.0.1 - - [19/Apr/2016:12:00:04 +0200] "GET / HTTP/1.1" 200 24 127.0.0.1 - - [19/Apr/2016:12:00:07 +0200] "GET /not_found/ HTTP/1.1" 404 7218 127.0.0.1 - - [19/Apr/2016:12:00:09 +2000] "GET /favicon.ico HTTP/1.1" 200 3638 127.0.0.1 - - [19/Apr/2016:12:00:15 +0200] "GET / HTTP/1.1" 200 24

Slide 9

Slide 9 text

Filebeat: collect and ship 9 127.0.0.1 - - [19/Apr/2016:12:00:04 +0200] "GET / HTTP/1.1" 200 24 127.0.0.1 - - [19/Apr/2016:12:00:07 +0200] "GET /not_found/ HTTP/1.1" 404 7218 127.0.0.1 - - [19/Apr/2016:12:00:09 +2000] "GET /favicon.ico HTTP/1.1" 200 3638 { "message" : "127.0.0.1 - - [19/Apr/2016:12:00:04 +0200] \"GET / HTTP/1.1\" 200 24" } { "message" : "127.0.0.1 - - [19/Apr/2016:12:00:07 +0200] \"GET /not_found/ HTTP/1.1\" 404 7218" } { "message" : "127.0.0.1 - - [19/Apr/2016:12:00:09 +2000] \"GET /favicon.ico HTTP/1.1\" 200 3638" }

Slide 10

Slide 10 text

Elasticsearch: enrich and index 10 { "message" : "127.0.0.1 - - [19/Apr/2016:12:00:04 +0200] \"GET / HTTP/1.1\" 200 24" } { "request" : "/", "auth" : "-", "ident" : "-", "verb" : "GET", "@timestamp" : "2016-04-19T10:00:04.000Z", "response" : "200", "bytes" : "24", "clientip" : "127.0.0.1", "httpversion" : "1.1", "rawrequest" : null, "timestamp" : "19/Apr/2016:12:00:04 +0200" }

Slide 11

Slide 11 text

‹#› How does ingest node work?

Slide 12

Slide 12 text

Ingest pipeline 12 Pipeline: a set of processors grok date remove document enriched document

Slide 13

Slide 13 text

Define a pipeline PUT /_ingest/pipeline/apache-log { "processors" : [ { "grok" : { "field": "message", "pattern": "%{COMMONAPACHELOG}" } }, { "date" : { "match_field" : "timestamp", "match_formats" : ["dd/MMM/YYYY:HH:mm:ss Z"] } }, { "remove" : { "field" : "message" } } ] } 13

Slide 14

Slide 14 text

Index a document Provide the id of the pipeline to execute PUT /logs/apache/1?pipeline=apache-log { "message" : "127.0.0.1 - - [19/Apr/2016:12:00:04 +0200] \"GET / HTTP/1.1\" 200 24" } 14

Slide 15

Slide 15 text

GET /logs/apache/1 { "request" : "/", "auth" : "-", "ident" : "-", "verb" : "GET", "@timestamp" : "2016-04-19T10:00:04.000Z", "response" : "200", "bytes" : "24", "clientip" : "127.0.0.1", "httpversion" : "1.1", "rawrequest" : null, "timestamp" : "19/Apr/2016:12:00:04 +0200" } What has actually been indexed 15

Slide 16

Slide 16 text

PUT /_ingest/pipeline/apache-log { … } GET /_ingest/pipeline/apache-log GET /_ingest/pipeline/* DELETE /_ingest/pipeline/apache-log Pipeline management Create, Read, Update & Delete 16

Slide 17

Slide 17 text

17 grok remove attachment convert uppercase foreach trim append gsub set split fail geoip join lowercase rename date

Slide 18

Slide 18 text

Extracts structured fields out of a single text field 18 Grok processor { "grok": { "field": "message", "pattern": "%{DATE:date}" } }

Slide 19

Slide 19 text

set, remove, rename, convert, gsub, split, join, lowercase, uppercase, trim, append 19 Mutate processors { "remove": { "field": "message" } }

Slide 20

Slide 20 text

Parses a date from a string 20 Date processor { "date": { "field": "timestamp", "match_formats": ["YYYY"] } }

Slide 21

Slide 21 text

Adds information about the geographical location of IP addresses 21 Geoip processor { "geoip": { "field": "ip" } }

Slide 22

Slide 22 text

Do something for every element of an array 22 Foreach processor { "foreach": { "field" : "values", "processors" : [ { "uppercase" : { "field" : "_value" } } ] } }

Slide 23

Slide 23 text

Raises an exception with a configurable message 23 Fail processor { "fail": { "message": "custom error" } }

Slide 24

Slide 24 text

Introducing new processors is as easy as writing a plugin 24 Plugins { "your_plugin": { … } }

Slide 25

Slide 25 text

‹#› Error handling

Slide 26

Slide 26 text

26 grok date remove { "message" : "127.0.0.1 - - [19/Apr/2016:12:00:00 +040] \"GET / HTTP/1.1\" 200 24" }

Slide 27

Slide 27 text

27 grok date remove { "message" : "127.0.0.1 - - [19/Apr/2016:12:00:00 +040] \"GET / HTTP/1.1\" 200 24" } 400 Bad Request unable to parse date [19/Apr/2016:12:00:00 +040]

Slide 28

Slide 28 text

28 { "message" : "127.0.0.1 - - [19/Apr/2016:12:00:00 +040] \"GET / HTTP/1.1\" 200 24" } grok date remove set on failure processors at the pipeline level

Slide 29

Slide 29 text

29 { "message" : "127.0.0.1 - - [19/Apr/2016:12:00:00 +040] \"GET / HTTP/1.1\" 200 24" } remove 200 OK grok date set on failure processors at the pipeline level

Slide 30

Slide 30 text

30 { "message" : "127.0.0.1 - - [19/Apr/2016:12:00:00 +040] \"GET / HTTP/1.1\" 200 24" } grok date remove set on failure processors at the processor level remove

Slide 31

Slide 31 text

31 { "message" : "127.0.0.1 - - [19/Apr/2016:12:00:00 +040] \"GET / HTTP/1.1\" 200 24" } grok date remove set remove 200 OK on failure processors at the processor level

Slide 32

Slide 32 text

‹#› Ingest node internals

Slide 33

Slide 33 text

cluster Default scenario 33 Client node1 logs 2P logs 3R CS node2 logs 3P logs 1R CS node3 logs 1P logs 2R CS Cluster State logs index: 3 primary shards, 1 replica each All nodes are equal: - node.data: true - node.master: true - node.ingest: true

Slide 34

Slide 34 text

cluster Default scenario 34 Client node1 logs 2P logs 3R CS node2 logs 3P logs 1R CS node3 logs 1P logs 2R CS Pre-processing on the coordinating node All nodes are equal: - node.data: true - node.master: true - node.ingest: true index request for shard 3

Slide 35

Slide 35 text

cluster Default scenario 35 Client node1 logs 2P logs 3R CS node2 logs 3P logs 1R CS node3 logs 1P logs 2R CS Indexing on the primary shard All nodes are equal: - node.data: true - node.master: true - node.ingest: true index request for shard 3

Slide 36

Slide 36 text

cluster Default scenario 36 Client node1 logs 2P logs 3R CS node2 logs 3P logs 1R CS node3 logs 1P logs 2R CS Indexing on the replica shard All nodes are equal: - node.data: true - node.master: true - node.ingest: true index request for shard 3

Slide 37

Slide 37 text

cluster Ingest dedicated nodes 37 Client node1 logs 2P logs 3R CS node2 logs 3P logs 1R CS node3 logs 1P logs 2R CS node4 CS node5 CS node.data: false node.master: false node.ingest: true node.data: true node.master: true node.ingest: false

Slide 38

Slide 38 text

cluster Ingest dedicated nodes 38 Client node1 logs 2P logs 3R CS node2 logs 3P logs 1R CS node3 logs 1P logs 2R CS node4 CS node5 CS index request for shard 3 Forward request to an ingest node

Slide 39

Slide 39 text

cluster Ingest dedicated nodes 39 Client node1 logs 2P logs 3R CS node2 logs 3P logs 1R CS node3 logs 1P logs 2R CS node4 CS node5 CS index request for shard 3 Pre-processing on the ingest node

Slide 40

Slide 40 text

cluster Ingest dedicated nodes 40 Client node1 logs 2P logs 3R CS node2 logs 3P logs 1R CS node3 logs 1P logs 2R CS node4 CS node5 CS index request for shard 3 Indexing on the primary shard

Slide 41

Slide 41 text

cluster Ingest dedicated nodes 41 Client node1 logs 2P logs 3R CS node2 logs 3P logs 1R CS node3 logs 1P logs 2R CS node4 CS node5 CS index request for shard 3 Indexing on the replica shard

Slide 42

Slide 42 text

‹#› Where can ingest pipelines be used?

Slide 43

Slide 43 text

43 Index api PUT /logs/apache/1?pipeline=apache-log { "message" : "…" }

Slide 44

Slide 44 text

44 Bulk api PUT /logs/_bulk { "index": { "_type": "apache", "_id": "1", "pipeline": "apache-log" } }\n { "message" : "…" }\n { "index": {"_type": "mysql", "_id": "1", "pipeline": "mysql-log" } }\n { "message" : "…" }\n

Slide 45

Slide 45 text

Scan/scroll & bulk indexing made easy 45 Reindex api POST /_reindex { "source": { "index": "logs", "type": "apache" }, "dest": { "index": "apache-logs", "pipeline" : "apache-log" } }

Slide 46

Slide 46 text

‹#› From postal address to geo_point From geo_point to postal address bano-ingest plugin

Slide 47

Slide 47 text

What is BANO? • French Open Data base for postal addresses • http://openstreetmap.fr/bano • http://bano.openstreetmap.fr/data/ 47 per department all addresses

Slide 48

Slide 48 text

BANO Format 48 • bano-976.csv sample (full.csv.gz has same format) 976030950H-26,26,RUE DISMA,97660,Bandrélé,CAD,-12.891701,45.202652
 976030950H-28,28,RUE DISMA,97660,Bandrélé,CAD,-12.891900,45.202700
 976030950H-30,30,RUE DISMA,97660,Bandrélé,CAD,-12.891781,45.202535
 976030950H-32,32,RUE DISMA,97660,Bandrélé,CAD,-12.892005,45.202564
 976030950H-3,3,RUE DISMA,97660,Bandrélé,CAD,-12.892444,45.202135
 976030950H-34,34,RUE DISMA,97660,Bandrélé,CAD,-12.892068,45.202450
 976030950H-4,4,RUE DISMA,97660,Bandrélé,CAD,-12.892446,45.202367
 976030950H-5,5,RUE DISMA,97660,Bandrélé,CAD,-12.892461,45.202248
 976030950H-6,6,RUE DISMA,97660,Bandrélé,CAD,-12.892383,45.202456
 976030950H-8,8,RUE DISMA,97660,Bandrélé,CAD,-12.892300,45.202555
 976030950H-9,9,RUE DISMA,97660,Bandrélé,CAD,-12.892355,45.202387 976030951J-103,103,RTE NATIONALE 3,97660,Bandrélé,CAD,-12.893639,45.201696 \_ ID | \_ Street Name | \ \_ Source \_ Geo point | | \ |_ Street Number |_ Zipcode \_ City Name

Slide 49

Slide 49 text

Features • Download, transform and index BANO datasource • Create a new ingest processor 49 curl -XPUT 127.0.0.1:9200/_bano/17 curl -XPUT 127.0.0.1:9200/_bano/17,95,29 curl -XPUT 127.0.0.1:9200/_bano/_full curl -XPUT "localhost:9200/_ingest/pipeline/bano-test?pretty" -d '{ "description": "my_pipeline", "processors": [ { "bano": {} } ] }'

Slide 50

Slide 50 text

From structured address (french format)… 50 curl -XPOST "localhost:9200/_ingest/pipeline/bano-test/_simulate?pretty" -d '{ "docs": [ { "_index": "index", "_type": "type", "_id": "id", "_source": { "address": { "number": "25", "street_name": "georges", "zipcode": "17440", "city": "Aytré" } } } ] }'

Slide 51

Slide 51 text

To normalized address with coordinates… 51 "doc" : {
 "_source" : {
 "address" : { "zipcode" : "17440",
 "number" : "25",
 "city" : "Aytré",
 "street_name" : "georges" },
 "bano_address" : {
 "zipcode" : "17440",
 "number" : "25",
 "city" : "Aytré", "street_name" : "Boulevard Georges Clemenceau",
 "full_address" : "25, Boulevard Georges Clemenceau 17440 Aytré",
 "location" : {
 "lon" : -1.122966,
 "lat" : 46.130368
 }
 }
 }
 }

Slide 52

Slide 52 text

From a geo point… 52 curl -XPOST "localhost:9200/_ingest/pipeline/bano-test/_simulate?pretty" -d '{ "docs": [ { "_index": "index", "_type": "type", "_id": "id", "_source": { "location": { "lat": 46.135283, "lon": -1.113750 } } } ] }'

Slide 53

Slide 53 text

To the closest full address… 53 "doc" : {
 "_source" : {
 "location" : {
 "lon" : -1.11375,
 "lat" : 46.135283
 },
 "bano_address" : {
 "zipcode" : "17440",
 "number" : "1",
 "city" : "Aytré",
 "street_name" : "Rue du Petit Versailles",
 "full_address" : "1, Rue du Petit Versailles 17440 Aytré",
 "location" : {
 "lon" : -1.113564,
 "lat" : 46.135343
 }
 }
 }
 }
 }

Slide 54

Slide 54 text

Combine with other ingest processors 54 curl -XPUT "localhost:9200/_ingest/pipeline/bano-test-4?pretty&verbose" -d '{
 "description": "debug",
 "processors": [ {
 "geoip" : {
 "field" : "ip"
 }
 }, {
 "bano": {
 "location_lat_field": "geoip.location.lat",
 "location_lon_field": "geoip.location.lon"
 }
 } ]
 }'

Slide 55

Slide 55 text

From an IP address… 55 curl -XPOST "localhost:9200/_ingest/pipeline/bano-test-4/_simulate?pretty" -d '{
 "docs": [ {
 "_index": "index",
 "_type": "type",
 "_id": "id",
 "_source": {
 "ip" : "82.229.80.187"
 }
 } ]
 }'

Slide 56

Slide 56 text

To the closest full address… 56 "doc" : {
 "_source" : {
 "ip" : "82.229.80.187",
 "geoip" : {
 "continent_name" : "Europe", "city_name" : "Cergy",
 "country_iso_code" : "FR", "region_name" : "Val d'Oise",
 "location" : { "lon" : 2.0761, "lat" : 49.0364 }
 },
 "bano_address" : {
 "zipcode" : "95000",
 "number" : "3",
 "city" : "Cergy",
 "location" : {
 "lon" : 2.075687,
 "lat" : 49.037202
 },
 "full_address" : "3, Avenue des Trois Fontaines 95000 Cergy",
 "street_name" : "Avenue des Trois Fontaines"
 }
 }
 }

Slide 57

Slide 57 text

Other features • GET _bano/xx endpoint to get information about existing BANO data • source / destination fields can be changed • data can be stored within another cluster • data can come from another URL (ie. local files) • Bano plugin uses aliases behind the scene 57

Slide 58

Slide 58 text

Writing an ingest plugin 58 public class IngestBanoPlugin extends Plugin { 
 public void onModule(NodeModule nodeModule) throws IOException {
 nodeModule.registerProcessor("bano",
 (templateService, registry) -> new BanoProcessor.Factory());
 } }

Slide 59

Slide 59 text

Writing the processor factory 59 public static final class Factory extends AbstractProcessorFactory { @Override
 public BanoProcessor doCreate(String processorTag, Map config) { // Read the bano processor config
 String cityField = readStringProperty("bano", processorTag, config, "city_field", // We read here the value of "city_field" in config "address.city"); // If not set we will read from "address.city" by default // Do the same for other fields // Create the processor instance return new BanoProcessor(field1, field2, cityField, ...); }


Slide 60

Slide 60 text

Writing the processor 60 public final class BanoProcessor extends AbstractProcessor { @Override
 public void execute(IngestDocument ingestDocument) {
 // Implement your logic code here if (ingestDocument.hasField(cityField)) { String city = ingestDocument.getFieldValue(cityField, String.class) // Like searching in elasticsearch with a city field Location location = banoEsClient.searchByCity(city); // Then modify the document as you wish Map locationObject = new HashMap<>();
 locationObject.put("lat", location.getLat());
 locationObject.put("lon", location.getLon());
 ingestDocument.setFieldValue("location", locationObject); } } }

Slide 61

Slide 61 text

‹#› Demo time!

Slide 62

Slide 62 text

‹#› https://www.elastic.co/downloads/elasticsearch Go get Elasticsearch 5.0.0-alpha3!

Slide 63

Slide 63 text

‹#› Watch this space: https://github.com/dadoonet And follow me on Twitter! Bano ingest plugin David Pilato Developer | Evangelist @dadoonet