<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Luděk Veselý]]></title><description><![CDATA[Software Developer]]></description><link>https://www.ludekvesely.cz/</link><generator>Ghost 0.11</generator><lastBuildDate>Thu, 21 Nov 2024 09:34:04 GMT</lastBuildDate><atom:link href="https://www.ludekvesely.cz/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Elasticsearch - nabídka školení]]></title><description><![CDATA[Nabízím konzultace Elasticsearch. Pomohu vám pochopit základy, implementovat české vyhledávání, provést konfiguraci pro produkční provoz.]]></description><link>https://www.ludekvesely.cz/elasticsearch-nabidka-konzultaci-a-skoleni/</link><guid isPermaLink="false">6d521979-d9b3-4b1e-a312-475cbdfaff39</guid><category><![CDATA[Elasticsearch]]></category><dc:creator><![CDATA[Luděk Veselý]]></dc:creator><pubDate>Thu, 19 Oct 2017 20:32:02 GMT</pubDate><media:content url="https://www.ludekvesely.cz/content/images/2017/10/27169312421_0d1d00dc2b_k-2.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.ludekvesely.cz/content/images/2017/10/27169312421_0d1d00dc2b_k-2.jpg" alt="Elasticsearch - nabídka školení"><p>Rádi byste pronikli do tajů Elasticsearch a dozvěděli se, jak s jeho pomocí naimplementovat vyhledávání na míru vaším potřebám? Chcete se dozvědět o úskalích vyhledávání v češtině a jak je řešit? Zajímá vás, jestli máte vše v Elasticsearch dobře nastavené a co všechno vám tento nástroj nabízí?</p>

<h2 id="nabzmkolenelasticsearch">Nabízím školení Elasticsearch</h2>

<p>Pro všechny tyto případy nabízím školení, během kterého vás seznámím se základy práce s Elasticsearch, podrobně vysvětlím jeho možnosti co se vyhledávání a analytiky týče, nebo poradím s jeho nasazením do produkčního prostředí. Během školení se také můžeme zastavit u konkrétních problémů, se kterými se ve vaší firmě při implementaci Elasticsearch potýkáte.</p>

<p>Přijedu školit do vaší firmy, přičemž školení může být jednodenní nebo dvoudenní. Jednodenní varianta je zaměřena na implementaci fulltextového vyhledávání. V případě dvoudenní varianty pak zbývá více prostoru pro procvičení a také pro důkladnější prozkoumání celého Elastic Stacku.</p>

<p>Co se osnovy týče, tak se nejprve seznámíme se samotným Elasticsearch, ukážeme si, <strong>jak do něj data ukládat</strong>, jak je číst a jak pracovat s celým clusterem. Následně se podíváme základní <strong>datové typy</strong>, jejich využití v <strong>mappingu</strong> a jakým způsobem lze psát <strong>queries</strong> a v Elasticsearch vyhledávat. Poté již můžeme jít více do hloubky a napsat si vlastní <strong>analyzer</strong>, který bude perfektně zpracovávat texty, v kterých vyhledáváne. Ukážeme si, jak v Elasticsearch vyhledávat i s <strong>překlepy</strong>, nebo jak naimplementovat <strong>našeptávač</strong>. Elasticsearch ale není jen fulltext, takže se také dostane na <strong>agregace</strong> a <strong>skriptování</strong>. V neposlední řadě se také podíváme na <strong>řazení</strong> výsledků - to totiž kromě samotné relevance může zohledňovat například popularitu produktu ve vašem e-shopu.</p>

<h2 id="kdejsemelasticsearchpouval">Kde jsem Elasticsearch používal?</h2>

<p>S Elasticsearch jsem začal pracovat nejprve na slevovém portálu slever.cz, kde jsem řešil vyhledávání produktů, logování a základní analytiku (statistiky prodejnosti atd.). Následně jsem jej používal na projektu adboos.com pro ukládání a analýzu statistik stažených z PPC systémů. Poté jsem jej používal na atoto.cz, kde jsem s jeho pomocí implementoval vyhledávání alternativních košíků, fulltextové vyhledávání, našeptávání a základní sběr logů. Nyní s jeho pomocí vytvářím kontroly transakčích dat pro Sodexo, hojně jej také využíváme v Memsource, kde aktuálně pracuji.</p>

<p>Kromě toho o Elasticsearch <a href="https://www.ludekvesely.cz/tag/elasticsearch/">publikuji články</a> a věnoval jsem se mu i při psaní své <a href="https://www.ludekvesely.cz/diplomova-prace-vyhledavani-jako-sluzba/">diplomové práce</a>, kdy jsem vytvářel fulltextové vyhledávání, které je poskytované jako služba.</p>

<h2 id="nevhejtesemiozvat">Neváhejte se mi ozvat!</h2>

<p>V případě zájmů o školení se mi neváhejte ozvat - veškeré kontaktní údaje jsou k dispozici na <a href="https://www.ludekvesely.cz/kontakt/">stránce s kontakty</a>.</p>

<p>Luděk Veselý <br>
E-mail: ludek.vesely@gmail.com <br>
Tel.: 776 129 218</p>]]></content:encoded></item><item><title><![CDATA[Seriál Elasticsearch: 5. Pokročilé fulltextové vyhledávání]]></title><description><![CDATA[V této kapitole se dozvíte, jak navrhnout vyhledávání tak, aby fungovalo co nejlépe. Vyhledávání ve více polích a v textu analyzovaném různými způsoby.]]></description><link>https://www.ludekvesely.cz/serial-elasticsearch-5-pokrocile-fulltextove-vyhledavani/</link><guid isPermaLink="false">b8524596-00f1-4631-86ad-ac55d8111d8f</guid><category><![CDATA[Elasticsearch]]></category><dc:creator><![CDATA[Luděk Veselý]]></dc:creator><pubDate>Thu, 12 Oct 2017 19:06:32 GMT</pubDate><content:encoded><![CDATA[<p>V tuto chvíli umíme vytvořit fulltextové vyhledávání v češtině nad názvem produktu. V praxi je však situace zpravidla složitější - vyhledávat chceme ve více polích dokumentu, v každém pak s jinou logikou. V této kapitole si tak předvedeme návrh a implementaci pokročilejšího fulltextového vyhledávání.</p>

<h2 id="poadavkynavyhledvn">Požadavky na vyhledávání</h2>

<p>Budeme vytvářet vyhledávání, které odpovídá následujícím požadavkům:</p>

<ul>
<li>Hledá se primárně v názvu produktu, nehledě na tvarosloví</li>
<li>Dále se hledá se v názvu produktu s ohledem na české tvarosloví</li>
<li>Hledá se i v popisku produktu, avšak s nejnižší prioritou</li>
</ul>

<h2 id="datavkterchsevyhledv">Data, v kterých se vyhledává</h2>

<p>Vyhledávat se bude v následujících produktech:</p>

<h5 id="produkt1">Produkt č. 1:</h5>

<ul>
<li>Název: Jablka golden 1 ks</li>
<li>Popisek: Veškeré ovoce je prémiové kvality</li>
</ul>

<h5 id="produkt2">Produkt č. 2:</h5>

<ul>
<li>Název: Jablko idared</li>
<li>Popisek: Kvalitní a čerstvé ovoce</li>
</ul>

<h5 id="produkt3">Produkt č. 3:</h5>

<ul>
<li>Název: Müsli</li>
<li>Popisek: Křupavé müsli s jablky</li>
</ul>

<h2 id="vyhledvanvrazy">Vyhledávané výrazy</h2>

<p>Po uložení produktů do Elasticsearch budeme vyhledávání ladit pomocí následujících výrazů:</p>

<ul>
<li><code>jablka idared</code></li>
<li><code>jablka</code></li>
<li><code>idared</code></li>
</ul>

<p>Pro výraz <code>jablka idared</code> budeme jako první očekávat produkt <code>Jablko idared</code>, protože se v názvu shodují obě slova. Následně budeme očekávat <code>Jablka golden 1 ks</code>, protože oba produkty mají slovo jablko v názvu. Jako poslední budeme očekávat produkt <code>Müsli</code>, který má slovo jablko pouze v popisku.</p>

<p>Pro výraz <code>jablka</code> budeme očekávat jako první produkt <code>Jablka golden 1 ks</code> a jako druhý <code>Jablko idared</code>. Sice mají slovo jablko v názvu shodně oba produkty, ten první jej má však ve stejném tvaru, jako je hledaný výraz. Jako třetí by se měl ve výsledcích objevit produkt <code>Müsli</code> , který slovo jablko obsahuje pouze v popisku.</p>

<p>Nakonec pro výraz <code>idared</code> očekáváme jako první nalezený produkt <code>Jablko idared</code>. Měl by to být zároveň jediný nalezený produkt, ostatní tento výraz neobsahují ani v názvu, ani v popisku.</p>

<h2 id="vytvoenindexuauloendat">Vytvoření indexu a uložení dat</h2>

<p>Pro vyhledávání vytvoříme index s jedním typem, přičemž každý dokument bude tvořen poli <code>title</code> a <code>description</code>. Již při vytváření indexu musíme přemýšlet, jak se v jednotlivých polích bude vyhledávat. </p>

<p>Začneme popiskem, ve kterém se bude vyhledávat v českém jazyce, nehledě na tvarosloví, velikost písmen, nebo diakritiku. Vzhledem k tomu, že půjde zpravidla o delší text, je vhodné vypustit slova nevýznamná pro vyhledávání.</p>

<p>V názvu produktu pak budeme chtít vyhledávat dvěma způsoby. Jednak budeme vyhledávat obdobně jako v případě popisku (jen nebude třeba vypouštět žádná slova - vzhledem k délce názvů produktů mohou mít významnou informační hodnotu). V názvech produktů však budeme chtít vyhledávat také slova, která přesně odpovídají zadanému výrazu a ta pak ve výsledcích vyhledávání zobrazit výše. Musíme tedy toto pole uložit i ve tvaru nepřevedeném na základaní tvar.</p>

<h3 id="nastavenindexuamapovn">Nastavení indexu a mapování</h3>

<p>Z těchto požadavků vyplývá, jaké analyzéry bude potřeba nakonfigurovat. Budou celkem tři, přičemž všechny budou dělit slova mezerami, text převádět na malá písmena a odstraňovat diakritiku. V tom ostatním se však budou lišit: Analyzér pro popisek musí vypouštět stop slova a převádět je na základní tvar. První analyzér pro titulek je bude jen převádět na základní tvar, druhý analyzér už ale nebude vypouštět stop slova, ani slova převádět na základní tvar. Kompletní nastavení indexu (včetně mappingu) tak bude vypadat následovně:</p>

<pre><code class="language-js">PUT products  
{
  "settings": {
    "number_of_shards": "1",
    "number_of_replicas": "0",
    "analysis": {
      "analyzer": {
        "czech_hunspell_stopwords": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "min_length",
            "czech_stop",
            "czech_hunspell",
            "lowercase",
            "czech_stop",
            "icu_folding",
            "unique_on_same_position"
          ]
        },
        "czech_hunspell": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "czech_hunspell",
            "lowercase",
            "icu_folding",
            "unique_on_same_position"
          ]
        },
        "czech_lowercase": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "icu_folding"
          ]
        }
      },
      "filter": {
        "czech_hunspell": {
          "type": "hunspell",
          "locale": "cs_CZ"
        },
        "czech_stop": {
          "type": "stop",
          "stopwords": [
            "že",
            "_czech_"
          ]
        },
        "unique_on_same_position": {
          "type": "unique",
          "only_on_same_position": true
        },
        "min_length": {
          "type": "length",
          "min": 2
        }
      }
    }
  },
  "mappings": {
    "products": {
      "properties": {
        "title": {
          "type": "keyword",
          "fields": {
            "czech_hunspell": {
              "type": "text",
              "analyzer": "czech_hunspell"
            },
            "czech_lowercase": {
              "type": "text",
              "analyzer": "czech_lowercase"
            }
          }
        },
        "description": {
          "type": "text",
          "analyzer": "czech_hunspell_stopwords"
        }
      }
    }
  }
}
</code></pre>

<p>Nejsložitější je zde analyzér <code>czech_hunspell_stopwords</code>, ve kterém nejprve odstraňujeme krátká a nevýznamná slova - následující analýza je totiž výpočetně náročná a je tak vhodné před ní co nejvíce slov odfiltrovat. Veškeré nastavení filtrů by však mělo být známé na základě předchozí kapitoly tohoto seriálu.</p>

<p>Nové je ale nastavení pro pole <code>title</code>, které je ukládáno dvěma způsoby - pomocí analyzérů <code>czech_hunspell</code> a <code>czech_lowercase</code>. Při vyhledávání se k ním bude přistupovat jako <code>title.czech_hunspell</code> a <code>title.czech_lowercase</code>.</p>

<p>Do vytvořeného indexu můžeme uložit produkty. Abychom nemuseli postupně vykonávat request pro každá dokument, je možné využít <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html">Bulk API</a> a data tak do Elasticsearch uložit naráz:</p>

<pre><code class="language-js">POST _bulk  
{"index": {"_index": "products", "_type": "products", "_id": "1"}}
{"title": "Jablka golden 1 ks", "description": "Veškeré ovoce je prémiové kvality"}
{"index": {"_index": "products", "_type": "products", "_id": "2"}}
{"title": "Jablko idared", "description": "Kvalitní a čerstvé ovoce"}
{"index": {"_index": "products", "_type": "products", "_id": "3"}}
{"title": "Müsli", "description": "Křupavé müsli s jablky"}
</code></pre>

<h2 id="vyhledvn">Vyhledávání</h2>

<p>Nyní bude třeba vytvořit vyhledávací dotaz. Vyhledávat se bude ve více polích, nevystačíme si tedy s klasickým vyhledáváním pomocí <code>match</code>. Možným řešením by bylo použít těchto dotazů více a ty mít v rámci <code>bool</code> query, nicméně toto řešení je poměrně těžkopádné. Jako ideální pro tento případ se jeví použití <code>multi_match</code> query:</p>

<pre><code class="language-js">GET products/_search  
{
  "query": {
    "multi_match": {
      "query": "jablko", 
      "fields": [
        "title.czech_lowercase^2",
        "title.czech_hunspell",
        "description^0.3"
      ]
    }
  }
}
</code></pre>

<p>Dotaz <code>multi_match</code> se od <code>match</code> liší tím, že mu lze předat seznam více polí, v nichž má vyhledávat. Vyhledává se tedy v titulku, který je indexován dvěma způsoby (<code>title.czech_lowercase</code> a <code>title.czech_hunspell</code>) i v popisku (<code>description</code>). </p>

<p>Vyhledávání navíc zohledňuje i váhu jednotlivých polí při vyhledávání přidáním <code>^2</code> resp. <code>^0.3</code> za název pole. Zde je v poli <code>title.czech_lowercase</code> vyhledáváno s nejvyšší prioritou (skóre při vyhledávání v tomto poli je násobeno dvěma) a v poli <code>description</code> naopak s prioritou výrazně nižší (0.3 krát než u <code>title.czech_hunspell</code>).</p>

<p>Výsledné vyhledávání můžeme otestovat na výrazech definovaných v úvodu, kdy zjistíme, že implementované vyhledávání plně odpovídá požadavkům. Například při vyhledávání výrazu <code>jablka</code> jsou správně nalezeny všechny produkty v pořadí <code>Jablka golden 1 ks</code> (slovo jablka v titulku ve shodném pádu), <code>Jablko idared</code> (slovo jablka v titulku v jiném pádu), <code>Müsli</code> (slovo jablka v popisku):</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/10/Console_-_Kibana-2.png" alt="Vyhledávání v Elasticsearch ve více polích"></p>

<h2 id="daltipyprovyhledvn">Další tipy pro vyhledávání</h2>

<p>Záleží na požadavcích na vyhledávání i na datech, v nichž se vyhledává, jak vhodně nastavit váhy mezi jednotlivými poli. Pro úplnost jetě doplním, že ve výchozím stavu je způsob vyhledávání nastaven na <code>best_fields</code>, kdy se bere v potaz nejlepší skóre při vyhledávání v jednotlivých polích. K dispozici je ale i <code>most_fields</code>, kdy jsou získána skóre ve všech polích a ta jsou následně pronásobena. Dostupných konfigurací je samožřejmě <a href="https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-multi-match-query.html">mnohem více</a>, to už je ale nad rámec tohoto seriálu.</p>

<p>Další možností, jak zlepšit vyhledávání může být použití algoritmického stemmeru jako doplňku k stemmeru slovníkovému. Tento stemmer může pomoci vykrýt případy, kdy slovník nezná některá indexovaná slova. Dalším zlepšením by mohlo být přidání token filtru <a href="https://www.elastic.co/guide/en/elasticsearch/reference/5.6/analysis-shingle-tokenfilter.html">Shingle</a>, který vygeneruje všechny možné kombinace sousedních slov a pomůže tak zpřesnit vyhledávání frází. Samostatnou kapitolou je pak neúplné vyhledávání, ať už se jedná o překlepy, nebo našeptávání.</p>]]></content:encoded></item><item><title><![CDATA[Seriál Elasticsearch: 4. Fulltextové vyhledávání v češtině]]></title><description><![CDATA[V této kapitole se dozvíte, jak implementovat fulltextové vyhledávání pomocí Elasticsearch česky - jak nastavit analyzéry a filtry a jak psát dotazy.]]></description><link>https://www.ludekvesely.cz/serial-elasticsearch-4-fulltextove-vyhledavani-v-cestine/</link><guid isPermaLink="false">4c6a413d-9cf2-48d7-aa74-8bba61294c57</guid><category><![CDATA[Elasticsearch]]></category><dc:creator><![CDATA[Luděk Veselý]]></dc:creator><pubDate>Thu, 28 Sep 2017 23:09:04 GMT</pubDate><content:encoded><![CDATA[<p>Nejprve obecně k fulltextovému vyhledávání. Patrně nejznámější způsob, jak "fulltextově" vyhledávat, je použití operátoru <code>LIKE %%</code> v relační databázi. Tento přístup však není bezchybný - nedokáže nalézt všechny tvary slov a navíc ani není dostatečně rychlý.</p>

<p>Předpokládejme dva produkty - <code>Jahody čerstvé</code> a <code>Čerstvá šťáva</code>. Pokud bude uživatel vyhledávat výraz <code>cerstvy</code>, pomocí operátoru <code>LIKE</code> ani jeden z produktů nenalezneme. Slova se v názvech produktů od hledaného výrazu liší tím, že nejsou ve stejném tvaru, mají různou velikost písmen a obsahují diakritiku. Pokud by se podařilo jednotlivá slova názvu produktu převést do shodného tvaru a vyhledávání by probíhalo právě v nich, už by byla úspěšnost vyhledávání lepší. Lepší možností by bylo použití operátoru <code>MATCH</code>, ukážeme si ale, jak lze detailně nastavit fulltextové vyhledávání v Elasticsearch.</p>

<p>Procesu, kdy z textu vybíráme důležitá slova a ty ukládáme v základním tvaru, aby podle nich bylo možné vyhledávat, se nazývá <strong>indexace</strong>. Jde o činnost podobnou tvorbě rejstříku v knize. Soubor, ve kterém jsou uloženy termíny, v nichž se vyhledává, se nazývá <strong>invertovaný index</strong>. Úpravám textu na slova v základním tvaru se pak v kontextu Elasticsearch říká <strong>analýza</strong>.</p>

<h2 id="analzatextu">Analýza textu</h2>

<p>Při analýze textu probíhájí postupně úpravy, které se dají zařadit do následujících kategorií:</p>

<ul>
<li>filtrace znaků (character filters): odstranění nechtěných znaků ze vstupu (html značky nebo interpunkce)</li>
<li>tokenizace (tokenizers): rozdělení vstupního textu na slova (tokeny), zpravidla mezerami</li>
<li>filtrace tokenů (token filters): jde o úpravy nad jednotlivými slovy, může jít o převedení do prvního pádu, odstranění předpon/přípon, diakritiky nebo vypuštění nepodstatných slov</li>
</ul>

<p>Nastavením všech těchto dílčích částí vzniká <strong>analyzér</strong>. Nastavení analyzérů se liší povahou dat a požadovaným způsobem vyhledáváním v nich. Různá bude také konfigurace pro různé jazyky, v tomto seriálu se však budeme zabývat pouze češtinou.</p>

<p>Nastavení analyzérů je součástí konfigurace indexu. Při jejich změně tak je třeba vytvořit nový index s novým nastavením a uložit do něj znovu data. Analyzérů může být v rámci indexu vytvořeno více, u každého pole dokumentu se pak při vytváření mapování definuje, jaký analyzér bude použit.</p>

<p>Nyní postupně vytvoříme analyzér pro indexaci českých textů. Analyzér budeme zkoumat skrz endpoint <code>_analyze</code>.</p>

<h3 id="vchozanalyzr">Výchozí analyzér</h3>

<p>Elasticsearch po instalaci disponuje několika připravenými analyzéry. Pro pokročilé vyhledávání v češtině sice nebudou úplně dostačovat, v některých případech však mohou postačovat. Výchozím analyzérem v Elasticsearch je <code>standard</code>, který převede text na malá písmena, odstraní většinu interpunkce a rozdělí slova mezerami na jednotlivé termy. </p>

<p>Vytvoříme nový index <code>products</code> (bez žádného dalšího nastavení - analyzér <code>standard</code> je vždy dostupný) a necháme zanalyzovat název ukládaného produktu <code>Jahody čerstvé - ve vaničce</code>:</p>

<pre><code class="language-js">// smazání dříve vytvořeného indexu
DELETE products

// vytvožení nového prázdného indexu
PUT products  
{
  "settings": {
    "index": {
      "number_of_shards": "1",
      "number_of_replicas": "0"
    }
  }
}

// Otestování analyzéru
GET products/_analyze  
{
  "analyzer": "standard",
  "text": "Jahody čerstvé - ve vaničce"
}
</code></pre>

<p>Po spuštění těchto příkazů v Kibaně obdržíme následující výstup:</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/10/Console_-_Kibana.png" alt="Kibana - výchozí analyzér"></p>

<p>Výstupem jsou výrazy (termy) <code>jahody</code>, <code>čerstvé</code>, <code>ve</code>, <code>vaničce</code>. Slova byla rozdělěna mezerami, převedena na malá písmena, zmizela pomlčka. Kdybychom hledali výraz <code>jahody</code>, už bychom produkt nalezli, protože při jeho analýze stejným analyzérem bychom dostali slovo <code>jahody</code>, které je uvedeno v termínech hledaného produktu. Stále si však neumíme poradit s diakrtikou (vyhledat <code>cerstve</code>) ani s tvaroslovím (vyhledat <code>čerstvá jahoda</code>).</p>

<p>Částečně nám s tím může pomoci použití předdefinovaného českého analyzéru:</p>

<pre><code class="language-js">DELETE products

PUT products  
{
  "settings": {
    "index": {
      "number_of_shards": "1",
      "number_of_replicas": "0",
      "analysis": {
        "analyzer": {
          "czech": {
            "type": "czech"
          }
        }
      }
    }
  }
}

GET products/_analyze  
{
  "analyzer": "czech",
  "text": "Jahody čerstvé - ve vaničce"
}
// "jahod", "čerstv", "vaničk"
</code></pre>

<p>Výstupem jsou slova převedená na malá písmena a ořezaná o koncovky. Díky tomu je možné vyhledat i slova v různých tvarech - při vyhledávání dojde také k oříznutí koncovek a porovnávají se pak tato analyzovaná slova.</p>

<p>Pro dosažení lepších výsledků však postupně vytvoříme vlastní analyzér, který by měl nakonec poskytovat lepší výsledky vyhledávání.</p>

<h3 id="pevodnamalpsmena">Převod na malá písmena</h3>

<p>Nejprve tedy vytvoříme vlastní (<code>custom</code>) analyzér, který dělá totéž jako <code>standard</code> bez nastavení češtiny. Ten budeme dále rozšiřovat o další způsoby analýzy textu. Tento analyzér při indexaci rozdělí text na jednotlivá slova a ta převede na malá písmena:</p>

<pre><code class="language-js">DELETE products

PUT products  
{
  "settings": {
    "index": {
      "number_of_shards": "1",
      "number_of_replicas": "0",
      "analysis": {
        "analyzer": {
          "czech": {
            "type": "custom",
            "tokenizer": "standard",
            "filter": ["lowercase"]
          }
        }
      }
    }
  }
}
</code></pre>

<p>Z nastavení můžeme vyčíst, že:</p>

<ul>
<li>Vytváří se analyzér s názvem <code>czech</code> v indexu <code>products</code></li>
<li>Analyzér je tvořen jedním token filtrem <code>lowercase</code> (další budeme přidávat)</li>
<li>Analyzér je dále tvořen tokenizérem <code>standard</code> (tokenizér je vždy jen jeden)</li>
<li>Token filtr <code>lowercase</code> převede každý vytvořený token na malá písmena</li>
</ul>

<p>Tímto analyzérem můžeme zanalyzovat znovu titulek produktu</p>

<pre><code class="language-js">GET products/_analyze  
{
  "analyzer": "czech",
  "text": "Jahody čerstvé - ve vaničce"
}
// jahody, čerstvé, vaničce
</code></pre>

<p>Výstup je shodný jako v případě použití <code>standard</code> analyzéru. Aby bylo vyhledávání v češtině použitelné, bude třeba analyzér rozšířit o další filtry.</p>

<h3 id="odstranndiakritiky">Odstranění diakritiky</h3>

<p>Dalším krokem je přidání filtru pro odstranění diakritiky. V Elasticsearch je pro tento účel dostupný filtr <code>asciifolding</code>. Ten převádí všechny ne-ascii znaky na jejich ascii variantu, tedy například <code>Č</code> ⇒ <code>C</code>, <code>ř</code> ⇒ <code>r</code> atd.</p>

<pre><code class="language-js">DELETE products

PUT products  
{
  "settings": {
    "index": {
      "number_of_shards": "1",
      "number_of_replicas": "0",
      "analysis": {
        "analyzer": {
          "czech": {
            "type": "custom",
            "tokenizer": "standard",
            "filter": ["asciifolding", "lowercase"]
          }
        }
      }
    }
  }
}

GET products/_analyze  
{
  "analyzer": "czech",
  "text": "Jahody čerstvé - ve vaničce"
}
// jahody, cerstve, ve, vanicce
</code></pre>

<p>Do seznamu filtrů přibyl <code>asciifolding</code>, přičemž tokeny těmito filtry prochází postupně - nejprve je odstraněna diakritika, následně je převedeno na malá písmena. Výstup provedené analýzy je už o něco použitelnější než v předchozím případě: <code>jahody</code>, <code>cerstve</code>, <code>ve</code>, <code>vanicce</code>.</p>

<p>Token filtr  <code>asciifolding</code> je však poměrně jednoduchý, pro plnou funkci češtiny je lepší použít filtr <code>icu_folding</code>. Ten není automaticky součástí Elasticsearch, nainstalovali jsme jej v první kapitole tohoto seriálu. Filtr <code>icu_folding</code> navíc oproti <code>asciifolding</code> počítá s významem jednotlivých znaků v rámci daného jazyka. Například ví, že písmena <code>c</code> a <code>h</code> za sebou tvoří písmeno <code>ch</code>. Díky tomu je možné například správně řadit podle české abecedy. Lépe si také poradí se speciálními znaky UTF-8, je však třeba počítat s tím, že je taková analýza dražší - je tedy nutné zvážit, zda pro daný účel nebude <code>asciifolding</code> dostačovat.</p>

<h3 id="tvaroslov">Tvarosloví</h3>

<p>Nyní se dostáváme k tomu, co je u českého jazyka komplikovanější než například u angličtiny. Slova totiž mění svůj tvar - dochází k skloňování u jmen, časování u sloves a dalším změnám, obecně řečeno dochází k <strong>ohýbání slov</strong>. Abychom dokázali nalézt tatáž slova v různých tvarech, převedeme je do jejich základního tvaru, tedy například prvního pádu jednotného čísla v případě podstatných jmen. Způsobů, jak zjistit základní tvar je více, s Elasticsearch budeme používat dva - algoritmickou a slovníkovou stematizaci.</p>

<h4 id="algoritmickstematizace">Algoritmická stematizace</h4>

<p>Stemmer je algoritmus, který pro nalezení základního tvaru využívá sady pravidel daného jazyka (například seznamu koncevek), což má své výhody i nevýhody. Výhodou je, že takový stemmer nemusí znát všechna slova v daném jazyce, pouze pracuje s sadou pravidel, pomocí nichž velmi rychle  převede slovo na základní tvar (nebo jen odstraní koncovky). Nevýhodou je pak určitá nepřesnost, kdy mohou být slova převáděna chybně, protoženení snadné obsáhnout všechna pravidla a výjimky daného jazyka.</p>

<p>V Elasticsearch je český stemmer standardně k dispozici, stačí jej jen přidat do nastavení analyzéru jako další filtr. </p>

<p>Do nastavení analyzéru tak přibyde sekce <code>filter</code>, která obsahuje nastavení dostupných filtrů. Zde je filtr <code>stemmer</code> nastaven na použití češtiny pomocí <code>"name": "czech"</code>. Tato konfigurace je nazvána <code>czech_stemmer</code> a je použita v analyzátoru <code>czech</code>:</p>

<pre><code class="language-js">DELETE products

PUT products  
{
  "settings": {
    "index": {
      "number_of_shards": "1",
      "number_of_replicas": "0",
      "analysis": {
        "analyzer": {
          "czech": {
            "type": "custom",
            "tokenizer": "standard",
            "filter": [
              "czech_stemmer", 
              "asciifolding", 
              "lowercase"
            ]
          }
        },
        "filter": {
          "czech_stemmer": {
            "type": "stemmer",
            "name": "czech"
          }
        }
      }
    }
  }
}

GET products/_analyze  
{
  "analyzer": "czech",
  "text": "Jahody čerstvé - ve vaničce"
}
// jahod, cerstv, ve, vanick
</code></pre>

<p>Výstupem provedené analýzy jsou termíny <code>jahod</code>, <code>cerstv</code>, <code>ve</code>, <code>vanick</code>. Zde je vidět, že Elasticsearch zahazuje nalezené přípony a vznikají tak neexistující slova. Pokud ale budeme vyhledávat slovo <code>vanička</code>, bude také převedno na <code>vanick</code>, je tedy toto chování v pořádku.</p>

<h4 id="stematizacepomocslovnku">Stematizace pomocí slovníku</h4>

<p>Přesnějšího převodu slov na základní tvar lze dostáhnout použitím slovníku obsahující veškerá slova pro daný jazyk. To není nic nereálného - textové editory takové slovníky obsahují a právě proto umí červeně podtrhávat chyby.</p>

<p>Elasticsearch disponuje filtrem <code>hunspell</code>, který umí využít volně dostupných slovníků Hunspell, které používá například kancelářský balík Open Office. Pokud je nemáte v Elasticsearch nainstalované, návod naleznete v <a href="https://www.ludekvesely.cz/serial-elasticsearch-2-instalace/">druhém dílu</a> tohoto seriálu. Slovníky jsou textové soubory obsahující slova daného jazyka včetně informací o tom, jak se skloňují nebo časují. Ty jsou uležené ve složce s konfigurací Elasticsearch, v nastavení filtru pak stačí jen definovat, jaký slovník se má použít. Pokud máme český slovník uložený ve složce <code>config/cs_CZ</code>, v nastavení filtru použijeme jako jazyk <code>cs_CZ</code>. Nahradíme tedy filtr <code>stemmer</code> za <code>hunspell</code> a můžeme porovnat výsledky analýzy:</p>

<pre><code class="language-js">DELETE products

PUT products  
{
  "settings": {
    "index": {
      "number_of_shards": "1",
      "number_of_replicas": "0",
      "analysis": {
        "analyzer": {
          "czech": {
            "type": "custom",
            "tokenizer": "standard",
            "filter": [
              "czech_hunspell",
              "asciifolding",
              "lowercase"
            ]
          }
        },
        "filter": {
          "czech_hunspell": {
            "type": "hunspell",
            "locale": "cs_CZ"
          }
        }
      }
    }
  }
}

GET products/_analyze  
{
  "analyzer": "czech",
  "text": "Jahody čerstvé - ve vaničce"
}
// jahoda, jahoda, cerstvy, ve, vanicka
</code></pre>

<p>Výstupem provedené analýzy jsou termíny <code>jahoda</code>, <code>jahoda</code>, <code>cerstvy</code>, <code>ve</code>, <code>vanicka</code>. Ve výstupu se objevuje slovo <code>jahoda</code> dvakrát - filtr <code>hunspell</code> totiž vytvořil dvě slova s rozdílným počátečním písmenem (<code>Jahoda</code> a <code>jahoda</code>), která byla následně převedena na malá písmena. Řešením by bylo provést převod na malá písmena jako první v řadě filtrů.</p>

<p>Výstupem analýzy jsou tak existující slova v základním tvaru. Výhodou tohoto přístupu je větší přesnost oproti použití algoritmické stematizace. Nevýhodou je však to, že slovník nemůže pokrýt všechna existující slova v daném jazyce, ať už jde o různá nářečí, hantýrku nebo různé hovorové výrazy. Oproti algoritmické stematizaci je také tento filtr náročnější výkonově, musí totiž celý slovník načíst do paměti a v něm složitěji vyhledávat. Většinou si tedy vystačíme se stamatizací algoritmickou, pro dosažení lepších výsledků a cenu vyšší složitosti je však vhodné využít stematizaci pomocí slovníku.</p>

<p>Využití slovníku má také tu výhodu, že můžeme definovat vlastní sadu slov, která jsou například specifická pro danou oblast. Lze tak Elasticsearch "naučit" pracovat se slovy, která ve slovnících nejsou. Samostatnou kapitolou je pak práce se slovy, která mají stejný význam (synonyma). Pro tento účel lze využít filtr <code>synonym</code>, který může použít existující seznam synonym (je také součástí Hunspell slovníků) nebo lze definovat vlastní.</p>

<h3 id="odstrannnevznamnchslov">Odstranění nevýznamných slov</h3>

<p>Poslední důležitou částí analyzéru je filtrace slov nepodstatných pro vyhledávání. V názvech produktů jich pravděpodobně mnoho nebude, nicméně při indexaci delších textů zjistíme, že řada slov se vyskytuje napříč dokumenty tak často, že podle nich prakticky nelze vyhledávat. Jde zpravidla o spojky nebo předložky. Elasticsearch si s tím částečně poradí sám - při vyhledávání také počítá s významností jednotlivých termínů vůči četnosti jejich výskytu v celém indexu, je však zbytečné jej vytěžovat indexací takových slov.</p>

<p>Taková slova se nazývají <strong>stop slova</strong> a Elasticsearch disponuje jejich sadou pro češtinu, k dispozici jsou jako <code>_czech_</code> v rámci filtru <code>stop</code>. Filtraci stopslov je možné zobecnit a filtrovat slova dle jejich délky - k tomu je možné použít filtr <code>length</code>.</p>

<p>Při analýze také můžou vzniknout duplicitní slova, jako se stalo při slovníkové stematizaci slova s velkým počátečním písmenem. Zbavit se těchto duplicitních slov lze filtrem <code>unique</code>. Je však důležité povolit možnost <code>only_on_same_position</code>, která zabrání mazání duplicit napříč celým indexovaným textem. Tím bysme přišli o to, že vícekrát se vyskytující slovo je důležité pro indexovaný text. Nastavení těchto filtrů může vypadat následovně:</p>

<pre><code class="language-js">DELETE products

PUT products  
{
  "settings": {
    "index": {
      "number_of_shards": "1",
      "number_of_replicas": "0",
      "analysis": {
        "analyzer": {
          "czech": {
            "type": "custom",
            "tokenizer": "standard",
            "filter": [
              "czech_stop",
              "czech_length",
              "czech_unique"
            ]
          }
        },
        "filter": {
          "czech_stop": {
            "type": "stop",
            "stopwords": ["že", "_czech_"]
          },
          "czech_length": {
            "type": "length",
            "min": 2
          },
          "czech_unique": {
            "type": "unique",
            "only_on_same_position": true
          }
        }
      }
    }
  }
}

GET products/_analyze  
{
  "analyzer": "czech",
  "text": "Jahody čerstvé - ve vaničce"
}
// Jahody, čerstvé, vaničce
</code></pre>

<p>Výstupem této analýzy jsou slova <code>Jahody</code>, <code>čerstvé</code>, <code>vaničce</code>. </p>

<h2 id="kompletnanalyzrproetinu">Kompletní analyzér pro češtinu</h2>

<p>Nyní známe všechna důležitá nastavení, abychom mohli vytvořit funkční analyzér pro češtinu. Je třeba říct, že neexistuje jediné správné a optimální nasavení analýzy, různá povaha dat a různé požadavky na vyhledávání budou vyžadovat různá nastavení analyzérů. I v rámci jednoho indexu tak lze vytvořit analyzérů více a použít zvlášť pro jednotlivá pole dokumentů. Je také nutné vzít v potaz množství dat a požadavky na výkonnost, kdy bude nutné nalézt rovnováhu mezi přesností a rychlostí indexace a vyhledávání.</p>

<p>V tuto chvíli tak definujeme analyzér, který může být výchozím bodem při implementaci a ladění českého vyhledávání.</p>

<pre><code class="language-js">DELETE products

PUT products  
{
  "settings": {
    "index": {
      "number_of_shards": "1",
      "number_of_replicas": "0",
      "analysis": {
        "analyzer": {
          "czech": {
            "type": "custom",
            "tokenizer": "standard",
            "filter": [
              "czech_stop",
              "czech_hunspell",
              "lowercase",
              "czech_stop",
              "icu_folding",
              "unique_on_same_position"
            ]
          }
        },
        "filter": {
          "czech_hunspell": {
            "type": "hunspell",
            "locale": "cs_CZ"
          },
          "czech_stop": {
            "type": "stop",
            "stopwords": ["že", "_czech_"]
          },
          "unique_on_same_position": {
            "type": "unique",
            "only_on_same_position": true
          }
        }
      }
    }
  }
}
</code></pre>

<p>V tomto analyzéru nejprve odstraníme stop slova, protože chceme minimalizovat množství slov, které se poměrně draze převádí pomocí slovníku na základní tvar. Stop slova včak nejsou k dispozici ve všech tvarech, je tedy nutné tento filtr následně opakovat. Dále jsou tokeny převedeny na malá písmena a odstraněna diakritika. Nakonec jsou odstraněny duplicity. </p>

<p>Nyní můžeme definovat mapování, které pro pole <code>title</code> využije nastavení tohoto analyzéru a uložit do indexu několik dokumentů:</p>

<pre><code class="language-js">PUT products/_mapping/products  
{
  "products": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "czech"
      }
    }
  }
}

PUT products/products/1  
{
  "title": "Jahody čerstvé - ve vaničce"
}

PUT products/products/2  
{
  "title": "Jahoda mražená"
}

PUT products/products/3  
{
  "title": "Maliny - vanička"
}
</code></pre>

<p>V těchto dokumentech můžeme konečně vyhledávat, nezávisle na pádu jmen, diakritice nebo velikosti písmen:</p>

<pre><code class="language-js">GET products/_search  
{
  "query": {
    "match": {
      "title": "jahody"
    }
  }
}
// "Jahoda mražená", "Jahody čerstvé - ve vaničce"

GET products/_search  
{
  "query": {
    "match": {
      "title": "Vanicka"
    }
  }
}
// "Maliny - vanička", "Jahody čerstvé - ve vaničce"
</code></pre>

<p>Výše uvedené vyhledávání pak v Kibaně vypadá následovně:</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/10/Console_-_Kibana-1.png" alt="Fulltextové vyhledávání v češtině"></p>

<hr>

<p>V tuto chvíli máme k dispozici základ pro vyhledávání v češtině. V <a href="https://www.ludekvesely.cz/serial-elasticsearch-5-pokrocile-fulltextove-vyhledavani/">následující kapitole</a> se budeme věnovat pokročilejšímu vyhledávání v reálných datech, kdy budeme kombinovat vyhledávání v různých polích s různou váhou.</p>]]></content:encoded></item><item><title><![CDATA[Seriál Elasticsearch: 3. První kroky, ukládání a zobrazení dat]]></title><description><![CDATA[<p>V tomto díle se seznámíme s základní funkčností Elasticsearch - vytvoříme index, uložíme první dokumenty a provedeme jejich vyhledání. </p>

<p>Předpokladem je mít spuštěný Elasticsearch a Kibanu - postup případné instalace naleznete v <a href="https://www.ludekvesely.cz/serial-elasticsearch-2-instalace/">předchozí kapitole</a>.</p>

<h3 id="restapi">REST API</h3>

<p>S Elasticsearch se komunikuje prostřednictvím <a href="https://www.zdrojak.cz/clanky/rest-architektura-pro-webove-api/">REST API</a>. Díky tomu je možné se dotazovat na</p>]]></description><link>https://www.ludekvesely.cz/serial-elasticsearch-3-prvni-kroky-ukladani-a-zobrazeni-dat/</link><guid isPermaLink="false">820cfc71-bf99-4c2d-883b-aaa8ed932b2b</guid><category><![CDATA[Elasticsearch]]></category><dc:creator><![CDATA[Luděk Veselý]]></dc:creator><pubDate>Tue, 12 Sep 2017 23:26:24 GMT</pubDate><content:encoded><![CDATA[<p>V tomto díle se seznámíme s základní funkčností Elasticsearch - vytvoříme index, uložíme první dokumenty a provedeme jejich vyhledání. </p>

<p>Předpokladem je mít spuštěný Elasticsearch a Kibanu - postup případné instalace naleznete v <a href="https://www.ludekvesely.cz/serial-elasticsearch-2-instalace/">předchozí kapitole</a>.</p>

<h3 id="restapi">REST API</h3>

<p>S Elasticsearch se komunikuje prostřednictvím <a href="https://www.zdrojak.cz/clanky/rest-architektura-pro-webove-api/">REST API</a>. Díky tomu je možné se dotazovat na data bez nutnosti instalace speciálního klienta, některé dotazy je možné provést pouhým zadáním odpovídající URL do webového prohlížeče. Pro ostatní úkony lze použít například konzolový nástroj <code>curl</code>. Veškerá data jsou odesílána a přijímána ve formátu JSON. Pro efektivní práci je však vhodnější použít některý z nástrojů s grafickým rozhraním jako jsou Postman nebo Kibana.</p>

<h3 id="kibana">Kibana</h3>

<p>V následujících příkladech budu veškeré dotazy provádět prostřednictvím nástroje Kibana. Umožňuje totiž zvýraznění syntaxe, automatické formátování dotazu, našeptávání při formulaci dotazu a procházení historie provedených dotazů. Pro dotazování se Elasticsearch je to aktuálně asi nejlepší nástroj.</p>

<p>Otevřte ve webovém prohlížeči <code>http://localhost:5601</code> a přejděte na záložku <strong>Dev Tools</strong>. Zobrazí se rychlá nápověda, pokračute kliknutím na modré tlačítko <code>Get to work</code>. Nyní jsou k dispozici dva panely - v levém je možné psát dotaz, v pravém jsou následně vidět odpovědi. Napsaný dotaz je možné spustit buď kliknutím na zelenou šipku, nebo klávesovou zkratkou <code>CMD + Enter</code> v případě OS X. Zkusme rovnou spustit připravený dotaz <code>GET _search</code>. Ten provede vyhledání všech dokumentů, které jsou v Elasticsearch uloženy.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/09/Console_-_Kibana-1.png" alt="Kibana - Dev Tools"></p>

<h3 id="stavclusteru">Stav clusteru</h3>

<p>Ještě než začneme do Elasticsearch ukládat data, můžeme zjistit, jak vypadá celý cluster (v našem případě tvořený jediným nodem). Stavem je myšleno jednak to, zda Elasticsearch jako takový běží v pořádku bez chyb, jednak také to, jaké indexy a typy dokumentů obsahuje. V dokumentaci je pro zjištění stavu doporučováno využít dotazů:</p>

<pre><code class="language-js">GET _cat/health?v  
GET _cat/indices?v  
</code></pre>

<p>Ty však nevrací data v přehledné podobě. Pro tento účel je přehlednější využít zobrazení stavu v nástroji Cerebro, dostupném na adrese <code>http://localhost:9000</code>.</p>

<h3 id="vytvoenindexu">Vytvoření indexu</h3>

<p>Nejprve je nutné vytvořit index, aby bylo kam data vůbec ukládat. To lze provést HTTP metodou PUT následovanou názvem indexu a jeho nastavením. Vytvořme index <code>products</code>, do kterého budeme ukládat produkty, které budeme následně vyhledávat.</p>

<pre><code class="language-js">PUT products  
{
    "settings" : {
        "index" : {
            "number_of_shards" : 1, 
            "number_of_replicas" : 0 
        }
    }
}
</code></pre>

<p>Tento příkaz spusťte v Kibaně, vytvoří se tak index s názvem <code>products</code>. Vytvořený index bude mít podle použitého nastavení jeden shard a žádné repliky. Zjednodušeňe řečeno s tímto nastavením nemůže docházet k žádné replikaci dat, což je pro lokální vývoj na jednom stroji v pořádku. V produkčním prostředí by pak bylo nastavení odlišné v závislosti na dostupném hardware - k tomu se dostaneme v pozdějších kapitolách seriálu.</p>

<p>Při spouštění příkazu v Kibaně je možné psát více dotazů pod sebe. Můžeme tak mít rozpracovaných více dotazů a spouštět je, aniž bychom museli otevírat nové okno prohlížeče. Lze tak spustit výše uvedený dotaz a  následně zkontrolovat výsledek provedené operace dalším dotazem. Například nastavení vytvořeného indexu ověříme dotazem <code>GET products/_settings</code>. Pokud neproběhl podle našich představ, lze index smazat pomocí <code>DELETE products</code> a pokračovat úpravou předchozích příkazů:</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/09/Console_-_Kibana-2.png" alt="Kibana - vytvoření indexu"></p>

<p>Provedené dotazy je možné zobrazit po kliknutí na <code>History</code> v pravém horním rohu Kibany. Není tak problém se vrátit k dříve provedenému tvaru dotazu a na něm dále pracovat.</p>

<p>Stav vytvořených indexů je však daleko přehlednější sledovat prostřednictvím nástroje Cerebro. Přejděte na URL <a href="http://localhost:9000/#/overview?host=http:%2F%2Flocalhost:9200">http://localhost:9000</a>, zobrazí se veškeré potřebné informace o clusteru:</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/09/cerebro_elasticsearch-tutorial_-1.png" alt="Cerebro - stav clusteru"></p>

<p>Pro nás je v tuto chvíli důležitá tabulka obsahující vytvořené indexy. Pokud nevidíte index <code>.kibana</code>, stačí zaškrtnout checkbox <code>.special (1)</code>. V této tabulce je vidět vytvořený index <code>products</code> tvořený jediným shardem. Pokud kliknete na dropdown vedle názvu indexu, zobrazí se menu vedoucí na zobrazení nastavení indexu, jeho editaci, statistiky a další možnosti. Cerebro nedisponuje ničím, co by Elasticsearch sám o sobě neuměl, jen jsou data graficky vizualizována a editace řady nastavení je možná prostřednictvím připravených formulářů.</p>

<p>V záhlaví je vidět žlutý pruh, který značí, že cluster není na 100% v pořádku. Důvodem je definovaná replika indexu <code>.kibana</code>. Ta se nemá kde vytvořit, protože je celý cluster tvořen jediným počítačem. Pro opravu stačí vyvolat menu (dropdown) u indexu <code>.kibana</code>, pokračovat na <code>index settings</code> a následně nastavit <code>index.number_of_replicas</code> na hodnotu <code>0</code>. Po uložení klinutím na <code>save</code> by měl být stav clusteru v záhlaví stránky zelený.</p>

<h2 id="vytvoenmapovn">Vytvoření mapování</h2>

<p>Elasticsearch je bezschémový, což znamená, že při ukládání dokumentů není třeba předem definovat jejich podobu - vytvoří se automaticky při indexaci dokumentů. V praxi však ale většinou budeme schéma dokumentů chtít definovat předem. Důvod je prostý - s každým polem budeme chtít pracovat jiným způsobem, což Elasticsearch nemůže předem vědět. Například v názvu produktu budeme chtít vyhledávát fulltextově, ale název výrobce budeme chtít vyhledat jen podle přesné shody, bylo by neefektivní obě pole ukládat zpracovaná stejným způsobem.</p>

<p>Pro tento účel je nutné nejprve vytvořit mapování (mapping) - definovat strukturu dokumentů. Stále pak lze indexovat dokumenty, které mají nová pole, která nejsou v mapování popsaná. V jednom indexu však musí mít jedno pole stále stejný typ, jinak se nezdaří ukládání nového dokumentu. Při vytváření mapování je třeba určit, pro jaký index a typ je vytvářeno, jaká pole jakých datových typů obsahuje a jak jsou případně indexována pole pro fulltextové vyhledávání. </p>

<p>Vytvoření jednoduchého mapování produktů by mohlo vypadat následovně:</p>

<pre><code class="language-js">PUT products/_mapping/products  
{
  "products": {
    "properties": {
      "id": {
        "type": "integer"
      },
      "title": {
        "type": "text"
      },
      "brand": {
        "type": "keyword"
      }
    }
  }
}
</code></pre>

<p>Zde vytváříme v indexu <code>products</code> typ <code>products</code>. V tomto typu budou ukládány dokumenty obsahující celočíselné <code>id</code>, titulek (<code>title</code>) ukládaný jako text a název značky <code>brand</code> ukládaný jako keyword. V obou případech (<code>text</code> i <code>keyword</code>) jde o textový řetězec (string), v prvním případě je však předpokládáno fulltextové vyhledávání a string je tak ukládán zpracovaný pro tento účel. V druhém případě je text uložen tak jak je - předpokládá se vyhledávání podle přesné shody (na filtraci dle výrobců bude na webu použit checkbox, ne textový input).</p>

<h3 id="uloendokumentu">Uložení dokumentu</h3>

<p>Nyní lze do vytvořeného indexu uložit dokumenty, které budeme následně vyhledávat. Dokument lze uložit následujícím způsobem: </p>

<pre><code class="language-js">POST products/products  
{
  "id": 1,
  "title": "Lednička Calex",
  "brand": "Calex"
}

POST products/products  
{
  "id": 2,
  "title": "Lednička Gorenje",
  "brand": "Gorenje"
}
</code></pre>

<p>Při ukladání matodou POST jsou vždy vytvářeny nové dokumenty s automaticky generovaným unikátním <code>_id</code>. Pokud bychom chtěli použít naše <code>id</code>, je třeba dotaz modifikovat použitím metody PUT:</p>

<pre><code class="language-js">PUT products/products/1  
{
  "id": 1,
  "title": "Lednička Calex",
  "brand": "Calex"
}

PUT products/products/2  
{
  "id": 2,
  "title": "Lednička Gorenje",
  "brand": "Gorenje"
}
</code></pre>

<p>Uložené dokumenty můžeme následně vyhledat pomocí <code>GET /products/products/_search</code>:</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/09/Console_-_Kibana-4.png" alt="Kibana - uložené produkty"></p>

<p>Uložené dokumenty mají automaticky generované <code>_id</code>. Pokud bychom jim chtěli vnutit vlastní id, stačilo by jej přidat do použitého endpointu a použít metodu PUT:</p>

<pre><code class="language-js">PUT products/products/1  
{
  "id": 1,
  "title": "Lednička Calex",
  "brand": "Calex"
}

PUT products/products/2  
{
  "id": 2,
  "title": "Lednička Gorenje",
  "brand": "Gorenje"
}
</code></pre>

<p>To, že byly dokumenty uloženy, lze také zkontrolovat v Cerebro - pod názvem indexu <code>products</code> přibude informace o počtu produktů: <code>docs: 2</code>.</p>

<h3 id="vyhledndokumentu">Vyhledání dokumentu</h3>

<p>Důkladně se fulltextovému vyhledávání věnuji v následující kapitole, v tuto chvíli vyhledáme uložené dokumenty pouze za účelem pochopení formulace vyhledávání.</p>

<p>Při vyhledávání je odesílán GET požadavek na endpoint <code>http://localhost:9200/products/products/_search</code>, kde <code>products</code> označuje nejprve název indexu, poté název typu. Název typu i indexu je možné vynechat - pak bude vyhledáváno v celém indexu, respektive v celém clusteru. V aktuální podobě by byly nalezeny veškeré dokumenty, pro skutečné vyhledávání je třeba formulovat tvar dotazu. Pokud bychom chtěli vyhledávát výraz <code>gorenje</code> v titulcích produktů, dotaz by vypadal následovně:</p>

<pre><code class="language-js">GET products/products/_search  
{
  "query": {
    "match": {
      "title": "gorenje"
    }
  }
}
</code></pre>

<p>Zde tento výsledek vyhledáváme fulltextově a pouze v titulku. Díky tomu byl nalezen jeden odpovídající produkt, nezávisle na velikosti písmen. Pokud byste však hledali <code>lednicka</code>, nebude nalezen žádný produkt. To z důvodu použití výchozího nastavení, kdy Elasticsearch neví, že má pracovat s češtinou.</p>

<p>Mohli bychom chtít vyhledat i podle názvu značky uloženém v poli <code>brand</code>. V tomto případě nás zajímá pouze přesná shoda, navíc je název uložen jako <code>keyword</code>. Proto použijeme <code>term</code> namísto <code>match</code>:</p>

<pre><code class="language-js">GET products/products/_search  
{
  "query": {
    "term": {
      "brand": "Gorenje"
    }
  }
}
</code></pre>

<p>Rozdíl mezi dotazy typu <code>match</code> a <code>term</code> by se dal připodobnit k otázce, zda produkt odpovídá danému výrazu (ano nebo ne -> <code>term</code>), nebo jako hodně odpovídá danému výrazu (<code>match</code>).</p>

<h3 id="tvarodpovdi">Tvar odpovědi</h3>

<p>Pro výše provedené dotazy obdržíme následující odpověď:</p>

<pre><code class="language-js">{
  "took": 0,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0.6931472,
    "hits": [
      {
        "_index": "products",
        "_type": "products",
        "_id": "AV54Ufs6nieqNAkX7QD5",
        "_score": 0.6931472,
        "_source": {
          "id": 2,
          "title": "Lednička Gorenje",
          "brand": "Gorenje"
        }
      }
    ]
  }
}
</code></pre>

<p>Response shora obsahuje:</p>

<ul>
<li><code>took</code>: Čas v ms, který zabralo vykonání dotazu</li>
<li><code>timed_out</code>: Informace, zda se vše stihlo v časovém limitu</li>
<li><code>_shards</code>: Informace o shardech, na nichž byl dotaz vykonán</li>
<li><code>_hits</code>: Výsledky vyhledávání (nalezené dokumenty)</li>
</ul>

<p>Pole <code>_hits</code> pak obsahuje pole, v němž je každý prvek tvořen:</p>

<ul>
<li><code>_index</code>: Index, ve kterém je nalezený dokument uložen</li>
<li><code>_type</code>: Typ, ve kterém je dokument uložen</li>
<li><code>_id</code>: ID uloženého dokumentu</li>
<li><code>_score</code>: Míra, kterou dokument odpovídá dotazu</li>
<li><code>_source</code>: Uložený dokument</li>
</ul>

<h3 id="formtquery">Formát query</h3>

<p>Při vyhledávání v Elasticsearch však budeme chtít s výsledky dále manipulovat - řadit je, stránkovat. Samotný dotaz je tak nutné rozšířit o další části, přičemž mezi nejčastěji používané patří:</p>

<ul>
<li><code>query</code>: Samotný dotaz</li>
<li><code>size</code>: Počet vrácených dokumentů, obdoba LIMIT z SQL, defaultní hodnota je <code>10</code></li>
<li><code>from</code>: Offset při vracení dokumentů, obdoba OFFSET z SQL</li>
<li><code>sort</code>: Definice způsobu řazení výsledků</li>
<li><code>aggs</code>: Agregace - výpočty nad všemi dokumenty odpovídající dotazu (minimální/maximální cena, výpis značek)</li>
</ul>

<p>Typický dotaz do Elasticsearch obsahující výše uvedená pole může vypadat následovně:</p>

<pre><code class="language-js">GET products/products/_search  
{
  "query": {
    "match": {
      "title": "Calex"
    }
  },
  "size": 5,
  "from": 0,
  "sort": [
    {
      "id": "asc"
    }
  ],
  "aggs": {
    "ids": {
      "terms": {
        "field": "brand"
      }
    }
  }
}
</code></pre>

<p>Po jeho spuštění obdržíme prvních maximálně 5 nalezených produktů, sežazených podle <code>id</code>. Dále v poli <code>aggs</code> obdržíme seznam všech dostupných značek.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/09/Console_-_Kibana-5.png" alt="Elasticsearch - query"></p>

<hr>

<p>V tuto chvíli umíme spouštět dotazy do Elasticsearch vytvářet dokumenty a následně je vyhledat. V <a href="https://www.ludekvesely.cz/serial-elasticsearch-4-fulltextove-vyhledavani-v-cestine/">následující kapitole</a> se dozvíte, jak dát dohromady fulltextové vyhledávání v českém jazyce.</p>]]></content:encoded></item><item><title><![CDATA[Seriál Elasticsearch: 2. Instalace]]></title><description><![CDATA[V této kapitole se dozvíte jak zprovoznit Elasticsearch, kde stáhnout české slovníky. Naučíte se také nainstalovat nástroje Kibana a Cerebro.]]></description><link>https://www.ludekvesely.cz/serial-elasticsearch-2-instalace/</link><guid isPermaLink="false">4db5d4ea-988c-4f4d-ac05-2a7bacb8a9a2</guid><category><![CDATA[Elasticsearch]]></category><dc:creator><![CDATA[Luděk Veselý]]></dc:creator><pubDate>Thu, 31 Aug 2017 23:31:49 GMT</pubDate><content:encoded><![CDATA[<p>Prvním předpokladem pro práci s Elasticsearch je mít jej stažený a spuštěný. Způsobů, jak jej zprovoznit je více a záleží na operačním systému, který používáte. Příklady zde uvedené jsou prováděny v OS X, měly by být shodné i pro Linux.</p>

<p>V zásadě jsou možnosti jak zprovoznit Elasticsearch:</p>

<ul>
<li>Stažení ZIP archivů</li>
<li>Použít Docker</li>
<li>MSI installer pro Windows</li>
</ul>

<p>V tuto chvíli se bavíme o zprovoznění lokálního vývojového prostředí, instalaci v produkčním prostředí se budeme věnovat až v závěru tohoto seriálu. Existují také další způsoby instalace, například pomocí deb nebo rpm balíčků. Cílem tohoto seriálu však není poskytnout všechny možné případy, od toho je k dispozici <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/_installation.html">oficiální dokumentace</a>.</p>

<p>Vzhledem k tomu, že je Elasticsearch psaný v Javě, stačí stáhnout připravené ZIP archivy a v nich spustit patřičné soubory. V této kapitole popisuji veškeré nutné kroky, na závěr jsou však k dispozici v jediném BASH skriptu. Pokud však máte nainstalovaný Docker, je se jeho použití pravděpodovně nejjednodušší způsob, jak Elasticsearch a další nástroje spustit. Pro Windows je pak připraven MSI instalátor s grafickým průvodcem.</p>

<h2 id="prerekvizity">Prerekvizity</h2>

<p>Pokud ještě nemáte stažený <a href="https://github.com/ludekvesely/elasticsearch-tutorial">repozitář z GitHubu k tomuto tutoriálu</a>, nyní je ta pravá chvíle.</p>

<pre><code class="language-bash"># stažení repozitáře
git clone git@github.com:ludekvesely/elasticsearch-tutorial.git

# přesunutí se do stažené složky
cd elasticsearch-tutorial  
</code></pre>

<p>Tato složka obsahuje jak instalační skripty, tak soubory pro vytvoření stacku v Dockeru. V tomto návodu začneme nejprve instalací pomocí jednotlivých příkazů - použití výsledného skriptu nebo dockeru pak tuto práci jen automatizuje.</p>

<h2 id="instalacestaenmziparchiv">Instalace stažením ZIP archivů</h2>

<p>Pro spuštění Elasticsearch je nutné mít korektně nainstalovanou Javu, doporučována je verze Oracle JDK 1.8.0 nebo vyšší. Stáhnout ji lze z <a href="http://docs.oracle.com/javase/8/docs/technotes/guides/install/install_overview.html">webu Oracle</a>. To, že máte nainstalovanou správnou verzi je možné ověřit následujícím příkazem:</p>

<pre><code class="language-bash">java -version  
# java version "1.8.0_05"
# Java(TM) SE Runtime Environment (build 1.8.0_05-b13)
# Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode)

echo $JAVA_HOME  
# /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home
</code></pre>

<p>Pro instalaci z terminálu jsou navíc vyžadovány další konzolové nástroje: <code>wget</code>, <code>unzip</code> a <code>tar</code>. Ty můžete nainstalovat prostřednictvím vašeho balíčkovacího manažera (např. Homebrew v případě OS X, apt-get v případě Ubuntu). Pro dotazování se Elasticsearch z konzole je nejjednodušší použití nástroje <code>curl</code>.</p>

<p>Pro zachování konzistence a předejití možným nedorozumněním spouštějte všechny následující konzolové příkazy ve složce stařeného repozitáře (<code>elasticsearch-tutorial</code>).</p>

<p>Je třeba také zmínit, že nové verze Elasticsearch a vlastně všech produktů firmy Elastic jsou vydávány poměrně často. Může se tak stát, že aktuální verze je o mnoho vydání novější. V tom případě zpravidla stačí upravit číslo verze (zde <code>5.6.2</code>) ve skriptech za odpovídající verzi.</p>

<h3 id="staenelasticsearch">Stažení Elasticsearch</h3>

<p>Ze všech dostupných variant bude na všech operačních systémech funkční ta využívající stažení a následné rozbalení ZIP archivu. Pokračujte tedy na adresu <a href="https://www.elastic.co/downloads/elasticsearch">www.elastic.co/downloads/elasticsearch</a> a klikněte na možnost <strong>ZIP</strong>.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/10/Download_Elasticsearch_Free_-_Get_Started_Now___Elastic-1.png" alt="Stažení Elasticsearch"></p>

<p>Stažený archiv rozbalte do libovolné složky, její obsah bude vypadat následovně:</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/10/elasticsearch-tutorial.png" alt="Stažený a rozbalený Elasticsearch"></p>

<p>Celý postup je také možné provést z terminálu následujícími příkazy:</p>

<pre><code class="language-bash"># stažení archivu
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6.2.zip

# rozbalení staženého archivu
unzip elasticsearch-5.6.2.zip  
</code></pre>

<h4 id="zkladnkonfigurace">Základní konfigurace</h4>

<p>Nyní je možné nastavit základní parametry Elasticsearch editací souboru <code>elasticsearch.yml</code> ve složce <code>config</code>. První úpravou je nastavení <code>cluster.name</code>, čímž docílíme toho, že se nebude snažit spuštěný Elasticsearch připojit na jiné nepřejmenované běžící instance Elasticsearch. Výsledná podoba souboru je pak následující:</p>

<pre><code class="language-yaml"># soubor elasticsearch-5.6.2/config/elasticsearch.yml
cluster.name: elasticsearch-tutorial  
</code></pre>

<p>Pokud instalujete Elasticsearch pomocí konzolových příkazů, lze použít již připravený konfigurační soubor - stačí jej nakopírovat na správné místo příkazem:</p>

<pre><code class="language-bash">cp elasticsearch.yml elasticsearch-5.6.2/config/  
</code></pre>

<h4 id="instalaceeskhoslovnku">Instalace českého slovníku</h4>

<p>Pro správnou funkci češtiny při vyhledávání je třeba nainstalovat český slovník. Stačí stáhnout příslušné soubory projektu <a href="http://hunspell.github.io">Hunspell</a>. K dispozici jsou pro řadu jazyků, české jsou připraveny <a href="https://github.com/ludekvesely/elasticsearch-tutorial/tree/master/hunspell">na GitHubu v repozitáři tohoto tutoriálu</a>. Slovník je tvořen třemi soubory <code>cs_CZ.aff</code>, <code>cs_CZ.dic</code> a <code>settings.yml</code>, které uložte do složky <code>elasticsearch-5.6.2/config/hunspell/cs_CZ</code>.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/10/elasticsearch-tutorial-1.png" alt="Stažené slovníky v Elasticsearch"></p>

<h4 id="instalacepluginuicu">Instalace pluginu ICU</h4>

<p>Posledním doplňkem pro korektní funkčnost češtiny je <a href="https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-icu.html">plugin ICU</a>, který umožňuje správnou práci s kódováním Unicode v českém jazyce. Jeho instalace je možná zadáním následujícího příkazu:</p>

<pre><code class="language-bash">elasticsearch-5.6.2/bin/elasticsearch-plugin install analysis-icu  
</code></pre>

<h3 id="sputnelasticsearch">Spuštění Elasticsearch</h3>

<p>Nyní nám již nebrání nic ve spuštění Elasticsearch - stačí spustit spustitelný soubor umístěný ve složce <code>bin</code>:</p>

<pre><code class="language-bash">elasticsearch-5.6.2/bin/elasticsearch  
</code></pre>

<p>Následně se zobrazí log běžícího Elasticsearch. Spuštění chvíli trvá. Jakmile se v logu objeví, že se jeho stav změnil z <code>red</code> na <code>green</code>, můžete jeho spuštění ověřit otevřením adresy <code>http://localhost:9200</code> ve webovém prohlížeči.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/10/localhost_9200.png" alt="Spuštěný Elasticsearch"></p>

<h3 id="nstrojeproprciselasticsearch">Nástroje pro práci s Elasticsearch</h3>

<p>Abychom mohli s Elasticsearch rozumně pracovat (psát dotazy pro ukládání dokumentů nebo vyhledávání), je vhodné nainstalovat další nástroje, které takovou práci usnadní.</p>

<h4 id="kibana">Kibana</h4>

<p>Kibana je grafické rozhraní, které se umí připojit na Elasticsearch a vizualizovat data, která jsou v něm uložena. Primárním účelem tohoto nástroje je rychlé vyhledávání v uložených datech a vytváření vizualizací (grafů a tabulek) a jejich skládání do komplexních dashboardů. Její součástí je však také editor, který umožňuje pohodlné vykonávání příkazů, zvýrazňuje syntaxi, a při psaní dotazů pomáhá našeptáváním. </p>

<p>Kibana je ke stažení na adrese <a href="https://www.elastic.co/downloads/kibana">www.elastic.co/downloads/kibana</a>. Zde stáhněte verzi dle vašeho operačního systému. Pro OS X je k dispozici právě jedna:</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/10/Download_Kibana_Free_-_Get_Started_Now___Elastic.png" alt="Kibana - stažení"></p>

<p>Stažený archiv rozbalte, spustitelný soubor je opět ve složce <code>bin</code>.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/10/elasticsearch-tutorial-2.png" alt="Kibana - stažený archiv"></p>

<p>Stažení a spuštění je možné opět provést také z terminálu. Použijte k tomu následující příkazy:</p>

<pre><code class="language-bash"># stažení
wget https://artifacts.elastic.co/downloads/kibana/kibana-5.6.2-darwin-x86_64.tar.gz

# rozbalení archivu
tar xzf kibana-5.6.2-darwin-x86_64.tar.gz

# spuštění
kibana-5.6.2-darwin-x86_64/bin/kibana  
</code></pre>

<p>Po jejich provedení můžete otevřít adresu <code>http://localhost:5601</code> ve svém webovém prohlížeči, kde bude třeba provést prvotní konfiguraci. Název indexu nastavte jako <code>*</code> a jako pole s časem nastavte <code>I don't want to use Time Filter</code>. Následně klikněte na tlačítko <code>Create</code>.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/09/Kibana.png" alt="Kibana - nastavení"></p>

<p>Nyní se vytvořilo prvotní nastavení a jsou tak k dispozici ostatní záložky v grafickém rozhraní. Nás bude nejvíce zajímat předposlední záložka <code>Dev Tools</code>, pod kterou je dostupný editor. </p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/09/Console_-_Kibana.png" alt="Kibana - Dev Tools"></p>

<p>Zde již můžeme vytvářet a spouštět dotazy, využívat našeptávání a zvýrazňování syntaxe. Můžete zkusit spustit předpřipravený dotaz <code>GET _search</code>, který provede vyhledávání nad všemi dokumenty uloženými v Elasticsearch. V pravé části okna je vidět, že byly nalezeny 2 dokumenty (<code>"total": 2</code>). Jde o uložené nastavení vytvořené v předchozím kroku.</p>

<h4 id="cerebro">Cerebro</h4>

<p>Posedním instalovaným nástrojem je <a href="https://github.com/lmenezes/cerebro">Cerebro</a>. Sice není pro vývoj nezbytně nutný, nabízí však funkce, kterými Kibana nedisponuje - umožňuje především správu a monitoring clusteru, tedy zobrazení jeho stavu a úpravu konfigurace. Je však užitečný i pro lokální vývoj, kdy graficky zobrazí všechny dostupné indexy. Lepší možnosti monitoringu sice poskytuje X-pack, který jde do Kibany doinstalovat, je však placený, Cerebro je kompletně zdarma. </p>

<p>Pro stažení je třeba přejít na stránku <a href="https://github.com/lmenezes/cerebro/releases">releases na GitHubu</a>, kde je ke stažení ZIP archiv.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/10/Releases_-_lmenezes_cerebro.png" alt="Cerebro - stažení"></p>

<p>Stažený soubor rozbalte, spustitelný soubor <code>cerebro</code> se nachází ve složce <code>bin</code>. Spustit jej můžete z terminálu příkazem <code>cerebro-0.6.8/bin/cerebro</code>. Po spuštění je k dispozici grafické rozhraní na adrese <code>http://localhost:9000</code>. Zde je nutné nastavit, kde je Dostupný Elasticsearch. Zadejte <code>http://localhost:9200</code> a pokračujte tlačítkem <code>Connect</code>.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/10/localhost_9000___connect.png" alt="Cerebro - nastavení připojení"></p>

<p>Po připojení k Elasticsearch je vidět základní statistika - počty indexů, dokumentů, nebo vytížení hardware. Aktuálně však není vidět žádný index - po kliknutí na <code>.special</code> se ale zobrazí index <code>.kibana</code> s uloženým nastavením Kibany.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/09/cerebro_elasticsearch-tutorial_.png" alt="Cerebro"></p>

<p>Kompletní stažení a instalaci je opět možné provést z terminálu ve složce <code>01-install</code>:</p>

<pre><code class="language-bash"># stažení archivu
wget https://github.com/lmenezes/cerebro/releases/download/v0.6.6/cerebro-0.6.8.zip

# rozbalení archivu
unzip cerebro-0.6.8.zip

# spuštění
cerebro-0.6.8/bin/cerebro  
</code></pre>

<h3 id="vslednskriptproinstalaci">Výsledný skript pro instalaci</h3>

<p>Veškeré výše provedené kroky jsem sepsal do skriptu <a href="https://github.com/ludekvesely/elasticsearch-tutorial/blob/master/install.sh">install.sh</a>, po jehož spuštění by měl být korektně nainstalovaný Elasticsearch včetně všech pluginů a podpůrných nástrojů. Pokud jste se tedy ztratili v některém z výše uvedených kroků, zde jsou uvedeny veškeré potřebné příkazy ve správném pořadí. </p>

<p>Po úspěšné instalaci je možné použít skript <a href="https://github.com/ludekvesely/elasticsearch-tutorial/blob/master/start.sh">start.sh</a>, který spustí všechny stažené nástroje. Kompletní instalace a spuštění tedy vypadá následovně:</p>

<pre><code class="language-bash">git clone git@github.com:ludekvesely/elasticsearch-tutorial.git  
cd elasticsearch-tutorial  
./install.sh
./start.sh
</code></pre>

<h2 id="instalacepomocdockeru">Instalace pomocí Dockeru</h2>

<p>Pokud máte nainstalovaný Docker a nástroj docker-compose, je situace o něco jdnodušší. Stažený repozitář obsahuje soubor <code>docker-compose.yml</code>, ve kterém je definované, jak jednotlivé kontejnery spustit. Příkazem <code>docker-compose up</code> se tedy vytvoří a spustí vše potřebné (Elasticsearch, Kibana, Cerebro). Celý postup od klonování repozitáře z GitHubu tedy vypadá následovně:</p>

<pre><code class="language-bash">git clone git@github.com:ludekvesely/elasticsearch-tutorial.git  
cd elasticsearch-tutorial  
docker-compose up  
</code></pre>

<p>Po spuštění jsou k dispozici všechny služby na stejných portech jako při nativní instalaci. Po skončení práce je možné celý stack zastavit příkazem <code>docker-compose stop</code>, případně kompletně smazat příkazem <code>docker-compose rm</code>.</p>

<h2 id="instalacevewindows">Instalace ve Windows</h2>

<p>Ve Windows možná nemáte dostupné všechny nástroje pro stažení a spuštění Elasticsearch, případně dáváte přednost grafickému rozhraní při instalaci. Pro tento případ je k dispozici MSI instalátor, který vás instalací a konfigurací provede. Podrobný návod jak jej použít je k dispozici v <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/_installation.html#_installation_example_with_msi_windows_installer">dokumentaci Elasticsearch</a>.</p>

<h2 id="shrnut">Shrnutí</h2>

<p>V této kapitole jsme stáhnuli Elasticsearch, doinstalovali dopňky nutné pro správnou funkci českého vyhledávání a následně jej spustili. Z tohoto stavu vychází další díly totho seriálu. Nyní můžeme přejít k ukládání prvních dokumentů v <a href="https://www.ludekvesely.cz/serial-elasticsearch-3-prvni-kroky-ukladani-a-zobrazeni-dat/">následující kapitole</a>.</p>]]></content:encoded></item><item><title><![CDATA[Seriál Elasticsearch: 1. Základní pojmy]]></title><description><![CDATA[Elasticsearch - základní pojmy, které musíte znát: shardy, repliky, dokumenty, index, mapování, invertovaná index, fulltextové vyhledávání.]]></description><link>https://www.ludekvesely.cz/serial-elasticsearch-1-zakladni-pojmy/</link><guid isPermaLink="false">03c99a06-ceef-48a3-8e55-4d4f3c35b0f0</guid><category><![CDATA[Elasticsearch]]></category><dc:creator><![CDATA[Luděk Veselý]]></dc:creator><pubDate>Thu, 31 Aug 2017 23:31:35 GMT</pubDate><content:encoded><![CDATA[<p>Než se pustíme do instalace a budování vyhledávání, považuji za důležité se seznámit se základními pojmy a principy, které nás budou celým seriálem provázet. Vysvětluji zde jak pojmy, které souvisí s fulltextovým vyhledáváním, tak také ty, které jsou důležité při práci s nástrojem Elasticsearch.</p>

<h2 id="indexydokumenypoletypy">Indexy, dokumeny, pole, typy</h2>

<p>Tak jako se v relační databázi setkáváme s tabulkami, sloupci a řádky, tak zde se setkáváme s pojmy obdobnými. Elasticsearch ale není relační databáze, jde o dokumentové úložiště, zařadit jej můžeme do NoSQL databází.</p>

<p><strong>Dokument</strong> je textový soubor, který obsahuje informace, v nichž bude probíhat vyhledávání. V případě Elasticsearch jde konkrétně o soubor formátu JSON. Pokud bychom mluvili o produktu prodávaném v e-shopu, dokument uložený v Elasticsearch by mohl v nejjednodušší podobě vypadat následovně:</p>

<pre><code class="language-js">{
  "id": 102146,
  "title": "Apple iPhone 7 32GB bílý",
  "brand": "Apple",
  "price": 21190
}
</code></pre>

<p>Dokument je tvořen poli (<strong>fields</strong>) různých datových typů. V případě Elasticsearch není nutné je definovat předem, Elasticsearch je ve výchozím stavu vytvoří sám na základě struktury dokumentu. Proto je označován jako bezschémové úložiště. V rámci indexu však musí mít jedno pole vždy totožný datový typ. Není možné například uložit id jednou jako integer a podruhé jako string - na to je třeba pamatovat, pokud data kopírujete z jiné databáze typu MongoDB.</p>

<p>Dokumenty jsou ukládány do <strong>indexu</strong>, což je obdoba databázového schématu ve světě relačních databází. Na jeho úrovni je možné nastavovat parametry úložiště společné pro celou sadu dokumentů. V rámci indexu jsou pak definovány typy (<strong>type</strong>), což označuje skupinu dokumentů obdobného tvaru. Pokud bychom chtěli do Elasticsearche ukládat produkty a objednávky, bylo by možné je uložit do jednoho indexu a vytvořit dva typy - orders a products. V praxi je však výhodnější takto odlišné dokumenty uložit do různých indexů, protože většina konfigurace je dostupná právě na úrovni indexu. Pokud by měl produkt a objednávka pole se stejným názvem, ale různým datovým typem, nebylo by možné je ani do společného indexu uložit.</p>

<p>Můžeme zde nalézt analogii k přístupu relačních databází. Pro představu by se dal vztah mezi pojmy Elasticsearch a relační databáze vyjádřit následovně:</p>

<table>  
<thead style="background-color:#CCC">  
</thead><td><b>Elasticsearch</b></td><td><b>Relační databáze</b></td>  
  
<tbody>  
<tr>  
<td> Index </td><td> Databáze </td>  
</tr>  
<tr>  
<td> Typ (type) </td><td> Tabulka </td>  
</tr>  
<tr>  
<td> Dokument (document) </td><td> Záznam (řádek tabulky) </td>  
</tr>  
<tr>  
<td> Pole (field) </td><td> Atribut (sloupec tabulky) </td>  
</tr>  
</tbody>  
</table>

<h2 id="clusterreplikyshardy">Cluster, repliky, shardy</h2>

<p>Elasticsearch je od počátku navržen tak, aby běžel v cloudu. Při produkčním nasazení tak budete pravděpodobně chtít vytvořit <strong>cluster</strong> - nasadit jej na více serverů, což umožní distribuovat zátěž a zvýšit dostupnost služby. </p>

<p>K tomu, aby mohl být index dostupný na více serverech, jsou využívány <strong>shardy</strong>, což označuje fyzické rozdělení indexu na více částí. Při rozdělení indexu na shardy lze urychlit vyhledávání - dotazy jsou spouštěny na každém shardu zvlášť, dochází tak k jejich paralelizaci. </p>

<p>Aby bylo zajištěno, že nedojde ke ztrátě dat, jsou k shardům vytvářeny jejich repliky. Pokud tak dojde k výpadku serveru, pravděpodobně se nachází kopie ztracených dat na některém z dalších serverů (<strong>nodů</strong>), která je ihned využita a automaticky replikována na zdravé servery.</p>

<p>V rámci tohoto tutoriálu se nastavení shardů a replik budeme věnovat až v samém závěru, pro lokální vývoj je dostačující vytvoření jediného shardu bez replik. Takové nastavení pravděpodobně postačí i na první produkční spuštění, je však třeba počítat s tím, že data nejsou nikde replikována.</p>

<h2 id="fulltextovvyhledvn">Fulltextové vyhledávání</h2>

<p>Přestože to může znít triviálně, definujme ještě pojem fulltextové vyhledávání. Uživatel si nejprve musí představit, co chce vlastně vyhledávat a na základě této představy zformulovat dotaz, který zadá do vyhledávacího pole. Vyhledávač musí následně vyhodnotit, co chtěl uživatel vyhledat a vrátit mu co nejrelevantnější výsledky. A právě to je to obtížné na celém vyhledávání - výsledky jsou psané člověkem přirozeným jazykem se všemi chybami a nepřestnostmi, které jsou s tím spojeny. Naším cílem je však dodat takové výsledky, které jej uspokojí a dodají mu tak určitý zážitek z proběhlého vyhledávání. Lidsky řečeno - uživatel najde to, co hledá.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/08/dp-ludek-vesely_pdf__page_25_of_78_.png" alt="Schéma fulltextového vyhledávání"></p>

<h2 id="indexace">Indexace</h2>

<p>Indexace je proces, při kterém jsou dokumenty ukládány do (invertovaného) indexu. Nejde o nějakou specialitu Elasticsearch, pojem jako takový je znám mnohem déle a označuje proces, při kterém jsou textové dokumenty ukládány do speciálního úložiště (indexu), ve tvaru optimalizovaném pro vyhledávání. Představte si to jako rejstřík knihy, ve kterém jsou uspořádány důležité termíny v základním tvaru, seřazeny podle abecedy. Bylo by totiž nemyslitelné pro nalezení hledaného termínu procházet celou knihu, stránku po stránce.</p>

<p>Při indexaci se tedy dokument transformuje do tvaru, který umožňuje vyhledávání. Takové transformace jsou například použití pouze relevantních slov (a vypuštění těch nedůležitých), jejich převedení na základní tvar (jednotné číslo, první pád...) a následné uložení do vhodného úložiště.</p>

<hr>

<p>Tímto jsme se seznámili s základními pojmy a můžeme se pustit do <a href="https://www.ludekvesely.cz/serial-elasticsearch-2-instalace/">instalace</a> Elasticsearch a dalších potřebných nástrojů.</p>]]></content:encoded></item><item><title><![CDATA[Seriál Elasticsearch: Úvod]]></title><description><![CDATA[Seriál o vyhledávání pomocí Elasticsearch. Začátečníky i pokročilé seznámí s Elasticsearch a vysvětlí, jak implementovat fulltextové vyhledávání v češtině.]]></description><link>https://www.ludekvesely.cz/serial-elasticsearch-uvod/</link><guid isPermaLink="false">db101026-f141-40e2-971b-e53fd42773de</guid><category><![CDATA[Elasticsearch]]></category><dc:creator><![CDATA[Luděk Veselý]]></dc:creator><pubDate>Thu, 31 Aug 2017 23:31:11 GMT</pubDate><media:content url="https://www.ludekvesely.cz/content/images/2017/08/elastic.png" medium="image"/><content:encoded><![CDATA[<img src="https://www.ludekvesely.cz/content/images/2017/08/elastic.png" alt="Seriál Elasticsearch: Úvod"><p>V tomto seriálu se budu snažit vysvětlit problematiku <strong>fulltextového vyhledávání</strong> a postupně jej implementovat pomocí nástroje <strong>Elasticsearch</strong>. Fulltextové vyhledávání je poměrně komplexní problematika, budu se ji tedy snažit rozdělit na několik samostatných částí a ty postupně projít. </p>

<p>Nebudu se zaměřovat na implementaci vyhledávacího nástroje pomocí konkrétního programovacího jazyka - knihovny pracující s Elasticsearch jsou obdobné napříč různými jazyky, a vesměs kopírují API, kterým disponuje Elasticsearch. Budu se snažit uvést i související teoretické znalosti - věřím, že hlubší znalost dané problematiky je užitečná k nalezení optimálního řešení problému.</p>

<p>V následujících dílech tohoto seriálu postupně projdu:</p>

<ol>
<li><a href="https://www.ludekvesely.cz/serial-elasticsearch-1-zakladni-pojmy/">Základní pojmy</a>  </li>
<li><a href="https://www.ludekvesely.cz/serial-elasticsearch-2-instalace/">Instalace Elasticsearch</a>  </li>
<li><a href="https://www.ludekvesely.cz/serial-elasticsearch-3-prvni-kroky-ukladani-a-zobrazeni-dat/">Ukládání a zobrazení dat</a>  </li>
<li><a href="https://www.ludekvesely.cz/serial-elasticsearch-4-fulltextove-vyhledavani-v-cestine/">Fulltextové vyhledávání v češtině</a>  </li>
<li><a href="https://www.ludekvesely.cz/serial-elasticsearch-5-pokrocile-fulltextove-vyhledavani/">Pokročilé fulltextové vyhledávání</a></li>
</ol>

<p>Na konci tohoto seriálu byste měli být schopni implementovat fulltextové vyhledávání v českém jazyce s pomocí Elasticsearch. Veškeré zdrojové kódy jsou dostupné na <a href="https://github.com/ludekvesely/elasticsearch-tutorial">GitHubu</a>.</p>

<h2 id="trochamotivacevodem">Trocha motivace úvodem</h2>

<p>A jaké jsou hlavní důvody, proč použít pro vyhledávání právě Elasticsearch? Především má <strong>velké množství funkcí</strong>, kterými lze vyhledávání vytvořit přesně na míru danému účelu. Elasticsearch disponuje podporou češtiny a dalších jazyků, poradí si se skloňováním, časováním, synonymy nebo našeptáváním. Zároveň však disponuje základním nastavením pro řadu jazyků, s kterým vyhledávání obstojně funguje bez složitého nastavování.</p>

<p>Dalším důvodem pro jeho použití je <strong>rychlost</strong>, s kterou je možné vyhledávát. Omezením není ani rostoucí množství dat, Elasticsearch je možné jednoduše horizontálně <strong>škálovat</strong>, stačí přidat další servery do clusteru a Elasticsearch se o zbytek postará sám. S tím souvisí také to, že data mohou být automaticky replikována mezi servery, při výpadku některého z nich tak nedochází k výpadku celé služby ani ztrátě dat.</p>

<p>Fulltextovým vyhledáváním však funkce Elasticsearch nekončí. Lze jej využít pro výpočty nad nalezenými daty, která mohou být například použita pro vytvoření filtrů na webu. Dalším možným použitím je vytvoření nástroje pro ukládání logů ze všech možných zdrojů a následné vytváření vizualizací a dashboardů pomocí nástroje Kibana.</p>

<p>Pokud jste připraveni vrhnout se do tajů fulltextového vyhledávání a Elasticsearch poznat důkladněji, pokračujte prosím <a href="https://www.ludekvesely.cz/serial-elasticsearch-1-zakladni-pojmy/">následující kapitolou</a>.</p>]]></content:encoded></item><item><title><![CDATA[Proč používat Kubernetes]]></title><description><![CDATA[<p>S rostoucí popularitou Dockeru roste i popularita nástrojů pro orchestraci kontejnerů. Mezi všemi nástroji <a href="https://apprenda.com/blog/kubernetes-now-leads-linkedin-awesome-stats-infographic/">tak trochu vyčnívá</a> Kubernetes. Zde je například vývoj zmínek o Kubernetes v otázkách na Stack Overflow:</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/03/kubernetesgrapg.png" alt="Kubernetes popularity in Stack Overflow questions"></p>

<p>Ke Kubernetes jsem se dostal při deploymentu nové verze portálu <a href="https://market.atoto.cz">atoto.cz</a> a ve srovnání s nástroji, které jsem používal</p>]]></description><link>https://www.ludekvesely.cz/proc-pouzivat-kubernetes/</link><guid isPermaLink="false">ae4f2f2b-5743-4a50-9adb-7ee91adc6af3</guid><category><![CDATA[Docker]]></category><category><![CDATA[Kubernetes]]></category><dc:creator><![CDATA[Luděk Veselý]]></dc:creator><pubDate>Thu, 31 Aug 2017 23:21:20 GMT</pubDate><media:content url="https://www.ludekvesely.cz/content/images/2017/03/k8s-ui-dashboard.png" medium="image"/><content:encoded><![CDATA[<img src="https://www.ludekvesely.cz/content/images/2017/03/k8s-ui-dashboard.png" alt="Proč používat Kubernetes"><p>S rostoucí popularitou Dockeru roste i popularita nástrojů pro orchestraci kontejnerů. Mezi všemi nástroji <a href="https://apprenda.com/blog/kubernetes-now-leads-linkedin-awesome-stats-infographic/">tak trochu vyčnívá</a> Kubernetes. Zde je například vývoj zmínek o Kubernetes v otázkách na Stack Overflow:</p>

<p><img src="https://www.ludekvesely.cz/content/images/2017/03/kubernetesgrapg.png" alt="Proč používat Kubernetes"></p>

<p>Ke Kubernetes jsem se dostal při deploymentu nové verze portálu <a href="https://market.atoto.cz">atoto.cz</a> a ve srovnání s nástroji, které jsem používal předtím (Docker Cloud, Swarm, Docker Compose) mi připadal jako něco obřího a komplikovaného. S tím, jak přibývaly problémy, které bylo třeba řešit, začalo se ukazovat, jak je tato platforma promyšlená a proč je o ni stále rostoucí zájem.</p>

<p>V tomto článku se pokusím vypíchnout ty nejzásadnější body, které považuji za killer-features jsou to dle mého důvody, kvůli kterým stojí za to Kubernetes alespoň vyzkoušet.</p>

<h3 id="klovnvysokdostupnost">Škálování, vysoká dostupnost</h3>

<p>Klíčovou vlastností Kubernetes je to že sám řeší nasazování kontejnerů, sleduje jejich dostupnost a snaží se efektivně využít dostupné výpočetní kapacity a udržet tak aplikaci co nejdostupnější. Kubernetes vytváří abstrakci nad dostupným hradwarem, všechny připojené servery jsou dostupné jako by se jednalo o jeden stroj.</p>

<p>Využívá k tomu objekty, které mají různý účel. Na nejnižší úrovni se pracuje s pody, což je nejmenší samostatně nasaditelná část aplikace. V jejich rámci jsou provozovány kontejnery, které jsou vždy nasazeny společně. Pro optimální distribuci zátěže je možné definovat, kolik replik (podů) bude mít každá služba. Ty jsou pak rozloženy napříč jednotlivými servery (nody). V rámci clusteru pak může existovat více namespaců, lze tak nasazovat více nezávislých aplikací na jeden cluster.</p>

<h3 id="zerodowntimedeployment">Zero downtime deployment</h3>

<p>Pokud nasazujete kontejnery do produkce, je třeba vymyslet mechanismus, jakým přepnout ze staré verze aplikace na novou. V případě samotného Dockeru je to poměrně obtížné - je třeba nakonfigurovat proxy, která směruje traffic na správný kontejner, poté nastartovat nový, následně přepnout proxy, pak ten starý odstranit... Nebo se prostě smířit s krátkým výpadkem.</p>

<p>V Kubernetes lze nasadit bez výpadku už jen pomocí správné konfigurace. Jednak je třeba nastavit <code>readinessProbe</code> (<a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/">dokumentace</a>). Pomocí této kontroly Kubernetes pozná, že je pod spuštěn (je nastartován web server atd.) a může na něj směřovat traffic. Druhou věcí je provedení <a href="https://kubernetes.io/docs/tutorials/kubernetes-basics/update-intro/">Rolling Update</a>. Pomocí tohoto nastavení Kubernetes ví, že má nejprve spouštět nové pody a průběžně vypínat ty staré, přičemž je definováno, kolik nejméně jich musí být v každou chvíli dostupných. Celá konfigurace deploymentu by pak vypadala následovně:</p>

<pre><code class="language-yaml">kind: Deployment  
apiVersion: extensions/v1beta1  
metadata:  
  name: rolling-update-deploy
spec:  
  replicas: 1
  template:
    metadata:
      labels:
        app: rolling-update-deploy
    spec:
      containers:
      - name: rolling-update-deploy
        image: gcr.io/google_containers/liveness
        readinessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
  minReadySeconds: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
</code></pre>

<p>V tomto útržku kódu je důležité:</p>

<ul>
<li>Je použit image <code>gcr.io/google_containers/liveness</code>, pravděpodobně ale bude chtít použít svůj vlastní</li>
<li>Kontrola probíhá zasláním HTTP požadavku na port <code>8080</code> a cestu <code>/health</code>, pro úspěch musí server vrátit kód <code>200</code></li>
<li>Rolling update je pomocí <code>maxSurge</code> a <code>maxUnavailable</code> nastaven tak, že je vždy jeden funkční pod k dispozici</li>
</ul>

<p>Samotný update aplikace pak probíhá následovně</p>

<ul>
<li>Je spuštěn nový pod</li>
<li>Každých 5 s se posílá HTTP request na <code>:8080/healthz</code></li>
<li>Jakmile request vrátí kód 200, Kubernetes service směřuje komunikaci na nový pod</li>
<li>Starý pod je odstraněn</li>
</ul>

<h3 id="sdlenkonfiguracheselkl">Sdílení konfigurací, hesel, klíčů</h3>

<p>V samotném Dockeru je problém s tím, jak hesla, tokeny nebo klíče. Lze je buď přímo psát do kódu aplikace, nebo ukládat do ENV prověnných, vždy je ale trochu problematické je sdílet napříč kontejnery.</p>

<p>Kubernetes využívá pro tento účel <a href="https://kubernetes.io/docs/concepts/configuration/secret/">secrets</a>, což jsou konfigurační soubory uložené v clusteru. Ty je pak možné připojit do kontejnerů jako volume dále s nimi pracovat.</p>

<h3 id="persistencedatvolumes">Persistence dat, volumes</h3>

<p>Přestože je Docker ideální pro běh bezstavových služeb, nevyhneme se nutnosti perzistence dat. Příkladem jsou databáze, u kterých je žádoucí, aby data v nich uložená přežila restart kontejneru. </p>

<p>V kuberntes je možné definovat <a href="https://kubernetes.io/docs/concepts/storage/volumes/">volumes</a>, které mohou být následně do kontejneru připojeny. Data tak mohou být uloženy na úložišti mimo cluster a není třeba se starat o to, na kterém serveru fyzicky existují. Takovým úložištěm může být GCE Persistent Disk, AWS EBS Volume nebo některé z dalších <a href="https://kubernetes.io/docs/concepts/storage/volumes/#types-of-volumes">podporovaných úložišť</a>.</p>

<h3 id="cronjobstasks">Cron jobs, tasks</h3>

<p>V praxi je kromě stále běžících služeb (databáze nebo webový server) nutné nasazovat skripty, které jsou pouštěny jednou, nebo v pravidelných intervalech. Na fyzickém serveru je pro tento účel typicky využit Cron, ve světě kontejnerů je však situace složitější. Možností je instalovat Cron přímo do kontejnerů, Kubernetes však tuto funkcionalitu nabízí sám o sobě. </p>

<p>V případě jednou spouštěných příkazů lze využít <a href="https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/">Jobs</a>. V nich je definován kontejner, který běží do svého ukončení. Jejich speciální variantou jsou <a href="https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/">Cron Jobs</a>, které navíc obsahují definici časů, kdy se mají spouštět.</p>

<h3 id="komunitadokumentace">Komunita, dokumentace</h3>

<p>Kubernetes disponuje <a href="https://kubernetes.io/docs/reference/">obsáhlou dokumentací</a>, která popisuje veškerou dostupnou funkčnost. Ta je doplněna řadou <a href="https://kubernetes.io/docs/tutorials/">tutoriálů</a>, které usnadňují první kroky při seznamování s Kubernetes.</p>

<p>Díky stále rostoucí populárnosti Kubernetes existuje řada návodů, které oficiální materiály doplňují a popisují řešení konkrétních problémů. Na <a href="https://github.com/kubernetes/kubernetes">GitHubu</a> je pak možné sledovat rapidní vývoj této platformy a řešit případné problémy.</p>

<h2 id="nevhody">Nevýhody</h2>

<p>Samozřejmě, že Kubernetes neřeší všechny problémy jen tak a je třeba počítat s jistými nevýhodami, nebo spíše vlastnostmi tohoto nástroje. Každý už si musí zvážit, jestli pro jeho situaci bude vhodné použít Kubernetes, nebo se poohlédnout po jiném řešení.</p>

<h3 id="komplexnost">Komplexnost</h3>

<p>Kubernetes je rozsáhlý systém a jaho pochopení zabere nějaký čas. S tím je třeba počítat. Dokumentace je sice přesná a lze v ní nalézt vše potřebné, nicméně i její načtení nějaký čas zabere. </p>

<p>Může se také zdát, že triviální spuštění jednoho kontejneru je složité oproti spouštění přes Docker Compose. To, co v docker-compose.yml zabere pár řádků znamená v konfiguraci Kubernetes vytvoření deploymentu s šablonou podu, vytvoření service, případně dalších pomocných objektů.</p>

<p>Problém s komplexností se snaží řešit projekt <a href="http://rancher.com/kubernetes/">Rancher</a>, který tvoří jakousi obálku nad API Kubernetes a umožňuje s ním komunikovat skrz pěkné uživatelské rozhraní, které je navíc kompatibilní s konfiguračními soubory pro Docker Compose.</p>

<h3 id="nekompatibilitasdockercliacompose">Nekompatibilita s Docker CLI a Compose</h3>

<p>S komplexností systému souvisí i to, že je Kubernetes nekompatibilní se soubory <code>docker-compose.yml</code> a příkazy jako <code>docker run ...</code>. To nebylo možné použít vzhledem k množství funkcí a konfigurací, které má Kubernetes navíc. Příkaz <code>kubectl</code> se prostě chová jinak a musíte se jej naučit používat. To může být problém, pokud používáte Docker na lokále i na produkci - najednou je třeba mít vše dvakrát.</p>

<p>Řešení jsou vzásadě tři - buď se naučit pracovat s <code>kubectl</code> a jiným formátem konfiguračních soborů, což je dle mého nejlepší řešení. Další možností je spustit si Kubernetes lokálně pomocí <a href="https://github.com/kubernetes/minikube">Minikube</a>, v tu chvíli používám jeden nástroj u sebe i na produkci. Třetí možností je opět komunikovat s Kubernetes skrz Rancher. a použít tak stávající soubory <code>docker-compose.yml</code>.</p>

<h3 id="cena">Cena</h3>

<p>Posledním aspektem, který je třeba zvážit, je cena provozu clusteru. Pokud si Kubernetes spouštíte na svém železe, nic se nemění, samotný Kubernetes je zdarma. Ve chvíli, kdy se člověk rozhodne přejít do cloudu, je cena za provoz serverů prostě vyšší - je třeba zvážit, zda je cena za přechod adekvátní. </p>

<p>Konkrétně přechod do Google Cloudu má řadu pozitiv - cluster je spuštěn během chvíle, není třeba se starat o provoz master nodu, aktualizace. Vše je snadno napojitelné na zbytek cloudu (úložiště, load balancery), snadno lze nastavit automatické škálování. Cena oproti stejně výkonnému serveru hostovanému u levnějšího providera je ale vyšší.</p>

<h2 id="zdroje">Zdroje</h2>

<ul>
<li><a href="https://kubernetes.io/docs/">Dokumentace Kubernetes - tutoriály, návody, popis API</a></li>
<li><a href="https://cloud.google.com/container-engine/">Google Container Engine (GKE) - Kubernetes spravovaný Googlem</a></li>
<li><a href="http://rancher.com/kubernetes/">Rancher - orchestrace kontejnerů podporující Kubernetes</a></li>
<li><a href="http://info.rancher.com/deploying-scaling-kubernetes-ebook">E-book o nasazování Kubernetes pomocí Rancher</a></li>
</ul>]]></content:encoded></item><item><title><![CDATA[Diplomová práce: Vyhledávání jako služba]]></title><description><![CDATA[<p>V rámci své diplomové práce jsem se rozhodl vytvořit nástroj, který bude poskytovat fulltextové vyhledávání jako služba, přičemž hlavní sílovou skupinou jsou e-shopy.</p>

<p>Typický <strong>případ použití</strong> vypadá následovně:</p>

<ul>
<li>Provozovatel e-shopu chce implementovat fulltextové výhledávání, má k dispozici data v XML formátu</li>
<li>Zaregistruje se v službě a zadá URL souboru s</li></ul>]]></description><link>https://www.ludekvesely.cz/diplomova-prace-vyhledavani-jako-sluzba/</link><guid isPermaLink="false">b075d0c1-5ff1-4728-ac77-15b93c6b456b</guid><category><![CDATA[Škola: FM TUL a FIS VŠE]]></category><dc:creator><![CDATA[Luděk Veselý]]></dc:creator><pubDate>Wed, 26 Jul 2017 22:58:52 GMT</pubDate><media:content url="https://www.ludekvesely.cz/content/images/2017/07/apisearch-screen-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://www.ludekvesely.cz/content/images/2017/07/apisearch-screen-1.png" alt="Diplomová práce: Vyhledávání jako služba"><p>V rámci své diplomové práce jsem se rozhodl vytvořit nástroj, který bude poskytovat fulltextové vyhledávání jako služba, přičemž hlavní sílovou skupinou jsou e-shopy.</p>

<p>Typický <strong>případ použití</strong> vypadá následovně:</p>

<ul>
<li>Provozovatel e-shopu chce implementovat fulltextové výhledávání, má k dispozici data v XML formátu</li>
<li>Zaregistruje se v službě a zadá URL souboru s produkty</li>
<li>Následně obdrží přístup k API, vyhledávání na e-shop implementuje jako volání tohoto API</li>
</ul>

<p>A <strong>proč</strong> by měl někdo takovou službu <strong>používat</strong>?</p>

<ul>
<li>Zpracování českého jazyka není triviální</li>
<li>Implementace vyhledávání svépomocí zabere nějaký čas</li>
<li>Rychlost vyhledávání je klíčová (s rostoucí zítěží a množstvím dat)</li>
</ul>

<iframe width="560" height="315" src="https://www.youtube.com/embed/_9wyPL2JbUQ" frameborder="0" allowfullscreen></iframe>  

<p><em>Video: Ukázka aplikace z pohledu uživatele</em></p>

<h3 id="detailyimplementace">Detaily implementace</h3>

<p>Pro samotné vyhledávání a ukládání dat jsem použil <strong>Elasticsearch</strong> z důvodu podpory velkého množství funkcí pro fulltextové vyhledávání, výkonnost a snadnou škálovatelnost. Veškerá funkčnost služby je dostupná přes API, přičemž aplikace je psaná v jazyce <strong>Go</strong>. Hlavním důvodem je rychlost - ať už jde o samotnou aplikaci, rychlost vývoje nebo deploymentu. Aplikace je nasazována automaticky pomocí <strong>CircleCI</strong> a její distribuce je řešena pomocí nástroje <strong>Docker</strong>. Zdrojové kódy jsou uloženy na <strong>GitHubu</strong> a dokumentace API je dostupná v nástroji <strong>Apiary</strong>.</p>

<h3 id="zdrojeaodkazy">Zdroje a odkazy</h3>

<ul>
<li><a href="http://public.ludekvesely.cz/diplomova-prace/Diplomova-prace-Ludek-Vesely.pdf">Text diplomové práce v PDF</a></li>
<li><a href="https://github.com/ludekvesely/diploma-thesis">Zdrojové kódy textu práce (LaTex)</a></li>
<li><a href="https://github.com/apisearch">Zdrojové kódy aplikace na github.com</a></li>
<li><a href="http://docs.apisearch.apiary.io/#reference">Dokumentace API na apiary.com</a></li>
<li><a href="https://apisearch.ludekvesely.cz/">Nasazená aplikace</a> (aktuálně pozastavena)</li>
<li><a href="https://youtu.be/_9wyPL2JbUQ">Screencast, ukázka aplikace na youtube.com</a></li>
</ul>]]></content:encoded></item><item><title><![CDATA[Centralizace logů Dockeru pomocí Elastic Stacku]]></title><description><![CDATA[Návod jak zprovoznit agregaci logů v Dockeru pomocí logspout a ELK stacku. Spírejte logy kontejnerů, ukládejte je do Elasticsearche a vizualizujte v Kibaně.]]></description><link>https://www.ludekvesely.cz/docker-a-logovani/</link><guid isPermaLink="false">5f6e52de-3482-4801-96e5-523e13f8a149</guid><category><![CDATA[Docker]]></category><category><![CDATA[Elasticsearch]]></category><dc:creator><![CDATA[Luděk Veselý]]></dc:creator><pubDate>Tue, 03 May 2016 22:00:39 GMT</pubDate><media:content url="https://www.ludekvesely.cz/content/images/2016/01/elk.png" medium="image"/><content:encoded><![CDATA[<img src="https://www.ludekvesely.cz/content/images/2016/01/elk.png" alt="Centralizace logů Dockeru pomocí Elastic Stacku"><p>Dokud vám na serveru běží pár kontejnerů, dají se jejich logy procházet ručně pomocí příkazu <code>docker logs</code>. S rostoucím počtem kontejnerů je ale obtížnější v logách něco najít. V tu chvíli může situaci usnadnit agregace logů. Jakmile máte logy na jednom místě, můžete v nich vyhledávat a filtrovat, je práce s nimi snažší. Jedním z nástrojů, který toto umožňuje se jmenuje <strong>Elastic Stack</strong>, dříve známý jako ELK Stack.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/05/logy-agregace.png" alt="Centralizace logů Dockeru pomocí Elastic Stacku"></p>

<p>Pro absolvování tohoto tutoriálu předpokládám nainstalovaný <code>Docker</code> a <code>docker-compose</code>. Pokud jej ještě nemáte, postupujte podle <a href="https://docs.docker.com/engine/installation/">oficiálního návodu</a>. Tento článek předpokládá, že Docker běží nativně a kontejnery jsou dostupné přes <code>localhost</code>. Protože je ale tato funkčnost zatím oficiálně jen na Linuxu (pro OS X je ve verzi beta), je možné, že máte docker ve virtuálním stroji vytvořeným pomocí <code>docker-machine</code>. V tom případě nahraďte <code>localhost</code> ip adresou, kterou získáte pomocí <code>docker-machine ip</code>.</p>

<h2 id="elasticstack">Elastic Stack</h2>

<p>Elastic Stack sám o sobě není jediná aplikace, jde o sadu tří nástrojů, konkrétně se jedná o Elasticsearch, Logstash a Kibana. Ty bývají často použity společně (a například u Kibany by to ani jinak nešlo, protože se umí připojit jen na Elasticsearch).</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/05/elk-stack-wide.png" alt="Centralizace logů Dockeru pomocí Elastic Stacku"></p>

<p>Elastic stack oproti ELK stacku nenabízí prakticky nic nového, jde především o sjednocení verzí, nemusíte si tedy hlídat, které verze Elasticsearche, Kibany a Logstashe jsou vzájemně kompatibilní. Více na webu <a href="https://www.elastic.co/v5">elastic.co</a>. Pojďme se nyní podívat na jednotlivé části stacku.</p>

<h3 id="logstash">Logstash</h3>

<p>Logstash je nástroj, který umí vzít nějaký vstup, transformovat jej a následně jej někam uložit. Do konfiguračního souboru můžete zapsat všemožné kombinace vstupů, výstupů a filtrů. Vstupem může být soubor, data v Redisu nebo nějakém message brokeru. Můžete je také pouze nechat naslouchat na určeném portu a data mu dodávat z vaší aplikace, nebo použít nějaký jiný nástroj. Výstupem může být například Elasticsearch, soubor, standardní výstup... Popis všech možností by byl hodně dlouhý, odkážu radši na oficiální dokumentaci, kde můžete najít jak <a href="https://www.elastic.co/guide/en/logstash/current/input-plugins.html">možné vstupy</a> tak <a href="https://www.elastic.co/guide/en/logstash/current/output-plugins.html">výstupy</a>. Filtrem se rozumí různé transformace a parsování vstupních dat, například rozpadnutí záznamu v Apache access logu, CSV, JSONu...</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/05/logstash-wide.png" alt="Centralizace logů Dockeru pomocí Elastic Stacku"></p>

<h3 id="elasticsearch">Elasticsearch</h3>

<p>Elasticsearch je škálovatelné úložiště, do kterého můžete uložit lobovolný dokument ve formátu JSON. Je bezschémový, nemusíte tedy předem definovat sloupce jako v relační databázi. Je také škálovatelný, stačí spustit více instancí, data se automaticky rozloží mezi nody a dotaz můžete poslat na kterýkoliv z nich. Disponuje velmi dobrým fulltextovým vyhledáváním, pro které bývá často primárně použit, to však v tomto případě nevyužijeme.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/05/elasticsearch-wide-1.png" alt="Centralizace logů Dockeru pomocí Elastic Stacku"></p>

<h3 id="kibana">Kibana</h3>

<p>Kibana je poslední částí, se kterou budete nakonec pracovat nejčastěji. Je to webové rozhraní, které umí číst data z Elasticsearche a z nich vytvářet grafy, tabulky, metriky. Nad vytvořenými dashboardy můžete fulltextově vyhledávat, filtrovat data. Vytvořený dashboard v Kibaně může vypadat například takto:</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/05/kibana-wide.png" alt="Centralizace logů Dockeru pomocí Elastic Stacku"></p>

<h3 id="sputnelasticstacku">Spuštění Elastic Stacku</h3>

<p>Protože budeme agregovat logy Dockeru, předpokládám, že máte Docker nainstalovaný. Spustit samotný Elastic Stack je otázkou vytvoření souboru <code>docker-compose.yml</code> a následným spouštěním pomocí nástroje <code>docker-compose</code>. </p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/05/elastic-stack-docker-compose-2.png" alt="Centralizace logů Dockeru pomocí Elastic Stacku"></p>

<p>V soboru <code>docker-compose.yml</code> definuji tři sekce, z káždé následně vznikne jeden kontejner. Pro každou sekci využiji oficiální Docker image. Důležité je zpřístupnění portu <code>5601</code>, abychom mohli otevřít Kibanu v browseru. V commandu logstashe je definováno jak bude spuštěn. Parametr <code>-e</code> znamená, že bude následovat konfigurace (která je jinak v souboru). V ní je definován jako vstup podt <code>5000</code> a jako výstup Elasticsearch. Vstupní port <code>5000</code> je také zveřejněn.</p>

<pre><code class="language-yml">kibana:  
  image: kibana
  links:
    - 'elastic:elasticsearch'
  ports:
    - '5601:5601'
logstash:  
  image: logstash
  command: 'logstash -e "input { tcp { port =&gt; 5000 } } output { elasticsearch { hosts =&gt; elastic } }"'
  ports:
    - '5000/tcp:5000/tcp'
  links:
    - elastic
elastic:  
  image: elasticsearch
</code></pre>

<p>Uložte tento soubor a následně spusťte příkaz <code>docker-compose up</code>. Mělo by být vidět, jak jednotlivé služby startují:</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/05/docker-compose-up.png" alt="Centralizace logů Dockeru pomocí Elastic Stacku"></p>

<p>Nyní zapíšeme do Logstashe jednu zprávu. Otevřete další terminál a zadejte:</p>

<pre><code>nc localhost 5000 &lt;&lt;&lt; "This is my log message"  
</code></pre>

<p>Zpráva <code>This is my log message</code> by měla být uložená v Elasticsearch. Nyní otevřete prohlížeč na adrese <a href="http://localhost:5601">http://localhost:5601</a> a potvrďte výchozí nastavení. Dále přejděte do sekce Discover, měla by tam být vidět zalogovaná zpráva.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/04/kibana-start-pattern.png" alt="Centralizace logů Dockeru pomocí Elastic Stacku">
<img src="https://www.ludekvesely.cz/content/images/2016/04/kibana-discover.png" alt="Centralizace logů Dockeru pomocí Elastic Stacku"></p>

<p>Pokud jste došli až sem, gratuluji, právě jste úspěšně spustili ELK Stack, zapsali zprávu do Logstashe, který ji uložil do Elasticsearche a následně zobrazili v Kibaně.</p>

<h2 id="propojendockeruaelasticstacku">Propojení Dockeru a Elastic Stacku</h2>

<p>Nyní stojíme před otázkou jak dostat logy kontejnerů do Logstashe. To je vzásadě možné několika způsoby:</p>

<h5 id="1logujesamotnaplikace">1. Loguje samotná aplikace</h5>

<p>Pokud máte v kontejneru vlastní aplikaci, můžete do ní doprogramovat zápis logů do Logstashe. Například v PHP pomocí Monologu - nastavíte adresu a port a vesele logujete. Zde je problém, jak získat ostatní logy, například webserveru nebo databáze. Lepší je dle mého všechny logy směřovat na standardní výstup a o sběr logů se postarat na úrovni Dockeru.</p>

<h5 id="2aplikacelogujedosouborulogstashjejtejakovstup">2. Aplikace loguje do souboru, Logstash jej čte jako vstup</h5>

<p>Pokud už nyní logujete do souboru, nabízí se možnost jej zpřístupnit jako volume a Logstash nakonfigurovat, aby jej použil jsko vstup. Zde jsou problémy stejné jako výše, navíc je problém se sdílením souborů, pokud Docker běží na víc serverech. </p>

<h5 id="3aplikacelogujenastandardnvstuplogysbrdalsluba">3. Aplikace loguje na standardní výstup, logy sbírá další služba</h5>

<p>Toto je lepší varianta - ke všem logům, které kontejnery produkují přistupuji jednotným způsobem. Ve výchozím stavu Docker zapisuje tyto logy jako JSON do filesystemu, zbývá tedy zajistit čtení těchto souborů.</p>

<h5 id="4aplikacelogujenastandardnvstupkontejnermnastavenyloggingdrivers">4. Aplikace loguje na standardní výstup, kontejner má nastaveny logging-drivers</h5>

<p>Obdoba předchozí varianty s tím rozdílem, že kontejneru při spuštění řeknete kam má logovat. Kontejner tak aktivně zapisuje logy na dané umístění, v předchozím případě je kontejner pasivní a logy jsou sbírány. Nahradíte tak výchozí logování do souboru ve formátu JSON. Tato funkčnost byla do Dockeru přidána ve verzi 1.6, <a href="https://docs.docker.com/engine/admin/logging/overview/">aktuálně je k dispozici několik driverů</a>, mezi issues na GitHubu jsem nalezl zmínku o <a href="https://github.com/docker/docker/issues/14949">driveru pro logstash</a>, aktuálně je asi nejlepší variantou syslog.</p>

<p>Dále budu popisovat třetí variantu, protože umožní zapnutí logování nezávisle na běžících kontejnerech. Není teda nic měnit, jen se spustí další služba, která logy sbírá a přeposílá do Logstashe.</p>

<h3 id="sbrlogkontejnerlogspout">Sběr logů kontejnerů: logspout</h3>

<p>Pro tento účel byl vytvořen nástroj <strong>logspout</strong> (<a href="https://github.com/gliderlabs/logspout">GitHub</a>, <a href="https://hub.docker.com/r/gliderlabs/logspout/">Docker Hub</a>). Jde o připravený image, který se spustí, zpřístupní se mu socket docker démona skrz který čte logy všech běžících kontejnerů a přeposílá je dál. Automaticky zjišťuje nové kontejnery, takže existující stacky lze používat beze změny.</p>

<p>Propojení docker démona a logspoutu je pouze zpřístupněním souboru <code>/var/run/docker.sock</code>. Zbývá propojit logspout a Logstash. Logspout má několik adaptérů pro směřování výstupu: tcp, udp, syslog... Upravíme tedy soubor <code>docker-compose.yml</code> - doplníme sekci <code>logspout</code>:</p>

<pre><code class="language-yml">logspout:  
  image: gliderlabs/logspout:v3
  command: 'udp://logstash:5000'
  links:
    - logstash
  volumes:
    - '/var/run/docker.sock:/tmp/docker.sock'
kibana:  
  image: kibana
  links:
    - 'elastic:elasticsearch'
  ports:
    - '5601:5601'
logstash:  
  image: logstash
  command: 'logstash -e "input { udp { port =&gt; 5000 } } output { elasticsearch { hosts =&gt; elastic } }"'
  links:
    - elastic
elastic:  
  image: elasticsearch
</code></pre>

<p>Dále jsem provedl změny v sekci <code>logstash</code>. Smazal jsem část s nastavením portů - k Logstashi přistupuje pouze logspout, což je automaticky zajištěno (kontenery v stacku na sebe vidí jako by byly na lokální síti), není tedy nutné ho zveřejňovat. Dále jsem upravil vstup Logstashe z <code>tcp</code> na <code>udp</code>. Nějakou dobu totiž trvá, než se Logstash spustí a pokud použiji spojení <code>tcp</code>, logspout ihned po spuštění skončí, protože se nemůže připojit na spouštějící se Logstash. UDP je nespojované spojení, logspout tak stále skouší zprávy poslat a je mu jedno, jestli na daném portu něco běží nebo ne, nevyžaduje žádné potvrzení.</p>

<p>Pokud vám ještě běžý starý stack, zabijte jej pomocí <code>ctrl + c</code>, případně i smažte kontejnery pomocí <code>docker-compose rm -f</code>. Nový stack spusťte opět příkazem <code>docker-compose up</code>.</p>

<p>Nyní jsou logy všech kontejnerů routovány přes logspout a Logstash do Elasticsearche. Můžeme zkusit spustit kontejner, který pouze zaloguje jednu zprávu a skončí:</p>

<pre><code>docker run --rm alpine echo Hello world  
</code></pre>

<p>Pokud se podíváte do Kibany na <a href="http://localhost:5601">http://localhost:5601</a>, na záložce Discover by měla být vidět jako poslední zalogovaná zpráva <code>Hello world</code>:</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/05/kibana-hello-world.png" alt="Centralizace logů Dockeru pomocí Elastic Stacku"></p>

<p>Jistě jste si všimli spousty zpráv zalogovaných níže. Logspout automaticky přijímá logy všech kontejnerů, tedy i běžící Kibany, Logstashe a Elasticsearche. Pokud nechcete logy některého kontejneru zpracovávat, je třeba mu přidat proměnnou prostředí <code>LOGSPOUT</code> shodnotou <code>ignore</code>. To je možné doplněním do souboru <code>docker-compose.yml</code>. Funkční konfigurace, kdy jsou agregovány logy všech kontejnerů kromě spuštěného Elastic Stacku by tedy vypadala následovně:</p>

<pre><code class="language-yml">logspout:  
  image: gliderlabs/logspout:v3
  command: 'udp://logstash:5000'
  links:
    - logstash
  volumes:
    - '/var/run/docker.sock:/tmp/docker.sock'
kibana:  
  image: kibana
  links:
    - 'elastic:elasticsearch'
  ports:
    - '5601:5601'
  environment:
    - LOGSPOUT=ignore
logstash:  
  image: logstash
  command: 'logstash -e "input { udp { port =&gt; 5000 } } output { elasticsearch { hosts =&gt; elastic } }"'
  links:
    - elastic
  environment:
    - LOGSPOUT=ignore
elastic:  
  image: elasticsearch
  environment:
    - LOGSPOUT=ignore
</code></pre>

<h3 id="pidnnzvukontejnerudolog">Přidání názvu kontejneru do logů</h3>

<p>Pokud použijeme spojení logspoutu a Logstashe přes UDP, nezjistíme z jakého kontejneru logy pochází. Abychom to zjistili, je nutné použít jako adaptér syslog. Pro to je třeba upravit nastavení logspoutu i Logstashe. V případě logspoutu stačí upravit v <code>docker-compose.yml</code> část <code>command: 'udp://logstash:5000'</code> na <code>command: 'syslog://logstash:5000'</code>. Obdobnou úpravu je třeba provést na vstupu Logstashe - namísto <code>input { udp { port =&gt; 5000 } }</code> použít <code>input { syslog { port =&gt; 5000 } }</code>. Do message uložené v Elasticsearch se tak dostane několik dalších informací, mimo jiné název kontejneru a název stacku. Taková zpráva vypadá následovně:</p>

<pre><code>2016-05-03T22:32:26.510Z 172.17.0.5 &lt;14&gt;1 2016-05-03T22:32:26Z 79445d78c31a hopeful_goldstine 1936 - - Hello world  
</code></pre>

<p>To však není úplně přehledné, je tedy ještě přidat sekci <code>filter</code>, která tuto zprávu zparsuje:</p>

<pre><code>filter {  
  grok {
    match =&gt; { "message" =&gt; "%{SYSLOG5424PRI}%{NONNEGINT:ver} +(?:%{TIMESTAMP_ISO8601:ts}|-) +(?:%{HOSTNAME:service}|-) +(?:%{NOTSPACE:containerName}|-) +(?:%{NOTSPACE:proc}|-) +(?:%{WORD:msgid}|-) +(?:%{SYSLOG5424SD:sd}|-|) +%{GREEDYDATA:msg}" }
  }
  syslog_pri { }
  date {
    match =&gt; [ "syslog_timestamp", "MMM  d HH:mm:ss", "MMM dd HH:mm:ss" ]
  }
  mutate {
    remove_field =&gt; [ "message", "priority", "ts", "severity", "facility", "facility_label", "severity_label", "syslog5424_pri", "proc", "syslog_severity_code", "syslog_facility_code", "syslog_facility", "syslog_severity", "syslog_hostname", "syslog_message", "syslog_timestamp", "ver" ]
  }
  mutate {
    remove_tag =&gt; [ "_grokparsefailure_sysloginput" ]
  }
  mutate {
    gsub =&gt; [
      "service", "[0123456789-]", ""
    ]
  }
  if ("" in [msg]) {
    mutate {
      rename =&gt; { "msg" =&gt; "message" }
    }
  }
  mutate {
    remove_field =&gt; [ "tags" ]
  }
}
</code></pre>

<p>Tím se ale stane zápis v <code>docker-compose.yml</code> dost nepřehledný, lepší je si vytvořit vlastní image - napsat Dockerfile. Jeden takový jsem vytvořil, je dostupný na <a href="https://hub.docker.com/r/ludekvesely/logstash-json/">Docker Hubu</a>. Kromě zpracování zprávy ve formátu syslogu umí i zpracovat JSON, je tedy možné upravit vaši aplikaci tak, aby logovala ve formátu JSON a Logstash to automaticky zpracuje. Konečný docker-compose.yml bude vypadat následovně:</p>

<pre><code class="language-yml">logspout:  
  image: gliderlabs/logspout:v3
  command: 'syslog://logstash:5000'
  links:
    - logstash
  volumes:
    - '/var/run/docker.sock:/tmp/docker.sock'

logstash:  
  image: ludekvesely/logstash-json
  environment:
    - DROP_NON_JSON=false
    - STDOUT=false
  links:
    - elasticsearch

kibana:  
  image: kibana
  environment:
    - LOGSPOUT=ignore
  links:
    - elasticsearch
  ports:
    - '5601:5601'

elasticsearch:  
  image: elasticsearch
  environment:
    - LOGSPOUT=ignore
</code></pre>

<p>Nyní bude v Kibaně k dispozipi zalogovaná zpráva včetně názvu kontejneru a stacku. V případě, že byla zpráva ve formátu JSON, bude i ten zpracován. Ted už zbývá vytvořit par přehledných tabulek a grafů v Kibaně a uložit si je jako dashboard. </p>

<p>Poslední úpravou před spuštěním na produkci bude pravděpodobně nastavení volume pro Elasticsearch, aby data přežila restart kontejneru. Nezapomeňte zabezpečit Kibanu, aby nebylo přístupná celému světu (například pluginem do ES Shield/SearchGuard). Pokud nechcete Kibanu kontrolovat každý den, nastavte si notifikace (plugin do ES Watcher nebo ElastAlert).</p>

<h2 id="zdroje">Zdroje:</h2>

<ul>
<li><a href="http://nathanleclaire.com/blog/2015/04/27/automating-docker-logging-elasticsearch-logstash-kibana-and-logspout/">http://nathanleclaire.com/blog/2015/04/27/automating-docker-logging-elasticsearch-logstash-kibana-and-logspout/</a></li>
<li><a href="https://blog.tutum.co/2015/05/26/log-searching-and-analysis-with-tutum-and-an-elk-2/">https://blog.tutum.co/2015/05/26/log-searching-and-analysis-with-tutum-and-an-elk-2/</a></li>
<li><a href="https://www.zdrojak.cz/clanky/sber-logu-z-kontejneru">https://www.zdrojak.cz/clanky/sber-logu-z-kontejneru</a></li>
<li><a href="http://www.slideshare.net/raychaser/6-million-ways-to-log-in-docker-nyc-docker-meetup-12172014">http://www.slideshare.net/raychaser/6-million-ways-to-log-in-docker-nyc-docker-meetup-12172014</a></li>
<li><a href="http://pt.slideshare.net/sematext/docker-logging-webinar">http://pt.slideshare.net/sematext/docker-logging-webinar</a></li>
<li><a href="https://www.loggly.com/blog/top-5-docker-logging-methods-to-fit-your-container-deployment-strategy/">https://www.loggly.com/blog/top-5-docker-logging-methods-to-fit-your-container-deployment-strategy/</a></li>
</ul>]]></content:encoded></item><item><title><![CDATA[Nástroje pro práci s Elasticsearch]]></title><description><![CDATA[Nástroje, které usnadní práci s Elasticsearch. Kopf, Kibana, Postman, Elasticdump a další.]]></description><link>https://www.ludekvesely.cz/nastroje-pro-praci-s-elasticsearch/</link><guid isPermaLink="false">dcb6ba8e-d3b0-4d7c-bd95-d8f704c4745d</guid><category><![CDATA[Elasticsearch]]></category><dc:creator><![CDATA[Luděk Veselý]]></dc:creator><pubDate>Sun, 01 May 2016 20:08:42 GMT</pubDate><media:content url="https://www.ludekvesely.cz/content/images/2016/05/kopf-cluster.png" medium="image"/><content:encoded><![CDATA[<blockquote>
  <img src="https://www.ludekvesely.cz/content/images/2016/05/kopf-cluster.png" alt="Nástroje pro práci s Elasticsearch"><p>O Elasticsearch právě <a href="https://www.ludekvesely.cz/serial-elasticsearch-uvod/">píši seriál</a>, který určitě stojí za přečtení!</p>
</blockquote>

<p>Elasticsearch je snadno škálovatelné úložiště umožňující pokročilé fulltextové vyhledávání, komunikace s ním probíhá skrz REST API. Komunikovat s ním pouze skrz terminál není úplně nejpřívětivější, existuje však několik nástrojů, které používání Elasticsearch zjednoduší. Toto je ucelený seznam všech nástrojů se kterými jsem se setkal.</p>

<p><em>Níže popisované nástroje jsou kompatibilní s Elasticsearch verze 2.x.x. U verze 5 a vyšší se některé věci změnily - Sense (a také Marvel) je <a href="https://www.elastic.co/guide/en/kibana/current/console-kibana.html">součástí nástroje Kibana</a>, Kopf není plugin, ale samostatná aplikace nazvaná <a href="https://github.com/lmenezes/cerebro">Cerebro</a>.</em></p>

<h2 id="managementsgrafickmrozhranm">Management s grafickým rozhraním</h2>

<h4 id="kopf">Kopf</h4>

<p>Kopf je webové rozhraní umožňující vizualizaci a správu Elasticsearch clusteru. Instalace je možná dvěma způsoby, buď jako plugin do Elasticsearch, nebo jako samostatná aplikace. Pokud máte Elasticsearch nainstalovaný lokálně, nejjednodušší je se přepnout do jeho složky a Kopf nainstalovat příkazem:</p>

<pre><code>./bin/plugin install lmenezes/elasticsearch-kopf/2.1.1
</code></pre>

<p>Nyní by měl být Kopf přístupný zadáním adresy <code>http://localhost:9200/_plugin/kopf</code> do webového prohlížeče. Vidět je stav clusteru - zda je v pořádku (zelená/oranžová/červená lišta v záhlaví), jaké má nody (řádky tabulky), indexy (sloupce) a jak jsou rozmístěny shardy.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/05/kopf-cluster-1.png" alt="Nástroje pro práci s Elasticsearch"></p>

<p>Kromě vizalizace lze Elasticsearch spravovat - vytvářet indexy, měnit nastavení, spravovat percolatory, aliasy, snapshoty, templaty... K dispozici je i stránka pro posílání requestů, která napovídá názvy indexů a umožňuje procházet historii proběhlých requestů. Více informací včetně detailního návodu k instalaci je na <a href="https://github.com/lmenezes/elasticsearch-kopf">githubu</a>.  </p>

<h4 id="elastichq">ElasticHQ</h4>

<p>Webové rozhraní umožňující správu Elasticsearch clusteru, funkčně je dost podobný Kopfu. Jsou celkem <a href="https://github.com/royrusso/elasticsearch-HQ#getting-started">tři způsoby jak jej spustit</a> - může běžet jako samostatná aplikace, plugin v Elasticsearch nebo lze spustit rovnou bez instalace v prohlížeči po zadání adresy <code>http://www.elastichq.org/app/index.php?url=http://localhost:9200</code>. Je však nutné mít povolené CORS v elasticsearch.yml. </p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/05/elastichq.png" alt="Nástroje pro práci s Elasticsearch"></p>

<h4 id="marvel">Marvel</h4>

<p>Pokud chcete vidět přesně co se v clusteru děje, sledovat detailně jednotlivé metriky, umí to právě Marvel. Aktuálně je bohužel instalace trochu složitější než dřív, což je způsobeno tím, že Marvel není zdarma. Nyní je součástí <a href="https://www.elastic.co/v5">x-packu</a>, který je nabízen jako balíček několika nástrojů, mezi něž Marvel patří. Ke stažení je na <a href="https://www.elastic.co/downloads/marvel">webu Elasticu</a>, kde naleznete i <a href="https://www.elastic.co/guide/en/marvel/current/getting-started.html">dokumentaci</a>.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/05/marvel-2-1.png" alt="Nástroje pro práci s Elasticsearch"></p>

<h2 id="vizualizace">Vizualizace</h2>

<h4 id="kibana">Kibana</h4>

<p>Kibana je nástroj umožňující vizualizaci dat uložených v Elasticsearch ve webovém prohlížeči. Je to samostatná aplikace, kterou <a href="https://www.elastic.co/downloads/kibana">lze stáhnout z webu Elasticu</a>. Po stažení a rozbalení archivu stačí v konzoli spustit příkazem <code>./bin/kibana</code> ve složce s rozbaleným archivem. Dostupná pak bude v prohlížeči na adrese <code>http://localhost:5601</code>. </p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/05/kibana-dashboard.png" alt="Nástroje pro práci s Elasticsearch"></p>

<p>V záhlaví jsou čtyři položky:</p>

<ul>
<li>Discover: Vyhledávání a výběr sloupců v tabulce</li>
<li>Visualize: Vytvoření vizualizace (grafu/tabulky/mapy/...) z uloženého nebo nového vyhledávání</li>
<li>Dashboard: Několik uložených vizualizací nebo vyhledávání na jedné stránce</li>
<li>Settings: Nastavení (pattern pro výběr indexu, nastavení polí, import/export)</li>
</ul>

<p>Sama o sobě je Kibana silný nástroj umožňující vytvořit vizualizace z dat několika kliknutími, ty je pak možné vložit jako iframe do jiné aplikace, nebo jen použít dotaz, který kibana vygenerovala. Rozšiřitelná je pomocí <a href="https://www.elastic.co/guide/en/kibana/current/kibana-plugins.html">řady pluginů</a>. Veškeré informace i Kibaně jsou pak dostupné <a href="https://www.elastic.co/guide/en/kibana/current/introduction.html">v dokumentaci</a></p>

<h4 id="grafana">Grafana</h4>

<p>Grafana je primárně určena pro vizualizaci dat z InfluxDB, nicméně se <a href="http://grafana.org/features/#elasticsearch">umí napojit i na Elasticsearch</a>. Vizuálně je dost podobná starší Kibaně. Pokud máte data uložená ve více databázích, může pro vás být Grafana tou pravou volbou. Více na <a href="http://grafana.org">grafana.org</a>.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/05/dashboard_ex.png" alt="Nástroje pro práci s Elasticsearch"></p>

<h2 id="dotazovn">Dotazování</h2>

<h4 id="sense">Sense</h4>

<p>Sense byl dříve samostatnou aplikací, nyní je dostupný jako plugin do Kibany. Je to konzole umožňující komunikovat s Elasticsearchem - vytvářet requesty, číst odpovědi. Přestože už je obdobná funkčnost v Kopfu, Sense má jednu důležitou výhodu - našeptávání.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/05/introduction_screen.png" alt="Nástroje pro práci s Elasticsearch"></p>

<p>Je to asi nejpohodlnější způsob, jak psát dotazy na elasticsearch. Všechny jeho možnosti jsou pak popsané <a href="https://www.elastic.co/guide/en/sense/current/introduction.html">v dokumentaci</a>.</p>

<h4 id="postman">Postman</h4>

<p>Postman není určen primárně pro použití s Elasticsearch, je to nástroj umožňující komunikovat obecně s jakýmkoli API. Dostupný je jako rozšíření pro Google Chrome nebo jako samostatná aplikace pro OS X. Výhodou je možnost si requesty ukládat pro budoucí použití, případně automatizovat jejich spouštění. </p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/05/light-theme-main-view-min.png" alt="Nástroje pro práci s Elasticsearch"></p>

<h4 id="adminer">Adminer</h4>

<p>Nesmím zapomenout zmínit také český nástroj Adminer Jakuba Vrány. Ten od verze 4 podporuje také NoSQL databáze, mezi něž Elasticsearch spadá. Adminer jsem zkoušel, ale měl problém s zobrazením složitějších dat v tabulce. Od té doby jsem jej nezkoušel, takže je možné, že v současné verzi už budou tyto problémy odladěny. Stažení je možné na webu <a href="https://www.adminer.org/cs/">adminer.org</a>.</p>

<h2 id="managementkonzolovnstroje">Management - konzolové nástroje</h2>

<h4 id="elasticdump">ElasticDump</h4>

<p>Elasticdump je konzolový nástroj umožňující import a export dat z/do Elasticsearch. Je to vpodstatě taková obdoba Mysqldumpu. Nevýhodou je, že vyžaduje <code>npm</code>. Pokud ale chcete rychle zálohovat menší množství dat, nebo dostat data z produkce na lokál, může elasticdump dobře posloužit. Více na <a href="https://www.npmjs.com/package/elasticdump">npmjs.com</a>.</p>

<pre><code># instalace
npm install elasticdump -g

# dump indexu do souboru (vytvoreni zalohy)
elasticdump \  
  --input=http://production.es.com:9200/my_index \
  --output=/data/my_index_mapping.json \
  --type=mapping
elasticdump \  
  --input=http://production.es.com:9200/my_index \
  --output=/data/my_index.json \
  --type=data

# stazeni dat z produkce na lokal
elasticdump \  
  --input=http://production.es.com:9200/my_index \
  --output=http://localhost:9200/my_index \
  --type=mapping
elasticdump \  
  --input=http://production.es.com:9200/my_index \
  --output=http://localhost:9200/my_index \
  --type=data
</code></pre>

<p>Alternativou by mohl být <a href="https://github.com/skratchdot/elasticsearch-tools">elasticsearch-tools</a>. Nabízí podobnou funkčnost jako elasticdump, nemám s ním ale zkušenosti.</p>

<h4 id="curator">Curator</h4>

<p>Pokud vytváříte denní indexy (<code>logstash-2016-05-01</code>, <code>logstash-2016-05-02</code>, ...), pravděpodobně budete chtít ty staré uzavírat nebo rovnou mazat. Nástroj který to umí automaticky se jmenuje Curator a je přímo od Elasticu. Více informací o tom jak ho použít naleznete v <a href="https://www.elastic.co/guide/en/elasticsearch/client/curator/current/index.html">oficiální dokumentaci</a>, případně na <a href="https://github.com/elastic/curator">GitHubu</a>.</p>

<h2 id="notifikace">Notifikace</h2>

<p>Předposlední kategorií jsou nástroje umožňující zasílat notifikace na základě událostí uložených v Elasticsearch. Příkladem může být například zaslání e-mailu, pokud počet chyb aplikace (které si ukládáte do ES) vzroste nad určitou hodnotu. Nástroje, které toto umožňují jsou dva a liší se tím, že jeden je přímo od Elasticu a je placený (Watcher), zatímco druhý je opensource (ElastAlert).</p>

<h4 id="elasticwatcher">Elastic Watcher</h4>

<ul>
<li><a href="https://www.elastic.co/products/watcher">Web</a></li>
<li><a href="https://www.elastic.co/guide/en/watcher/current/index.html">Dokumentace</a></li>
</ul>

<h4 id="elastalert">ElastAlert</h4>

<ul>
<li><a href="https://github.com/Yelp/elastalert">Repozitář na GitHubu</a></li>
<li><a href="https://elastalert.readthedocs.io/en/latest/">Dokumentace</a></li>
</ul>

<h2 id="zabezpeen">Zabezpečení</h2>

<p>Pokud chcete data v Elasticsearch někomu zpřístupnit, ale nechcete mu dát plný přístup k clusteru, je třeba použít některý z nástrojů, který jej zabezpečí. Například lze po zadání hesla umožnit přístup pouze k určitému indexu a to pouze pro čtení. Situace je podobná jako v případě nástrojů pro notifikace - jsou dva, z toho jeden oficiální ale placení a druhý opensource.</p>

<h4 id="elasticshield">Elastic Shield</h4>

<ul>
<li><a href="https://www.elastic.co/products/shield">Web</a></li>
<li><a href="https://www.elastic.co/guide/en/shield/current/index.html">Dokumentace</a></li>
</ul>

<h4 id="searchguard">SearchGuard</h4>

<ul>
<li><a href="http://floragunn.com/searchguard/">Web</a></li>
<li><a href="https://github.com/floragunncom/search-guard">GitHub</a></li>
</ul>

<hr>

<p>To jsou nástroje, které vám mohou zpříjemnit soužití s Elasticsearch. Víte o nějakém zajímavém nástroji, který zde chybí? Podělte se o něj v diskuzi!</p>]]></content:encoded></item><item><title><![CDATA[Pár tipů jak napsat lepší Dockerfile]]></title><description><![CDATA[Tipy jak napsat lepší Dockerfile a vytěžit tak z Dockeru maximum. Není váš Dockerfile příliš komplikovaný? Využívejte Docker naplno.]]></description><link>https://www.ludekvesely.cz/par-tipu-jak-napsat-lepsi-dockerfile/</link><guid isPermaLink="false">400ca723-1a68-4918-a2c5-b46651a0c2d8</guid><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Luděk Veselý]]></dc:creator><pubDate>Mon, 04 Jan 2016 23:34:12 GMT</pubDate><media:content url="https://www.ludekvesely.cz/content/images/2016/01/dockerfile-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://www.ludekvesely.cz/content/images/2016/01/dockerfile-1.png" alt="Pár tipů jak napsat lepší Dockerfile"><p>Málokdo používá Docker tak že si spustí nějaký základní image, provede změny a zavolá <code>docker commit</code>. Většinou napíšete <strong>Dockerfile</strong>, ve kterém je definované, jak se má požadaný image vytvořit. Díky tomu je možné build automatizovat, provádět na integračním serveru a opakovaně. Usnadní to třeba aktualizaci nástrojů na požadovanou verzi (nedávno jsem aktualizoval Elasticsearch a <a href="https://github.com/ludekvesely/elasticsearch-cz/commit/49bc489dc6e4c17e905f9f170f86393a35c5c4da">nebylo to tak hrozné</a>). Díky kešování výsledků mezikroků to ani není moc pomalé. Přesto je třeba si uvědomit pár základních věcí a využít tak možnosti Dockeru naplno.</p>

<h2 id="vhodnzvolenbaseimage">Vhodně zvolený base image</h2>

<p>Na začátku každého Dockerfile stojí <code>FROM ...</code>. Dost lidí nad tím očividně nepřemýšlí a zvolí nějakou linuxovou distribuci, nejčastěji Ubuntu. Schválně se podívejte na GitHub, kolikrát soubor Dockerfile obsahuje <code>FROM ubuntu</code>. Pokud vyhledáte všechny soubory Dockerfile, zjistíte, že z přibližně <a href="https://github.com/search?utf8=✓&amp;q=language%3ADockerfile+filename%3ADockerfile&amp;type=Code&amp;ref=searchresults">170 000</a> nalezených souborů jich asi <a href="https://github.com/search?utf8=✓&amp;q=from+ubuntu+language%3ADockerfile+filename%3ADockerfile&amp;type=Code&amp;ref=searchresults">50 000</a> obsahuje frázi <code>FROM ubuntu</code>. Výsledky nelze brát uplně přesně, nelze totiž vyhledávat pomocí přesné shody, jako ukazatel ale postačí. Navíc zde nejsou započitány image které jako základ použijí image který používá Ubuntu.</p>

<p>Co je na tom špatného? Proč nepoužít operační systém který dobře znám, na serveru už jej dávno mám a vidím jej často i u oficiálních obrazů? Je třeba si uvědomit, že v době vzniku Ubuntu a podobných distibucí (CentOS, Debian, ...) byl Docker vzdálenou budoucností a distribuce mezitím dost nabobtnaly. Nesou si tak s sebou spoustu nástrojů, které se na serveru můžou hodit, ale <strong>v běžícím kontejneru je vůbec nevyužijete</strong>. Poslat celý image Ubuntu po internetu tak chvíli zabere, instalace jediného nástroje trvá věčnost, s využitím RAM to také není úplně růžové. Jak z toho ven?</p>

<pre><code class="language-docker">FROM alpine  
</code></pre>

<p>Řešením je jako základ použít <a href="https://hub.docker.com/_/alpine/">Alpine Linux</a>. Jedná se o minimální verzi linuxu (5MB!) s připraveným balíčkovacím nástrojem. V dokumentaci mají příklad, který dokonale popisuje jak problém řeší. Chci vytvožit image s nainstalovaným MySQL klientem, abych se mohl přihlásit k MySQL databázi. Nic víc, nic míň, jen jeden balíček. V případě použití Ubuntu by Dockerfile vypadal následovně:</p>

<pre><code class="language-docker">FROM ubuntu-debootstrap:14.04  
RUN apt-get update -q \  
  &amp;&amp; DEBIAN_FRONTEND=noninteractive apt-get install -qy mysql-client \
  &amp;&amp; apt-get clean \
  &amp;&amp; rm -rf /var/lib/apt
ENTRYPOINT ["mysql"]  
</code></pre>

<p>Sestavit image trvalo 19 vteřin a jeho výsledná velikost je 164 MB. S použitím Alpine by to vypadalo následovně:</p>

<pre><code class="language-docker">FROM gliderlabs/alpine:3.3  
RUN apk add --no-cache mysql-client  
ENTRYPOINT ["mysql"]  
</code></pre>

<p>Build za 3 vteřiny a velikost image je 36 MB (<a href="https://github.com/gliderlabs/docker-alpine#usage">Zdroj</a>). Představte si že chcete mít v kontejneru každý konzolový příkaz, který vám na serveru běží na pozadí - najednou použití Dockeru dává větší smysl. Použití Alpine s sebou nese určité změny oproti Ubuntu - místo shellu BASH je použit ASH a <a href="https://pkgs.alpinelinux.org/packages">balíčky</a> se instalují pomocí <code>apk</code> místo <code>apt-get</code>.</p>

<h2 id="minimalizacepotupkazvdockerfile">Minimalizace počtu příkazů v Dockerfile</h2>

<p>Zejména se to týká příkazu <code>RUN</code> použitého opakovaně za sebou. Podívejme se na tento Dockerfile:</p>

<pre><code class="language-docker">FROM alpine  
RUN apk update  
RUN apk add php-cli  
RUN apk add wget  
RUN wget http://domain.com/run.php  
ENTRYPOINT ["php" "run.php"]  
</code></pre>

<p>Co je zde špatně? Nejprve je třeba si uvědomit, jak Docker image vytváří. Po provedení každého image vzniká jeho nová vrstva, která se jakoby nabalí na tu předchozí. Vlastně taková sněhová koule valící se z kopce. Problém je, že každá vrstva se uloží. K čemu ale potřebuji výsledek <code>apt-get install wget</code>, který mi sloužil jen ke stažení souboru v následujícím kroku? Lepší by bylo příkaz nainstalovat, stáhnout soubor a opět odinstalovat (pominu fakt, že by šel nahradit příkazem <code>ADD</code>), přičemž po odinstalaci by bylo dobré dostranit i všechny dočasné soubory. Nelze také opominout režiji spojenou s zpraováním jednotlivých vrstev. Lepší by tedy bylo všechny příkazy provést v jednom kroku:</p>

<pre><code class="language-docker">FROM alpine  
RUN apk update &amp;&amp; \  
    apk add php-cli &amp;&amp; \
    apk add wget &amp;&amp; \
    wget http://domain.com/run.php &amp;&amp; \
    apk del wget &amp;&amp; \
    rm -rf /tmp/* &amp;&amp; \
    rm -rf /var/cache/apk/*
ENTRYPOINT ["php" "run.php"]  
</code></pre>

<p>Pokud by to působilo Dockerfile nepřehledným, lze vytvořit shellový skript (například install.sh) a v Dockerfile ho přidat a spustit:</p>

<pre><code class="language-docker">FROM alpine  
COPY install.sh /install.sh  
RUN bash /build.sh  
ENTRYPOINT ["php" "run.php"]  
</code></pre>

<h2 id="vhodnpoadpkaz">Vhodné pořadí příkazů</h2>

<p>Docker při buildu ve výchozím nastavení používá cache pro každou vrstvu image. Jakmile dostane příkaz (například <code>COPY install.sh /install.sh</code>), nejprve zjistí, zda již nemá výsledek a pokud ano tak jej použije. Při <code>COPY</code> souboru sjistí jeho hash, porovná s keší a případně použije již existující výsledek. Pokud u jednoho příkazu nepoužije keš, platí to i pro všechny následující příkazy. Toho je třeba maximálně využít a to co se často mění přesunout na konec Dockerfile. Takto například vypadá nevhodný image:</p>

<pre><code class="language-docker">FROM alpine  
COPY run.php /run.php  
RUN apk update &amp;&amp; \  
    apk add php-cli &amp;&amp; \
    rm -rf /tmp/* &amp;&amp; \
    rm -rf /var/cache/apk/*
ENTRYPOINT ["php" "/run.php"]  
</code></pre>

<p>V čem je problém? Při změně zdrojového kódu v souboru <code>run.php</code>, což Docker zjistí hned na druhém řádku, musí provést i všechny další kroky, tedy nainstalovat PHP. Předpokládám ale, že zdrojové kódy se mění vždy, kdežto nový balíček s PHP nevychází příliš často. Lepší tedy bude instalaci PHP přesunout na začátek a při dalších buildech jej vůbec neinstalovat, ale využít cache:</p>

<pre><code class="language-docker">FROM alpine  
RUN apk update &amp;&amp; \  
    apk add php-cli &amp;&amp; \
    rm -rf /tmp/* &amp;&amp; \
    rm -rf /var/cache/apk/*
COPY run.php /run.php  
ENTRYPOINT ["php" "/run.php"]  
</code></pre>

<p>To že se používá cache je vidět v logu Dockeru. Pokud spustíte <code>docker build</code>, u každého kroku uvidíte buď <code>running in...</code> (není použita cache) nebo <code>using cache</code> (je použita).</p>

<h2 id="conejjednodukontejnery">Co nejjednodušší kontejnery</h2>

<p>Filozofii Dockeru nejlépe odpovídá situace, kdy je v něm spuštěn jeden proces, ten produkuje log na standardní výstup a končí odpovídajícím návratovým kódem. Chápu, že ne vždy je to možné, ale je dobré takto přemýšlet a pokud se rozhodnu mít v kontejneru více procesů, měl bych pro to mít dobrý důvod. Já takhle používám NGINX a PHP-FPM, protože mi jeden bez druhého nedávají smysl a nechci řešit restart a deploy každého kontejneru zvlášť. Podobné je to s logováním - nepřijde mi dobrý nápad řešit uvnitř kontejneru kam bude logovat, prostě vypisuji na standardní výstup a o sběr logů ať se postará někdo jiný. Pro tento účel používám <a href="http://gliderlabs.com/blog/2015/03/31/new-logspout-extensible-docker-logging/">Logspout</a> a pokud vypadne, neovlivní to nijak chod ostatních kontejnerů.</p>

<h2 id="dalpostehy">Další postřehy</h2>

<h4 id="copyvsadd">COPY vs ADD</h4>

<p>Moc jsem nechápal rozdíl mezi <code>ADD</code> a <code>COPY</code>, z dokumentace jsem byl trochu zmatený:</p>

<p>ADD:  </p>

<blockquote>
  <p>The <code>ADD</code> instruction copies new files, directories or remote file URLs from <code>&lt;src&gt;</code> and adds them to the filesystem of the container at the path <code>&lt;dest&gt;</code>.</p>
</blockquote>

<p>COPY:  </p>

<blockquote>
  <p>The <code>COPY</code> instruction copies new files or directories from <code>&lt;src&gt;</code> and adds them to the filesystem of the container at the path <code>&lt;dest&gt;</code>.</p>
</blockquote>

<p>V praxi jde o to, že <code>ADD</code> umí to samé co <code>COPY</code>, navíc ale ještě umí:</p>

<ul>
<li>načíst soubor z URL (není tedy třeba instalovat wget/curl)</li>
<li>rozbalit některé archivy (není tedy třeba volat <code>tar xvf ...</code>)</li>
</ul>

<p>V některých starších verzích Dockeru bylo <code>COPY</code> označené jako deprecated, to ale neplatí a pokud nevíte který zvolit, stačí se držet jedním pravidlem:</p>

<ul>
<li>pokud to jde použít <code>COPY</code></li>
</ul>

<h4 id="entrypointvscmd">ENTRYPOINT vs CMD</h4>

<p>Definici příkazu který se má provést při spuštění kontejneru jsem viděl více způsoby - pomocí <code>ENTRYPOINT</code>, <code>CMD</code>, v hranatých závorkách i bez nich. Jak to tedy je? Uvedu příklad:</p>

<pre><code class="language-docker">ENTRYPOINT ["/bin/bash"]  
CMD ["ls"]  
</code></pre>

<p>Zde se při spuštění kontejneru provede <code>/bin/bash ls</code>. Oba příkazy se tedy spojí a provedou jako jeden příkaz. <code>CMD</code> lze definovat při spuštění kontejneru, měl by tedy obsahovat něco, co se může měnit. </p>

<p>Lze také vynechat hranaté závorky, co se stane? Pokud bychom zapsali <code>ENTRYPOINT ["/run.sh"]</code>, provedlo by se <code>/run.sh</code>. Pokud ale zapíšeme <code>ENTRYPOINT run.sh</code>, provede se <code>/bin/sh -c /run.sh</code>. Více k tématu naleznete v <a href="https://docs.docker.com/engine/reference/builder/#entrypoint">dokumentaci</a>.</p>

<h2 id="zvrem">Závěrem</h2>

<p>Také jste se setkali s nějakými záludnostmi při psaní Dockerfile? Podělte se o ně! Protože je dobré si informace načíst z více zdrojů, přihodím pár odkazů.</p>

<ul>
<li><a href="https://docs.docker.com/engine/articles/dockerfile_best-practices/">Best practises for writing Dockerfiles</a> přímo od Dockeru</li>
<li><a href="http://crosbymichael.com/dockerfile-best-practices.html">Další seznam best practises</a></li>
<li><a href="https://docs.docker.com/engine/reference/builder/">Dockerfile reference</a> - naprostý základ</li>
<li><a href="https://github.com/sillelien/base-alpine">Image postavený na Alpine</a> řešící některé známé problémy</li>
<li>Další užitečné image postavené na Alpine Linux: <a href="https://hub.docker.com/r/develar/java/">Java</a>, <a href="https://hub.docker.com/r/jbergknoff/redis/">Redis</a>, <a href="https://hub.docker.com/r/leanlabs/php/~/dockerfile/">PHP</a>, <a href="https://hub.docker.com/r/ludekvesely/elasticsearch-cz/">Elasticsearch s českým slovníkem</a></li>
</ul>]]></content:encoded></item><item><title><![CDATA[Deployment PHP (Nette) aplikace pomocí Dockeru a Tutum]]></title><description><![CDATA[Jak využít Docker pro vývoj (nejen) PHP aplikací a jejich snadný deployment. Příklad automatického deploymentu Nette aplikace pomocí služby Tutum.]]></description><link>https://www.ludekvesely.cz/deployment-nette-php-aplikace-pomoci-dockeru/</link><guid isPermaLink="false">e5364347-37cb-45a7-bb30-57d806f36e59</guid><category><![CDATA[PHP]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Nette]]></category><dc:creator><![CDATA[Luděk Veselý]]></dc:creator><pubDate>Thu, 31 Dec 2015 02:15:37 GMT</pubDate><media:content url="https://www.ludekvesely.cz/content/images/2015/12/docker-php.png" medium="image"/><content:encoded><![CDATA[<img src="https://www.ludekvesely.cz/content/images/2015/12/docker-php.png" alt="Deployment PHP (Nette) aplikace pomocí Dockeru a Tutum"><p><em><strong>Update</strong>: tento článek je mírně zastaralý, Tutum je přejmenován na <a href="https://cloud.docker.com">Docker Cloud</a> a má nové grafické rozhraní.</em></p>

<p>Pokud máte aplikaci psanou v PHP, je možné její deployment řešit několika způsoby. Například můžete rozběhnout prostředí ručně připojením na server přes SSH, použít automatizovaný nástroj typu Puppet nebo Chef, použít některý z dostupných PAAS. Pokud ale chcete mít vše ve svých rukách a zároveň si chcete práci maximálně zjednodušit (například <a href="https://www.adboos.com">u nás</a> je pro běh aplikace vyžadován NGINX, PHP, RabbitMQ, Elasticsearch, Redis, Nodejs, Phantomjs a databáze Percona), nabízí se použití Dockeru. To s sebou nese řadu výhod:</p>

<ul>
<li>Spuštění celého prostředí je možné "jedním příkazem"</li>
<li>Služby jsou izolované, paralelně mi tedy může běžet například databáze v různých verzích</li>
<li>Konfiguraci lze snadno přenést na testovací nebo produkční server, vždy mám jistotu že je vše stejné</li>
<li>Proces je možné automatizovat</li>
</ul>

<p>Je však třeba počítat i s nevýhodami tohoto řešení. Některé Docker image nejsou úplně optimálně vytvořené a jsou tak zbytečně velké, což v kombinaci s pomalým připojením k internetu může trochu zdržovat. Je také třeba počítat, že použití Dockeru o něco zvýší využití procesoru a paměti.</p>

<h2 id="pedpoklady">Předpoklady</h2>

<p>Tento článek je rozdělený na několik částí - nejprve spustíme webserver s PHP aplikací lokálně, následně totéž spustíme na serveru a nastavíme automatický deployment při každé změně kódu. Nakonec nastíním možnosti další využití a škálování nasazené aplikace.</p>

<p>Pokud si budete chtít spustit kontejnery lokálně, uude třeba nainstalovat Docker a Docker Compose a git. K sdílení kódu bude použit GitHub, bude tedy třeba mít účet na GitHubu (pokud chcete deployovat vlastní kód). Dále předpokládám server s operačním systémem Linux (Ubuntu, Debian, CentOS nebo Fedora).</p>

<h2 id="sputnprostedlokln">Spuštění prostředí lokálně</h2>

<blockquote>
  <p>Pokud vás zajímá samotný deployment na produkci a nachcete si hrát s Dockerem lokálně, tuto kapitolu klidně přeskočte. </p>
</blockquote>

<p>Prvním krokem je spustit <a href="https://github.com/nette/tutorial-quickstart">Nette Quickstart</a> lokálně. Tato aplikace pro svůj běh vyžaduje webserver s PHP a databázi MySQL. Předpokládám že máte nainstalovaný Docker, pokud ne, postupujte podle <a href="https://docs.docker.com/engine/installation/">oficiálního návodu</a>. Dalším nástrojem, který budeme potřebovat je <a href="https://docs.docker.com/compose/">Docker Compose</a>, který umožňuje spuštění a propojení několika kontejnerů pomocí jediného konfiguračního souboru. Ověříme, že oba nástroje máme v pořádku nainstalované:</p>

<pre><code class="language-bash">bash-3.2$ docker -v  
Docker version 1.7.1, build 786b29d  
bash-3.2$ docker-compose -v  
docker-compose version: 1.3.0  
CPython version: 2.7.9  
OpenSSL version: OpenSSL 1.0.1j 15 Oct 2014  
</code></pre>

<h3 id="staenzdrojovchkdaplikace">Stažení zdrojových kódů aplikace</h3>

<p>Prvním krokem je stažení zdrojových kódů aplikace. Já jsem si vytvořil fork quickstartu, do kterého následně přidám nové soubory. Můžete ale zkusit libovolnou PHP aplikaci. </p>

<p><img src="https://www.ludekvesely.cz/content/images/2015/12/nette_tutorial-quickstart.png" alt="Deployment PHP (Nette) aplikace pomocí Dockeru a Tutum"></p>

<p>V terminálu se přepneme do požadované složky a pomocí gitu zdrojové soubory naší aplikace stáhneme z GitHubu (můžete stáhnout můj fork). </p>

<pre><code class="language-bash">bash-3.2$ git clone git@github.com:ludekvesely/tutorial-quickstart.git  
Cloning into 'tutorial-quickstart'...  
remote: Counting objects: 236, done.  
remote: Total 236 (delta 0), reused 0 (delta 0), pack-reused 236  
Receiving objects: 100% (236/236), 349.68 KiB | 0 bytes/s, done.  
Resolving deltas: 100% (82/82), done.  
Checking connectivity... done.  
bash-3.2$ cd tutorial-quickstart/  
bash-3.2$ ls -l  
total 104  
drwxr-xr-x   8 ludekvesely  staff    272 Dec 29 02:16 app  
-rw-r--r--   1 ludekvesely  staff    829 Dec 29 02:16 composer.json
-rw-r--r--   1 ludekvesely  staff  39125 Dec 29 02:16 composer.lock
-rw-r--r--   1 ludekvesely  staff   7233 Dec 29 02:16 database.sql
drwxr-xr-x   3 ludekvesely  staff    102 Dec 29 02:16 log  
drwxr-xr-x   3 ludekvesely  staff    102 Dec 29 02:16 temp  
drwxr-xr-x   4 ludekvesely  staff    136 Dec 29 02:16 tests  
drwxr-xr-x  11 ludekvesely  staff    374 Dec 29 02:16 www  
</code></pre>

<p>Zdrojové kódy máme stažené a nyní bude třeba vytvořit kontejner, ve kterém aplikace poběží.</p>

<h3 id="vytvoejkontejnerusapacheaphp">Vytvořejí kontejneru s Apache a PHP</h3>

<p>Kontejner je spuštěná image, my tedy nejprve musíme vytvořit image, kterou budeme moci spustit. Při vytváření Docker image je dobré se nejprve podívat, zda už nějaký vhodný neexistuje. Začít vyhledávat je možné na adrese <a href="https://hub.docker.com">hub.docker.com</a>. My použijeme image s připraveným Apache a PHP, který spravuje společnost <a href="https://tutum.co">tutum.co</a> (využijeme ji pak pro deploy), můžete jej najít na <a href="https://hub.docker.com/r/tutum/apache-php/">docker hubu</a> a jeho zdrojové kódy na <a href="https://github.com/tutumcloud/apache-php">githubu</a>.</p>

<p>V rootu aplikace vytvoříme soubor <code>Dockerfile</code>. Jeho obsah bude vypadat následovně:</p>

<pre><code class="language-docker">FROM tutum/apache-php

RUN apt-get update &amp;&amp; apt-get install -yq git php5-sqlite &amp;&amp; \  
    rm -rf /var/lib/apt/lists/* &amp;&amp; rm -fr /app

COPY . /app

RUN chmod 777 log temp &amp;&amp; \  
    composer install &amp;&amp; \
    rm -rf composer* Dockerfile .git &amp;&amp; \
    sed -i "s/DocumentRoot \/var\/www\/html/DocumentRoot \/var\/www\/html\/www/g" /etc/apache2/sites-available/000-default.conf

ENV ALLOW_OVERRIDE true  
</code></pre>

<p>Co to ten Dockerfile je, k čemu a jak se použije? Je to vpodstatě seznam příkazů, který se provede před spuštěním webserveru. Můžete si to představit jako když máte server s čistou instalací a toto je posloupnost kroků, které se musí provést před spuštěním aplikace. Určitě vás napadne, jestli není zbytečné a pomalé pokaždé provádět <code>apt-get install</code>. Každý příkaz v Dockerfile se provede a uloži jako další vrstva vytvářeného obrazu. Před vykonáním každého kroku Docker zkontroluje, zda je opravdu nutné ho provést - například před kopírováním souboru si porovná jeho hash s cachovaným hashem z minulosti a krok provede až pokud se liší, v opačném případě pouze použije vrstvu, kterou již v minulosti vytvořil.</p>

<p>Projděme si jednotlivé části Dockerfile. Zápis <code>FROM tutum/apache-php</code> značí, z kterého image je Dockerfile odvozený. Vpodstatě to znamená, že tento Dockerfile končí tam, kde předchozí skončil - podívat se na něj můžete <a href="https://github.com/tutumcloud/apache-php/blob/master/Dockerfile">na GitHubu</a>. </p>

<p>Následující řádky začínající <code>RUN apt-get update &amp;&amp; apt-get install -yq git php5...</code> nainstalují nástroje potřebné k rozběhnutí aplikace. Nette vyžaduje php5-sqlite, nakonec smažeme nepotřebné soubory. Za <code>RUN</code> může přijít jakýkoliv příkaz, který bude spuštěn v bashi.</p>

<p>Příkaz <code>COPY . /app</code> nakopíruje kód aplikace do image, <code>.</code> značí aktuální adresář (tedy složky app, temp, log, www...), je možné uvést adresář nebo konkrétní soubor, <code>/app</code> značí kam se soubory nakopírují. </p>

<p>Následně řádky začínající <code>RUN chmod 777 log temp...</code> nastaví potřebná práva nakopírovaným adresářům, odstraní nepotřebné soubory (adresář .git nemá na produkci co dělat) a pomocí sedu provede změny konfigurace Apache.</p>

<p>Posledním krokem <code>ENV ALLOW_OVERRIDE true</code> je nastavení proměnné ALLOW_OVERRIDE na true. Ta je pak v kontejneru vždy dostupná - můžeme k ní přistupovat jako k jakékoliv jiné proměnné, například ji vypsat příkazem <code>echo $ALLOW_OVERRIDE</code>. Po spuštění kontejneru je ale spuštěn <a href="https://github.com/tutumcloud/apache-php/blob/master/run.sh">tento soubor</a>, kde se na základě této proměnné povolí mod rewrite.</p>

<p>Dockerfile a zdrojové kódy apikace máme připraveny, můžeme tedy provést build. Ve rootu aplikace spustíme <code>docker build -t nette-quickstart .</code>. Parametr -t značí, jak si výsledný image pojmenujeme (jaký bude mít tag). Důležitá je tečka na konci, kdy se build provede v aktuálním adresáři, kde je také očekáván Dockerfile.</p>

<pre><code class="language-bash">bash-3.2$ docker build -t nette-quickstart .  
Sending build context to Docker daemon 523.3 kB  
Sending build context to Docker daemon  
Step 0 : FROM tutum/apache-php  
 ---&gt; cdced04212b6
Step 1 : RUN apt-get update &amp;&amp; apt-get install -yq php5-sqlite &amp;&amp;     rm -rf /var/lib/apt/lists/* &amp;&amp; rm -fr /app  
 ---&gt; Running in dfce886a3ceb
Ign http://archive.ubuntu.com trusty InRelease  
Get:1 http://archive.ubuntu.com trusty-updates InRelease [64.4 kB]  
Get:2 http://archive.ubuntu.com trusty-security InRelease [64.4 kB]

...

Processing triggers for libapache2-mod-php5 (5.5.9+dfsg-1ubuntu4.14) ...  
 ---&gt; 0af4e3fa2efe
Removing intermediate container dfce886a3ceb  
Step 2 : COPY . /app  
 ---&gt; 3f09c2f8c42c
Removing intermediate container fc3e65c222c1  
Step 3 : RUN chmod 777 log temp &amp;&amp;     composer install &amp;&amp;     rm -rf composer* Dockerfile .git &amp;&amp;     sed -i "s/DocumentRoot \/var\/www\/html/DocumentRoot \/var\/www\/html\/www/g" /etc/apache2/sites-available/000-default.conf  
 ---&gt; Running in 22809fbf0bb1
Warning: This development build of composer is over 60 days old. It is recommended to update it by running "/usr/local/bin/composer self-update" to get the latest version.  
Loading composer repositories with package information  
Installing dependencies (including require-dev) from lock file  
Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. Run update to update them.  
  - Installing dg/adminer-custom (v1.6.1)
    Downloading: 100%

...

  - Installing nette/tester (v1.4.0)
    Downloading: 100%

nette/utils suggests installing ext-intl (for script transliteration in Strings::webalize() and toAscii())  
Generating autoload files  
 ---&gt; e026f4842c1c
Removing intermediate container 22809fbf0bb1  
Step 4 : ENV ALLOW_OVERRIDE true  
 ---&gt; Running in f59173c8afee
 ---&gt; 8e15d5de7d02
Removing intermediate container f59173c8afee  
Successfully built 8e15d5de7d02  
</code></pre>

<p>Ve výstupu můžete vidět, že pro každý příkaz v Dockerfile je následně proveden jeden krok, odtud například <code>Step 4 : ENV ALLOW_OVERRIDE true</code> v logu. Pokud build spustíme znovu, pod každým krokem uvidíme <code>---&gt; Using cache</code>, což znamená, že Docker nezjistil změnu a pokračuje dalším krokem. Jakmile ale upravíme jediný soubor, v kroku <code>COPY . /app</code> zjistí pomocí kontrolního součtu změnu a tento a následující kroky už jsou provedeny. Z toho vyplývá, že věci, které se často nemění (instalace nástrojů, nastavení proměnných) je dobré mít před věcmi, které se mění často (např. nakopírování zdrojových kódů aplikace).</p>

<p>Image máme připravený, zbývá ho jen spustit. To provedeme příkazem <code>docker run --rm -p 8012:80 nette-quickstart</code>. Po zadání příkazu se spustí kontejner a uvolní svůj port 80, ukončit jej můžeme zkratkou <code>ctrl + c</code>. Parametr <code>--rm</code> značí, že bude po ukončení kontejner automaticky odebrán a můžeme jej tak spustit znovu. Parametr <code>-p</code> definuje které porty kontejneru mají být zveřejněny (zde port 80, na kterém běží Apache) a pod jakým portem mají být zveřejněny (zde 8012, pod kterým bude webová stránka dostupná v prohlížeči). Pokud bysme jej nezadali, zvolí se náhodný volný port.</p>

<p>Spuštěný kontejner lze dobře prohlédnout pomocí nástroje Kitematic, pokud jej nemáte nainstalovaný, <a href="http://www.ludekvesely.cz/docker-na-windows-a-os-x/">zkuste můj návod</a>.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2015/12/kitematic.png" alt="Deployment PHP (Nette) aplikace pomocí Dockeru a Tutum"></p>

<p>Zde lze prohlížet log (který je i v terminálu), náhled webové stránky a nastavení kontejneru. A co zde není zajímavého - server error v náhledu webové stránky! Jak ale zjistit v čem je problém, jak se dostat do kontejneru? Ideální by bylo, kdyby byly chyby vidět přímo v logu, samotné logování by ale vydalo na samostatný článek, nyní si ukážeme, jak ladit běžící kontejner. V horní liště má Kitematic možnost <code>EXEC</code>, klikneme na ni a otevře se terminál v běžícím kontejneru. Nyní můžu prohlížet a editovat soubory, spouštět příkazy... Podíváme se co Tracy zalogovala:</p>

<pre><code class="language-bash"># cat log/exception.log
[2015-12-31 01-08-09] Nette\Database\ConnectionException: SQLSTATE[HY000] [2003] Can't connect to MySQL server on '127.0.0.1' (111) in /app/vendor/nette/database/src/Database/DriverException.php:25 caused by PDOException: SQLSTATE[HY000] [2003] Can't connect to MySQL server on '127.0.0.1' (111) in /app/vendor/nette/database/src/Database/Connection.php:70  @  http://192.168.99.100:8012/  @@  exception-2015-12-31-01-08-09-377469fd79b0ea3f7414b9d5d0198f74.html
</code></pre>

<p>Z logu je patrné, že se aplikace nemůže připojit k databázi, protože žádná neběží. </p>

<h3 id="sputnmysqldatabzeapropojenswebserverem">Spuštění MySQL databáze a propojení s webserverem</h3>

<p>Spustit databázi můžeme dvěma způsoby - buď ji nainstalovat do existujícího kontejneru a oba procesy držet při životě nějakým správcem procesů (například supervisord), jak to tutum dělá ve svém image <a href="https://github.com/tutumcloud/lamp">LAMP</a>. Druhou variantou je spustit nový kontejner s databází a oba pak propojit. Filozofii dockeru lépe odpovídá druhá varianta. Obecně je lepší držet kontejnery co nejmenší a nejjednodušší. V jednom kontejneru by měl ideálně běžet jeden proces, lépe se pak kontejnery udržují, pokud vše dáme do jednoho kontejneru, vytrácí se pak výhody, které Docker přináší. To, jaké všechny kontejnery budeme spouštět a jak je mezi sebou propojíme nám usnadní Docker Compose, kde celou sadu kontejnerů definujeme v souboru <code>docker-compose.yml</code>, který bude vypadat následovně:</p>

<pre><code class="language-yml">web:  
  build: .
  links:
    - db
  ports:
    - "80"
db:  
  image: tutum/mysql
  environment:
    STARTUP_SQL: "/tmp/database.sql"
    ON_CREATE_DB: test
    MYSQL_PASS: testpass
  volumes:
    - ./database.sql:/tmp/database.sql:ro
</code></pre>

<p>Tento yml soubor má dvě sekce - web a db. Dle sekce web by se měl provést build v aktuálním adresáři (tak jak jsme provedli příkazem docker build...). Zveřejní se port 80 a připojena bude k službě <code>db</code>. Ta je definována v další sekci a vychází z image <a href="https://hub.docker.com/r/tutum/mysql/">tutum/mysql</a>. V proměnných jsou definovány přihlašovací údaje a název databáze. Důležitá je část <code>volumes</code>, kde je definováno že soubor <code>database.sql</code> bude připojen do kontejneru jako <code>/tmp/database.sql</code> v režimu read-only. V proměnné <code>STARTUP_SQL</code> je pak definována cesta právě k tomuto souboru a použije se při <a href="https://github.com/tutumcloud/mysql/blob/master/5.6/run.sh#L54">inicializaci databáze</a>. </p>

<p>Ještě musíme nastavit připojení k databázi v aplikaci úpravou souboru <code>app/config/config.local.neon</code>.</p>

<pre><code class="language-yml">database:  
  dsn: 'mysql:host=db;port=3306;dbname=test'
  user: admin
  password: testpass
  options:
    lazy: yes
</code></pre>

<p>Nyní můžeme zavolat příkaz <code>docker-compose up</code> a oba kontejnery se spustí a propojí. V Kitematic by měly být oba kontejnery vidět a web by měl jít otevřít v prohlížeči.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2015/12/blog-nette.png" alt="Deployment PHP (Nette) aplikace pomocí Dockeru a Tutum"></p>

<p>Tímto jsme schopni spustit PHP aplikaci v Dockeru, připravit databázi a kontejnery propojit, vše jediným příkazem <code>docker-compose up</code>. Pokud vám něco uniklo, podívejte se na <a href="https://github.com/ludekvesely/tutorial-quickstart/commit/05184e416e8679eddcb36982632708a5b063ce5d">tento commit</a>. Kód můžeme pushnout na github a zavřít terminál, pro deployment na server už nám bude stačit jen webový prohlížeč.</p>

<h2 id="deploymentaplikacenaprodukci">Deployment aplikace na produkci</h2>

<p>Pro spuštění aplikace na serveru by bylo možné se na něj přes SSH připojit, nainstalovat Docker, stáhnout kód a spustit kontejnery. Existuje však služba Tutum, která toto všechno umí skrze webový prohlížeč a celý proces nasazení aplikace automatizovat a monitorovat. </p>

<p>Přihlaste se na adrese <a href="https://dashboard.tutum.co/accounts/login/">https://dashboard.tutum.co/accounts/login/</a> - můžete pomocí účtu na Docker Hubu, GitHubu nebo pomocí vlastní e-mailové adresy. </p>

<p><img src="https://www.ludekvesely.cz/content/images/2015/12/tutum-login.png" alt="Deployment PHP (Nette) aplikace pomocí Dockeru a Tutum"></p>

<p>Po vytvoření účtu ho musíme propojit s GitHubem a serverem. </p>

<h3 id="propojentutumaserveru">Propojení Tutum a serveru</h3>

<p>Nejprve musíme propojit server s Tutum, aby bylo kam deployovat - to je možné na záložce <em>Nodes</em>. Můžete si přidat účet například z Amazonu a servery tak vytvářet rovnou z rozhraní Tutum. Pokud ale už máte server vlastní, zvolte možnost <em>Bring your own node</em>. </p>

<p><img src="https://www.ludekvesely.cz/content/images/2015/12/byon.png" alt="Deployment PHP (Nette) aplikace pomocí Dockeru a Tutum"></p>

<p>Přihlaste se na server přes SSH a spusťte zkopírovaný příkaz. Nainstaluje se docker a všechny nástroje nutné pro chod Tutum.</p>

<h3 id="propojentutumagithubu">Propojení Tutum a GitHubu</h3>

<p>Pro propojení s GitHubem klikněte na záložku Repositories, propojte s GitHubem (pokud jste se přes něj přihlásili, pravděpodobně už to nebude třeba) a pokračujte možností <em>Create new repository</em>. Zvolte libovolný název a popis a potvrďte.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2015/12/create-repo.png" alt="Deployment PHP (Nette) aplikace pomocí Dockeru a Tutum"></p>

<p>Vytvoří se repository, do které bychom mohli pushnout image vytvořený lokálně v první kapitole. Zde je repository obdobou repozitáře na githubu - také můžu provést <code>docker push</code>, <code>docker pull</code>, mít různé tagy... My chceme ale mít vše automatizované, proto v divu <em>Automated build from GitHub</em> klikneme na tlačítko <em>Edit repository</em>. </p>

<p><img src="https://www.ludekvesely.cz/content/images/2015/12/auto-build.png" alt="Deployment PHP (Nette) aplikace pomocí Dockeru a Tutum"></p>

<p>Potvrdíme kliknutím na <em>Save and build</em>. Tutum vybere volný server z těch, které jsme k účtu připojili, tam provede build a v případě úspěchu pushne vytvořený image do repozitáře.</p>

<h3 id="vytvoenkontejnernaserveru">Vytvoření kontejnerů na serveru</h3>

<p>Jakmile je build hotový, v detailu repository zvolíme <em>Launch service</em>. Spustí se průvodce, který umožňí službu nakonfigurovat a spustit. V kroku service configuration můžeme novou službu pojmenovat a provedeme zde jedinou změnu - zveřejníme port 80. Pokud chceme, aby se aplikace nasadila automaticky po každém úspěšném buildu, zaškrtneme ještě možnost <em>Autoredeploy</em>.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/01/wizard1.png" alt="Deployment PHP (Nette) aplikace pomocí Dockeru a Tutum"></p>

<p>Krok <em>Environment variables</em> můžeme přeskočit a posledním nastavením před spuštěním je nastavení <strong>Volumes</strong>. Volumes slouží k persistenci dat - ukládají se mimo kontejner a přežijí tak jeho restart, což se hodí například u databází. Zde pomocí volumes zpřístupníme složku aplikace, ve které je dump databáze. Na řádek <em>Add volume</em> do pole <em>Container path</em> zadejte <code>/app</code> a klikněte na <em>Add</em>. Výsledek by měl vypadat následovně:</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/01/wizard-volumes-app.png" alt="Deployment PHP (Nette) aplikace pomocí Dockeru a Tutum"></p>

<p>Potvrdíme kliknutím na <em>Create and deploy</em>. Nyní se na server stahuje vytvořený image a následně se spouští. Průběh můžeme sledovat pod záložkou <em>Timeline</em>. Po spuštění je veškerý výstup vidět na záložce <em>Logs</em>. </p>

<p>Dalším krokem je spuštění databáze - v hlavním menu zvolíme <em>Services</em> a následně <em>Create service</em>. Image MySQL je možné vyhledat na záložce <em>Public repositories</em> -> <em>Search Docker hub</em> zadáním <code>tutum/mysql</code>.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/01/tutum-mysql.png" alt="Deployment PHP (Nette) aplikace pomocí Dockeru a Tutum"></p>

<p>Zvolíme vyhledaný image a v dalším kroku je důležité službu pojmenovat <code>db</code>. V konfikuraci Nette aplikace totiž máme definovanou databázi právě pod <code>db</code>. </p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/01/mysql-db.png" alt="Deployment PHP (Nette) aplikace pomocí Dockeru a Tutum"></p>

<p>V dalším kroku nastavíme proměnné prostředí. Je třeba nastavit <code>MYSQL_PASS</code> na <code>testpass</code> a <code>ON_CREATE_DB</code> na <code>test</code>. Dále je třeba vložit novou proměnnou na řádku <em>Add environment variable</em> <code>STARTUP_SQL</code> s hodnotou <code>/app/database.sql</code>, což je cesta k souboru s dumpem databáze. Ten by aktuálně v kontejneru dostupný nebyl, zpřístupníme ho nastavením volumes v dalším kroku. </p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/01/mysql-env.png" alt="Deployment PHP (Nette) aplikace pomocí Dockeru a Tutum"></p>

<p>Posledním krokem před spuštěním databáze je načtení volumes definovaných v předchozí službě. V kroku <em>Volumes</em> tedy na řádku <em>Add volumes from</em> zvolíme službu <code>nette-quickstart</code> a potvrdíme.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/01/mysql-volumes-from.png" alt="Deployment PHP (Nette) aplikace pomocí Dockeru a Tutum"></p>

<p>Potvrdíme kliknutím na <em>Create and deploy</em> a počkáme až se databáze spustí. Přes menu <em>Services</em> se vrátíme do služby nette-quickstart. Zde nás zajímá záložka <em>Endpoints</em>, na které vidíme seznam všech zveřejněných portů. Zde by měl být právě jeden, který můžeme otevřít.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/01/endpoints.png" alt="Deployment PHP (Nette) aplikace pomocí Dockeru a Tutum"></p>

<p>Po otevření bycho měli vidět spuštěnou a funkční aplikaci Nette Quickstart.</p>

<p><img src="https://www.ludekvesely.cz/content/images/2016/01/nette-quickstart-tutum.png" alt="Deployment PHP (Nette) aplikace pomocí Dockeru a Tutum"></p>

<h2 id="codl">Co dál?</h2>

<p>Prošli jsme celý proces, od kódu editovaného lokálně přes jeho zveřejnění na GitHubu až po spuštění aplikcace pomocí Docker a Tutum na serveru. Nyní můžeme provést změny v kódu, pushnout na GitHub a aplikace se automaticky aktualizuje. Pro skutečné použití na produkci by ale bylo dobré provést následující kroky:</p>

<ul>
<li><strong>Persistence dat</strong> v databázi pomocí nastavení volumes. Nyní se při restartu databáze data přemažou.</li>
<li>Aktualizace struktury databáze pomocí <strong>migrací</strong>. Jakmile totiž máme v databázi data, bylo by dobré provádět jen přírůstkové změny (aktuálně používám <a href="https://github.com/nextras/migrations">nextras/migrations</a> a fungují výborně).</li>
<li><strong>Zálohování</strong> databáze. Přístupů je více - například použít další kontejner s nainstalovaným mysqldump a nastavenými volumes from.</li>
<li><strong>Testy</strong>. Pokud do rootu zdrojových kódů přidáte soubor pro docker compose <code>docker-compose.test.yml</code>, který má definovanou sekci <code>sut</code> - test se provede při každém buildu. Více <a href="https://support.tutum.co/support/solutions/articles/5000638476-automated-testing">zde</a>.</li>
<li><strong>Škálování</strong>. Výhodou použití kontejnerů je snadné škálování - před web stačí předřadit <a href="https://github.com/tutumcloud/haproxy">proxy</a>, s jejíž pomocí můžete simulovat virtualhosty, rozdělovat zátěž.. Kontejnery pak můžou běžet na více serverech a lze tak snadno rozdělit jejich výkon.</li>
<li><strong>HTTPS</strong> - pokud už budete používat haproxy, je nastavení https otázkou přidání dvou proměnných v konfiguraci.</li>
<li><strong>Monitoring a logování</strong>  - o tom snad v některém z dalších článků.</li>
</ul>

<h2 id="alternativyorchestracekontejner">Alternativy orchestrace kontejnerů</h2>

<p>Tutum je zatím zdarma, jednou ale přejde na placený model. Je možné že cenu nasadí tak vysoko, že se provoz služby velmi prodraží a tak je třeba počítat s dostupnými alternativami.</p>

<ul>
<li><a href="https://clusterhq.com">ClusterHQ Flocker</a>: Super věc, při migraci kontejneru na jiný server migruje i data a lze tak v klidu například přemigrovat databázi na silnější server. Nemá webové rozhraní jako Tutum.</li>
<li><a href="http://rancher.com">Rancher</a>: Mají vlastní operační systém pro provoz kontejnerů a nástroj pro jejich správu. Vypadá slibně, není to ale služba, vše si člověk musí rozběhat sám.</li>
<li>Další nástroje pro orchestraci: Docker Swarm, Google Kubernetes, Apache Mesos, CoreOS Fleet. Nemám zkušenosti, mělo by ale jít o prověřené nástroje nabízející orchestraci kontejnerů.</li>
</ul>]]></content:encoded></item><item><title><![CDATA[Nette a Elasticsearch]]></title><description><![CDATA[Co to je Elasticsearch a jak je použít v Nette projektu? Návod pro použití Kdyby/Elasticsearch.]]></description><link>https://www.ludekvesely.cz/nette-a-elasticsearch/</link><guid isPermaLink="false">fa6888d5-9ad6-45e5-a2a0-0e42f2347ed7</guid><category><![CDATA[Elasticsearch]]></category><category><![CDATA[Nette]]></category><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Luděk Veselý]]></dc:creator><pubDate>Tue, 04 Aug 2015 22:36:35 GMT</pubDate><media:content url="https://www.ludekvesely.cz/content/images/2015/08/elastic-1.png" medium="image"/><content:encoded><![CDATA[<blockquote>
  <img src="https://www.ludekvesely.cz/content/images/2015/08/elastic-1.png" alt="Nette a Elasticsearch"><p>O Elasticsearch právě <a href="https://www.ludekvesely.cz/serial-elasticsearch-uvod/">píši seriál</a>, který určitě stojí za prozkoumání!</p>
</blockquote>

<p>Elasticsearch je škálovatelné úložiště umožňující nejen fultextové vyhledávání v reálném čase. Využít se dá několika způsoby, primárně byl ale vytvořen právě pro vyhledávání. Pokud máte data v MySQL nebo jiné relační databázi a zjišťujete, že vyhledávání pomocí <code>LIKE</code> už vám nestačí, nasazení Elasticsearch může být další logický krok. </p>

<h2 id="trochamotivacevodem">Trocha motivace úvodem</h2>

<p>Co vám tedy Elasticsearch nabízí a na co jej můžete použít? Pokusím se to shrnout ve třech bodech:</p>

<h5 id="1elasticsearchjakofulltextovvyhledvn">1. Elasticsearch jako fulltextové vyhledávání</h5>

<p>S pomocí volně dostupných slovníků můžete například vyhledávat klíčové slovo nehledě na skloňování, nebo naimplementovat našeptávač, "měli jste na mysli" a další pokročilé nástroje. Elasticsearch je postaven nad Apache Lucene a plně jej využívá.</p>

<h5 id="2elasticsearchjakorychldistribuovanloit">2. Elasticsearch jako rychlé distribuované úložiště</h5>

<p>Do Elasticsearch můžete uložit jakýkoli dokument ve formátu JSON. Pokud vám pak běží na více strojích, jsou vytvářeny kopie dokumentů a o data tak v případě výpadku nebo poruchy některého z serverů nepříjdete. Navíc je pro většinu výsledků používána cache, takže jsou odpovědi opravdu rychlé (za cenu pomalejšího ukládání).</p>

<h5 id="3elasticsearchjakoanalyticknstroj">3. Elasticsearch jako analytický nástroj</h5>

<p>V kombinaci s nástrojem <a href="https://www.elastic.co/products/kibana">Kibana</a> je možné Elasticsearch využít jako pokročilý analytický nástroj poskytující reporty v reálném čase. Můžete tak do Elasticsearch ukládat například access log webového serveru a v Kibaně pak vytvořit grafy s počty přístupů, mapy a další statistiky.</p>

<h2 id="instalaceelasticsearch">Instalace Elasticsearch</h2>

<h3 id="1staen">1. Stažení</h3>

<p>Pro použití v Nette projektu je spuštění otázkou několika minut. Nejprve navštivte stránky společnosti Elastic a <a href="https://www.elastic.co/downloads/elasticsearch">v sekci downloads stáhněte aktuální verzi v zip archivu</a>:</p>

<p><img src="https://www.ludekvesely.cz/content/images/2015/08/elasticsearch-download.png" alt="Nette a Elasticsearch"></p>

<p>Pokud budete chtít rozběhnout Elasticsearch na serveru s OS Linux, doporučuji využít <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-repositories.html">repozitářů</a> - dojde také k instalaci init skriptů. ZIP archiv rozbalte do vaší oblíbené složky (v mém případě Downloads) a vznikne adresářová struktura podobná této:</p>

<p><img src="https://www.ludekvesely.cz/content/images/2015/08/elasticsearch-unzipped.png" alt="Nette a Elasticsearch"></p>

<h3 id="2konfigurace">2. Konfigurace</h3>

<p>Před samotným spuštěním doporučuji upravit konfiguraci v souboru elasticsearch.yml (druhá šipka). Otevřete jej v libovolném editoru - jde o prostý textový soubor. Vzorová konfigurace pro lokální vývoj by mohla vypadat takto:</p>

<pre><code class="language-language">cluster.name: mylocalcluster  
node.name: "My Node"  
node.local: true  
index.number_of_shards: 1  
index.number_of_replicas: 0  
</code></pre>

<p>Pokusím se vysvětlit jednotlivé řádky konfigurace.</p>

<p><code>cluster.name</code> je název clusteru, ve kterém se daný uzel (node) nachází. Elasticsearch sám prozkoumává síť a snaží se připojit ostatní uzly se stejným jménem <code>cluster.name</code> do jednoho clusteru. Změnou tak předejdete tomu, že se vám Elasticsearch chová podivně, pokud jej používá více vývojářů na lokální síti.</p>

<p><code>node.name</code> je název uzlu který spouštíte, slouží pro jeho jednoznačnou identifikaci například v pluginu kopf. Pokud necháte parametr prázdný, vygeneruje se náhodné jméno.</p>

<p><code>node.local</code> umožní pouze lokální přístup k Elasticsearch. Pozor na bezpečnost, pokud necháte na produkci zvenčí dostupný port 9200 - i tam se může hodit "lokální" přístup k nodu.</p>

<p><code>index.number_of_shards</code> a <code>index.number_of_replicas</code> nemá smysl pro lokální vývoj nastavovat jinak. Pokud vám vypadne jediný počítač, na kterém máte data, je jedno v kolika kopiích byla uložena :). Při nasazení na produkci doporučuji rozmyslet nastavení <code>index.number_of_shards</code>, protože po vytvoření indexu jej již nezměníte.</p>

<h3 id="3sputn">3. Spuštění</h3>

<p>Před spuštěním se ujistěte, že máte nainstalovanou javu, pokud ne, bude třeba ji <a href="https://java.com/en/download/">stáhnout</a>. Nyní spusťte soubor <code>elasticsearch</code> ve složce bin (první šipka v obrázku s rozbaleným archivem). Můžete tak provést z konzole:</p>

<pre><code class="language-bash">$ pwd
/Users/ludekvesely/Downloads/elasticsearch-1.7.1
$ ./bin/elasticsearch
</code></pre>

<p>To že se povedlo Elasticsearch spustit ověříte na adrese <a href="http://localhost:9200">http://localhost:9200</a>. Nové verze vznikají velmi rychle, nelekejte se tedy mé starší verze 1.5.2. ;-)</p>

<p><img src="https://www.ludekvesely.cz/content/images/2015/08/elasticsearch-running.png" alt="Nette a Elasticsearch"></p>

<h2 id="vytvoennetteprojektu">Vytvoření Nette projektu</h2>

<p>Předpokládám že s Nette již máte zkušenosti a pravděpodobně jen chcete využít možností Elasticsearch v existujícím projektu. Pokud přesto nemáte žádný projekt vytvořený, <a href="https://getcomposer.org/download/">nainstalujte si composer</a> a následně pomocí příkazu</p>

<pre><code class="language-bash">composer create-project nette/web-project  
cd web-project  
chmod -R a+rw temp log  
</code></pre>

<p>Vytvořte prázdný projekt. Tato varianta je od verze Nette 2.3 preferovaná před nette/sandbox, více na <a href="http://forum.nette.org/en/22719-the-nette-nette-is-dead-long-live-the-nette-web-project">fóru Nette</a>. Projekt přesuňte do veřejně přístupného adresáře vašeho webového serveru a po přístupu na soubor index.php ve složce web-project/www byste měli vidět úvodní obrazovku:</p>

<p><img src="https://www.ludekvesely.cz/content/images/2015/08/nette-web-project.png" alt="Nette a Elasticsearch"></p>

<h2 id="instalacekdybyelasticsearch">Instalace Kdyby/ElasticSearch</h2>

<p>Otevřte terminál ve složce s projektem a zadejte příkaz</p>

<pre><code class="language-bash">composer require kdyby/elastic-search  
</code></pre>

<p>Nyní se nainstalovaly knihovny ruflin/elastica (samotná knihovna pracující s Elasticsearch) a kdyby/elastic-search (napojení na Nette). Zaktualizovaly se také soubory composer.json a composer.lock.</p>

<p>Dále je třeba rozšíření zaregistrovat v Nette a nakonfigurovat. To je maximálně jednoduché - do souboru <code>app/config/config.neon</code> přidejte následující řádky:</p>

<pre><code class="language-ini">extensions:  
    search: Kdyby\ElasticSearch\DI\SearchExtension

search:  
    host: 127.0.0.1
    port: 9200
</code></pre>

<p>No a to je vlastně všechno! Nyní si stačí kdekoli vyžádat <code>Kdyby\ElasticSearch\Client</code>, která dědí od <code>Elastica\Client</code> a s připojením dále pracovat <a href="http://elastica.io/getting-started/storing-and-indexing-documents.html">podle dokumentace knihovny Elastica</a>.</p>

<h2 id="pouitknihovnyelasticavnetteaplikaci">Použití knihovny Elastica v Nette aplikaci</h2>

<p>Ve vzorové aplikaci pro zachování jednoduchosti ukáži použití v již vytvořeném presenteru <code>HomepagePresenter.php</code>, ve vaší aplikaci ale tuto logiku doporučuji přenést do modelu. Otevřte tedy zmíněný soubor <code>HomepagePresenter.php</code> a do třídy <code>HomepagePresenter</code> přidejte:</p>

<pre><code class="language-php">private $client;

public function __construct(\Kdyby\ElasticSearch\Client $client)  
{
    $this-&gt;client = $client;
}
</code></pre>

<p>Nyní můžete využívat připojení k Elasticsearch pomocí proměnné <code>$client</code>. Například můžu nejprve <a href="https://www.elastic.co/guide/en/elasticsearch/reference/1.6/_create_an_index.html">vytvořit index</a> s názvem <code>my-index</code>, nastavím mu počet <a href="https://www.elastic.co/guide/en/elasticsearch/guide/current/replica-shards.html?q=shar">shardů</a> roven jedné a budu pracovat s type nazvaným <code>data</code>.</p>

<pre><code class="language-php">$index = $this-&gt;client-&gt;getIndex('my-index');
$index-&gt;create(['number_of_shards' =&gt; 1]);
$type = $index-&gt;getType('data');
</code></pre>

<p>Následně mohu definovat <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html?q=mapping">mapování</a>. Například chci, aby se pro koaždé pole nazvané <code>@timestamp</code> použil datový typ datum:</p>

<pre><code class="language-php">$mapping = new \Elastica\Type\Mapping();
$mapping-&gt;setType($type);
$mapping-&gt;setParam('dynamic_templates', [
    ['template_timestamp' =&gt; [
        'match' =&gt; '@timestamp',
        'mapping' =&gt; [
            'type' =&gt; 'date', 
            'format' =&gt; 'date_time_no_millis'
        ]
    ]]
]);
$mapping-&gt;send();
$type-&gt;setMapping($mapping);
</code></pre>

<p>Nyní lze zaindexovat dokument, v tomto příkladu pole <code>$raw</code>:</p>

<pre><code class="language-php">$raw = [
    'name' =&gt; 'Nette',
    'year' =&gt; 2000
];
$document = new \Elastica\Document(1, $raw);
$type-&gt;addDocument($document);
$index-&gt;refresh();
</code></pre>

<p>Tím jsme dokument zaindexovali do Elasticsearch a měl by být vyhledatelný. Ověřit to můžeme jednoduchým vyhledáním všech dokumentů:</p>

<pre><code class="language-php">foreach ($type-&gt;search([])-&gt;getResults() as $result) {  
    dump($result-&gt;getSource());
}
</code></pre>

<p>Výstupem (<a href="http://localhost/web-project/www/">http://localhost/web-project/www/</a>) by pak měl být námi zaindexovaný dokument:</p>

<pre><code class="language-php">array (2)  
   name =&gt; "Nette" (5)
   year =&gt; 2000
</code></pre>

<p>Výsledný soubor <code>HomepagePresenter.php</code> by pak měl vypadat takto:</p>

<pre><code class="language-php">&lt;?php

namespace App\Presenters;

use Nette;


class HomepagePresenter extends Nette\Application\UI\Presenter  
{
    /**
     * @var \Kdyby\ElasticSearch\Client
     */
    private $client;

    public function __construct(\Kdyby\ElasticSearch\Client $client)
    {
        $this-&gt;client = $client;
    }

    public function actionDefault()
    {
        // name of index is 'my-index'
        $index = $this-&gt;client-&gt;getIndex('my-index');

        // create index with single shard
        $index-&gt;delete();
        $index-&gt;create(['number_of_shards' =&gt; 1]);

        // name of type is 'data'
        $type = $index-&gt;getType('data');

        // put mappings (every field called @timestamp will be stored as a date)
        $mapping = new \Elastica\Type\Mapping();
        $mapping-&gt;setType($type);
        $mapping-&gt;setParam('dynamic_templates', [
            ['template_timestamp' =&gt; [
                'match' =&gt; '@timestamp',
                'mapping' =&gt; ['type' =&gt; 'date', 'format' =&gt; 'date_time_no_millis']
            ]]
        ]);
        $mapping-&gt;send();
        $type-&gt;setMapping($mapping);

        // index an array into es
        $raw = [
            'name' =&gt; 'Nette',
            'year' =&gt; 2000
        ];
        $document = new \Elastica\Document(1, $raw);
        $type-&gt;addDocument($document);

        // refresh es index
        $index-&gt;refresh();

        // now we can search indexed document
        foreach ($type-&gt;search([])-&gt;getResults() as $result) {
            dump($result-&gt;getSource());
        }

        $this-&gt;terminate();
    }
}
</code></pre>

<h2 id="kamdl">Kam dál?</h2>

<p>Jsme u konce návodu jak rozběhnout knihovnu Kdyby/Elasticsearch v Nette projektu. Pro další informace o Elasticsearch doporučuji přímo web společnosti Elastic a jejich <a href="https://www.elastic.co/guide/index.html">oficiální dokumentaci</a>, pokud máte radši tištěnou literaturu, tak by měla být dobrá kniha <a href="https://www.elastic.co/blog/elasticsearch-definitive-guide">The Definitive Guide</a>. Pro práci s knihovnou Elastica je veškeré podrobné info na jejich webu <a href="http://elastica.io">elastica.io</a>. Zdrojové kódy knihovny Kdyby/ElasticSearch pak naleznete na <a href="https://github.com/Kdyby/ElasticSearch">githubu</a>.</p>]]></content:encoded></item><item><title><![CDATA[Docker na Windows a OS X]]></title><description><![CDATA[S pomocí Dockeru je možné vytvářet kontejnery, ve kterých můžete spouštět svou aplikaci. V tomto článku popisuji, jak rozjet Docker na OS X a Windows.]]></description><link>https://www.ludekvesely.cz/docker-na-windows-a-os-x/</link><guid isPermaLink="false">99142a2a-8dd6-4070-8207-ea187fa32cf9</guid><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Luděk Veselý]]></dc:creator><pubDate>Wed, 08 Jul 2015 23:02:47 GMT</pubDate><media:content url="https://www.ludekvesely.cz/content/images/2015/07/Kitematic.png" medium="image"/><content:encoded><![CDATA[<img src="https://www.ludekvesely.cz/content/images/2015/07/Kitematic.png" alt="Docker na Windows a OS X"><p><em><strong>Update</strong>: tento článek je mírně zastaralý, aktuálně je nejsnažší cesta využít <a href="https://docs.docker.com/docker-for-mac/install/">Docker for Mac</a> případně <a href="https://docs.docker.com/docker-for-windows/">Docker for Windows</a>.</em></p>

<p>Slyšeli jste už o Dockeru? Pokud ne, tak v rychlosti shrnu k čemu vám může být nápomocný. S jeho pomocí je možné vytvářet kontejnery, ve kterých můžete spouštět svou aplikaci. Pokud píšete v PHP, tak podobně jako do souboru composer.json zapisujete všechny závislosti, vytvoříte soubor Dockerfile, který popisuje jak aplikaci zprovoznit. Zvolíte základní image (těch je nepřeberná řada volně k dispozici), například Ubuntu. Dalšími příkazy v tomto souboru nainstalujete například požadovanou verzi PHP a nakopírujete zdrojové soubory. Máte tak jistotu že aplikace vždy poběží ve stejném prostředí.</p>

<p>To je velmi výhodné při lokálním vývoji. Prostě spustíte odpovídající kontejnery a aplikace běží. Není třeba trávit čas instalací software v odpovídajících verzích nebo jejich přepínáním. Obdobně lze pak aplikaci přenést na server - stačí  na serveru stáhnout image a spustit.</p>

<p>Princip Dockeru je podobný jako práce s virtualizovanými stroji. Hlavní rozdíl je ten, že kontejnery mají daleko menší režii. Přestože Docker nyní prochází bouřlivým vývojem, je zatím nativně podporován pouze v OS Linux. Co ale dělat pokud jej chcete používat i na Windows nebo OS X?</p>

<p>Řešení zatím není úplně triviální - je třeba rozběhnout virtualizovaný Linux a v něm teprve Docker, vyřešit synchronizaci mezi hostitelským a virtualizovaným OS...</p>

<h3 id="kitematic">Kitematic</h3>

<p>Naštěstí existuje aplikace, která tento problém elegantně řeší. Jmenuje se Kitematic a pro stažení vás odkáži na oficiální web: <a href="https://kitematic.com">https://kitematic.com</a>. </p>

<p>Nejprve tedy stáhněte Kitematic a spusťte jej. Úvodní obrazovka by měla vypadat takto:</p>

<p><img src="https://cloud.githubusercontent.com/assets/251292/6427882/3c02eeb2-bf5c-11e4-85f5-2b9198d5941d.png" alt="Docker na Windows a OS X"></p>

<p>Přemýšleli jste jak nainstalovat VirtualBox a správně jej nastavit? Kitematic to zvládne za vás. Můžou se objevit problémy pokud jste již v minulosti Docker instalovali, v mém případě vše proběhlo v pořádku a nyní je k dispozici hlavní obrazovka:</p>

<p><img src="https://cloud.githubusercontent.com/assets/251292/6427866/d574314c-bf5b-11e4-824d-946d41b174f4.png" alt="Docker na Windows a OS X"></p>

<p>Zde můžete spustit terminál, ve kterém lze spouštět příkazy Dockeru jako <code>docker run</code> a další. Nejrychlejší způsob spuštění kontejneru je ale kliknutím na <em>Create</em> u příslušného image. Po spuštění můžete vidět jak aplikace vypadá v prohlížeči a procházet připojené složky. Poslední možností je nastavení kontejneru - jaké porty jsou zveřejněny, jaké složky jsou připojeny a nastavení proměnných. </p>

<p><img src="https://cloud.githubusercontent.com/assets/251292/6427815/f7139772-bf59-11e4-8118-4fef00693985.png" alt="Docker na Windows a OS X"></p>

<p>Další informace a návody:</p>

<ul>
<li><a href="https://kitematic.com/docs/">https://kitematic.com/docs/</a></li>
<li><a href="https://docs.docker.com/kitematic/userguide/">https://docs.docker.com/kitematic/userguide/</a></li>
</ul>]]></content:encoded></item></channel></rss>