ラベル ElasticSearch の投稿を表示しています。 すべての投稿を表示
ラベル ElasticSearch の投稿を表示しています。 すべての投稿を表示

2025年9月21日日曜日

ElastAlert2 を使ってみる

ElastAlert2 を使ってみる

概要

前回ES9をdockerで構築しました
今回は ElastAlert2 を組み合わせて特定のログがES9に格納されたらアラートされる仕組みを作成してみます

環境

  • macOS 15.6.1
  • docker 28.4.0
    • ElasticSearch 9.0.7
    • Kibana 9.0.7
    • fluentd 1.19-1
    • ElastAlert 2.26.0

elastalert.yaml

EalstAlert2 共通の設定を作成します
接続する ElasticSearch9 の設定やルールを管理するディレクトリを指定します

  • vim elastalert.yaml
rules_folder: /opt/elastalert/rules

run_every:
  seconds: 10

buffer_time:
  minutes: 15

es_host: 192.168.1.152
es_port: 9200
use_ssl: true
verify_certs: false
es_username: elastic
es_password: xxx

writeback_index: elastalert_status

alert_time_limit:
  days: 2

rules/test_alert.yaml

とりあえずテスト用のアラートファイルを作成します
今回は必ずアラートが上がるように @timestmp フィールドを監視し5分間の間に2回レコードが格納された場合にアラートが上がるようにします

  • mkdir rules
  • rules/test_alert.yaml
name: "test_alert"
type: "frequency"
index: "fluentd*"
is_enabled: true
num_events: 2
realert:
  minutes: 5
terms_size: 50
timeframe:
  minutes: 5
timestamp_field: "@timestamp"
timestamp_type: "iso"
use_strftime_index: false
alert_subject: "Test {} 123 aa☃"
alert_subject_args:
  - "log"
alert_text: "Test {}  123 bb☃"
alert_text_args:
  - "source"
filter:
  - query:
      query_string:
        query: "@timestamp:*"
alert:
  - "slack"
slack_webhook_url: 'https://hooks.slack.com/services/xxx'
slack_channel_override: "#private"
slack_emoji_override: ":kissing_cat:"
slack_msg_color: "warning"
slack_parse_override: "none"
slack_username_override: "elastalert"

各種項目の説明

ルールの基本

name: "test_alert"
ルールの名前。Slack 通知などに出てくる。

type: "frequency"
「一定時間内に指定した件数以上のイベントがあるとアラートを出す」というタイプ。

index: "fluentd"
監視対象の Elasticsearch インデックス名。

is_enabled: true
このルールが有効になっている。

発火条件

num_events: 2
  イベント件数のしきい値。

timeframe: minutes: 5
  評価対象の時間範囲。
  → 「5分間に2件以上あればアラート」

realert: minutes: 5
  一度アラートが出た後、同じ条件で再度アラートを出すまでのクールダウン時間。
  → 5分間は抑止する。

terms_size: 50
  集計クエリのサイズ。frequency ルールでは多くの場合デフォルトでOK。

タイムスタンプ関連

timestamp_field: "@timestamp"
  イベントの時刻として使う Elasticsearch のフィールド。

timestamp_type: "iso"
  日時フォーマットの種類。ISO 8601 形式。

use_strftime_index: false
  インデックス名に日付を埋め込まない(例: fluentd-%Y.%m.%d ではなく fluentd 固定)。

通知メッセージ

alert_subject: "Test {} 123 aa☃"
  Slack の件名やタイトル部分に使うテンプレート。
  {} に alert_subject_args のフィールドが入る。

alert_subject_args: "log"
  → log フィールドの値が {} に挿入。

alert_text: "Test {} 123 bb☃"
  本文テンプレート。
  {} に alert_text_args のフィールドが入る。

alert_text_args: "log"
  → log フィールドの値が {} に挿入。

検索条件

filter:
  単純に @timestamp フィールドが存在する全ログを対象にする。
  実質「全件」になる。

ElastAlert2 コンテナの起動

ではコンテナを起動します
ホスト側に作成した各種設定ファイルがちゃんとコンテナにマウントされるようにしましょう

  • docker run -d --name elastalert --restart=always -v $(pwd)/elastalert.yaml:/opt/elastalert/config.yaml -v $(pwd)/rules:/opt/elastalert/rules jertel/elastalert2 --verbose

ログを見ると

Background alerts thread 0 pending alerts sent at 2025-09-17 23:35 UTCINFO:elastalert:1 rules loaded

でルールが有効になっていることが確認できます
また以下のログがあれば Slack に通知できているログになります

INFO:elastalert:Ran test_alert from 2025-09-17 23:54 UTC to 2025-09-17 23:55 UTC: 2 query hits (0 already seen), 1 matches, 1 alerts sent

動作確認

5分間待って Slack に通知が来ることを確認しましょう

以下のように変数部分が <MISSING VALUE> になる場合は ES9 上に指定のフィールドが存在するか確認してください

おまけ: 事前にルールファイルの確認をする

  • docker run --entrypoint "" --rm -v $(pwd)/elastalert.yaml:/opt/elastalert/config.yaml -v $(pwd)/rules:/opt/elastalert/rules jertel/elastalert2 elastalert-test-rule /opt/elastalert/rules/test_alert.yaml

最後に

ES9 と ElastAlert2 を連携させて特定のログが出た場合に Slack に通知する仕組みを試してみました
基本的には filter や timeframe などの監視条件をいろいろ変更してログ監視ルールを作成していく感じになります

また今回の構成だと rules ディレクトリにルールファイルをどんどん追加していく感じになりますが追加したルールファイルが1つでも壊れている(YAML構文エラーや必須のディレクティブが定義されていないなどがある)と ElastAlert2 自体が起動しないので注意してください

今回紹介した機能以外にもたくさんの機能があるので興味があれば参考サイトから公式のドキュメントを参照してみてください

参考サイト

2025年9月20日土曜日

docker で ElasticSearch9 を試す

docker で ElasticSearch9 を試す

概要

過去にES8を試しました
ES9が出たので久しぶりに試してみました

環境

  • macOS 15.6.1
  • docker 28.4.0
    • ElasticSearch 9.0.7
    • Kibana 9.0.7
    • fluentd 1.19-1

ElasticSearch の起動

  • docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" --name es docker.elastic.co/elasticsearch/elasticsearch:9.0.7

パスワードなどの確認や CA 証明書の取得などは同じ流れでした

  • docker exec -it es /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic -a
  • docker cp es:/usr/share/elasticsearch/config/certs/http_ca.crt .
  • curl --cacert http_ca.crt -u elastic https://localhost:9200
Enter host password for user 'elastic':
{
  "name" : "528f80bd51ca",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "pBMuWf9vTBuFEj1PHoZiFA",
  "version" : {
    "number" : "9.0.7",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "c6d8fb31b39450a223671e79141dd1c4b2759b5f",
    "build_date" : "2025-09-10T22:06:39.784049935Z",
    "build_snapshot" : false,
    "lucene_version" : "10.1.0",
    "minimum_wire_compatibility_version" : "8.18.0",
    "minimum_index_compatibility_version" : "8.0.0"
  },
  "tagline" : "You Know, for Search"
}

エンロールメントトークンも取得しておきます

  • docker exec -it es /usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibana --url "https://localhost:9200"

Kibana 起動

  • docker run -d -p 5601:5601 --name kibana docker.elastic.co/kibana/kibana:9.0.7

Kibana ログイン時に認証コードが必要なので取得しておきます

  • docker exec kibana bin/kibana-verification-code

ログ送信

fluentd コンテナを使って送信します

  • vim Dockerfile
FROM fluent/fluentd:v1.19-1

USER root
RUN gem install fluent-plugin-elasticsearch
  • docker build -t my_fluentd .

fluent.conf を作成します
ES9 に接続するには認証情報などが必要になります
ssl_verify=false を設定しないと fluentd -> es で SSL のエラーが発生しました

  • vim fluent.conf
<source>
  @type forward
  port 24224
  bind 0.0.0.0
</source>

<match docker.**>
  @type copy
  <store>
    @type stdout
  </store>
  <store>
    @type elasticsearch
    user elastic
    password xxx
    ca_file /fluentd/etc/http_ca.crt
    ssl_verify false
    scheme https
    host 192.168.1.152
    port 9200

    logstash_format true
    logstash_prefix fluentd
  </store>
</match>

@timestamp フィールドを使うので logstash_format: true を設定しています

  • docker run --name fluentd -d -p 24224:24224 -p 24224:24224/udp -v $(pwd):/fluentd/etc -e FLUENTD_CONF=fluent.conf my_fluentd

あとは fluentd にログを投げるコンテナを起動すれば OK です

  • docker run --rm --log-driver=fluentd --log-opt fluentd-address=host.docker.internal:24224 --log-opt tag="docker.{{.Name}}" alpine /bin/sh -c "while :;do date; sleep 3; done;"

動作確認

Kibana を確認して fluentd インデックスにログがあることを確認します
Discover で棒グラフが表示されない場合は

最後に

ElasticSearch9 を docker で動かしてみました
認証方法などはほぼ ES8 と変わってませんでした

fluentd からログを送信する際にも認証情報は必要になるので注意しましょう

参考サイト

2022年5月9日月曜日

ElasticSearch8 を docker で起動する

ElasticSearch8 を docker で起動する

概要

いろいろと起動方法が変わっていたので試してみました

環境

  • Ubuntu 18.04
  • docker 20.10.7
  • ElasticSearch 8.1.3
  • Kibana 8.1.3

max_map_count 変更

ERROR: [1] bootstrap checks failed. You must address the points described in the following [1] lines before starting Elasticsearch. というエラーが発生して ElasticSearch が起動しないので事前に変更します

  • sudo sysctl -w vm.max_map_count=262144

ElasticSearch 起動

シングルノードで起動します

  • docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" --name es docker.elastic.co/elasticsearch/elasticsearch:8.1.3

elastic ユーザのパスワードリセット

ElasticSearch にアクセスする elastic ユーザのパスワードを作成します
ターミナルに表示されたパスワードをメモしておきましょう

  • docker exec -it es /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic -a

ここで ElasticSearch にアクセスできるか確認

一旦アクセスできるか確認します
自動で生成されるクライアント証明書は localhost アクセス用のなので注意してください

  • docker cp es:/usr/share/elasticsearch/config/certs/http_ca.crt .
  • curl --cacert http_ca.crt -u elastic https://localhost:9200

Enrollment token の作成

Kibana が ElasticSearch にアクセスするためのトークンを生成します
ターミナルに表示される eyxxx という文字列をコピーしておきます

  • docker exec -it es /usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibana --url "https://localhost:9200"

Kibana の起動

  • docker run -d -p 5601:5601 --name kibana docker.elastic.co/kibana/kibana:8.1.3

動作確認

アクセスする URL は以下で確認できます

  • docker logs kibana

URL の後ろに code が含まれている URL にアクセスしましょう

http://192.168.100.10:5601/?code=xxxx

事前に取得した Enrollment token を入力します
Kibana -> ElasticSearch のアクセス経路は docker 内のネットワークを使用しているようなので ElasticSearch と Kibana は必ず同じネットワークに所属させるようにしてください (今回は default ネットワークを使っています)

設定が完了してログイン画面が表示されれば OK です

elastic ユーザでログインできることを確認しましょう

最後に

セキュリティ面がだいぶ強化されログインや Kibana からの接続時に証明書や認証が含まれるようになっていました

各種 SDK を使っている場合もいろいろとその辺りの変更が入っているかなと思います

参考サイト

2022年4月28日木曜日

docker で起動した Gitlab の production_json.log を fluentd で elasticsearch に飛ばす方法

docker で起動した Gitlab の production_json.log を fluentd で elasticsearch に飛ばす方法

概要

Gitlab のコンテナの標準出力のログは JSON 形式ではないので docker の fluentd ドライバが使えません
なのでコンテナ内に出力されるアプリケーションのログファイルを見る必要があります

ポイントはコンテナ内のログファイルをホストにマウントする点です

環境

  • Gitlab-ee 14.7.7
  • fluentd 1.3.2
  • fluent-plugin-elasticsearch 4.3.3
  • elasticsearch 7.17.1
  • elasticsearch 7.17.3
  • kibana 7.17.3

ElasticSearch 起動

  • docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:7.17.3

Kibana 起動

  • docker run -d -e ELASTICSEARCH_HOSTS=http://192.168.100.10:9200 -p 5601:5601 docker.elastic.co/kibana/kibana:7.17.3

fluentd ビルド&起動

  • vim Dockerfile
FROM fluent/fluentd

RUN apk add --update --virtual .build-deps \
        sudo build-base ruby-dev \
 && sudo gem install \
        elasticsearch -v 7.17.1 \
 && sudo gem install \
        fluent-plugin-elasticsearch -v 4.3.3 \
 && sudo gem sources --clear-all \
 && apk del .build-deps \
 && rm -rf /var/cache/apk/* \
           /home/fluent/.gem/ruby/2.5.0/cache/*.gem
  • vim fluent.conf
<source>
  @type tail
  format json
  path /fluentd/gitlab_log/production_json.log
  pos_file /fluentd/gitlab_log/production_json.log.pos
  tag gitlab.production
  keep_time_key true
</source>

<match gitlab.production>
  @type copy
  <store>
    @type stdout
  </store>
  <store>
    @type elasticsearch
    host 192.168.100.10
    port 9200
    index_name gitlab.production
    type_name fluentd
	    logstash_format true
    time_key time
  </store>
</match>
  • docker build -t my_fluentd .
  • docker run -d -v $(pwd):/fluentd/etc -v /path/to/log/gitlab-rails:/fluentd/gitlab_log -e FLUENTD_CONF=fluent.conf -e FLUENT_UID=998 my_fluentd

ポイント

  • Gitlab のログをホスト側でマウントしてそれを tail で飛ばす
  • 上記の場合は fluentd コンテナの /fluentd/gitlab_log にログをマウントしている
  • マウント先は /fluentd 配下でないと権限がないと言われて怒られる
  • また fluent ユーザの UID は 998 にしている、998は Gitlab 上で動作している git ユーザの UID でログファイルの権限が git ユーザの権限になっている、fluent ユーザのデフォルトの UID は 1000 になっており 1000 のまま fluentd コンテンを起動するとログの権限が 1000 になり Gitlab からログが書き込めずエラーになるのでそれの対応になる
  • production_json.log にはタイムスタンプ用に time フィールドがあるが keep_time_key: true を設定しないと消えるので注意
  • Elasticsearch のバージョンに合わせて elasticsearch-ruby のバージョンも合わせる必要がある
  • ログファイルの権限と fluentd 側の権限 (uid, gid) は合わせる必要がありそう

最後に

他のログも同じように転送することができます

2021年1月13日水曜日

Zipkin のデータストレージに ElasticSearch を使う方法

概要

前回は In-Memory ストレージを使ったので再起動するとデータが消えてしました
今回はストレージに ElasticSearch を使ってデータの永続化を行ってみました

環境

  • Zipkin 2.23
  • ElasticSearch7.9.3

ネットワークの作成

  • docker create network test

このネットワークに Zipkin と ElasticSearch コンテナを属させます

ElasticSearch コンテナの起動

今回は zipkin が用意している ElasticSearch イメージを使って ElasticSearch コンテナを起動します


* docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" --network test --name es docker.elastic.co/elasticsearch/elasticsearch:7.9.3
* curl 'localhost:9200/_cat/health'

green が返ってくることを確認しましょう

Zipkin コンテナの起動

STORAGE_TYPE=elasticsearch にして起動します


* docker run -d -p 9411:9411 -e STORAGE_TYPE=elasticsearch -e ES_HOSTS=es:9200 --network test --name zipkin openzipkin/zipkin

動作確認用のアプリは前回の Sinatra アプリを使用しています
これでアプリを使って Zipkin にトレース情報を送信してみると以下のようにちゃんと ElasticSearch 側にインデックスが作成されているのが確認できると思います
また何回かアクセスしてみると docs.count が増えるのも確認できると思います


health status index uuid pri rep docs.count docs.deleted store.size pri.store.size yellow open zipkin-span-2021-01-12 KqvMFHDPSamS4jRX52DQrQ 5 1 1 0 416b 416b


また http://localhost:9411/zipkin/ にアクセスして serviceName=zipkin-test で検索してもちゃんとトレース情報が確認できると思います


最後に

Zipkin + ElasticSearch を試してみました
STORAGE_TYPE を変更するだけで簡単に使うことができます
既存の ElasticSearch があるのであればそれをそのまま流用してもいいと思います

参考サイト

2021年1月9日土曜日

fluentd -> kafka -> logstash -> elasticsearch の順番でログを格納する方法

概要

fluentd -> kafka -> logstash -> elasticsearch という順番でログを確認する方法を紹介します
fluentd, kafka, elasticsearch の構築方法は過去に紹介しているので別記事を参照しています

環境

  • kafka 2.6.0
  • logstash 7.10.1
  • elasticsearch 6.4.0

fluent-kafka-plugin が動作する環境の構築

こちらを参考に構築してください
kafka と fluent-kafka-plugin がインストールされた fluent コンテナが起動している状態になれば OK です

elasticsearch の構築

こちらを参考に構築してください
9200 ポートで elasticsearch にアクセスできれば OK です

logstash のインストール

  • brew install logstash

logstash kafka input plugin のインストールと設定

ここが今回の肝になる部分です
logstash-integration-kafka を使います

  • logstash-plugin install logstash-integration-kafka

インストールが完了したら設定ファイルを作成していきます
kafka からデータを受け取って elasticsearch に流す設定を定義します

  • cp /usr/local/etc/logstash/logstash-sample.conf /usr/local/etc/logstash/logstash.conf
  • vim /usr/local/etc/logstash/logstash.conf
input {
  kafka {
    bootstrap_servers => "192.168.1.2:9092"
    topics => ["test"]
    decorate_events => true
  }
}

filter {
  json {
    source => "message"
  }
}

output {
  elasticsearch {
    hosts => ["http://192.168.1.2:9200"]
    index => "%{[@metadata][kafka][topic]}-%{+YYYY.MM.dd}"
  }
}


decorate_events を true にしないと [@metadata][kafka][topic] が output セクションで使えないので true にしています

また filter で json を使っています
どうやらデフォルトでは kafka から受け取ったログはすべて「message」というフィールドに格納されています
なのでそれぞれのフィールドに分割するために filter を挟んでいます

  • logstash -f /usr/local/etc/logstash/logstash.conf

起動に少し時間がかかりますが ERROR がでなければ OK です

動作確認

まずは適当なログを fluentd コンテナに送ります

  • docker run --rm --log-driver=fluentd --log-opt fluentd-address=192.168.1.2:24224 --log-opt tag="docker.{{.Name}}" alpine /bin/sh -c "while :;do echo \"{\\\"timestamp\\\":\\\"$(date)\\\",\\\"msg\\\":\\\"hello\\\"}\"; sleep 3; done;"


ある程度待った後に elasticsearch にインデックスが作成されているか確認しましょう

  • curl 'http://192.168.1.2:9200/_cat/indices?v'
  • curl 'http://192.168.1.2:9200/test-2021.01.06?pretty'

最後に

必ずしも kafka は必要ではないですがスケールによっては必要になります

参考サイト

2019年10月3日木曜日

efk 環境を docker for Mac でサクっと構築する

概要

この記事この記事を組み合わせてサクっと efk (ElasticSearch + fluentd + Kibana) 環境を構築する方法を紹介します
docker コンテナとして動作させるので VM などは不要です

今回はわかりやすくすべて docker コマンドで起動します
慣れてきたら docker-compose にしても OK だと思います

環境

  • macOS 10.14.6
  • docker for Mac 19.03.2
  • ElasticSearch 7.4.0
  • Kibana 7.4.0

Elasticsearch 起動

  • docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:7.4.0

Kibana 起動

  • docker run -d -e ELASTICSEARCH_HOSTS=http://192.168.99.1:9200 -p 5601:5601 docker.elastic.co/kibana/kibana:7.4.0

192.168.99.1 の IP アドレスの部分は docker for Mac が動作しているホストマシンの IP アドレスを指定してください
7.4.0 の場合 ELASTICSEARCH_URL ではなく ELASTICSEARCH_HOSTS になっているので注意してください

fluentd 起動

  • vim Dockerfile
FROM fluent/fluentd

RUN apk add --update --virtual .build-deps \
        sudo build-base ruby-dev \
 && sudo gem install \
        fluent-plugin-elasticsearch \
 && sudo gem sources --clear-all \
 && apk del .build-deps \
 && rm -rf /var/cache/apk/* \
           /home/fluent/.gem/ruby/2.4.0/cache/*.gem
  • docker build -t my_fluentd .
  • vim fluent.conf
<source>
  @type forward
  port 24224
  bind 0.0.0.0
</source>

<match docker.**>
  @type copy
  <store>
    @type stdout
  </store>
  <store>
    @type elasticsearch
    host 192.168.99.1
    port 9200
    index_name fluentd
    type_name fluentd
  </store>
</match>
  • docker run -d -p 24224:24224 -p 24224:24224/udp -v $(pwd):/fluentd/etc -e FLUENTD_CONF=fluent.conf my_fluentd

192.168.99.1 の IP アドレスの部分は docker for Mac が動作しているホストマシンの IP アドレスを指定してください

nginx 起動

ログを格納するための確認用のコンテナです
自身のアプリがあればそれでも OK です

  • docker run -d --log-driver=fluentd --log-opt fluentd-address=192.168.99.1:24224 --log-opt tag="docker.{{.Name}}" --name web -p 80:80 nginx

192.168.99.1 の IP アドレスの部分は docker for Mac が動作しているホストマシンの IP アドレスを指定してください

動作確認

動作しているコンテナは以下の通りです

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fc73ffd85690 nginx "nginx -g 'daemon of…" 11 seconds ago Up 9 seconds 0.0.0.0:80->80/tcp web b13985e947df docker.elastic.co/kibana/kibana:7.4.0 "/usr/local/bin/dumb…" 2 minutes ago Up 2 minutes 0.0.0.0:5601->5601/tcp romantic_chebyshev 07213efdf16d my_fluentd "/bin/entrypoint.sh …" 6 minutes ago Up 6 minutes 5140/tcp, 0.0.0.0:24224->24224/tcp, 0.0.0.0:24224->24224/udp nervous_hofstadter ea9a1c8cee93 docker.elastic.co/elasticsearch/elasticsearch:7.4.0 "/usr/local/bin/dock…" 13 minutes ago Up 13 minutes 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp distracted_mcclintock

とりあえず動作確認用の nginx にアクセスしてみます

  • curl localhost/hoge

Kibana でログが来ているか確認します

http://192.168.99.1:5601/ にブラウザでアクセスしてください
まずは Kibana 上でインデックスを作成します

左メニューから「Management」を選択します

Kibana の「Index Patterns」を選択します

「Create index pattern」を選択します

今回の場合 fluentd というインデックスでログが飛んできます
fluentd と入力しインデックスが見つかったら「Next step」を選択します

「Create index pattern」を選択します
timestamp などが必要な場合はここでフィールドを選択して作成します

こんな感じで作成できれば OK です

あとは左メニューから「Discovery」を選択し先程アクセスしたログが出ていれば OK です

最後に

docker を使ってサクっと efk 環境を構築する方法を紹介しました
Kibana の挙動などを確認したいときに便利かなと思います

今回は nginx のログを格納していますがアプリがあればそれでも OK です
また format も使っていないので生ログの文字列をそのまま格納しているので適宜 format を使って必要なフィールドごとに格納すると良いかなと思います

2019年6月16日日曜日

k8s 上に ElasticSearch7.1 クラスタを構築してみた

概要

前回 docker-compose で構築しました
今回は k8s 上に構築するのにチャレンジしました
StatefulSet + Headless Service で構築しています
シングル構成から複数台構成まで順を追って説明します

環境

  • macOS 10.14.5
  • minikube 1.1.0
  • ElasticSearch 7.1

とりあえず run (Deployment)

まずは kubctl run でシングル構成で立ち上げます

  • kubectl run elasticsearch --image=docker.elastic.co/elasticsearch/elasticsearch:7.1.1 --env="discovery.type=single-node" --port=9200

kubectl run は Deployment を使っています
YAML 定義を確認するには -o yaml --dry-run オプションを付与します

作成されたリソースを確認するには Deployment, ReplicaSet, Pod を確認します

  • kubectl get deploy,rs,po
NAME                                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.extensions/elasticsearch   1/1     1            1           12s

NAME                                             DESIRED   CURRENT   READY   AGE
replicaset.extensions/elasticsearch-775d564d6c   1         1         1       12s

NAME                                 READY   STATUS    RESTARTS   AGE
pod/elasticsearch-775d564d6c-h4hxs   1/1     Running   0          12s

動作確認は exec を使って直接 Pod の localhost にアクセスしてみましょう

  • kubectl exec elasticsearch-775d564d6c-h4hxs curl localhost:9200/_cat/health
  • kubectl exec elasticsearch-775d564d6c-h4hxs curl localhost:9200/_cat/nodes

リソースの削除は Deployment を削除すれば OK です

  • kubectl delete deploy elasticsearch

シングル構成 (Pod)

次に YAML ファイルを定義してシングル構成を構築してみます
先程 run で Deployment 経由の構築はしたので今回は Pod リソースを使った YAML ファイルを定義してみます
※先程の run に --restart=Never を付与すれば Pod のみになりますが勉強のため自分で YAML ファイルを作成しています

  • vim es_master_pod.yml
apiVersion: v1
kind: Pod
metadata:
  name: es-pod
  labels:
    app: es
spec:
  containers:
  - name: es
    image: docker.elastic.co/elasticsearch/elasticsearch:7.1.1
    env:
    - name: discovery.type
      value: single-node
    ports:
    - containerPort: 9200
      name: api
    - containerPort: 9300
      name: gossip
  restartPolicy: Never

ポイントは envdiscovery.type を渡している点です
これを apply しましょう

  • kubectl apply -f es_master_pod.yml

作成されるリソースは Pod になります

  • kubectl get po
NAME     READY   STATUS    RESTARTS   AGE
es-pod   1/1     Running   0          4s

動作確認します

  • kubectl exec es-pod curl localhost:9200/_cat/health

リソースの削除は Pod を削除すれば OK です

  • kubectl delete po es-pod

シングル構成 (StatefulSet)

次にシングル構成を StatefulSet を使って構築します
今回は 3 台構成のクラスタを StatefulSet を使って構築します
そのための準備としてまずはシングル構成を構築します

  • vim kubectl apply -f es_master_sts1.yml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: es-cluster
spec:
  selector:
    matchLabels:
      app: es
  serviceName: "es-cluster"
  replicas: 1
  template:
    metadata:
      labels:
        app: es
    spec:
      containers:
      - name: es
        image: docker.elastic.co/elasticsearch/elasticsearch:7.1.1
        env:
        - name: discovery.type
          value: single-node
        ports:
        - containerPort: 9200
          name: api
        - containerPort: 9300
          name: gossip
        volumeMounts:
        - name: data
          mountPath: /data
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

PVC (PersistentVolumeClaim) を追加しています
template.spec.containers は先程の Pod の定義とほぼ同じです
selector.matchLabelstemplate.metadata.labels は同じになるようにします
これで起動してみましょう

  • kubectl apply -f es_master_sts1.yml

作成されるリソースは StatefulSet, Pod, PVC になります

  • kubectl get sts,po,pvc
NAME                          READY   AGE
statefulset.apps/es-cluster   1/1     49s

NAME               READY   STATUS    RESTARTS   AGE
pod/es-cluster-0   1/1     Running   0          48s

NAME                                      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/data-es-cluster-0   Bound    pvc-ae9aa9e8-8e3a-11e9-8372-080027d5c49e   1Gi        RWO            standard       48s

動作確認しましょう

  • kubectl exec es-cluster-0 curl localhost:9200/_cat/health

ホスト名も確認してみましょう
StatefulSet を使っているのでホスト名が固定されているのが確認できると思います

  • kubectl exec es-cluster-0 hostname

削除は StatefulSet と PVC を削除すれば OK です

  • kubectl delete sts es-cluster
  • kubectl delete pvc data-es-cluster-0

3 台クラスタ構成 (StatefulSet + Headless Service)

さてここからが本番です
3 台構成のクラスタを StatefulSet を使って構築します
また Headless Service も使います
Headless Service を使うとホスト名+サブドメイン (サービス名) で他の Pod にアクセスすることができます
クラスタを構成するにあたり IP ではなくホスト名を使いたいので Headless Service を使います

  • vim es_master_svc.yml
apiVersion: v1
kind: Service
metadata:
  name: es-cluster
  labels:
    app: es
spec:
  clusterIP: None
  ports:
  - port: 9200
    name: api
  - port: 9300
    name: gossip
  selector:
    app: es

まずは Headless Service を作成します

  • kubectl apply -f es_master_svc.yml

これで同じ namespace 配下の Pod 間で名前解決ができるようになります
次に StatefulSet を作成します

  • vim kubectl apply -f es_master_sts3.yml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: es-cluster
spec:
  selector:
    matchLabels:
      app: es
  serviceName: "es-cluster"
  replicas: 3
  template:
    metadata:
      labels:
        app: es
    spec:
      containers:
      - name: es
        image: docker.elastic.co/elasticsearch/elasticsearch:7.1.1
        env:
        - name: HOSTNAME_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: node.name
          value: $(HOSTNAME_NAME).es-cluster
        - name: cluster.name
          value: k8s-cluster
        - name: discovery.seed_hosts
          value: es-cluster-0.es-cluster,es-cluster-1.es-cluster,es-cluster-2.es-cluster
        - name: cluster.initial_master_nodes
          value: es-cluster-0.es-cluster,es-cluster-1.es-cluster,es-cluster-2.es-cluster
        - name: ES_JAVA_OPTS
          value: "-Xms512m -Xmx512m"
        ports:
        - containerPort: 9200
          name: api
        - containerPort: 9300
          name: gossip
        volumeMounts:
        - name: data
          mountPath: /data
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

だいぶ長くなりました
が一つずつ見ればそこまで複雑ではありません
serviceName: "es-cluster" は Headless Service の metadata.name と同じにします
3 台構成にするので replicas: 3 にしましょう
一番のポイントは env になります
ElasticSearch のクラスタを構成する場合 node.namediscovery.seed_hosts, cluster.initial_master_nodes そしてマシンの hostname はすべて合わせたほうが無難です
IP でもできるようですが個人的にはハマりやすいのでできれば名前でアクセスできるようにしましょう
今回はそのために Headless Service を使っています

まず HOSTNAME_NAMEfieldPath: metadata.name でホスト名を環境変数に設定しています
今回の 3 台構成であれば es-cluster-0, es-cluster-1, es-cluster-2 がそれぞれ動的に代入されます
そしてそれを使って node.name$(HOSTNAME_NAME).es-cluster にしています
なぜわざわざマシンのホスト名にサブドメインの .es-cluster を付与しているのかというと Headless Service の場合他の Pod にアクセスするにはホスト名だけではなくサブドメインも必要になります
なので discovery.seed_hostscluster.initial_master_nodeses-cluster-0.es-cluster という感じにしなければクラスタに所属させるホストを見つけることができません
でここに指定した値と node.name は同じにする必要があるためわざわざこのような方法にしています
(もしかするともっと簡単な方法があるかもしれませんが)

  • kubectl apply -f es_master_sts3.yml

あとはこれを適用して確認してみます

  • kubectl get sts,po,pvc,svc
NAME                          READY   AGE
statefulset.apps/es-cluster   3/3     45s

NAME               READY   STATUS    RESTARTS   AGE
pod/es-cluster-0   1/1     Running   0          45s
pod/es-cluster-1   1/1     Running   0          40s
pod/es-cluster-2   1/1     Running   0          35s

NAME                                      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/data-es-cluster-0   Bound    pvc-f581cf6c-8e3c-11e9-8372-080027d5c49e   1Gi        RWO            standard       45s
persistentvolumeclaim/data-es-cluster-1   Bound    pvc-f8371d19-8e3c-11e9-8372-080027d5c49e   1Gi        RWO            standard       40s
persistentvolumeclaim/data-es-cluster-2   Bound    pvc-fae468fa-8e3c-11e9-8372-080027d5c49e   1Gi        RWO            standard       36s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)             AGE
service/es-cluster   ClusterIP   None         <none>        9200/TCP,9300/TCP   17h
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP             17h

クラスタを構築するまでに少し時間がかかるので待ちましょう

  • kubectl exec es-cluster-0 curl -- -s localhost:9200/_cat/health
1560473021 00:43:41 k8s-cluster green 3 3 0 0 0 0 0 0 - 100.0%
  • kubectl exec es-cluster-0 curl localhost:9200/_cat/nodes
172.17.0.7 26 98 36 1.61 2.13 1.32 mdi * es-cluster-1.es-cluster
172.17.0.6 14 98 41 1.61 2.13 1.32 mdi - es-cluster-0.es-cluster
172.17.0.8 16 98 33 1.61 2.13 1.32 mdi - es-cluster-2.es-cluster

こんな感じで取得できるようになれば OK です

minikube を使っている場合

minikube の場合初期リソースが少ないため以下を実施しないとクラスタ構成時に Pod が起動しませんでした

メモリ増設

  • minikube config set memory 4096
  • minikube delete
  • minikube start

max_map_count 増設

  • minikube ssh
  • sudo sysctl -w vm.max_map_count=262144

failover を確認する

master になっている Pod を 1 台削除してみましょう

  • kubectl delete po es-cluster1

StatefulSet なので Pod が削除されても自動で復活します
再作成中はしばらく 2 台構成のクラスタ状態になります
復活したら再度 health と nodes を確認してみましょう

  • kubectl exec es-cluster-0 curl -- -s localhost:9200/_cat/health
1560473297 00:48:17 k8s-cluster green 3 3 0 0 0 0 0 0 - 100.0%
  • kubectl exec es-cluster-0 curl -- -s localhost:9200/_cat/nodes
172.17.0.6 19 98 32 1.05 1.43 1.20 mdi - es-cluster-0.es-cluster
172.17.0.8 23 98 32 1.05 1.43 1.20 mdi * es-cluster-2.es-cluster
172.17.0.7 14 98 26 1.05 1.43 1.20 mdi - es-cluster-1.es-cluster

failover し master が es-cluster-1 から es-cluster-2 になっているのがわかります
また再作成された es-clsuter-1 も自動的にクラスタに再ジョインいしていることがわかります
リソースの削除は以下の通りです

  • kubectl delete sts es-cluster
  • kubectl delete pvc data-es-cluster-0 data-es-cluster-1 data-es-cluster-2
  • kubectl delete svc es-clsuter

トラブルシューティング

今回の 3 台構成にたどり着くまでに何度もトライアンドエラーしました
今回のポイントは Headless Service を使ってホスト名+サブドメインでクラスタ間のアクセスをする点かなと思います
あとは minikube のリソース周りもはまりポイントかなと思います

これらのミスに気づいた方法としては minikube ssh してログインし直接 docker logs で ElasticSearch コンテナのログを確認していました
StatefulSet は Pod の作成に失敗すると自動でコンテナを再作成します
なのでコンテナが削除される前に docker logs -f f93400366819 でログを眺めるのが一番良いと思います
リソースが足りない場合にエラーは大抵これで解決できます

StatefulSet の場合 restartPolicy を Never に設定できません
なので今回のように restartPolicy=Never にできる Pod リソースだけを使って原因調査するのもありかなと思います

最後に

ElasticSearch7.1 のクラスタ構成を k8s 上に構築してみました
記事内では StatefulSet + Headless Service を使って構築しています
調べると他にも CRD (Custom Resource Definition) を使ったり Deployment を使って構築しているサンプルもありました
個人的に試した感じだとクラスタ間はホスト名を使ってアクセスできるようにしたほうが運用も楽になる感じがするのが自分は StatefulSet + Headless Service のリソースを使って構築した感じです

この辺りのリソースやコントローラのチョイスは経験が大きく影響するので自分にあったものを選択するのが良いかなと思います

参考サイト

2019年6月15日土曜日

docker-compose で ElasticSearch7.1 クラスタを構築してみた

概要

公式のドキュメントに docker-compose で立ち上げる YAML があったので試してみました
公式だとうまく動作しない部分があったので少し変更しています
※公式だとノードを追加した際にうまく動作しません

環境

  • macOS 10.14.5
  • docker 18.09.2
  • docker-compose 1.23.2
  • ElasticSearch 7.1.1

P.S 20190621 docker for mac の場合メモリの割り当て上限を増やすこと

デフォルトは 2.0GiB なので 4.0GiB に変更しましょう
そうすれば公式のデフォルトの YAML ファイルでも動作します

docker-compose

  • vim docker-compose.yml
version: '2.2'
services:
  es01:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.1.1
    container_name: es01
    hostname: es01
    environment:
      - cluster.name=docker-cluster
      - discovery.seed_hosts=es01,es02
      - cluster.initial_master_nodes=es01,es02
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - esdata01:/usr/share/elasticsearch/data
    ports:
      - 9200:9200
    networks:
      - esnet
  es02:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.1.1
    container_name: es02
    hostname: es02
    environment:
      - cluster.name=docker-cluster
      - discovery.seed_hosts=es01,es02
      - cluster.initial_master_nodes=es01,es02
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - esdata02:/usr/share/elasticsearch/data
    networks:
      - esnet

volumes:
  esdata01:
    driver: local
  esdata02:
    driver: local

networks:
  esnet:

2 台構成になっています
ボリュームプラグインは local を使っています
docker on Mac の場合、データ領域は専用の VM になるので注意してください (参考)

ネットワークは esnet という名前の専用ネットワークを作成しています
このネットワーク上では各ノードは services に定義した名前でアクセスできます
あとはクラスタを構成するための環境変数の設定とコンテナの ulimit の設定になります

起動したらクラスタのステータスを確認しましょう
green になっていれば OK です

  • curl http://127.0.0.1:9200/_cat/health
1560309775 03:22:55 docker-cluster green 2 2 2 1 0 0 0 0 - 100.0%
  • curl localhost:9200/_cat/nodes
192.168.160.3 31 89 3 1.13 0.71 0.27 mdi - es02
192.168.160.2 32 89 3 1.13 0.71 0.27 mdi * es01

es01 が master になりました

設定ファイルを確認してみる

環境変数に設定したデータは設定ファイルに反映されています
コンテナとして立ち上げた場合設定ファイルは /usr/share/elasticsearch/config/elasticsearch.yml に配置されています

  • docker-compose exec es01 cat /usr/share/elasticsearch/config/elasticsearch.yml
cluster.name: "docker-cluster"
network.host: 0.0.0.0

中身はほとんど書かれていないようです
では設定した環境変数はどこに渡されているかというとプログラムを実行する引数に渡されています
わかりやすいようにコマンドを工夫しています

  • for i in $(docker exec es01 ps aux); do echo ${i} | grep '^-E'; done
-Ecluster.initial_master_nodes=es01,es02
-Ecluster.name=docker-cluster
-Ediscovery.seed_hosts=es01,es02

こんな感じで渡されていました
おそらく環境変数は上記のようにすべてプログラムに渡すようになっているので他のパラメータも環境変数で渡すことができると思います

テストデータを入れてみる

テストデータも提供してくれているのでそれを使います

  • curl -O https://download.elastic.co/demos/kibana/gettingstarted/7.x/shakespeare.json

まずはマッピングを作成します

curl -X PUT "localhost:9200/shakespeare" -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "properties": {
    "speaker": {"type": "keyword"},
    "play_name": {"type": "keyword"},
    "line_id": {"type": "integer"},
    "speech_number": {"type": "integer"}
    }
  }
}
'

作成できたらデータを投入します
bulk API というのがあるのでこれを使います
データが大きいので少し時間がかかります

  • curl -s -o /dev/null -H 'Content-Type: application/x-ndjson' -XPOST 'localhost:9200/shakespeare/_bulk?pretty' --data-binary @shakespeare.json

成功したら確認しましょう

  • curl -X GET "localhost:9200/_cat/indices?v"
health status index       uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   shakespeare egTnaoHnS3SQ5fWuOZkgfg   1   1     111396            0     39.1mb         19.5mb
  • curl -X GET "localhost:9200/shakespeare/_count"
{"count":111396,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0}}

一度コンテナを削除し立ち上げ直してもデータが残っていることが確認できると思います

  • docker-compose down
  • docker-compose up -d
  • curl -X GET "localhost:9200/shakespeare/_count"

=> 111396

ノードを追加するには

試しにスケールしてみましたがコンテナ名を指定しているため当然アウトでした

  • docker-compose up --scale es02=2

=> ERROR: for es02 Cannot create container for service es02: Conflict. The container name "/es02" is already in use by container

docker-compose.yml に es03 を追記してクラスタに es03 も追加するように記載してみます

  • docker-compose down

一旦コンテナを削除します
-v は付けずにデータボリュームは残します
そして 3 台目のホスト情報を YAML ファイルに記載します

  • vim docker-compose.yml
version: '2.2'
services:
  es01:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.1.1
    container_name: es01
    hostname: es01
    environment:
      - cluster.name=docker-cluster
      - discovery.seed_hosts=es01,es02,es03
      - cluster.initial_master_nodes=es01,es02,es03
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - esdata01:/usr/share/elasticsearch/data
    ports:
      - 9200:9200
    networks:
      - esnet
  es02:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.1.1
    container_name: es02
    hostname: es02
    environment:
      - cluster.name=docker-cluster
      - discovery.seed_hosts=es01,es02,es03
      - cluster.initial_master_nodes=es01,es02,es03
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - esdata02:/usr/share/elasticsearch/data
    networks:
      - esnet
  es03:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.1.1
    container_name: es03
    hostname: es03
    environment:
      - cluster.name=docker-cluster
      - discovery.seed_hosts=es01,es02,es03
      - cluster.initial_master_nodes=es01,es02,es03
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - esdata03:/usr/share/elasticsearch/data
    networks:
      - esnet

volumes:
  esdata01:
    driver: local
  esdata02:
    driver: local
  esdata03:
    driver: local

networks:
  esnet:

services に es03 が追加になったのとそれに伴い volumes も追加しています
また各ホストの discovery.seed_hostscluster.initial_master_nodes に es03 の情報を追加しています
これで再度コンテナを起動しましょう

  • docker-compose up -d

すると今度は 3 台構成で起動します

  • curl localhost:9200/_cat/health
1560386296 00:38:16 docker-cluster green 3 3 0 0 0 0 0 0 - 100.0%
  • curl localhost:9200/_cat/nodes
192.168.176.3 16 96 88 5.93 2.61 1.03 mdi - es02
192.168.176.4 20 96 88 5.93 2.61 1.03 mdi * es01
192.168.176.2 17 96 88 5.93 2.61 1.03 mdi - es03

master は変わらず es01 でした

failover させてみる

では es01 をダウンさせてみましょう

  • docker-compose stop es01

しばらくしてから es02 に問い合わせると 2 台構成になっているのが確認できると思います

  • docker-compose exec es02 curl localhost:9200/_cat/health
1560386492 00:41:32 docker-cluster green 2 2 0 0 0 0 0 0 - 100.0%
  • docker-compose exec es02 curl localhost:9200/_cat/nodes
192.168.176.2 40 73 3 0.38 1.41 0.85 mdi * es03
192.168.176.3 40 73 3 0.38 1.41 0.85 mdi - es02

master は es01 -> es03 になったようです
これで再度 es01 を起動してみましょう

  • docker-compose start es01

また helth と nodes を確認すると 3 台構成に戻っているのが確認できると思います
また master は変わらず es03 になっていると思います

ポイント解説

過去に Ubuntu 上で ElasticSearch7.1 のクラスタ環境を構築したときとほぼ同じになります
カーネルパラメータ系はイメージ側で対応してくれています
公式の docker-compose.yml で ulimit だけ指定があったのでそのまま使っています
コンテナの hostname を指定し elasticsearch を起動時のオプションで node.name を指定しないようにしました
あと bootstrap.memory_lock=true も不要だったので削除しました

また今回はノードを追加する際に down -> up で追加しましたが down させないで docker-compose.yml を編集して up しても追加できると思います

最後に

docker-compose.yml で ElasticSearch7.1 のクラスタを構築してみました
公式のままだとノード追加がうまくいかなかったので少し修正しています

Kibana や Cerebo と連携するときは同じ docker-compose.yml 内に定義してあげるかホストの 9200 を見に行くようにすれば OK です
ただ今回は es01 しか 9200 を LISTEN していないので es01 がダウンした場合は必ず restart してあげる必要があります
もしくはそれが面倒なら 3 台の ElasticSearch の前に nginx などを立てて 9200 をバランシングしてあげる必要があるかなと思います

参考サイト

2019年6月14日金曜日

Ubuntu16.04 上に ElasticSearch7.1 クラスタを構築してみた

概要

ElasticSearch の 7.1 で 3 台構成のクラスタを組んでみました
Vagrant で 3 台の Ubuntu を構築しそれぞれで ElasticSearch のプロセスを 1 つずつ起動してクラスタを組んでみます
今回は Vagrant を使いましたが EC2 や GCE などのクラウドリソースでも問題ないです

環境

  • Ubuntu 16.04 LTS
  • Vagrant 2.1.1
  • ElasticSearch 7.1.1

Vagrantfile

3 台作成します
メモリは 1024 にしていますが余裕があればもっと高い値を設定してください
box ファイルは ubuntu/xenial64 を使用しています
ホスト間で通信できるようにインタフェースを一つ追加してます

Vagrant.configure("2") do |config|
  config.vm.define "vm01" do |v|
    v.vm.box = "ubuntu/xenial64"
    v.vm.network "private_network", ip: "192.168.99.200"
    v.vm.provider "virtualbox" do |vb|
      vb.memory = "1024"
    end
  end
  config.vm.define "vm02" do |v|
    v.vm.box = "ubuntu/xenial64"
    v.vm.network "private_network", ip: "192.168.99.201"
    v.vm.provider "virtualbox" do |vb|
      vb.memory = "1024"
    end
  end
  config.vm.define "vm03" do |v|
    v.vm.box = "ubuntu/xenial64"
    v.vm.network "private_network", ip: "192.168.99.202"
    v.vm.provider "virtualbox" do |vb|
      vb.memory = "1024"
    end
  end
end
  • vagrant up

VM 設定

それぞれに ssh して作業します

  • vagrant ssh vm01
  • vagrant ssh vm02
  • vagrant ssh vm03

Vagrant ユーザで作業する上でカーネルパラメータなどのチューニングが必要なので行います

  • sudo vim /etc/security/limits.conf
vagrant hard nproc 4096
vagrant soft nproc 4096
vagrant hard nofile 65535
vagrant soft nofile 65535

各ホストをホスト名でアクセスできるように hosts ファイルに記載します

  • sudo vim /etc/hosts
192.168.99.200 vm01
192.168.99.201 vm02
192.168.99.201 vm02

あとはホスト名を設定しましょう
ここで設定したホスト名は ElasticSearch のクラスタを構成する際に使われるノード名 (node.name) でそのまま使用します

  • sudo hostnamectl set-hostname vm01
  • sudo hostnamectl set-hostname vm02
  • sudo hostnamectl set-hostname vm02

最後に再起動しましょう

  • sudo reboot -h now

elasticsearch インストール

各ホストで行います
tar.gz ファイルをダウンロードして解凍するだけで OK です

  • curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.1.1-linux-x86_64.tar.gz
  • tar -xvf elasticsearch-7.1.1-linux-x86_64.tar.gz

elasticsearch 起動 (vm01, vm02)

まずは 2 台構成のクラスタを構築します
どちらも同じコマンドを実行するのでそれぞれに ssh して実行しましょう
直前に sysctl を実行していますが「VM 設定」のところで /etc/sysctl.conf に記載しても OK です

  • cd elasticsearch-7.1.1/
  • sudo sysctl -w vm.max_map_count=262144
  • ES_JAVA_OPTS="-Xms512m -Xmx512m" ./bin/elasticsearch -d -p $(hostname).pid -Ecluster.name=my_cluster -Ediscovery.seed_hosts=vm01,vm02 -Ecluster.initial_master_nodes=vm01,vm02 -Enetwork.host=_enp0s9_

いろいろとパラメータを指定しています
-d はデーモンとして実行、-p は pid ファイルを生成します

クラスタを構築する上で大事なのは -E 付きのオプションです
まず cluster.name=my_cluster はクラスタ名を指定します
好きな名前を付けて OK です
他のホストで起動するときも同じクラスタ名を指定することで同一クラスタに所属されることができます
discovery.seed_hosts=vm01,vm02 はクラスタに所属させるホストを指定します
cluster.initial_master_nodes=vm01,vm02 はクラスタ構築時にマスターノードになれるホストを指定します
network.host=_enp0s9_ はホスト間でやり取りするネットワークを指定します
enp0s9 はインタフェースを指定しており 192.168.99 帯の固定 IP が振られたインタフェースを指定しています
また今回は node.name を指定していません
指定しなかった場合は ElasticSearch が自動的にホスト名を引っ張ってノード名に設定するので今回は指定しませんでした

しばらくすると 9200 と 9300 が 192.168.99 の IP で LISTEN します
2 台のホストで LISTEN したらクラスタが構築されているか確認しましょう

クラスタの状態確認

API を叩きて確認します
vm01, vm02 どちらのホストでも問題ないので API をコールしてみましょう

$curl $(hostname):9200/_cat/health
1560334522 10:15:22 my_cluster green 2 2 0 0 0 0 0 0 - 100.0%
$ curl $(hostname):9200/_cat/nodes
192.168.99.200 37 94 14 0.30 0.21 0.08 mdi - vm01
192.168.99.201 25 93 14 0.31 0.26 0.14 mdi * vm02

ノードが 2 台ありかつステータスが green になっていれば問題なくクラスタが構築されています
なお今回は vm02 が初期状態では master に選ばれました

ホスト追加 (vm03)

では vm03 も追加してみます
先程の vm01, vm02 と同じように elasticsearch のプロセスを起動すれば OK です

  • cd elasticsearch-7.1.1/
  • sudo sysctl -w vm.max_map_count=262144
  • ES_JAVA_OPTS="-Xms512m -Xmx512m" ./bin/elasticsearch -d -p $(hostname).pid -Ecluster.name=my_cluster -Ediscovery.seed_hosts=vm01,vm02,vm03 -Ecluster.initial_master_nodes=vm01,vm02,vm03 -Enetwork.host=_enp0s9_

discovery.seed_hostscluster.initial_master_nodes に vm03 が追加されています
あとは同じです
9200 と 9300 が LISTEN したら同じようにクラスタの状態を API で確認しましょう
3 台になっていることが確認できれば OK です
なおホストを追加しても master は変わりませんでした

$ curl $(hostname):9200/_cat/health
1560337529 11:05:29 my_cluster green 3 3 0 0 0 0 0 0 - 100.0%
$ curl $(hostname):9200/_cat/nodes
192.168.99.202 33 93 0 0.08 0.12 0.05 mdi - vm03
192.168.99.200 19 92 0 0.07 0.03 0.01 mdi - vm01
192.168.99.201 36 93 0 0.00 0.00 0.00 mdi * vm02

failover 確認 (vm02)

最後に failover を確認しましょう
master であった vm02 のプロセスを kill してみます

  • kill -SIGTERM $(cat vm02.pid)

すると別のホストが master に昇格しステータスが green に戻ります
今回の場合は特にデータやアクセスもないので確認が難しいですが failover 中はステータスが red or yellow になり瞬間的にクラスタが使えない状態になるようです

$ curl $(hostname):9200/_cat/health
1560338344 11:19:04 my_cluster green 2 2 0 0 0 0 0 0 - 100.0%
$ curl $(hostname):9200/_cat/nodes
192.168.99.202 26 93 0 0.00 0.00 0.00 mdi - vm03
192.168.99.200 38 92 1 0.00 0.02 0.00 mdi * vm01

再度 vm02 の elasticsearch を起動しましょう
vm02 を再度起動する場合はなぜか discovery.seed_hostscluster.initial_master_nodes に vm03 を追加しないでも大丈夫でした (もしかしたら追加したほうがいいかも)

  • ES_JAVA_OPTS="-Xms512m -Xmx512m" ./bin/elasticsearch -d -p $(hostname).pid -Ecluster.name=my_cluster -Ediscovery.seed_hosts=vm01,vm02 -Ecluster.initial_master_nodes=vm01,vm02 -Enetwork.host=_enp0s9_

これで vm02 が再度クラスタに加わりました

$ curl $(hostname):9200/_cat/health
1560338437 11:20:37 my_cluster green 3 3 0 0 0 0 0 0 - 100.0%
$ curl $(hostname):9200/_cat/nodes
192.168.99.200 24 92 0 0.00 0.01 0.00 mdi * vm01
192.168.99.202 38 93 0 0.00 0.00 0.00 mdi - vm03
192.168.99.201 25 94 0 0.23 0.10 0.03 mdi - vm02

ハマったポイント

Vagrant の場合デフォルトのホスト名が ubuntu-xenial になっています
これで node.name を指定してやっていたのですがうまくできなかったので hostname を設定して行いました
また、discovery.seed_hostscluster.initial_master_nodes を IP で指定していたのですがうまくいかなかったのでホスト名を指定するようにしました
ホスト名にした場合ホスト間の通信はホスト名で引ける必要があるので /etc/hosts に記載しています

また基本はトライアンドエラーを何度も行ったのですが data/ に前の間違ったクラスタ情報があると何度やっても失敗したので次にトライする場合はフォルダごと削除するようにしましょう

最後に

ElasticSearch 7.1 でクラスタ構成を組んでみました
VM で作業したのでカーネルパラメータなどのチューニングも必要になります
基本は起動する際のパラメータを駆使してクラスタを構成することができますがパラメータではなく config/elasticsearch.yml に記載しても効果は同じなのでパラメータが嫌いな方は設定ファイルに記載しても OK です

また今回、ホストはすべて node.master=true で行いました
ノードの種類もいろいろあるらしいので興味があれば見てください
よくよく調べると今回のやり方は Bootstrap Cluster というやり方のようです
docker でクラスタを構成場合も同じ方法を使っていました

参考サイト

2019年6月8日土曜日

docker で cerebro を立ち上げるメモ

概要

cerebo は ElasticSearch 用の管理画面です
ElasticSearch のクラスタの管理やインデックスの管理が UI でできます
Site Plugin が非推奨になり分離した感じです

環境

  • macOS 10.14.5
  • docker 18.09.2
  • cerebo 0.8.3

cerebro 起動 

  • docker run -d -p 9000:9000 lmenezes/cerebro

elasticsearch 起動

  • docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.7.2

cerebro 設定

localhost:9000 にアクセスすると cerebro の管理画面が起動します

elasticsearch を起動したホストの IP とポートを指定するとアクセスできます
localhost でアクセスするとうまく動作しなかったので IP で指定しましょう

最後に

とりあえず docker で cerebro を立ち上げるところまでやってみました
ポイントは IP でアクセスする点かなと思います
あとは管理画面から index の登録やノードの管理をすることができます

参考サイト

2018年9月5日水曜日

ElasticSearch と Kibana を Docker 上に個別に立てて連携する

概要

よく docker-compose.yaml を使って同一ホスト上に ElasticSearch と Kibana を構築例があります
ただその場合だとそれぞれを別のホストでコンテナとして立ち上げたい場合に困ります
今回はそれぞれのコンテナを別のホストに構築することを想定して立ち上げて連携する方法を紹介します

環境

  • macOS 10.13.6
  • docker 18.06.0-ce
  • ElasticSearch (docker.elastic.co/elasticsearch/elasticsearch:6.4.0)
  • Kibana (docker.elastic.co/kibana/kibana:6.2.4)

ElasticSearch を立ち上げる

  • docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.4.0

これで OK です
ホストマシンの IP の 9200 番にアクセスすることができます

  • curl http://192.168.99.1:9200/_cat/health

基本はここがエンドポイントになるので Kibana もここにアクセスします

Kibana を立ち上げる

  • docker run -d -e ELASTICSEARCH_URL=http://192.168.99.1:9200 -p 5601:5601 docker.elastic.co/kibana/kibana:6.2.4

ポイントは ELASTICSEARCH_URL でホストの IP で公開した ElasticSearch を指定するところです
これで Kibana を立ち上げるとホストの IP の 5601 でダッシュボードにアクセスすることができます

http://192.168.99.1:5601/

今回は ElasticSearch も Kibana も同一 IP のホストで run しましたが、それぞれ別のホストで立ち上げても問題なく動作します

今回の構成のポイント

ElasticSearch と Kibana の通信はコンテナ間で直接行われるわけではなく、一旦ホストマシンを介して行われることになります
docker-compose の link 機能などを使うとわざわざホストにポートを公開することなく ElasticSearch と Kibana 間で通知することができます

ただ、その場合は同一ホストもしくはホストを跨いでもコンテナ間で通信できるように Swarm なりを構築する必要があります
しかし今回の構成であればホストの IP に対してアクセスできればいいだけなので Swarm を組んだりする必要はなくホスト間で単純に通信できれば OK なだけです

ElasticSearch はいろいろな場所から参照される可能性があるので今回の構成のようにホストの IP で通信もしくは LB 経由で通信できるようになっている便利かなと思います

最後に

Docker 上で ElasticSearch と Kibana を別のホストで動かす方法を紹介しました
わざわざ Swarm を組んだり docker-compose を作成する必要がないです
もちろん docker-compose で一元管理したいと言う場合はそれでも問題ないですが ElasticSearch はスケールアウトも考慮すると個別のホスト (またはクラスタ) で管理したくなるのが心情かなと思います

参考サイト