あのときのログ

思ったこと、経験したことを忘れないようにするためのメモ。

VagrantでElasticsearchクラスタを作ってみる

まえがき

FEK(fluentd + Elasticsearch + Kibana)によるログ可視化環境を試しに組んでみたところ、ログ検索を担当するElasticsearchが一番のボトルネックだった。

Kibanaのダッシュボードにいくつも定義されたグラフから来る複雑なクエリのお陰でCPU、メモリはカツカツだし、1日1GB以上生成されるログでDISKもパンパン、全てのリソースが一番最初に音を上げてしまう明らかなボトルネックになることがわかった。

そこで、Elasticsearchをクラスタ構成にして負荷分散を図り、さらにDISK容量もスケールアウトできないか。

Elasticsearchはスケールアウト可能だとは聞いていたので、手元のPC上でVMを立て冗長構成を試してみた。 その時の構築メモを残しておく。

※かなり時間が経ってしまって、バージョンなどは古くなってしまったがあしからず。

環境

HW

 MacBook Air (OSX Yosemite 10.10.5)

VM

 CentOS 6.5 ( on Vagrant 1.6.3 )

SW

  • OpenJDK (64bit) 1.8.0_51
  • Elasticsearch 1.7.1

Elasticsearchは実施時点で最新のものを利用。

Vagrantインスタンスを立てる

以下の様なVagrantfileを用意して、vagrant up

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "centos65_x86_64"

  config.vm.define :web1 do |web|
    web.vm.network :private_network, ip: "192.168.33.10"
    web.vm.provider "virtualbox" do |vb|
      vb.memory = "1048"
    end
  end

  config.vm.define :web2 do |web|
    web.vm.network :private_network, ip: "192.168.33.11"
    web.vm.provider "virtualbox" do |vb|
      vb.memory = "1048"
    end
  end

  config.vm.define :web3 do |web|
    web.vm.network :private_network, ip: "192.168.33.12"
    web.vm.provider "virtualbox" do |vb|
      vb.memory = "1048"
    end
  end

end

Elasticsearchのインストール

Elasticsearchを動かすにはJavaが必要。 なのでインストールの順番はJava→Elasticsearchとする。

Javaのインストール

yumでインストールするが、バージョンを選ぶためにyum searchしてからインストールするバージョンを指定。

$ sudo yum search openjdk
                   ・
                   ・
                 (略)
java-1.8.0-openjdk.x86_64 : OpenJDK Runtime Environment
java-1.8.0-openjdk-debug.x86_64 : OpenJDK Runtime Environment with full debug on
java-1.8.0-openjdk-demo.x86_64 : OpenJDK Demos
                   ・
                   ・
                 (略)

# 1.8をインストール
$ sudo yum install java-1.8.0-openjdk.x86_64

Elasticsearch本体のインストール

Elasticsearchの公式サイトからダウンロードするのか一覧が確実。

$ sudo wget https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-1.7.1.zip

設定

elasticsearch.yml で以下の設定を3台とも同じにする。

cluster.name: mycluster

host名が名前解決出来ないと起動時にエラーになるため、/etc/hostsに設定追加。

192.168.33.10 web1
192.168.33.11 web2
192.168.33.12 web3

起動

以上ができたら起動。

# ./elasticsearch-1.7.1/bin/elasticsearch

(以下、web2のstdout。 ※web2 → web1 → web3 の順に起動されている)

[2015-09-01 05:27:23,269][INFO ][node                     ] [William Stryker] version[1.7.1], pid[2409], build[b88f43f/2015-07-29T09:54:16Z]
[2015-09-01 05:27:23,271][INFO ][node                     ] [William Stryker] initializing ...
[2015-09-01 05:27:23,536][INFO ][plugins                  ] [William Stryker] loaded [], sites []
[2015-09-01 05:27:23,667][INFO ][env                      ] [William Stryker] using [1] data paths, mounts [[/ (/dev/sda1)]], net usable_space [6.3gb], net total_space [7.8gb], types [ext4]
[2015-09-01 05:27:30,824][INFO ][node                     ] [William Stryker] initialized
[2015-09-01 05:27:30,830][INFO ][node                     ] [William Stryker] starting ...
[2015-09-01 05:27:31,134][INFO ][transport                ] [William Stryker] bound_address {inet[/192.168.33.11:9300]}, publish_address {inet[/192.168.33.11:9300]}
[2015-09-01 05:27:31,241][INFO ][discovery                ] [William Stryker] mycluster/m-MMbTLfQaK0lrPUbs-iGw
[2015-09-01 05:27:35,078][INFO ][cluster.service          ] [William Stryker] new_master [William Stryker][m-MMbTLfQaK0lrPUbs-iGw][web2][inet[/192.168.33.11:9300]], reason: zen-disco-join (elected_as_master)
[2015-09-01 05:27:35,125][INFO ][http                     ] [William Stryker] bound_address {inet[/192.168.33.11:9200]}, publish_address {inet[/192.168.33.11:9200]}
[2015-09-01 05:27:35,126][INFO ][node                     ] [William Stryker] started
[2015-09-01 05:27:35,299][INFO ][gateway                  ] [William Stryker] recovered [2] indices into cluster_state
[2015-09-01 05:28:01,266][INFO ][cluster.service          ] [William Stryker] added {[Styx and Stone][NC8yxTlOSfyiJ5vcEtKttA][web1][inet[/192.168.33.10:9300]],}, reason: zen-disco-receive(join from node[[Styx and Stone][NC8yxTlOSfyiJ5vcEtKttA][web1][inet[/192.168.33.10:9300]]])
[2015-09-01 05:28:27,478][INFO ][cluster.service          ] [William Stryker] added {[Tex Dawson][KpOh0tfRRfSItoKUw0XImQ][vagrant-centos65.vagrantup.com][inet[/192.168.33.12:9300]],}, reason: zen-disco-receive(join from node[[Tex Dawson][KpOh0tfRRfSItoKUw0XImQ][vagrant-centos65.vagrantup.com][inet[/192.168.33.12:9300]]])

動作確認

3台それぞれで、以下を確認。

$ curl http://192.168.33.10:9200    #web1〜web3それぞれ
{
  "status" : 200,
  "name" : "William Stryker",
  "cluster_name" : "mycluster",
  "version" : {
    "number" : "1.7.1",
    "build_hash" : "b88f43fc40b0bcd7f173a1f9ee2e97816de80b19",
    "build_timestamp" : "2015-07-29T09:54:16Z",
    "build_snapshot" : false,
    "lucene_version" : "4.10.4"
  },
  "tagline" : "You Know, for Search"
}

headプラグインを追加するとビジュアルで確認しやすい(インストールは1台でも良い)。

$ ./elasticsearch-1.7.1/bin/plugin -install mobz/elasticsearch-head

以下のURLで各ノードを確認できる。

http://192.168.33.11:9200/_plugin/head/

f:id:catnapper_mar:20160321232954p:plain

3台の場合は、同じデータが2箇所に配置されていることが分かる。

ノード障害時の挙動確認

障害で1ノードがダウンしてしまった場合のクラスタの挙動を確認する。

web1をシャットダウンすると、web2のstdoutに以下のように表示される。

[2015-09-01 06:21:09,425][INFO ][cluster.service          ] [William Stryker] removed {[Styx and Stone][NC8yxTlOSfyiJ5vcEtKttA][web1][inet[/192.168.33.10:9300]],}, reason: zen-disco-node_left([Styx and Stone][NC8yxTlOSfyiJ5vcEtKttA][web1][inet[/192.168.33.10:9300]])
[2015-09-01 06:21:09,541][INFO ][cluster.routing          ] [William Stryker] delaying allocation for [7] unassigned shards, next check in [59.8s]
[2015-09-01 06:22:09,678][INFO ][cluster.routing          ] [William Stryker] delaying allocation for [0] unassigned shards, next check in [0s]
[2015-09-01 06:22:10,101][INFO ][cluster.routing          ] [William Stryker] delaying allocation for [0] unassigned shards, next check in [0s]

どうなったか、再度ブラウザから確認。 f:id:catnapper_mar:20160321233401p:plain

2台に寄せられ、データが均等配分されることが確認出来る。

以上から、(4台以上の場合の検証も必要だが)使えるDISK容量はN-1台分と理解して良さそう。 DISK容量もスケールアウト可能と思われる。

参考

Elasticsearch のクラスタを構築する - Carpe Diem

Cluster機能を使う – AWSで始めるElasticSearch(2) | Developers.IO

crontab内のコマンドラインでの%(パーセント)の扱い

crontabそのものをcronで毎日日付つきでバックアップしようと思って仕込んだら全然うまく行かなかった時に調べて見つけた、神の啓示。

dateコマンド と crontab の コマンドライン中の %(パーセント)記号 - keigoiの日記

すっごくスッキリしたことは覚えてる。

ソースコンパイルしたPHPに後からpdo_pgsqlをインストールする

PHPからPostgresqlに繋ぎたいので、ソースコンパイルしたPHP環境(今回の例では5.4.7)に、pdo_pgsqlをインストールして下さいと頼まれた。 PHP環境の構築や拡張モジュールなどには正直なところ全く詳しくなかったので、 どうやるのか、あまりに見当つかないものの、調べてやってみることに。

と思ってしばらく探したら、以下の記事がほとんどドンピシャ大当たり。ありがとうございます。

PHPをコンパイルした環境に、後からpgsql.soなどのモジュールを追加する方法 - Web系エンジニアbcoのメモ帳

以下、自分の環境でやった手順をメモる。

前提となるphp環境について

  • php 5.4.7
  • phpのインストール先:/opt/php
  • /usr/local/bin/php は、/opt/php/bin/php へのSymlinkとしている
  • コンパイルしたソースの置き場所:/usr/local/src/php-5.4.7

作業手順

現環境のPHPコンパイルしたソースの置き場所にある、extentionディレクトリへ行く

# cd /usr/local/src/php-5.4.7/ext/pdo_pgsql

phpのパス確認

拡張モジュールは今使っているphpのパス上にあるツールを使って生成するため、phpのパス(インストールパス)を確認する。

# ls -l /usr/local/bin/php
lrwxrwxrwx 1 root root 16  5月 13 16:21 2014 /usr/local/bin/php -> /opt/php/bin/php

phpizeでconfigureファイルを作成

phpizeとは?

拡張モジュールをビルドする低レベルなビルドツール。autoconfやautomake m4等のビルドツールが別途必要になる。これを使用することにより、PHPをソースから再コンパイルすることなく拡張モジュールをビルドすることができる。
引用:phpizeとは - PHP用語 Weblio辞書

インストールパス上のphpizeを実行すると、configure実行ファイルが出来る。

# /opt/php/bin/phpize

configureを実行

ここでは、--with-pdo-pgsql を入れることと、--with-php-config で、現在のphpパス上にあるphp-config実行ファイルを指定する必要がある

# ./configure --with-pdo-pgsql --with-php-config=/opt/php/bin/php-config

make実行

# make
# make test
# make install
Installing shared extensions:     /opt/php/lib/php/extensions/no-debug-non-zts-20100525/

インストール完了。これで、拡張ディレクトリにpdo_pgsql.soファイルが出来る。

php.iniファイルを編集

/etc/php.ini に、; extensionsのディレクトリと対象のモジュールの名前を記述する。

; 拡張モジュールの置き場
extension_dir =  /opt/php/lib/php/extensions/no-debug-non-zts-20100525/

; 利用するextension
extension = pdo_pgsql.so

apacheを再起動

# /etc/init.d/httpd restart

1時間ごとにlogrotateする方法

ログファイル管理はlogrotateを使って週1回または1日1回ローテートするように仕掛けたらひとまずOK。だいたいのケースでは。

が、普通に設定したら最短で1日1回しかローテートできないので、ログファイルが数百MB、数GBと大きくなる場合にはOKとは言えない。 できれば1時間に1回ローテートできないものか。

単純な発想だが、1時間に1回、cronで強制的にローテート実行させることで可能である。 以下、logrotateの基本的な設定方法の確認の意味も込めて、手順をメモっておく。

1.アーカイブ先(olddir)作成

アーカイブ先を分けるのは必須ではないが、分けておくとログディレクトリ直下には常に最新のログのみが書き出されているのですっきりする。

# mkdir -p /var/log/nginx/archives

2.logrotate設定

毎時ローテートさせたいログファイル専用の設定ファイルを用意する。

# mkdir -p /etc/logrotate.hourly
# vi /etc/logrotate.hourly/nginx

気をつけるのはローテートさせる回数とファイル名(日付・タイムスタンプ付にする)。 あとは好みに応じて設定。

/var/log/nginx/huge_access.log {
    create 0644 nginx nginx
    daily              # cronで実行するため、実質的に意味なし
    rotate 720         # 1ヶ月保持出来るローテート回数(24回/日 × 30日)
    missingok
    notifempty
    compress
    dateext             # ファイル名に日付を付ける
    dateformat -%Y%m%d-%s   # 毎時生成されるためタイムスタンプを付ける
    olddir /var/log/nginx/archives
    sharedscripts
    postrotate
         [ ! -f /var/run/nginx.pid ] || kill -USR1 `cat /var/run/nginx.pid`
    endscript
}

※dateext, dateformatの "%s" は、logrotateのバージョンによって使えない場合もある。

3. 設定テスト

-d デバッグモード(dry-runされて、ローテートされない)で実行し、設定エラーが出ないか確認

# logrotate -dv /etc/logrotate.hourly/nginx

4. 初回実行

新規にローテート対象となったファイルは、初回実行時に /var/lib/logrotate.status にエントリ追加されるだけでローテートされないので、cronでスケジュール実行される前に1回手動で実行する

# logrotate -v /etc/logrotate.d/logrotate.hourly/nginx

※1回目は、いずれのログも /var/lib/logrotate.status にエントリがない、または既に毎時ローテートされているファイル のどちらかのため、 dailyローテートのテストとしては、ローテート対象外(log does not need rotating)という結果になる。

5.ローテート履歴(logrotate.status) 確認

対象のログが、logrotate.status エントリに本日日付で入っていることを確認。

# cat /var/lib/logrotate.status

logrotate state -- version 2
      ・
      ・
    (略)
"/var/log/nginx/huge_access.log" 2015-8-29                      #←これ

6. cron設定

5までで、毎日3:00以降の任意のタイミング(cron.dailyの実行タイミング)でローテートが実施されるようにはなった。 ただ、これだけでは日次ローテートしかされないため、cronで毎時実行させる。

# logrotate -f で強制ローテート実行
0 * * * * /usr/sbin/logrotate -f -s /var/lib/logrotate.status /etc/logrotate.hourly/nginx 

これで設定完了。

・・・と、これを書いている最中、既に3.8.5からhourly設定可能になっているという情報を見かけ。今度使ってみよ。

https://fedorahosted.org/logrotate/browser/tags/r3-8-8/CHANGES

参考

logrotate 設定方法

タイトルとか決めてないけどこのままでもいいかもしんない: logrotateでnginxのログを1時間ごとにローテートをする

Gitでリモートブランチをローカルにチェックアウトする

今のところ頻繁にやる作業ではないため、忘れがち。 なんだっけ・・・とググってたらドンピシャだったので備忘録代わりに貼らせて頂きます。

sessan.hatenablog.com