ELK stack com Docker compose + API/Curl

Já é de grande conhecimento da comunidade de tecnologia que a stack ELK (Elasticsearch, Kibana, Logstash) veio pra ficar, o Elasticsearch, o Kibana e o Logstash são softwares open source criados pela empresa elastic. Conhecido por suas APIs REST simples e sua natureza, velocidade e escalabilidade distribuídas, o Elasticsearch é o componente central da stack, que em conjunto com as demais ferramentas permite a ingestão, enriquecimento, armazenamento, análise e visualização de dados.

Com esse conjunto de ferramentas é possível centralizar e armazenar bilhões de registros, efetuar buscas instantâneas, importar dados de diferentes formatos, coletar métricas de desempenho, gerar relatórios combinando vários filtros e criar dashboards dinâmicos, que transformam dados em informação com apenas alguns cliques. São ferramentas essenciais que facilitam e auxiliam a tomada de decisão e que podem ser utilizadas em várias áreas, como a da tecnologia da informação, administração, finanças, saúde etc.

Sem mais delongas, vamos por a mão na massa!

Para este lab vou utilizar o docker-compose da minha máquina local para subir a stack. Com o docker instalado e funcionando vamos baixar o repo: git@github.com:tbernacchi/meu-elk-compose.gite executar o compose:

➜ ~ git clone git@github.com:tbernacchi/meu-elk-compose.git
➜ meu-elk-compose ll
total 8
-rw-r--r-- 1 tadeu staff 1.6K Apr 23 14:08 docker-compose.yml
drwxr-xr-x 3 tadeu staff 96B Apr 21 19:42 elasticsearch
drwxr-xr-x 3 tadeu staff 96B Apr 21 20:05 kibana
drwxr-xr-x 5 tadeu staff 160B Apr 21 20:05 logstash

➜ meu-elk-compose docker-compose up -d
Creating elasticsearch … done
Creating kibana … done
Creating logstash … done

Vamos ver se subiu bonitinho?

➜  ~ curl --silent -X GET -H "Content-Type: application/json" http://localhost:9200/ | jq
{
"name": "fM2qT7v",
"cluster_name": "tadeu-elk-docker",
"cluster_uuid": "0paL7iryTbWQ6gcVMlyirw",
"version": {
"number": "6.5.3",
"build_flavor": "default",
"build_type": "tar",
"build_hash": "159a78a",
"build_date": "2018-12-06T20:11:28.826501Z",
"build_snapshot": false,
"lucene_version": "7.5.0",
"minimum_wire_compatibility_version": "5.6.0",
"minimum_index_compatibility_version": "5.0.0"
},
"tagline": "You Know, for Search"
}

Oiaaa que belezinhaa…agora vamos brincar com a API do elastic utilizando o curl.

Saúde do cluster:

➜ ~ curl --silent -X GET -H "Content-Type: application/json" http://localhost:9200/_cluster/health | jq

Todas as configurações:

➜ ~ curl --silent -X GET -H "Content-Type: application/json" http://localhost:9200/_nodes/_all/settings| jq

Verificar os índices:

➜ ~ curl --silent -X GET -H "Content-Type: application/json" http://localhost:9200/_cat/indices

Para verificar o status de todos os índices:

➜ ~ curl --silent -X GET -H "Content-Type: application/json" http://localhost:9200/_cluster/health\?level=indices | jq

Para listar um índice:

➜ ~ curl -s -XGET --header 'Content-Type: application/json' http://localhost:9200/kibana_sample_data_logs/_search\?pretty

Agora vamos criar um índice usando esse sample da elastic: https://download.elastic.co/demos/kibana/gettingstarted/accounts.zip

➜ ~ wget https://download.elastic.co/demos/kibana/gettingstarted/accounts.zip
➜ ~ unzip accounts.zip
Archive: accounts.zip
inflating: accounts.json

Criamos o índice:

➜ ~ curl --silent -X PUT -H "Content-Type: application/json" -d' {
"mappings": {
"_doc": {
"properties": {
"index": { "type": "integer"},
"account_number": { "type": "integer" },
"balance": { "type": "integer" },
"firstname": { "type": "text" },
"lastname": { "type": "text" },
"age": { "type": "integer" },
"gender": { "type": "text" },
"address": { "type": "text" },
"employer": { "type": "text" },
"email": { "type": "text" },
"city": { "type": "text" },
"state": { "type": "text" }
}
}
}
}' http://localhost:9200/teste | jq
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "teste"
}

Podemos verificar a estrutura do documento com o mapping:

➜ ~ curl --silent -X GET -H "Content-Type: application/json" http://localhost:9200/teste/_mappings| jq

E a quantidade de documentos ‘docs.count’, repare (0):

➜ ~ curl --silent -X GET -H "Content-Type: application/json" http://localhost:9200/_cat/indices/teste\?v
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open teste d0Il6n18RhygxpYXJOCAhQ 5 1 0 0 1.1kb 1.1kb

Vamos inserir documentos (tipo bulk) nesse índice que criamos utilizando o arquivo accounts.json.

➜ ~ curl --silent -H "Content-Type: application/json" -XPOST http://localhost:9200/teste/_doc/_bulk --data-binary "@accounts.json"  | jq

Se tudo ocorreu bem podemos verificar executando um ‘cat’ no índice que criamos:

➜ ~ curl --silent -X GET -H "Content-Type: application/json" http://localhost:9200/_cat/indices/teste\?v
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open teste 5oPPTbWdQASG4u9_edj7OQ 5 1 1000 0 289.5kb 289.5kb

Para contar os documentos:

➜ ~ curl -s -XGET --header 'Content-Type: application/json' http://localhost:9200/teste/_count\?pretty

Vamos fazer uma busca nesse índice que criamos, vamos buscar o usuário de id 995 do accounts.json.

➜  ~ tail -n3 accounts.json
{"index":{"_id":"995"}}{"account_number":995,"balance":21153,"firstname":"Phelps","lastname":"Parrish","age":25,"gender":"M","address":"666 Miller Place","employer":"Pearlessa","email":"phelpsparrish@pearlessa.com","city":"Brecon","state":"ME"}

Query:

➜ ~ curl -s -XGET --header 'Content-Type: application/json' http://localhost:9200/teste/_search -d '{
"query": {
"match": {
"_id": "995"
}
}
}' | jq
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 1,
"hits": [
{
"_index": "teste",
"_type": "_doc",
"_id": "995",
"_score": 1,
"_source": {
"account_number": 995,
"balance": 21153,
"firstname": "Phelps",
"lastname": "Parrish",
"age": 25,
"gender": "M",
"address": "666 Miller Place",
"employer": "Pearlessa",
"email": "phelpsparrish@pearlessa.com",
"city": "Brecon",
"state": "ME"
}
}
]
}
}


BACKUP e RESTORE

Vamos verificar se já existem snapshots:

➜ ~ curl -XGET localhost:9200/_snapshot/_all/
{}%

Vamos ajustar nosso container do elastic para o snaphost:

➜ ~  docker ps -a | grep elasticsearch:6.5.3
8b8471d73bad docker.elastic.co/elasticsearch/elasticsearch:6.5.3 "/usr/local/bin/dock…" 18 minutes ago Up 18 minutes 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp elasticsearch
➜ ~ docker exec -it 8b8471d73bad mkdir -p /elasticsearch-backup
➜ ~ docker exec -it 8b8471d73bad chown elasticsearch:elasticsearch /elasticsearch-backup
➜ ~ docker exec -it 8b8471d73bad bash -c "echo path.repo: [ /elasticsearch-backup ] >>
/usr/share/elasticsearch/config/elasticsearch.yml"
➜ ~ docker exec -it 8b8471d73bad chown elasticsearch: /elasticsearch-backup -R
➜ ~ docker restart 8b8471d73bad
8b8471d73bad

Vamos criar a estrutura para nosso snapshot:

➜ ~ curl --silent -X PUT -H "Content-Type: application/json" -d' {
"type": "fs",
"settings": {
"compress" : true,
"location": "/elasticsearch-backup"
}
}' http://localhost:9200/_snapshot/tadeu_teste

E criamos um snapshot:

➜ ~ curl --silent -X PUT -H "Content-Type: application/json" -d '{
"indices": "teste",
"include_global_state": false,
"wait_for_completion": true
}' http://localhost:9200/_snapshot/tadeu_teste/snapshot-number-one | jq
{
"accepted": true
}

Vamos verificar se criou alguma coisa:

➜ ~ docker exec -it 8b8471d73bad bash
[root@8b8471d73bad elasticsearch]# cd /elasticsearch-backup/
[root@8b8471d73bad elasticsearch-backup]# ls -l
total 20
-rw-rw-r-- 1 elasticsearch root 179 Apr 23 03:48 index-0
-rw-rw-r-- 1 elasticsearch root 8 Apr 23 03:48 index.latest
drwxrwxr-x 3 elasticsearch root 4096 Apr 23 03:48 indices
-rw-rw-r-- 1 elasticsearch root 103 Apr 23 03:48 meta-WrhLIWLGSImP4MujOSk-yQ.dat
-rw-rw-r-- 1 elasticsearch root 234 Apr 23 03:48 snap-WrhLIWLGSImP4MujOSk-yQ.dat

Vamos fazer um backupzinho do nosso snapshot, vamos usar ele pra um restore em outro node com elastic.

➜ ~ docker exec -it 8b8471d73bad bash -c "tar czvf elastic_snapshot.tar.gz /elasticsearch-backup"
tar: Removing leading `/' from member names
/elasticsearch-backup/
/elasticsearch-backup/index-0
/elasticsearch-backup/meta-WrhLIWLGSImP4MujOSk-yQ.dat
/elasticsearch-backup/indices/


Vamos testar o backup efetuando um restore em outro node com elastic up and running.

[root@elastic01 ~]# mkdir elasticsearch-backup
[root@elastic01 ~]# cd elasticsearch-backup
[root@elastic01 ~]# tar xzvf elastic_snapshot.tar.gz .
[root@elastic01 ~]# chown elasticsearch: elasticsearch-backup/ -R

Adicione a entrada no elasticsearch.yml:

path.repo : /elasticsearch-backup
[root@elastic01 ~]# systemctl restart elasticsearch

Agora criamos a estrutura para o restore, assim como no container porém com outro nome:

[root@elastic01 ~]# curl --silent -X PUT -H "Content-Type: application/json" -d' {
"type": "fs",
"settings": {
"compress" : true,
"location": "/elasticsearch-backup"
} }' http://elastic01:9200/_snapshot/tadeu_recovery
{"acknowledged":true}[root@elastic01 ~]#

Executamos o restore:

[root@elastic01 ~]# curl --silent -X POST -H "Content-Type: application/json" http://elastic01:9200/_snapshot/tadeu_recovery/snapshot-number-one/_restore
{"accepted":true}[root@elastic01 ~]#

E olha o danado ali:

[root@elastic01 ~]# curl --silent -X GET -H "Content-Type: application/json" http://elastic01:9200/_cat/indices/teste\?v
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open teste PgSohHv1RXOOeoBLedyUhw 5 1 1000 0 299.1kb 299.1kb

É isso…matando os containers…

➜ ~ docker-compose stop
Stopping logstash … done
Stopping kibana … done
Stopping elasticsearch … done

➜ ~ docker-compose rm -f
Going to remove logstash, kibana, elasticsearch
Removing logstash … done
Removing kibana … done
Removing elasticsearch … done

CONCLUSÃO
A stack ELK já há algum tempo está presente em grande parte do mundo de tecnologia e vem ganhando cada vez mais adeptos, uma ferramenta muito poderosa e exatamente como a fabricante promete, com APIs REST simples que garantem muita velocidade e escalabilidade em sua administração. Sua instalação com docker foi bem rápida e tranquila e gostei muito de brincar com o Curl.

Referências
https://www.elastic.co/pt/what-is/elasticsearch
https://www.elastic.co/guide/en/elasticsearch/reference/6.8/modules-snapshots.html
https://docs.docker.com/compose/