Elastic Stack を構築する 改め，Elastic Stack のクラスターを構築する

#はじめに
仕事でElastic Stack のクラスターを構築する可能性が出てきたので，プライベートの検証環境で勉強がてら構築して検証してみた．

構築した環境は以下の通り．断続的にログが転送されてくる環境にしたかったので，Web サーバーにアクセスするスクリプトを作成してそのログをSyslog サーバーに転送させ，そのログファイルをscp でElastic Stack に取り込む形にした．
Elasticsearch はElasticsearch の機能に集中させたいため，Kibana とLogstash とは分離させ，障害が発生しても機能するようにクラスター構成とした．

![elasticcluster.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438083/765cc7e0-86cb-7cdd-88ec-376a4dede649.png)

各ノードの構成は以下のとおりである．

|ノード名|	OS|	ミドルウェア/アプリケーション|
|:------------------------:|:------------|:------------|
|Webサーバー      |  Cent OS 8   |apache，rsyslog，jq (1.5)|
|Syslogサーバー   |Cent OS 8	|rsyslog，jq (1.5)|
|ログ処理サーバー  |Cent OS 8    |logstash (7.4.0)，kibana (7.4.0)，openjdk (1.8.0)，jq (1.5)|
|Elasticsearch #1|Cent OS 8    |elasticsearch (7.4.0)，openjdk (1.8.0)，jq (1.5)|
|Elasticsearch #2|Cent OS 8    |elasticsearch (7.4.0)，openjdk (1.8.0)，jq (1.5)|	
|Elasticsearch #3|Cent OS 8    |elasticsearch (7.4.0)，openjdk (1.8.0)，jq (1.5)|

#Web サーバーの構築
最初に，Web サーバーを用意し，そのアクセスログをsyslog にてSyslog サーバーに転送するインスタンスを構築する．

##apache の構築
apache をインストールする．

```terminal:実行コマンド
# dnf -y install httpd
```

Cent OS 8 から？IPアドレス指定だけでアクセスしようとすると403がレスポンスされる．それだと正常にアクセスできたかどうかわからなかったので，ドキュメントルート直下に以下のhtml ファイルを作成して配置した．curl でアクセスするとtest とだけレスポンスされる．

```html:/var/www/html/test.html
test
```

最後に，ログ転送関連の設定を行う．

##アクセススクリプトの作成
以下のスクリプトを作成し，アクセスログを定期的に発生させるようにした．
最初はsleep コマンドはなかったが，ないとログが多すぎるのでログ量を調整することにした．

```sh:access.sh
#!/bin/bash

while [ true ]
do
    curl http://192.168.226.135/test.html
    sleep 60	
done
```

##rsyslog の設定
rsyslog により，apache のアクセスログをSyslog サーバーに転送するため，apache の設定ファイルに以下を追記する．ユーザー定義のlocal0 を利用する．

```conf:/etc/httpd/conf/httpd.conf
CustomLog "|/usr/bin/logger -p local0.info -t httpd_access" combined
```

次に，rsyslog でSyslog サーバーに転送の設定を記述する．今回は，UDP でログ転送をする．

```conf:/etc/rsyslog.conf
local0.* @192.168.226.136:514
```

ファイアウォールの許可設定をしておく．

```terminal:実行コマンド
# firewall-cmd --add-port=80/tcp --permanent
# firewall-cmd --reload
```

最後にapache とrsyslogの自動実行をON にし，起動させる．

```terminal:実行コマンド
# systemctl enable httpd
# systemctl start httpd
# systemctl enable rsyslog
# systemctl start rsyslog
```

#Syslog サーバーの構築

Syslog サーバーでは，rsyslog に受信の設定を記述する．
送信側がlocal0 でログ転送するので，そのファシリティで受信したログの保存先を指定する．

```conf:/etc/rsyslog.conf
$ModLoad imudp
$UDPServerRun 514

local0.*    -/var/log/test/access.log
```

ファイアウォールの許可設定をしておく．

```terminal:実行コマンド
# firewall-cmd --add-port=514/udp --permanent
# firewall-cmd --reload
```

最後にrsyslogの自動実行をON にし，起動させる．

```terminal:実行コマンド
# systemctl enable rsyslog
# systemctl start rsyslog
```

#ログ処理サーバーの構築
Elastic Stack の役割は，Syslog サーバーに保存されているログファイルを取得してLogstash でパースおよびElasticsearch に取り込み，Kibana で可視化することである．

##ログファイルを取得するスクリプトの作成

Syslog サーバーに保存されているapache のアクセスログファイルを取得するため，以下のスクリプトを作成する．このスクリプトをcrontab により定期的に自動実行させる．
logstash サービスが，logstash ユーザーで実行されるため，取得したログファイルのオーナーをlogstash ユーザーに変更し，実行権限も付与しておく．

```sh:/home/elastic/log_copy.sh
#!/bin/bash

scp -i /root/.ssh/id_rsa root@192.168.226.136:/var/log/test/access.log /var/log/test
chown logstash /var/log/test/access.log
chmod +x /var/log/test/access.log
```

スクリプトを作成したら，実行権限を付与しておく．

```terminal:実行コマンド
# chmod +x /home/elastic/log_copy.sh
```

##crontab の設定

定期的にログファイルをSyslog サーバーから取得するため，crontab の設定を行う．以下のファイルを作成する．

```sh:/etc/cron.d/log_get
#分 時 月 日 曜日 ユーザ名 コマンド
*/5 * * * * root /home/elastic/log_copy.sh
```

このファイルにより，5分おきにlog_copy.sh が実行され，Syslog サーバーからapache のアクセスログファイルがログ処理サーバーのローカルにコピーされる．

##ssh の設定

scp によりファイルを取得するため，まずはssh に使用する公開鍵と秘密鍵のペアを作成する．
以下のコマンドは，ログ取り込みサーバー上で実行する．

```terminal:実行コマンド
# ssh-keygen -t rsa
# chmod 600 /root/.ssh/id_rsa
```

パスフレーズなどは設定せず，すべての質問はEnter を入力して進む．

次に，公開鍵をSyslog サーバーに登録する．以下からは，Syslog サーバー上で実行する．

```terminal:実行コマンド
# scp root@192.168.226.140:/root/.ssh/id_rsa.pub /root/.ssh/authorized_keys
# chmod 600 /root/.ssh/id_rsa.pub
```

これで公開鍵が登録できたので，次は設定ファイルより公開鍵認証でログインできるようにする．

```conf:/etc/ssh/sshd_config
PubkeyAuthentication yes
```

今回は設定実施しなかったが，インターネット公開系のシステムであったりすると，root ログインやパスワード認証を無効にする．

```conf:/etc/ssh/sshd_config
PermitRootLogin no           #root ログインを無効
PasswordAuthentication no    #パスワード認証を無効
```

ssh の設定が完了したら，ssh を再起動しておく．

```terminal:実行コマンド
# systemctl restart sshd
```

##java のインストール

Logstash やElasticsearch のインストールに必要なjava をインストールする．

```terminal:実行コマンド
# dnf -y install java-1.8.0-openjdk
```

##Logstash のインストール

Logstash をインストールする．インストールするためのファイルは，以下のページからダウンロードする．
https://www.elastic.co/jp/downloads/

今回は，「logstash-7.4.0.rpm」をインストールする．

```terminal:実行コマンド
# rpm -ivh logstash-7.4.0.rpm
```

##Logstash の設定

取り込むログファイルに関しての設定を行う．Logstash はinput，filter，output のパイプラインで取り込んだデータを処理する．
inputは，取り込むデータの指定，filter はデータのパース，output は，出力先を指定する形式である．

```conf:/etc/logstash/conf.d/access.conf
input {
 file {
   mode => "tail"
   path => ["/var/log/test/access.log"]
   sincedb_path => "/var/log/test/test_pointer"
   start_position => "beginning"
   type => "access"
  }
}

filter {
if[type] == "access" {
grok {
match => {
      "message" => "%{SYSLOGTIMESTAMP:syslog_timestamp} %{HOSTNAME:source} %{USERNAME}\[%{INT}\]: %{IPV4:destination} - - \[%{HTTPDATE:access_time}\] %{GREEDYDATA:message}"
remove_filed => ["message"]
}
}
}
}

output {
if[type] == "access" {
#debug
#stdout {
#codec => rubydebug
#}
 file {
   path => ["/var/log/test/result_access.log"]
       }
  elasticsearch {
                  hosts => ["192.168.226.137:9200", "192.168.226.138:9200", "192.168.226.139:9200"]
                  index => "test"
            }
}
}
```

上記設定ファイルで，syslog 経由で送信されてくるapache のログファイルをパースできる．

設定が正しくできているかはlogstash を設定ファイルを指定して実行させることで確認できる．その前に，以下の文を追記しておき，パスを通しておく．

```sh:/root/.bashrc
export PATH="$PATH:/usr/share/logstash/bin"
```

以下のコマンドを実行し，結果を確認する．

```terminal:実行コマンド
# logstash  -f "/etc/logstash/conf.d/access.conf"
```

以下のような形で，json 形式でログが取り込まれていればOK．

```json:ログ取り込み結果
{
  "message": [
    "Jan  3 01:36:13 localhost httpd_access[8378]: 【WebサーバーのIPアドレス】 - - [03/Jan/2020:01:36:13 -0500] \"GET /test.html HTTP/1.1\" 200 5 \"-\" \"curl/7.61.1\"",
    "\"GET /test.html HTTP/1.1\" 200 5 \"-\" \"curl/7.61.1\""
  ],
  "syslog_timestamp": "Jan  3 01:36:13",
  "destination": "【WebサーバーのIPアドレス】",
  "path": "/var/log/test/access.log",
  "access_time": "03/Jan/2020:01:36:13 -0500",
  "@timestamp": "2020-01-03T13:38:53.721Z",
  "host": "elastic1",
  "@version": "1",
  "source": "localhost"
}
```

##Kibana のインストール

今回は，「kibana-7.4.0-x86_64.rpm」をインストールする．ファイルはLogstash と同じページからダウンロードした．

```sh:実行コマンド
# rpm -ivh kibana-7.4.0-x86_64.rpm
```

##Kibana の設定

Kibana を使ってElasticsearch に蓄積されたデータを可視化する．

設定ファイルを以下のように記述する．追記した部分のみ抜粋する．

```yml:/etc/kibana/kibana.yml
server.host: "0.0.0.0"
elasticsearch.hosts: ["http://192.168.226.137:9200", "http://192.168.226.138:9200", "http://192.168.226.139:9200"] 
```

ファイアウォールの許可設定をしておく．

```sh:実行コマンド
# firewall-cmd --add-port=5601/tcp --permanent
# firewall-cmd --reload
```

最後にLogstash とKibana の自動実行をON にし，起動させる．

```terminal:実行コマンド
# systemctl enable logstash
# systemctl start logstash
# systemctl enable kibana
# systemctl start kibana
```

#Elasticsearch の構築

##Elasticsearch のインストール

今回は，「elasticsearch-7.4.0-x86_64.rpm」をインストールする．ファイルはLogstash と同じページからダウンロードした．

```terminal:実行コマンド
# rpm -ivh elasticsearch-7.4.0-x86_64.rpm
```


##Elasticsearch の設定

Elasticsearch は，3台でクラスターを構築し，それぞれデータノードおよびマスター候補ノードとして扱うよう設定する．以下の設定ファイルの記述は，node-1において構築に必要なため追記/修正したものを抜粋した．

```yml:/etc/elasticsearch/elasticsearch.yml
cluster.name: exam-cluster

node.name: node-1 #node-2，node-3とnode ごとに名前を変える

network.host: 0.0.0.0
http.port: 9200
transport.host: 192.168.226.137 #node-2，node-3とnode ごとに値を変える
transport.tcp.port: 9300

discovery.zen.ping.unicast.hosts: ["192.168.226.137", "192.168.226.138", "192.168.226.139"]
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]
```

ファイアウォールの許可設定をしておく．

```terminal:実行コマンド
# firewall-cmd --add-port=9200/tcp --permanent
# firewall-cmd --add-port=9300/tcp --permanent
# firewall-cmd --reload
```

最後にElasticsearch の自動実行をON にし，起動させる．

```terminal:実行コマンド
# systemctl enable elasticsearch
# systemctl start elasticsearch
```

クラスターのノードを確認するには以下のコマンドを実行する．

```terminal:クラスターのノードを確認
# curl http://192.168.226.137:9200/_cat/nodes
```

ノードが3台表示され，３台でクラスターが構築されていることが分かる．

```terminal:実行結果
192.168.226.138 12 87 1 0.00 0.00 0.00 dilm * node-2
192.168.226.137 10 91 1 0.00 0.00 0.00 dilm - node-1
192.168.226.139 13 87 1 0.00 0.01 0.00 dilm - node-3
```

クラスターの状態確認は以下のコマンドを実施する．

```terminal:クラスターの状態を確認
# curl http://192.168.226.137:9200/_cluster/health?pretty
```

実行すると，ステータスがgreen となっており，number_of_nodes の値も3となっており，3台で正常にクラスターが組まれていることが分かる．

```json:実行結果
{
  "cluster_name" : "exam-cluster",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 3,
  "number_of_data_nodes" : 3,
  "active_primary_shards" : 4,
  "active_shards" : 8,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 0,
  "active_shards_percent_as_number" : 100.0
}
```

データノードやマスター候補ノードの設定は明示的に設定しなかったが，デフォルトでデータノードおよびマスター候補ノードになることが分かった．

#今後の予定

以下，取り組みたい内容は以下のとおりである．

##複数の種類のログファイルの安全な取り込み

##最新データの更新 (未実装)

##他インデックスの情報の結合処理 (未実装)

##csvファイルの出力 (未実装)

#課題一覧
- logstash サービスから自動でログが取り込まれない【解決済み】
→ logstash サービスがlogstash ユーザーで実行されるため，取り込むログファイルがroot ユーザーなど実行ユーザーのおいて権限がない場合だと，Permission denied となり失敗する．ただし，root ユーザーでlogstash の実行ファイルを実行させると，root ユーザーで実行されるため，その問題が解消される．そのため，前回は実行ファイルの実行だと問題なかったが，サービス起動だとログが取り込まれなかった．ログを取り込むスクリプトでオーナーを変更することで解決した．

本事象は，/var/log/logstash/logstash-plain.log からエラーメッセージを確認することで解決できた．(/var/log/test にあるファイルに権限がないというエラーメッセージが出力されていた．)

- Elasticsearch でクラスターが組まれない【解決済み】
→ /etc/elasticsearch/elasticsearch.yml に以下の記述が必要だった．
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]

本事象は，/var/log/elasticsearch/exam-cluster.log からエラーメッセージを確認することで解決できた．(上記の記述がないというエラーメッセージが出力されていた．)

- 複数の種類のログファイルの安全な取り込み【未解決】
Logstash で複数のログファイルを複数の.conf で取り込もうとすると，ログの保存先インデックスが混ざる．(Aというインデックスにだけ保存したいログデータがBというインデックスにも保存されてしまう)
→ おそらくlogstash の設定ファイルにtype でif の条件式をfilter  やoutput に追記する必要があると思われる．