7,540
edits
Changes
→Swarm stack
=Bevezető=
A cél az, hogy minden a swarm cluster-ben futó konténer logját összegyűjtsük egy központi, nagyon hatékonyan kereshető adatbázisban, ami az Elasticsearch lesz. Az Elastichserach-be a szintén Elastic termékkel, a logStash-el fogjuk betölteni az adatokat. LogStash-ből is csak egy darabra van szükség, nem kell minden node-on fusson egy konténer belőle. A Logstash képes a sysout-ot átalakítani Elastichsearch dokumentumokra. A logStash-hez a logspout nevű program fogja elküldeni a sysout-ot minden node minden docker image-éből. A logspout rákapcsolódik a docker szoket-re, majd log proxiként továbbít minden logot a logStash konténernek. Értelem szerűen a logspout-ot minden egyes konténerre fel kell telepíteni.
:[[File:ClipCapIt-180930-152409.PNG]]
A logokat a szintén Elastic termékkel, a Kibana-val fogjuk vizualizálni, elemezni. Az előbb felsorolt 4 komponens az erre a célra létrehozott elk nevű overlay hálózaton fog egymással kommunikálni, ahol közvetlen el fogják egymást érni az overlay hálózatos IP címükkel. Mind az Elastichsearch, mind a Kibana webkonzol portját publikálni fogjuk az ingress hálózaton, hogy elérjük őket a böngészőből.
<br>
==ElasticSearch==
https://www.elastic.co/products/elasticsearch<br>
# '''Real-time Application Monitoring''' - Capture activity logs across your customer-facing applications and websites. Use Logstash to push these logs to your Elasticsearch cluster. Elasticsearch indexes the data and makes it available for analysis in near real-time (less than one second). You can then use Kibana to visualize the data and perform operational analyses like identifying outages and problems. With Elasticsearch’s geospatial analysis, you can identify the geographical region where the problem is occurring. Troubleshooting teams can then search the index and perform statistical aggregations to identify root cause and fix issues.
{{note|Nincs benne valódi tranzakció kezelés, ugyan vannak rá módszerek, hogy valami hasonlót meg lehessen valósítani}}
<br>
==Logstash==
A '''Logstash''' nem más mint egy log konverter. A LogStash-t fogjuk a logok betöltésére használni az Elasticsarch-be. Csak egyetlen egy példányt kell futtatni belőle az egész cluster-ben. A Logsput-tól meg fogja kapni a docker konténerek system logját, amit konvertálni fog Elasticsarch formátumra, aztán el fogja küldeni az Elastaticsearch-nek.
* Document -> Row
* Properties -> Column
Az én értésem szerint a típus bármilyen szó lehet, ez csak particionálja az adott indexbe behelyezett dokumentumokat. Pl: '''mycomany/accounting''' esetében a '''mycompany''' az index és az '''account'''-ing egy típus, amibe olyan dokumentumokat fogok rakni, amik az accounting témakörbe tartoznak. Keresésnél szintén meg lehet adni, hogy melyik típuson belül keresek.
$ curl -XGET localhost:9200/logs-2013-02-22,logs-2013-02-21/Errors/_search?query="q:Error Message"
</pre>
==Shards & Replicasedit & Routing==
===Alapok===
Az Elasticseach-ben minden index (adatbázis) fel van osztva kisebb tárolási egységekre, shard-okra (szilánkokra). Ez elsősorban arra szolgál, hogy ne ütközzünk bele egy node hardware korlátaiba lemezterület vagy cpu sebesség tekintetében, ezért az indexünket particionáljuk több node között. (Persze ha csak egy node-unk van, ez nem túl érdekes). Ezen felül minden shard-nak van 0 vagy több replikája, ami tökéletes másolata az adott shard-nak (szilánknak). Az írás mindig a primary shard-ra történik, de a keresés már párhuzamosítható a primary shard és a replikái között, ezzel nagyon felgyorsítva a keresést. Ezen felül a replikák failover kezelésre is szolgálnak, ha a primary shard-et tároló node meghal, akkor a replika lép a helyére. Fontos kikötés, hogy a replica shard-ok nem hozhatók létre ugyan azon a "fizikai" node-on ahol a primary van.
A shard-ok és replikák számát az index létrehozásakor kell megadni. Ha erről külön nem rendelkezünk, akkor alapértelmezetten 5 shard-et fog létrehozni minden indexhez és 1 replikált minden egyes shard-hez tehát összesen 10 shard-et, amiből 5 primary. Ha csak 1 darab node-unk van, akkor a replica shard-eket nem fogja tudni létrehozni az Elasticsarch, így az index állapota mindig "yellow" lesz. Ha már legalább két node-unk van, akkor akkor az 5 primary shard-et az egyik node-ra fogja rakni, míg az 5 replikát a másikra.
:[[File:ClipCapIt-180923-111738.PNG|500px]]
A fenti ábrán egy 3 node-os cluster-t láthatunk, amiben két index van összesen. Az '''order''' index úgy lett létrehozva, hogy 4 primary (tele zöld) és 4 replica (zöld keret) shard-ből álljon. Tehát 4 felé van particionálva az order index, alapértelmezetten az Elsaticsearch dönti el, hogy egy új dokumentum melyik shard-re kerüljön, ez a user számára átlátszó. Az order index-ben ezen felül minden pirmary shard-nek egy replikája van mindig másik node-on mint ahol a primary van.
Ezen felül a product nevű index úgy lett létrehozva, hogy csak kettő darab primary shard -et használjon replikák nélkül.
===Shard-ek definiálása===
Ha nem az alapértelmezett shard számot akarjuk használni (5 primary és 1-1 replica mindegyikhez) akkor a shard és replica számot az index létrehozásánál kell megadni. Az alábbi példában létre fogunk hozni egy '''mycompany''' nevű indexet 3 primary és 2-2 replica shard-al minden primary-hez így összesen 3 + 3*2 = 9 shard-unk lesz. .
<pre>
curl -XPUT "192.168.123.71:9200/mycompany" -H 'Content-Type: application/json' -d'
{
"settings" : {
"index" : {
"number_of_shards" : 3,
"number_of_replicas" : 2
}
}
}
'
</pre>
Ha most lekérdezzük az indexek listáját, láthatjuk, hogy 3 shard-el létrejött a mycompnay inevű indexünk.
<pre>
$ curl -XGET "192.168.123.71:9200/_cat/indices?v"
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open mycompany XhbjouKWSAeSgcBEkPzlkA 3 2 0 0 690b 690b
</pre>
{{note|Nagyon fontos, hogy előre kitaláljuk, fogalma sincs mi alapján, hogy hány shard-re és replikára lesz szükségünk az optimális teljesítményre való tekintettel. Később ezen nem ajánlatos változtatni, tehát ha nem megfelelő az alapértelmezett 5 master és 5 replica, akkor az index létrehozásánál meg kell adni ezeket}}
===Routing vs custom Routing===
https://www.elastic.co/blog/customizing-your-document-routing<br>
A routing az Elasticsearch világában azt jelenti, hogy melyik (primary) shard-re fog kerülni a dokumentum amit beszúrunk. Ha külön nem adjuk meg, akkor ez kvázi véletlen szerű lesz. A shard ID meghatározásához, vagyis hogy melyik shard-re kerüljön a beszúrandó dokumentum, a következő formulát használja az Elasticsearch:
shard_num = hash('''_routing''') % num_primary_shards
A modulo osztás biztosítja, hogy mindig egy valid shard számot kapjunk.
Ha ezt külön nem adjuk meg, akkor a dokumentum ID -ja lesz a '''_routing''' változó értéke. A dokumentum ID mezőjét vagy mi adjuk meg a dokumentum beszúrásakor a '''_ID''' paraméterrel, vagy az Elasticsearch-re bízzuk, hogy vegye elő a következő szabad ID-t. A document id -> routing id behelyettesítés biztosítja, hogy egyenletesen legyenek elosztva a dokumentumok az összes shard között, a keresés és a beszúrás is teljesen átlátszó a felhasználók számára shard szempontból.
Mikor rákeresünk egy dokumentumra, alapesetben az Elasticsearch-nek fogalma sincs, hogy melyik shard-ban van a dokumentum, így kiküld egy broadcast üzenetet az összes node-nak, akik ezt továbbítják párhuzamosan az összes shard-re, majd minden shard visszaküldi a keresés eredményét a gateway node-nak. Ez gyors, viszont nagyon erőforrás intenzív. A dokumentum mindig csak 1 darab primary shard-en van. Ha 20 primary shard-et használ az index, akkor ez 19 darab "felesleges" keresés.
Lehetőség van rá, hogy egyrészt beszúráskor megmondjuk az Elasticsearch-nek hogy melyik shard-re tegye a dokumentumot, illetve keresésnél megmondhatjuk, hogy melyiken keresse, ezzel eliminálhatjuk a felesleges kereséseket. Ezt hívják custom routing-nak.
Ha elhatároztuk hogy egy egész indexre, vagy típusra custom routing-ot akarunk használni, akkor best-practice ha beállítjuk hogy csak olyan dokumentumokat fogadjon el az Elasticsearch az adott indexbe/típusba, ahol meg van adva a custom routing.
A az előbb létrehozott '''mycompany''' index-be hozzunk létre egy '''order''' típust, amin beállítjuk, hogy csak custom routing-al megadott dokumentumokat lehet beleszúrni.
<pre>
curl -XPUT '192.168.123.71:9200/mycompany/order/_mapping' -H 'Content-Type: application/json' -d'
{
"order":{
"_routing":{
"required":true
}
}
}
'
</pre>
Ez nem kötelező, de így ki lehet erőszakolni a custom routing megadását, nehogy véletlen lemaradjon.
Most szúrjunk be egy dokumentumot a order típus alá egyedi routing-id-val. Ezt a '''routing''' paraméterrel kell megadni:
<pre>
$ curl -XPOST '192.168.123.71:9200/mycompany/order?routing=user123' -H 'Content-Type: application/json' -d'
{
"productName":"sample",
"customerID":"user123"
}
'
</pre>
{{note|Fizikailag nem tudjuk, hogy melyik shard-re fog kerülni a dokumentum. Csak azt mondtuk meg az Elasticsearch-nek, hogy mikor a hash-t képzi, akkor a routing-id helyére a képlet-be azt írja be hogy '''user123'''. Mikor a keresésénél ugyan ezt a '''routing-id'''-t adjuk majd meg, akkor a fenti képlet ugyan azt a shrad ID-t fogja visszaadni, tehát ugyan azzal a routing id-val mindig ugyan azt a shard id-t állítjuk elő!}}
Keressük meg a feint dokumentumot úgy hogy megmondjuk az Elasticsearch-nek hogy pontosan melyik shrad-ban keresse. Ahogy a beszúrásánál is, itt is megadjuk a '''routing=user123''' értéket, ami ugyan arra a shard ID-ra fog leképeződni, ahova korábban beszúrta.
<pre>
$ curl -XGET '192.168.123.71:9200/mycompany/order/_search?routing=user123&pretty' -H 'Content-Type: application/json' -d'
{
"query":{
"match_all":{}
}
}
'
</pre>
A match_all-al az összes dokumentumot lekérjük az '''order''' típusból.
És a válasz:
<pre>
...
{
"_index" : "mycompany",
"_type" : "order",
"_id" : "kwczBmYB5rEaU4uAZ6UJ",
"_score" : 1.0,
"_routing" : "user123",
"_source" : {
"productName" : "sample",
"customerID" : "user123"
}
</pre>
Láthatjuk benne a routing id-t a _routing mezőben. Persze a routing paraméter nélkül is kereshettünk volna, de akkor az összes shard-re el lett volna küldve a kérés.
<br>
==REST API==
<br>
=TelepítésELK stack telepítése=
<pre>
A logstash alapértelmezett konfigurációja itt van: '''/homeusr/adamshare/Projects/DockerCourselogstash/persistentstorepipeline/logstash/.conf'''. A pipeline mappában ezen kívül szerencsére nincs semmi, tehát gond nélkül mountolhatunk rá egy NFS meghajtót, ahova berakjuk a saját konfigurációs fájlunkat: <br>'''logstash.conf'''
<syntaxhighlight lang="C++">
input {
--detach=false \
--mount "type=volume,src=192.168.42.1/home/adam/Projects/DockerCourse/persistentstore/logstash/config/\
,dst=/confusr/share/logstash/pipeline,volume-driver=nfs" \
--network elk \
--reserve-memory 100m \
-e "LOGSPOUT=ignore" \
docker.elastic.co/logstash/logstash:6.4.0 bin/logstash -f /conf/logstash.conf
</pre>
A '''LOGSPOUT=ignore''' környezeti változóval azt mondjuk meg a logspout-nak, hogy erről a konténerről ne gyűjtse össze a logokat.
Viszonylag lassan indul el a telepítés után, kb 30 másodperc. Ha végre elindul, akkor az alábbi log sor jelenik meg: . Láthatjuk hogy sikeresen kapcsolódott az Elasicsearch-öz a konfigurációban megadott URL-en (amúgy ez az alapértelmezett, magától is itt keresné).
<pre>
# docker service logs -f logstash
...
[2018-09-29T10:42:06,449][INFO ] Elasticsearch pool URLs updated {:changes=>{:removed=>[], :added=>[http://elasticsearch:9200/]}}
[2018-09-29T10:42:06,506][INFO ] Running health check to see if an Elasticsearch connection is working {:healthcheck_url=>http://elasticsearch:9200/, :path=>"/"}
[2018-09-12T20:01:26,403][INFO ][logstash.inputs.metrics ] Monitoring License OK
[2018-09-12T20:01:27,999][INFO ][logstash.agent ] Successfully started Logstash API endpoint {:port=>9600}
===Telepítés===
A logspout az egyetlen komponens amit minden egyes node-ra ki kell rakni, hogy el tudja küldeni a logokat a helyi docker démontól a logstash-nek. Ezért bind mount-ot fogunk létrehozni a node-on futó docker soket-re, hogy hozzáférjen a logokhoz. Command line paraméterként adjuk át neki hogy hova kell küldeni az összegyűjtött logokat. Syslogként kell őket elküldeni az 51415-ös portra, ahol a logstash fogadja majd azokat és beküldi az Elasticsearch-be.
<pre>
docker service create --name logspout \
gliderlabs/logspout:v3.2.5 syslog://logstash:51415
</pre>
Fontos, hogy a logspout is az '''elk''' nevű overlay hálózatra csatlakozik, ezért közvetlen tud kommunikálni a logstash-el. A swarm DNS az overlay hálózaton fel fogja tudni oldani a logstash szolgáltatás nevet az '''elk''' hálózaton kapott IP címére.
<pre>
{{note|Ha nincs elég memória szabadon a node-on, akkor a logspout nem fog tudni elindulni. }}
===Tesztelés===
https://www.elastic.co/guide/en/kibana/current/docker.html<br>
===Volume plugin===
Három fontos mappáját kell kivezessük az NFS meghajtóra a Kibana konténernek:
* /usr/share/kibana/config: konfig mappa helye
* /usr/share/kibana/data : ez a munkaterület
* /usr/share/kibana/plugins : ide írnak a plugin-ek.
===Telepítés===
A Kibana-t is az '''elk''' overlay hálózatra fogjuk csatlakoztatni, így közvetlenül el fogja érni az elasticsearch szolgáltatást a 9200 porton. Az elasticsearch domain nevet fel fogja oldani a swarm DNS szerver a konténer IP címére az '''elk''' hálózaton. Az Elasticsearch szerver elérhetőségét most nem config fájlal adjuk meg, hanem a '''ELASTICSEARCH_URL''' környezeti változóval. A Kibana web-es felülete a '''5601'''-es porton érhető el, azt publikáljuk az ingress overlay hálózatra, így bármelyik node publikus IP címével el fogjuk érni a Kibana-t.
<pre>
-e ELASTICSEARCH_URL=http://elasticsearch:9200 \
-p 5601:5601 \
--mount "type=volume,src=192.168.42.1/home/adam/Projects/DockerCourse/persistentstore/kibana/config/\
,dst=/usr/share/kibana/config,volume-driver=nfs" \
--mount "type=volume,src=192.168.42.1/home/adam/Projects/DockerCourse/persistentstore/kibana/data/\
,dst=/usr/share/kibana/data,volume-driver=nfs" \
--mount "type=volume,src=192.168.42.1/home/adam/Projects/DockerCourse/persistentstore/kibana/plugins/\
,dst=/usr/share/kibana/plugins,volume-driver=nfs" \
docker.elastic.co/kibana/kibana:6.4.0
</pre>
</pre>
==Join statement==
===Áttekintés===
Lehetőség van rá hogy egyfajta relációt vezessünk be egy indexben lévő dokumentumok között, egy szülő-gyerek kapcsolatot. A relációt típusát előre definiálni kell az index létrehozása közben úgy hogy előre megadjuk hogy milyen típusú dokumentumok állnak itt relációban egymással és meg kell adni egy mezőt is ami mind a két dokumentum típusban szerepelni fog mint a join mező. A szülő esetében a join mező csak hivatkozik a típusra, amit az index létrehozása közben definiáltuk, míg a gyerekél egyrészt hivatkozik a típusra, másrészt a szülő ID-jára is.
A joint kapcsolatot nem szabad relációs adatbázis kapcsolatként használni. Csak akkor szabad használni ha a gyerek elemek lényegesen többen vannak mint a szülő elem, különben nagyon rossz lesz a performancia. Fontos megkötések vannak a join-ak kapcsolatban amitől egy kicsit úgy is tűnik, hogy nem igazán kiforrott ez még:
* Egy indexen belül csak egy darab join mapping definíció lehet
* A szülés és a gyerek muszáj hogy ugyan abban a shard-ban legyen, erre nekünk kell figyelni a létrehozás közben.
* Egy elemnek csak egy szülője lehet, de bármennyi gyereke.
===Használat===
Az alábbi példában létrehozzuk a '''mycompany''' nevű index-et, miben definiálunk egy relációt, ahol azt mondjuk meg, hogy a '''question''' a szülője az answer-nek. Ezek it nem szigorú értelembe vett dokumentum típusok, tehát nem kell hogy ''mycompany/question'' ill ''mycomapany/answer'' -be hozzuk őket létre. Ezek sokkal inkább egyfajta címkék, amiket majd rá kell aggatni a szülőre ill a gyerekre. Lényeg, hogy mikor majd létrehozzuk a szülő dokumentumot, akkor abban lennie kell majd egy '''my_join_field''' nevű mezőnek, aminek az értéke vagy question vagy és a gyerek dokumentumban pedig szintén lennie kell majd egy '''my_join_field''' nevű mezőnek aminek az értéke '''answer'''.
<pre>
curl -XPUT "192.168.123.71:9200/mycompany" -H 'Content-Type: application/json' -d'
{
"mappings": {
"_doc": {
"properties": {
"my_join_field": {
"type": "join",
"relations": {
"question": "answer"
}
}
}
}
}
}
'
</pre>
Adjunk hozzá egy szülő dokumentumot:
<pre>
curl -X PUT "192.168.123.71:9200/mycompany/_doc/2?refresh" -H 'Content-Type: application/json' -d'
{
"text": "This is another question",
"my_join_field": "question"
}
'
</pre>
Majd két gyerek objektumot. Mivel megkötés, hogy ugyan abba a shar-ba kell kerüljön mint a szülő, ezért ezt nekünk külön kézzel ki kell kényszeríteni. A szülő létrehozásakor nem adtunk meg routing id-t, így ahogy azt már korábban láthattuk, a shard meghatározásához a dokumentum ID-t használta fel az Elasticsearch, ami az '''1''' volt. Ha nem adnánk most itt meg routing ID-t, akkor a '''3'''-as és '''4'''-es ID-t használná fel az Elasticsearch (ami itt a két dokumentum ID), ami isten tudja melyik shard-ba esne. Ezért itt külön meg kell adni a '''routing''' paraméterrel az 1-es ID-t.
<pre>
curl -XPUT "192.168.123.71:9200/mycompany/_doc/3?routing=1&refresh&pretty" -H 'Content-Type: application/json' -d'
{
"text": "This is an answer",
"my_join_field": {
"name": "answer",
"parent": "1"
}
}
'
curl -XPUT "192.168.123.71:9200/mycompany/_doc/4?routing=1&refresh&pretty" -H 'Content-Type: application/json' -d'
{
"text": "This is an other answer",
"my_join_field": {
"name": "answer",
"parent": "1"
}
}
'
</pre>
A relációk megjelennek a dokumentumokon, ha lekérdezzük őket. A lekérdezéshez nagyon hasznos lesz a '''children aggregation''' (lásd lentebb)
<br>
==Bulk műveletek==
<br>
* '''Metric''': Aggregations that keep track and compute metrics over a set of documents.
* '''Bucketing''': A family of aggregations that build buckets, where each bucket is associated with a key and a document criterion. When the aggregation is executed, all the buckets criteria are evaluated on every document in the context and when a criterion matches, the document is considered to "fall in" the relevant bucket. By the end of the aggregation process, we’ll end up with a list of buckets - each one with a set of documents that "belong" to it.
* '''MetricPipeline''': Aggregations that keep track aggregate the output of other aggregations and compute their associated metrics over a set of documents.
* '''Matrix''': A family of aggregations that operate on multiple fields and produce a matrix result based on the values extracted from the requested document fields. Unlike metric and bucket aggregations, this aggregation family does not yet support scripting.
A metrika aggregációk olyan függvények, amik a dokumentumok bizonyos mezőinek az értékein valamilyen művelet hajtanak végre, tipikusan ezek numerikus mezők, a végeredmény egy vagy több szám. Pl a legegyszerűbb metrika aggregáció az átlag kiszámítása. Az aggregációt lehet keresési eredményekre alkalmazni, vagy más aggregációk kimenetére, és a kimenetet is fel lehet használni további aggregációkhoz, lekérdezésekhez.
{
"aggs" : {
"<aggregáció neve>" : { "<aggregáció típusa>" : { "field" : "<mező névparaméterek>" } }
}
}
</syntaxhighlight>
A végeredmény is egy dokumentum lesz, amiben lesz egy mező név a metrika nevével, amit itt megadtunk a lekérdezésben. Azt hogy ez a lekérdezés egy aggregáció lesz, azt az '''aggs''' kulcsszó jelöli. Ha egy lekérdezésben volt aggregáció, akkor a válasz legvégére az Elasticsearch odarakja az aggregations nevű listát. Minden egyes aggregációnak lesz egy külön listaeleme azzal a névvel, amit megadtunk az aggregáció definiálásakor. <syntaxhighlight lang="C++"> "aggregations" : { "<aggregáció neve>" : { "value(s)" : <végeredmény, ami lehet egy vagy több elemű> } }</syntaxhighlight> <br>===single-value numeric metrics aggregation===A single-value aggregációknál a végeredmény mindig 1 elemű, tehát a végeredmény mindig így néz ki, ahol az XXX egy darab szám. <syntaxhighlight lang="C++"> "aggregations" : { "<aggregáció neve>" : { "value" : XXX } }</syntaxhighlight> Példák egy értékű aggregációkra: * Átlag (average)* Súlyozott átlag (weighted average) * Cardinality (különböző elemek elemszáma, pl set={1,1,2,3,4,4} kardinalitása = 4
* '''value_type''': A hint about the values for pure scripts or unmapped fields (Optional)
A következő példában nézzük meg az életkorok átlagát súlyozva a számlák egyenlegévél. Tehát az átlag value mezője továbbra is az '''age''', és a súly a '''balance'''. A missing paraméter ugyan arra szolgál mint az előbb.
<pre>
curl -XPOST "http://192.168.123.71:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
"size": 0,
"aggs" : {
"waited_avg_age": {
"weighted_avg": {
"value": {
"field": "age",
"missing": 20
},
"weight": {
"field": "balance"
}
}
}
}
}
'
</pre>
A válasz nagyon hasonlít az előző példához:
<pre>
"aggregations" : {
"waited_avg_age" : {
"value" : 30.060448837377425
}
}
</pre>
{{note|A dokumentumokban, amit aggregálunk, több ugyan olyan mező is lehet, amit a '''value'''-ban megadunk, pl lehet akár három darab '''age''' mező is. Ilyenkor az összeset be fogja számolni a végeredménybe. Viszont a '''weight''' mezőből csak egy lehet egy dokumentumba, ha több van hibát fogunk kapni. Ilyenkor egy '''script'''-el lehet megmondani, hogy melyiket vegye figyelembe.
===multi-value numeric metrics aggregation===
A multi-value aggregációknál a végeredmény egy eredmény lista, vagy egy előre meghatározott objektum, attól függően, hogy milyen típusú metrika aggregációt használunk.
<syntaxhighlight lang="C++">
"aggregations" : {
"<aggregáció neve>" : {
"values" : { aaa=xxx, bbb=ccc, ...}
}
}
</syntaxhighlight>
* Stats aggregáció (statisztika egy mezőről)
* Extended Stats Aggregation
* Percentiles Aggregation
* ...
<br>
'''stats'''<br>
A legegyszerűbb a '''stats''' aggregáció, ami nem csinál mást, mint a dokumentum halmazban egy kiválasztott mezőre visszaadja a következő statisztikát:
''darabszám, min, max, átlag, és summa'', tehát ez nem más mint a négy alap single-value aggregáció ötvözése. Maradva a bankos példánál, nézzük meg a '''age''' mezőre a statisztikát. Nevezzük el az aggregációnkat '''age_stats'''-nak:
<pre>
curl -X POST "192.168.123.71:9200/bank/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
{
"aggs" : {
"age_stats" : { "stats" : { "field" : "age" } }
}
}
'
</pre>
A válasz 5 előre rögzített mezőt tartalmaz:
<pre>
"aggregations" : {
"age_stats" : {
"count" : 1000,
"min" : 20.0,
"max" : 40.0,
"avg" : 30.171,
"sum" : 30171.0
}
}
</pre>
<br>
'''Percentiles Aggregation'''<br>
Egy mintában a kiugró értékek megkeresésére szolgál. A teljes halmazt növekvő sorrendbe rendezi, majd megmutatja, hogy milyen elem van pl a halmaz hosszának a felénél, a halmaz hosszának a 90%-ánál és a 99%-ánál. Mivel a halmaz rendezett, tudhatjuk, hogy a 70%-hoz tartozó értéknél a halmazban az elemek 70%-a kisebb. Ezeket a %-okat mi határozhatjuk meg. Tipikusan a nagyobb százalékok lehetnek fontosabbak. A képlet az alábbi:
my_array[count(my_array) * (százalékos érték, pl 70%)/100]
Ez pl válaszidőknél sokkal személetesebb mint a min/max/átlag, amiből gyakran nem látszik hogy baj van. Pl adott a következő rendezett halmazunk (amik szimbolizáljanak válaszidőket másodpercben):
{1,2,3,4,4,5,5,6,10,20}
Azt szeretnénk tudni, hogy a kérések 70%-át maximum mekkora válaszidővel tudtuk kiszámolni:
my_array[count(my_array) * 0.7] = 6
Tehát itt a 7. elemét kell vegyük ehhez a halmaznak, tehát a kérések 70%-át 6 másodpercen belül ki tudtuk szolgálni.
A percentiles aggregációt a '''percentiles''' kulcsszóval kell definiálni. A keresett százalékokat a percents tömbbel kell megadni, itt növekvő sorrendben kell felsorolni számokat 0.1 és 99.9 között. Nézzük a szokásos banki életkor példát.
<pre>
curl -X GET "192.168.123.71:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
"size": 0,
"aggs" : {
"age_perc" : {
"percentiles" : {
"field" : "age",
"percents" : [1.0, 5.0, 25.0, 50.0, 75.0, 95.0, 99.0]
}
}
}
}
'
</pre>
A válaszban a stat aggregációval ellentétben nem egy fix adatszerkezet van, hanem egy dinamikus value lista, minden egyes a percents tömbben megadott százalékhoz tartozik egy érték. A percents tömb megadás nem kötelező, ha nem adjuk meg, pont ez az alapértelmezett felosztás. <br>
Azt láthatjuk, hogy 75% -a a banki ügyfeleknek nem idősebb mint 35 év. És így tovább.
<pre>
"aggregations" : {
"age_perc" : {
"values" : {
"1.0" : 20.0,
"5.0" : 21.0,
"25.0" : 25.0,
"50.0" : 31.0,
"75.0" : 35.0,
"95.0" : 39.0,
"99.0" : 40.0
}
}
}
</pre>
<br>
==Bucket aggregation==
A vödrös aggregációknál az indexben lévő dokumentumok részhalmazát egy vagy több kategóriába (vödörbe) soroljuk be. Ezekre a vödrökre aztán további aggregációkat alkalmazhatunk, vagy pusztán az érdekel minket, hogy hány dokumentum van egy vödörben (hisztogram típusú aggregációk)
===Filter aggregáció===
Egy darab vödröt képez egy szűrési feltétel alapján a megadott indexben. Igazából ez egy egyszerű keresés, aminek az eredményére aztán könnyen futtathatunk metrika típusú aggregációkat. A következő példában elsőnek összegyűjtjük a 30 éves ügyfeleket a filter bucket aggregációval, majd meghívjuk rá az avg metrika aggregációt.
<pre>
curl -X POST "192.168.123.71:9200/bank/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
{
"aggs" : {
"30_year_old_avg" : {
"filter" : { "term": { "age" : "30" } },
"aggs" : {
"avg_price" : { "avg" : { "field" : "balance" } }
}
}
}
}
'
</pre>
És a végeredményben láthatjuk, hogy összesen 47 darab 30 éves ügyfél volt, és az ő egyenlegüknek az átlaga 22841.
<pre>
"aggregations" : {
"30_year_old_avg" : {
"doc_count" : 47,
"avg_price" : {
"value" : 22841.106382978724
}
}
</pre>
===Filters aggregáció===
Ez a több vödrös változata a Filter-nek. Az aggregáció során tetszőleges számú vödröt képezhetünk, pl logszintek alapján, pl a warnings kap egy külön vödröt, a debug kap egy külön vödröt, és az infó kap egy külön vödröt. Aztán a végeredményt tetszőlegesen tovább processzálhatjuk. A végeredmény az lesz hogy az összes érintett dokumentum az indexen belül bekerül egy vödörbe. Tegyük külön vödrökbe a 30, 31 és 32 éves banki ügyfeleket:
<pre>
curl -X GET "192.168.123.71:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d'
{
"size": 0,
"aggs" : {
"messages" : {
"filters" : {
"other_bucket_key": "other_customers",
"filters" : {
"30_yers_old" : { "match" : { "age" : "30" }},
"31_yers_old" : { "match" : { "age" : "31" }},
"32_yers_old" : { "match" : { "age" : "32" }}
}
}
}
}
}
'
</pre>
Az összes nemilleszkedő dokumentum a bank indexbőlaz other_customers vödörbe fog kerülni.
És íme a végeredmény- 47 darab 30 éves ügyfél van, 61 darab 31 éves, 52 darab 32 éves, és ezen kívül még 840-en vannak.
<pre>
"aggregations" : {
"messages" : {
"buckets" : {
"30_yers_old" : {
"doc_count" : 47
},
"31_yers_old" : {
"doc_count" : 61
},
"32_yers_old" : {
"doc_count" : 52
},
"other_customers" : {
"doc_count" : 840
}
}
}
}
</pre>
<br>
===Hisztogram===
Ez egy nem kommulált hisztogramot képez automatikusan a megadott indxeben lévő dokumentumok egy mezőjéből. A végeredmény egy több vödrös aggregáció. A vödrök számát, vagyis a hisztogram csoportokat magától számolja ki, nekünk csak a felbontást (lépés méretet) kell megadni. A hisztogramokról bővebben itt olvashatunk: [[Metrics_and_Monitoring_in_swarm#Histogram|Metrics and Monitoring in swarm/Histogram]]
Készítsük el a banki ügyfelek korának hisztogramját 10 éves lépésekben:
<pre>
curl -X POST "192.168.123.71:9200/bank/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
{
"aggs" : {
"prices" : {
"histogram" : {
"field" : "age",
"interval" : 10
}
}
}
}
'
</pre>
A végeredményben 3 vödör lesz (három oszlopa lesz a hisztogramnak). Van két bazi magas oszlop, és a végén egy egészen kicsi:
<pre>
"aggregations" : {
"prices" : {
"buckets" : [
{
"key" : 20.0,
"doc_count" : 451
},
{
"key" : 30.0,
"doc_count" : 504
},
{
"key" : 40.0,
"doc_count" : 45
}
]
}
}
</pre>
:[[File:ClipCapIt-180926-231841.PNG|450px]]
<br>
===Szomszédossági mátrix===
A szomszédossági mátrix-al véges gráfokat írhatunk le egy kétdimenziós mátrixban. Egy V csúcsszámú gráfban a mátrix |V| × |V| (téglalap). Ha két végpont között fut él, akkor 1 szerepel a mátrixban, ha nem fut él akkor 0. Ha nincsenek hurkok, akkor a diagonális elemek értelem szerűen 0-ák. Ha a hurok is megengedett, akkor a hurok mindig duplán számít, mint az alábbi példában az 1-es végpont.
{|
|:[[File:ClipCapIt-180926-222451.PNG|150px]]
|:[[File:ClipCapIt-180926-222507.PNG]]
|}
....
<br>
==Pipeline aggregation==
A Pipeline aggregációk, ahogy azt a neve is mutatja, mindig egy metrika vagy vödrös aggregáció eredményén dolgozik (mint a Linux pipe). Két fő csoportba lehet őket sorolni:
Pipeline aggregations work on the outputs produced from other aggregations rather than from document sets, adding information to the output tree. There are many different types of pipeline aggregation, each computing different information from other aggregations, but these types can be broken down into two families:
* Parent
* Sibling
...
<br>
==Matrix aggregations==
...
=Kibana Web interface=
A Kibana konzolt bármelyik node "publikus" IP címén elérjük a 5601-es porton, amit publikáltunk az ingress overlay hálózaton. Keressük meg valamelyik node publikus IP címét. Sajnos a docker-machine ip nem a publikus címet adja vissza, ezért ezt csak a node-on belülről lehet kinyerni: # docker-machine ssh mg0 ifconfig | grep -A 1 eth0 | grep "inet addr" inet addr:'''<span style="color:red">192.168.123.71</span>''' Bcast:192.168.123.255 Mask:255.255.255.0 Lépjünk be a Kibana konzolra: http://192.168.123.71:5601/app/kibana#/home?_g=()
==Alapok==
<br>
{{note|A legjobb a '''Discover''' képernyőben, hogy szabad szavasan is lehet benne keresni, írjunk be bármilyen szöveget, amelyik logban az szerepelt, azt meg fogja mutatni, pl egy IP cím, vagy bármilyen más id}} Most keressünk a logstash-* patternekben. <br>Listázzuk ki az összes olyan logstash-* indexbe tartozó log bejegyzést, amit nem az elastch termékek generáltak: NOT program: kibana* and NOT program : logmanager_* Vagy szimplán keressünk rá a "hello logspout" üzenetre, amit a tesztelés céljából indított ubuntu konténer küldött be. Mivel szabad szavasan is lehet keresni, csak írjuk be felülre hogy "hello logspout". :[[File:ClipCapIt-180929-152229.PNG]]{{note|Figyeljünk a jobb felső sarokban keresési intervallum beállításokra. Alapértelmezetten csak 15 perces adatokban keres}} <br> ==Visualize==A Vizualize felületen 18 féle diagram típusból választhatunk::[[File:ClipCapIt-180926-233621.PNG|600px]] Válasszuk ki az indexet: :[[File:ClipCapIt-180926-233934.PNG]] Általában az Y tengelyre metrika aggregációkat helyezhetünk el, az X tengelyre meg vödrös aggregációkat. * Az Y tengelyre rakott metrika típusú aggregációk esetén a single-value típus csak egy vonalat fog eredményez (pl count, avg) míg a multi-value több sávot (pl. Pertentiles). Az Y tengelyen mindig a megtalált darabszám lesz (dokumentum darabszám) és ha főlé visszük az egeret, megmutatja az értkét* Az X tengelyre rakott aggregációk esetén az X tengelyen lesznek a vödrök, míg az Y tengelyen a vödrök értéke. Készítsük el újra a banki ügyfelek korának hisztogramját, amit a parancssoros lekérdezésben már megnéztünk. Ehhez válasszuk az X-Axis lehetőséget, majd ott a listából válasszuk ki a Histogram aggregációt 10 éves felbontással az '''age''' mezőre. Majd adjunk hozzá sub-aggregációt szintén az '''age''' mezőre de most már csak 3 éves felbontással. Láthatjuk hogy a három nagy oszlopot még felbontotta 3 éves részekre: :[[File:ClipCapIt-180926-235343.PNG]] <br> <br> =Visualize=Monitoring the ELK stack== A Kibana out of the box képes monitorozni a teljes ELK stack minden porcikáját, fel tudja kutatni mind a három komponens összes node-ját, ehhez nincs más dolgunk, mint hogy a '''Monitoring''' menüpontra kattintsunk, és elindítsuk a monitorozást: :[[File:ClipCapIt-180928-203046.PNG|800px]]
=Swarm stack=
'''elastic-stack.yml'''
<syntaxhighlight lang="C++">
version: '3'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:6.4.0
ports:
- "9200:9200"
networks:
- elk
volumes:
- "elasticsearch-conf:/usr/share/elasticsearch/config"
- "elasticsearch-data:/usr/share/elasticsearch/data"
environment:
- "discovery.type=single-node"
deploy:
restart_policy:
condition: on-failure
resources:
reservations:
memory: 500m
logstash:
image: docker.elastic.co/logstash/logstash:6.4.0
networks:
- elk
environment:
- "LOGSPOUT=ignore"
volumes:
- "logstash-conf:/usr/share/logstash/pipeline"
deploy:
restart_policy:
condition: on-failure
resources:
reservations:
memory: 100m
logspout:
image: gliderlabs/logspout:v3.2.5
networks:
- elk
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
environment:
- "SYSLOG_FORMAT=rfc3164"
command:
- "syslog://logstash:51415"
deploy:
mode: global
restart_policy:
condition: on-failure
kibana:
image: docker.elastic.co/kibana/kibana:6.4.0
networks:
- elk
volumes:
- "kibana-conf:/usr/share/kibana/config"
- "kibana-data:/usr/share/kibana/data"
- "kibana-plugins:/usr/share/kibana/plugins"
environment:
- "LASTICSEARCH_URL=http://elasticsearch:9200"
ports:
- 5601:5601
deploy:
restart_policy:
condition: on-failure
networks:
elk:
driver: overlay
volumes:
elasticsearch-conf:
driver: nfs
driver_opts:
share: 192.168.42.1:/home/adam/Projects/DockerCourse/persistentstore/elasticsearch/config
elasticsearch-data:
driver: nfs
driver_opts:
share: 192.168.42.1:/home/adam/Projects/DockerCourse/persistentstore/elasticsearch/data
logstash-conf:
driver: nfs
driver_opts:
share: 192.168.42.1:/home/adam/Projects/DockerCourse/persistentstore/logstash/config
kibana-conf:
driver: nfs
driver_opts:
share: 192.168.42.1:/home/adam/Projects/DockerCourse/persistentstore/kibana/config
kibana-data:
driver: nfs
driver_opts:
share: 192.168.42.1:/home/adam/Projects/DockerCourse/persistentstore/kibana/data
kibana-plugins:
driver: nfs
driver_opts:
share: 192.168.42.1:/home/adam/Projects/DockerCourse/persistentstore/kibana/plugins
</syntaxhighlight>
# docker stack deploy --compose-file docker-compose.yml logmanager
Ugyan a service-ek nevébe a swarm bele fogja tenni a logmanager előtagot (ami a stack neve), ettől függetlenül a compose fájlban szereplő névvel a stack service-ek még mindig tudják használni a DNS névfeloldást egymás IP címeinek a kitalálásár az '''elk''' overlay hálózaton. (A hálózat nevébe is megváltozik: logmanager_elk)
<pre>
# docker stack ls
NAME SERVICES
logmanager 4
</pre>
<pre>
# docker stack ps logmanager ...
</pre>
=Elasticsarch cluster=
==Áttekintés==
Alap esetben az ES cluster építése egy automatizált folyamat, a user elől el van fedve. Kicsit leegyszerűsítve nincs más dolgunk, mint hogy elindítani a kívánt számú ES példányt ugyan azzal a cluster névvel egy közös hálózaton, a cluster felépítése teljesen automatikusan végbe fog menni.
Négyféle alap node típus van:
* '''Master-eligible node''': A master node-ok vezérli a cluster infrastruktúrát. Nyilván tartja a cluster tagokat, vezérli az index-ek létrehozását, törlését, valamint dönt róla hogy shard melyik node-ra kerüljön (node.master = true)
* '''Data node''': Ezek a node-ok tárolják az adatbázis adatokat és hajtrák végre az adatmanipulációs és kereső műveleteket, lényegében ők a munkások. (node.data = true)
* '''Ingest node''': Ingest nodes are able to apply an ingest pipeline to a document in order to transform and enrich the document before indexing. With a heavy ingest load, it makes sense to use dedicated ingest nodes and to mark the master and data nodes as node.ingest: false.
* '''Tribe node''': több cluster között képes kapcsolatot teremteni, az egyetlen node típus ami több cluster-nek is a tagja lehet.
* '''Coordinating node''': A kliens kéréseket a Coordinating node-ok kapják meg és továbbítják a data node-oknak, akik a keresés eredményét visszaküldik a keresést indító Coordinating node-nak, összegzi az eredményeket és visszaküldi a kliensnek. Lényegében minden node egyben Coordinating node is, tehát bárhova beérkezhet a kliens kérés. Azonban nagyon nagy terhelés mellett készíthetünk dedikált Coordinating node-okat, amiken az előző négy típust kikapcsoljuk, és a klienseknek csak ezen node-okon keresztül kommunikálhatnak a cluster-el.
Elviekben egy node egyben több szerepben is lehet, sőt, alap beállítások mellett minden egyes létrehozott node egyben master, data és ingest node is egyben. Ez kis cluster méret mellett ideális, nincs más dolgunk mint hogy ugyan azokkal a beállításokkal elindítunk pl 5 node-ot, ezek automatikusan cluster-t fognak formálni és meg fogják választani a vezetőt. Nagyobb terhelés mellett viszont már érdemes specializált node-okat létrehozni, külön master és külön data node-okat. Ezen felül érdemes lehet szintén dedikált coordinating node-okat is bevezetni.
A node-ok létrehozásakor a minimum beállítás:
* Az interfész, ahol eléri a többi node-ot a cluster-ben
* A cluster node-ok listája
* Cluster név, ami azonosítja a cluster-t ahova csatlakoznia kell.
{{warning|Saját adat mappa minden data és master node-nak: Fontos hogy a data és master node-oknak saját data mappája legyen, amin nem osztozik más node-okkal, mert akkor összekeveredhetnek. }}
Két megközelítés közül választhatunk:
* '''Automatikus cluster formálás:''' egy darab swarm service-t definiálunk, és egyszerűen meghatározzuk a replikák számát, elindul több konténerben az Elasticsearch egy swarm service-ként: http://derpturkey.com/elasticsearch-cluster-with-docker-engine-swarm-mode/
* '''Kézi cluster létrehozás:''' Minden egyes cluster tagot külön swarm service-ként definiálunk a compose fájlban 1-es replika számmal, tehát előre pontosan megmondjuk, hogy hány darab fog futni, és hogy melyik node-nak milyen szerepe van: http://blog.ruanbekker.com/blog/2018/04/29/running-a-3-node-elasticsearch-cluster-with-docker-compose-on-your-laptop-for-testing/
{{note|A swarm-ra azért van szükség, hogy könnyedén ki tudjuk telepíteni a távoli VM-re az ES konténereket. Swarm nélkül minden egyes VM-re nekünk kéne kézzel kitenni. }}
==Discovery==
Az Elasticsearch a "Zen Discovery" szolgáltatást használja a cluster node-ok felkutatására és a master node kiválasztására.
'''discovery.zen.ping.unicast.hosts'''<br>
Ebben a paraméterben kell felsorolni a node-ok listáját. Szerencsére itt meg lehet adni olyan host nevet is, ami több IP címére oldódik fel. A swarm cluster-ben minden service névvel indított DNS lekérdezésre a swarm visszaadja az összes konténer IP címét akik a szolgáltatáshoz tartoznak.
'''discovery.zen.minimum_master_nodesedit'''<br>
Ebben a paraméterben kell megadni, hogy hány master node-nak kell jelen lennie egyszerre, ahhoz hogy fenntartónak ítéljék meg az egyes nódok a cluster-t. Ezzel el lehet kerülni, hogy hálózati hiba estén, mikor a cluster két fele izolálódik egymástól önálló életre keljen a két oldal, mert mind a kettő azt hiszi, hogy ők teljes cluster-t alkotnak, és beindul egy párhuzamos működés, ami visszafordíthatatlan károkat okozna a cluser-ben. (split brain)
(master_eligible_nodes / 2) + 1
To explain, imagine that you have a cluster consisting of two master-eligible nodes. A network failure breaks communication between these two nodes. Each node sees one master-eligible node… itself. With minimum_master_nodes set to the default of 1, this is sufficient to form a cluster. Each node elects itself as the new master (thinking that the other master-eligible node has died) and the result is two clusters, or a split brain. These two nodes will never rejoin until one node is restarted. Any data that has been written to the restarted node will be lost.
Now imagine that you have a cluster with three master-eligible nodes, and minimum_master_nodes set to 2. If a network split separates one node from the other two nodes, the side with one node cannot see enough master-eligible nodes and will realise that it cannot elect itself as master. The side with two nodes will elect a new master (if needed) and continue functioning correctly. As soon as the network split is resolved, the single node will rejoin the cluster and start serving requests again.
==Perzisztencia==
Ez itt a legnagyobb kérdés. Még akkor is ha nem dinamikusan létrehozott VM-eken futtatjuk az ES cluster-t, a swarm minden egyes újraindításkor más és más node-ra fogja rakni ugyan azt a node-ot.
...
===Produkciós beállítások===
Ha kivesszük a '''discovery.type=single-node''' paramétert, és ezen felül még a '''network.host''' paramétert is beállítjuk, az ES produkciós üzemmódban fog elindulni. Produkciós indulás közben sokkal szigorúbban ellenőrzi a kötelező beállításokat. Ebből a legfontosabb host operációs rendszernek (jelen esetben a boot2docker) a '''vm.max_map_count''' beállítása, amit fel kell emelni minimum '''262144'''-ra. Ha ez kevesebb, az adott node nem fog elindulni.
<pre>
docker-machine ssh mg0
...
sudo sysctl -w vm.max_map_count=262144
</pre>
==Egy lehetséges megoldás==
Mivel minden master és data node-nak saját perzisztencia store-ra van szüksége nem tehetjük meg simán egy darab swarm service-ként elindítjuk a cluster-t és aztán felskálázzuk (docker swarm scale). Tehát az világos, hogy minden data és manager node-ot külön service-ként kell definiálni. Viszont az ingress overlay hálózatra csak egy service-hez tudjuk a 9200-as portot definiálni. (feltéve, ha el akarjuk érni kívülről). Szerencsére a koordinációs node-oknak (amik nem végeznek se master, de data se Ingest tevékenységet, kizárólag a kliensek kéréseit rout-olják a megfelelő node-okhoz) nem kell hogy legyen mentett data mappája, így ezeket létre tudjuk hozzon több elemű swarm service-ként, mint Elasticsearch belépési pont, és akkor a swarm ingress hálózat még meg is oldja a load-balancing-ot.
===Közös konfigurációs fájl===
A közös konfigurációs fájlba felvesszük az összes cluster tag végpontját '''discovery.zen.ping.unicast.hosts''' paraméterben. A listában minden egyes sor egy swarm service neve, amit a swarm DNS felold konténer IP címére. Egyedül a '''elasticsearch_coord''' lesz több konténerből álló szolgáltatás, amik rá lesznek kötve az ingress hálózatra is, ezek lesznek az ES cluster belépési pontjai. Szerencsére a zen discovery képes olyan DNS válaszokat is kezelni, amik több végpontot adnak vissza. <br>
Az alábbi fájlt fel fogjuk csatolni az összes service-be NFS megosztással. <br>
/usr/share/elasticsearch/config/'''elasticsearc.yml'''
<syntaxhighlight lang="C++">
cluster.name: "my-cluster"
network.host: 0.0.0.0
discovery.zen.minimum_master_nodes: 2
discovery.zen.ping.unicast.hosts:
- elasticsearch_coord
- elasticsearch1
- elasticsearch2
- elasticsearch3
</syntaxhighlight>
Az összes ES node a közös elk nevű overlay hálózaton tud majd közvetlen kommunikálni egymással.
===Coordinating node-ok===
A Coordinating node-okat több elemű swarm service-ként fogjuk létrehozni. Ezek a node-ok lesznek a ES cluster belépési pontjai. Egyedül ebben a swarm service-ben lesz több mint egy konténer. Data mappát nem is csatolunk fel hozzá. Ahhoz hogy coordinating node-ként viselkedjen egy node be kell állítani, hogy se nem data, se nem master és se nem ingest tevékenységet nem végezhet. Ehhez létrehozhattunk volna egy külön konfigurációs fájlt a coordinating node-oknak, mi most itt beírtuk környezeti változóba. 3 példányt kértünk belőle. Az ingress hálózaton a 9200 -as porton érhetjük majd el a coordinating node-okat bármelyik swarm node IP címén.
<syntaxhighlight lang="C++">
elasticsearch_coord:
image: docker.elastic.co/elasticsearch/elasticsearch:6.4.0
ports:
- "9200:9200"
networks:
- elk
volumes:
- "es-conf:/usr/share/elasticsearch/config"
environment:
- node.data=false
- node.master=false
- node.ingest=false
deploy:
replicas: 2
restart_policy:
condition: on-failure
</syntaxhighlight>
===További node-ok definiálása===
Mivel most nem akarunk hatalmas cluster-t építeni, három további node-ot fogunk a cluster-hez adni, amik már mind a három szerepkörben benne lesznek (master, data és ingest). Mivel a master és az adat node-oknak már saját data mappára van szüksége, minden node-ot egy külön swarm service-ként fogunk definiálni saját volume plugin megosztással a perzisztens store-ban. Így bárhol is hozza létre őket a swarm, mindig ugyan azt a data mappát fogják megkapni.
<syntaxhighlight lang="C++">
elasticsearch1,2,3:
image: docker.elastic.co/elasticsearch/elasticsearch:6.4.0
ports:
- "9200:9200"
networks:
- elk
volumes:
- "es-conf:/usr/share/elasticsearch/config"
- "es-data1,2,3:/usr/share/elasticsearch/data"
environment:
- node.name=node1,2,3
deploy:
replicas: 1
restart_policy:
condition: on-failure
</syntaxhighlight>
A fenti compose blokkot háromszor kell a compose fájlba rakni a megfelelő sorszámmal a service, node és volume megosztás nevében (1,2,3)
==Docker stack fájl==
{{warning|Nincs még frissítve...}}
<syntaxhighlight lang="C++">
version: '3'
services:
....
networks:
elk:
driver: overlay
volumes:
elasticsearch-conf:
driver: nfs
driver_opts:
share: 192.168.42.1:/home/adam/Projects/DockerCourse/persistentstore/elasticsearch/config
es-data1:
driver: nfs
driver_opts:
share: 192.168.42.1:/home/adam/Projects/DockerCourse/persistentstore/elasticsearch/data1
es-data2:
driver: nfs
driver_opts:
share: 192.168.42.1:/home/adam/Projects/DockerCourse/persistentstore/elasticsearch/data2
</syntaxhighlight>