Guia de como criar um cluster com o centralizador de logs Graylog 3.3
Tags: graylog, elasticsearch, mongodb, haproxy
Published: 2020-10-08 12:56:35

Heeey!

Fiz um lab utilizando a arquitetura do Graylog de alta disponibilidade e resolvi criar um guia com os passos que fiz nessa jornada. 

Na documentação do graylog essa configuração se chama "[Multi-node Setup](https://docs.graylog.org/en/3.3/pages/configuration/multinode_setup.html#configure-multinode)". Utilizaremos 3 servidores com Graylog e MongoDB, 3 servidores com Elasticsearch e 1 com HAProxy. 

Para ter uma ideia. Nossa arquitetura vai ficar desta forma:

![](https://dev-to-uploads.s3.amazonaws.com/i/uj2xt2r7kjyzp462hmp4.png)

**OBS:**Os servidores foram criados com a distro CentOS 7. Logo, se fizer em outra distro vai ter que rever os processo de instalação. Vamos lá ;)

##Elasticsearch

O Elasticsearch é o mecanismo de busca e análise de dados que o Graylog utiliza para indexar os logs recebidos.

###**Instalação** 

O Graylog apenas suporta até a versão 6.x do Elasticsearch. 

>eu que sempre instalo a última versão das coisas descobri isso após ter finalizado o cluster </3

Um pré-requisito para todos servidores é o java. Então vamos começar por ele.
```
yum update && yum -y install java-1.8.0-openjdk-headless.x86_64
```

Para a instalação do Elasticsearch, vamos importar a  Elastic GPG key.
```
rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch 
```
E adicionar o arquivo `elasticsearch.repo` no `/etc/yum.repos.d/`.
```
$ vim elasticsearch.repo
[elasticsearch-6.x]
name=Elasticsearch repository for 6.x packages
baseurl=https://artifacts.elastic.co/packages/oss-6.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md
```

Após isso basta instalar
```
yum install elasticsearch-oss
```

###**Configuração** 

Como os meus servidores eu coloquei o mínimo de RAM (1G) precisei mudar algumas opções do Java, limitando o uso de memória dele. Caso tiver mais que 1G não precisa fazer essa etapa.
Altere as linhas:
```
$ vim /etc/elasticsearch/jvm.options
-Xms512m
-Xmx512m
```

Edite o arquivo de configuração:
```
$ vim /etc/elasticsearch/elasticsearch.yml
cluster.name: graylog
node.name: ${HOSTNAME}
network.host: 0.0.0.0
http.port: 9200
discovery.zen.ping.unicast.hosts: ["elastic1.lab", "elastic2.lab", "elastic3.lab"]
```

**OBS:** Para ficar melhor a identificação dos servers, configurei o DNS no `/etc/hosts`

Após configurar basta subir o serviço:
```
systemctl daemon-reload
systemctl enable elasticsearch.service
systemctl restart elasticsearch.service 
```

Você consegue checar o status do Elasticsearch através do `curl`:
```
$ curl http://elastic1.lab:9200 
{
  "name" : "elastic1",
  "cluster_name" : "graylog",
  "cluster_uuid" : "qMtqk9hUTcm9gT8a6wF7-w",
  "version" : {
    "number" : "6.8.12",
    "build_flavor" : "oss",
    "build_type" : "rpm",
    "build_hash" : "7a15d2a",
    "build_date" : "2020-08-12T07:27:20.804867Z",
    "build_snapshot" : false,
    "lucene_version" : "7.7.3",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}

```

No meu caso não foi de primeira, por um bloqueio do SELinux. Então caso não conseguir verifique se o SELinux ou o firewall está bloqueando a conexão.

Você pode apenas desabilitar o firewall ou deixá-lo ativo e liberar a conexão do elasticsearch.

Para desabilitar basta rodar o comando:
```
$ systemctl disable firewalld
```

Após replicar esses processos nos outros dois servidores, o nosso cluster do Elasticsearch estará pronto!

##MongoDB

**OBS:** Agora vamos configurar 3 servidores com mongo e Graylog, então recomendo no mínimo 2G

O MongoDB é um serviço de banco de dados NoSQL que o Graylog utiliza para armazenar os dados de configuração.

###**Instalação** 

Crie um arquivo `mongodb-org-4.4.repo` no `/etc/yum.repos.d/`
```
$ vim mongodb-org-4.4.repo
[mongodb-org-4.4]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.4/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-4.4.asc
```
Após isso basta instalar
`yum update && yum install -y mongodb-org`

###**Configuração** 

Crie os diretórios:
```
mkdir -p /var/lib/mongo
mkdir -p /var/log/mongodb
mkdir -p /data/db
```

Configure as permissões do usuário mongod:
```
chown -R mongod:mongod /var/lib/mongo
chown -R mongod:mongod /var/log/mongodb
```

Na documentação do mongo, tem uma seção para configurar a política do SELinux para que permita os acessos. Caso sua distro não use SELinux, pode pular essa etapa.

```
$ yum install checkpolicy policycoreutils-python
```
```
cat > mongodb_cgroup_memory.te <<EOF
module mongodb_cgroup_memory 1.0;

require {
    type cgroup_t;
    type mongod_t;
    class dir search;
    class file { getattr open read };
}

#============= mongod_t ==============
allow mongod_t cgroup_t:dir search;
allow mongod_t cgroup_t:file { getattr open read };
EOF
```
```
$ checkmodule -M -m -o mongodb_cgroup_memory.mod mongodb_cgroup_memory.te
$ semodule_package -o mongodb_cgroup_memory.pp -m mongodb_cgroup_memory.mod
$ semodule -i mongodb_cgroup_memory.pp
```

Após configurar basta subir o serviço:
```
$ systemctl daemon-reload
$ systemctl enable mongod
$ systemctl start mongod
```

Você consegue testar a conexão rodando:
```
mongo
```

**OBS:**Caso mostrar alguma falha, verifique o firewall.

Para a configuração das réplicas do mongo basta executar (antes é preciso parar a instancia do mongod que está rodando):
```
mongod  --replSet "rs0" --bind_ip localhost,mongodb1.lab --fork --logpath /var/log/mongodb/mongod
```
Sobre o esta linha de comando:
 * mude mongodb1.lab para o endereço do server
 * `--fork` é para que o processo seja executado em segundo plano
 * `--replSet` para inserir o nome da configuração de replicação.

Após fazer esses procedimentos nos 3 servers. **Em apenas um**, conecte ao mongo shell:
```
$ mongo
```
e inicialize a configuração da replica:
```
rs.initiate( {
   _id : "rs0",
   members: [
      { _id: 0, host: "mongodb1.devops.lab:27017" },
      { _id: 1, host: "mongodb2.devops.lab:27017" },
      { _id: 2, host: "mongodb3.devops.lab:27017" }
   ]
})
```
você consegue verificar a configuração rodando:
```
rs.status()
```
a saída deve estar mais ou menos desta forma:
```
rs0:PRIMARY> rs.status()
{
        "set" : "rs0",
        "date" : ISODate("2020-10-07T04:23:25.190Z"),
        "myState" : 1,
        "term" : NumberLong(12),
        "syncSourceHost" : "",
        "syncSourceId" : -1,
        "heartbeatIntervalMillis" : NumberLong(2000),
        "majorityVoteCount" : 2,
        "writeMajorityCount" : 2,
        "votingMembersCount" : 3,
        "writableVotingMembersCount" : 3,
        "optimes" : {
                "lastCommittedOpTime" : {
                        "ts" : Timestamp(1602044601, 9),
                        "t" : NumberLong(12)
                },
                "lastCommittedWallTime" : ISODate("2020-10-07T04:23:21.627Z"),
                "readConcernMajorityOpTime" : {
                        "ts" : Timestamp(1602044601, 9),
                        "t" : NumberLong(12)
                },
                "readConcernMajorityWallTime" : ISODate("2020-10-07T04:23:21.627Z"),
                "appliedOpTime" : {
                        "ts" : Timestamp(1602044604, 11),
                        "t" : NumberLong(12)
                },
                "durableOpTime" : {
                        "ts" : Timestamp(1602044602, 11),
                        "t" : NumberLong(12)
                },
                "lastAppliedWallTime" : ISODate("2020-10-07T04:23:24.975Z"),
                "lastDurableWallTime" : ISODate("2020-10-07T04:23:22.958Z")
        },
        "lastStableRecoveryTimestamp" : Timestamp(1602044492, 7),
        "electionCandidateMetrics" : {
                "lastElectionReason" : "electionTimeout",
                "lastElectionDate" : ISODate("2020-10-07T04:19:54.787Z"),
                "electionTerm" : NumberLong(12),
                "lastCommittedOpTimeAtElection" : {
                        "ts" : Timestamp(0, 0),
                        "t" : NumberLong(-1)
                },
                "lastSeenOpTimeAtElection" : {
                        "ts" : Timestamp(1601673721, 7),
                        "t" : NumberLong(11)
                },
                "numVotesNeeded" : 2,
                "priorityAtElection" : 1,
                "electionTimeoutMillis" : NumberLong(10000),
                "numCatchUpOps" : NumberLong(0),
                "newTermStartDate" : ISODate("2020-10-07T04:19:55.550Z"),
                "wMajorityWriteAvailabilityDate" : ISODate("2020-10-07T04:19:56.455Z")
        },
        "members" : [
                {
                        "_id" : 0,
                        "name" : "mongodb1.devops.lab:27017",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 424,
                        "optime" : {
                                "ts" : Timestamp(1602044604, 11),
                                "t" : NumberLong(12)
                        },
                        "optimeDate" : ISODate("2020-10-07T04:23:24Z"),
                        "syncSourceHost" : "",
                        "syncSourceId" : -1,
                        "infoMessage" : "",
                        "electionTime" : Timestamp(1602044395, 1),
                        "electionDate" : ISODate("2020-10-07T04:19:55Z"),
                        "configVersion" : 1,
                        "configTerm" : 12,
                        "self" : true,
                        "lastHeartbeatMessage" : ""
                },
                {
                        "_id" : 1,
                        "name" : "mongodb2.devops.lab:27017",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 219,
                        "optime" : {
                                "ts" : Timestamp(1602044604, 6),
                                "t" : NumberLong(12)
                        },
                        "optimeDurable" : {
                                "ts" : Timestamp(1602044601, 9),
                                "t" : NumberLong(12)
                        },
                        "optimeDate" : ISODate("2020-10-07T04:23:24Z"),
                        "optimeDurableDate" : ISODate("2020-10-07T04:23:21Z"),
                        "lastHeartbeat" : ISODate("2020-10-07T04:23:24.801Z"),
                        "lastHeartbeatRecv" : ISODate("2020-10-07T04:23:25.012Z"),
                        "pingMs" : NumberLong(0),
                        "lastHeartbeatMessage" : "",
                        "syncSourceHost" : "mongodb1.devops.lab:27017",
                        "syncSourceId" : 0,
                        "infoMessage" : "",
                        "configVersion" : 1,
                        "configTerm" : 12
                },
                {
                        "_id" : 2,
                        "name" : "mongodb3.devops.lab:27017",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 212,
                        "optime" : {
                                "ts" : Timestamp(1602044604, 6),
                                "t" : NumberLong(12)
                        },
                        "optimeDurable" : {
                                "ts" : Timestamp(1602044601, 5),
                                "t" : NumberLong(12)
                        },
                        "optimeDate" : ISODate("2020-10-07T04:23:24Z"),
                        "optimeDurableDate" : ISODate("2020-10-07T04:23:21Z"),
                        "lastHeartbeat" : ISODate("2020-10-07T04:23:24.801Z"),
                        "lastHeartbeatRecv" : ISODate("2020-10-07T04:23:25.031Z"),
                        "pingMs" : NumberLong(1),
                        "lastHeartbeatMessage" : "",
                        "syncSourceHost" : "mongodb1.devops.lab:27017",
                        "syncSourceId" : 0,
                        "infoMessage" : "",
                        "configVersion" : 1,
                        "configTerm" : 12
                }
        ],
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1602044604, 11),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1602044604, 11)
}

```

Crie um banco de dados e um usuário para o Graylog:
```
use graylog;
db.createUser( { user: "mongo_admin", pwd: "graylog", roles: [ { role: "root", db: "admin" } ] } )
```

Certo, com o Elasticsearch e o MongoDB configurados, vamos para o Graylog!

##Graylog
###**Instalação** 
```
$ rpm -Uvh https://packages.graylog2.org/repo/packages/graylog-3.3-repository_latest.rpm
$ yum update && yum -y install java-1.8.0-openjdk-headless.x86_64 graylog-server
```

###**Configuração** 

Você também consegue alterar o uso de memória do Java, o arquivo de configuração dessas opções que fica em `/etc/sysconfig/graylog-server`.

Antes de editar o arquivo de configuração, você irá precisar gerar duas senhas.

Para gerar a `password_secret` basta rodar o comando:
```
pwgen -N 1 -s 96
```
**obs:** caso não tenha o pwgen instalado basta seguir esses passos:
```
$ yum install epel-release
$ yum install pwgen
```

Para gerar a root_password_sha2, rode esse comando:
```
echo -n "Enter Password: " && head -1 </dev/stdin | tr -d '\n' | sha256sum | cut -d" " -f1
```

Após isso, edite o arquivo de configuração:
```
$ vim /etc/graylog/server/server.conf
is_master = true
password_secret = [gerada anteriormente]
root_password_sha2 = [gerada anteriormente]
root_timezone = America/Sao_Paulo
http_bind_address = graylog1.lab:9000
http_publish_uri = http://graylog.1.lab:9000/
elasticsearch_hosts = http://elastic1.lab:9200,http://elastic2.lab:9200,http://elastic3.lab:9200
elasticsearch_shards = 3
mongodb_uri = mongodb://mongo_admin:graylog@mongodb1.lab:27017,mongodb2.lab:27017,mongodb3.lab:27017/graylog?replicaSet=rs0
```
**obs**:
* apenas deixe `is_master = true` em um dos servidores
* modifique web.graylog1 para o endereço do server
* modifique a linha `elasticsearch_hosts` com os endereços dos servidores do elasticsearch
* modifique a linha `mongodb_uri`com os endereços dos servidores do mongo

Agora basta subir o server do graylog:
```
$ systemctl daemon-reload
$ systemctl enable graylog-server
$ systemctl start graylog-server
```

Após alguns minutos você irá conseguir acessar o endereço do graylog **http://graylog1.lab:9000/**. O login é admin e a senha é a que você inseriu no campo `root_password_sha2`.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ayprmeknvsv39na5lp93.png)
   
##HAProxy

o HAProxy vai servir como um balanceador de carga para o nosso cluster. Isso quer dizer que ao configurar o envio de logs, nós iremos mandar para o endereço do servidor que está o HAProxy e ele que vai ser o responsável por enviar para os servidores do graylog.

###**Instalação** 
```
yum update && yum install haproxy
```

###**Configuração** 
Edite o arquivo de configuração e insira os blocos:

```
$ vim /etc/haproxy/haproxy.cfg

listen stats
   bind :32700
   stats enable
   stats uri /
   stats hide-version
   stats auth devops:devopslab

listen syslog_1514
   bind *:1514
   mode tcp
   option tcplog
   timeout client 120s
   timeout server 120s
   default-server inter 2s downinter 5s rise 3 fall 2 maxconn 64 maxqueue 128 weight 100
   server graylog1 graylog1.lab:1514 check
   server graylog2 graylog2.lab:1514 check
   server graylog3 graylog3.lab:1514 check
```
O primeiro bloco é para a página de estatísticas que o HAProxy exibe, ao acessá-la você conseguirá ver o balanceamento de dados entres os servidores  do graylog. Basta acessar o endereço do server na porta 32700:

![http://web.graylog.lab:32700/](https://dev-to-uploads.s3.amazonaws.com/i/npq8eten1cbimnd6l6e4.png)

O segundo bloco é a configuração da porta onde o HAProxy irá "ouvir" e depois realizar o balanceamento entre os servers. O "check" é para que ele verifique se o server está up antes de enviar.


##Enviando log para o Graylog

Você consegue enviar praticamente qualquer log de sistema para o servidor do graylog. Neste guia eu vou demonstrar apenas como configurar o envio utilizando o rsyslog. 

Para outros tipos de log, você pode utilizar o [Graylog Sidecar](https://docs.graylog.org/en/3.3/pages/sidecar.html), é um serviço que roda em segundo plano coletando logs que deseja enviar para a API do Graylog.

O primeiro passo para isso é configurar o **Input** na API do graylog, ele é o responsável por criar uma porta específica para os variados tipos de log.

Acesse a API do Graylog (no meu caso http://graylog1.lab:9000/) e siga os passos:

1. Vá em **System**
2. Depois em **Inputs**
3. Selecione **SysLog TCP**
4. Selecione **Launch new Input**

Ao seguir esses passos irá abrir uma janela para que preencha os detalhes do **Input** então vamos preencher os campos:

1. Marque a opção Global
2. Title: “Log Syslog”
3. Bind address: 0.0.0.0
4. Port:1514
5. Clique em Salvar.

Ao clicar em salvar, verá que o status do input passará de **NOT RUNNING**  para **RUNNING**.

Agora que você já configurou o input, basta configurar o rsyslog da máquina que irá enviar os logs:

```
$ vim /etc/rsyslog.d/graylog.conf
*.* @@web.graylog.lab:1514;RSYSLOG_SyslogProtocol23Format
```
**obs:**Troque `web.graylog.lab` para o endereço do servidor do HAProxy

Reinicie o serviço
```
systemctl restart rsyslog
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ejgkhcqd4k3edy0jzofc.png)
Após todos esses passos, ao acessar a API do Graylog, na aba **Search** você conseguirá visualizar, fazer consultas e criar dashboards personalizadas :)

**última observação:** Quando lidamos com cluster é muito importante que todos os servidores estejam com o mesmo horário, a divergência pode gerar erros no graylog.


Vlw pessoal! \o/