Solr(Master-Slave) を Vagrant で構築する
Vagrant で Solr を Master-Slave で構築しました. Solr のバージョンは 6.2.0 です.
構成
Solr の構築はシェルでやることにしました.
D:\vagrant\solrmasterslave ┣Vagrantfile ┗create_solr.sh
Vagrantfile
- OS は CentOS 7 を使用します.
- プロビジョナでシェルを実行します.
slave_cnt = 2 Vagrant.configure("2") do |config| config.vm.box = "centos/7" config.proxy.enabled = true config.proxy.http = "http://user_id:password@proxy_address:proxy_port" config.proxy.https = "http://user_id:password@proxy_address:proxy_port" config.proxy.no_proxy = "localhost,127.0.0.1" config.vm.define "master" do | master | master.vm.hostname = "master" master.vm.network "private_network", ip: "192.168.33.40" master.vm.provision :hosts, :sync_hosts => true master.vm.provision :shell, path: "./create_solr.sh" end (1..slave_cnt).each do |i| config.vm.define "slave#{i}" do | slave | slave.vm.hostname = "slave#{i}" slave.vm.network "private_network", ip: "192.168.33.4#{i}" slave.vm.provision :hosts, :sync_hosts => true slave.vm.provision :shell, path: "./create_solr.sh" end end end
create_solr.sh
Standalone のときと同じです. ただ文書の登録は後のお楽しみにとっておきます.
# Java をインストールする yum install -y java-1.8.0-openjdk # Solr を構築する cd /usr/local/src/ yum install -y wget wget https://archive.apache.org/dist/lucene/solr/6.2.0/solr-6.2.0.tgz tar xzf solr-6.2.0.tgz ./solr-6.2.0/bin/install_solr_service.sh solr-6.2.0.tgz # core を作成する sudo -u solr /opt/solr/bin/solr create -c examplecore # 文書は登録しない # Port を開放する systemctl enable firewalld.service systemctl start firewalld.service firewall-cmd --zone=public --add-port=8983/tcp --permanent firewall-cmd --reload
Solr を構築する
> pwd D:\vagrant\solrmasterslave > vagrant up
Master を設定する
- Solr を停止します.
- core にある solrconfig.xml に Master の設定を追加します.
- Solr を起動します.
> vagrant ssh master $ su root $ service stop solr $ vi /var/solr/masterslave/master/data/examplecore/conf/solrconfig.xml $ service start solr
<requestHandler name="/replication" class="solr.ReplicationHandler" > <lst name="master"> <str name="replicateAfter">commit</str> <str name="replicateAfter">optimize</str> <str name="replicateAfter">startup</str> <str name="confFiles">schema.xml,stopwords.txt</str> </lst> </requestHandler>
Slave を設定する
- Solr を停止します.
- core にある solrconfig.xml に Slave の設定を追加します.
- Solr を起動します.
> vagrant ssh slave1 $ su root $ service stop solr $ vi /var/solr/masterslave/master/data/examplecore/conf/solrconfig.xml $ service start solr > vagrant ssh slave2 $ su root $ service stop solr $ vi /var/solr/masterslave/master/data/examplecore/conf/solrconfig.xml $ service start solr
<requestHandler name="/replication" class="solr.ReplicationHandler" > <lst name="slave"> <str name="masterUrl">http://master:8983/solr/examplecore</str> <str name="pollInterval">00:00:30</str> </lst> </requestHandler>
Replication 確認
Master に文書を登録して Slave に反映されるか確認します. まず,core を作成しただけなので検索結果が空であることを確認します.
> curl -X GET "http://192.168.33.40:8983/solr/examplecore/select?q=*:*&fl=id&wt=json&indent=on" --noproxy 192.168.33.40 { "responseHeader":{ "status":0, "QTime":23, "params":{ "q":"*:*", "indent":"on", "fl":"id", "wt":"json"}}, "response":{"numFound":0,"start":0,"docs":[] }} > curl -X GET "http://192.168.33.41:8983/solr/examplecore/select?q=*:*&fl=id&wt=json&indent=on" --noproxy 192.168.33.41 { "responseHeader":{ "status":0, "QTime":23, "params":{ "q":"*:*", "indent":"on", "fl":"id", "wt":"json"}}, "response":{"numFound":0,"start":0,"docs":[] }} > curl -X GET "http://192.168.33.42:8983/solr/examplecore/select?q=*:*&fl=id&wt=json&indent=on" --noproxy 192.168.33.42 { "responseHeader":{ "status":0, "QTime":23, "params":{ "q":"*:*", "indent":"on", "fl":"id", "wt":"json"}}, "response":{"numFound":0,"start":0,"docs":[] }}
Master に文書を登録します.
> vagrant ssh master -c "sudo -u solr /opt/solr/bin/post -c examplecore -p 8983 /opt/solr/example/exampledocs/*.xml"
再度検索してみます.
> curl -X GET "http://192.168.33.40:8983/solr/examplecore/select?q=*:*&fl=id&wt=json&indent=on" --noproxy 192.168.33.40 { "responseHeader":{ "status":0, "QTime":6, "params":{ "q":"*:*", "indent":"on", "fl":"id", "wt":"json"}}, "response":{"numFound":32,"start":0,"docs":[ { "id":"GB18030TEST"}, { "id":"SP2514N"}, { "id":"6H500F0"}, { "id":"F8V7067-APL-KIT"}, { "id":"IW-02"}, { "id":"MA147LL/A"}, { "id":"adata"}, { "id":"apple"}, { "id":"asus"}, { "id":"ati"}] }} > curl -X GET "http://192.168.33.41:8983/solr/examplecore/select?q=*:*&fl=id&wt=json&indent=on" --noproxy 192.168.33.41 { "responseHeader":{ "status":0, "QTime":11, "params":{ "q":"*:*", "indent":"on", "fl":"id", "wt":"json"}}, "response":{"numFound":32,"start":0,"docs":[ { "id":"GB18030TEST"}, { "id":"SP2514N"}, { "id":"6H500F0"}, { "id":"F8V7067-APL-KIT"}, { "id":"IW-02"}, { "id":"MA147LL/A"}, { "id":"adata"}, { "id":"apple"}, { "id":"asus"}, { "id":"ati"}] }} > curl -X GET "http://192.168.33.42:8983/solr/examplecore/select?q=*:*&fl=id&wt=json&indent=on" --noproxy 192.168.33.42 { "responseHeader":{ "status":0, "QTime":27, "params":{ "q":"*:*", "indent":"on", "fl":"id", "wt":"json"}}, "response":{"numFound":32,"start":0,"docs":[ { "id":"GB18030TEST"}, { "id":"SP2514N"}, { "id":"6H500F0"}, { "id":"F8V7067-APL-KIT"}, { "id":"IW-02"}, { "id":"MA147LL/A"}, { "id":"adata"}, { "id":"apple"}, { "id":"asus"}, { "id":"ati"}] }}
Replication が実行されて Master のインデックスが Slave にも反映されました.
あとがき
一気に Master-Slave 構成を構築する際の Vagrantfile,シェル,構成です. 共有ディレクトリを用意して,設定済みの solrconfig.xml を配布します(solrconfig.xml には Master,Slave の設定を追記しています).
slave_cnt = 2 Vagrant.configure("2") do |config| config.vm.box = "centos/7" config.vm.synced_folder "./shared", "/home/vagrant/shared", owner: "vagrant", group: "vagrant" config.proxy.enabled = true config.proxy.http = "http://user_id:password@proxy_address:proxy_port" config.proxy.https = "http://user_id:password@proxy_address:proxy_port" config.proxy.no_proxy = "localhost,127.0.0.1" config.vm.define "master" do | master | master.vm.hostname = "master" master.vm.network "private_network", ip: "192.168.33.40" master.vm.provision :hosts, :sync_hosts => true master.vm.provision :shell, path: "./create_solr.sh" master.vm.provision :shell, path: "./setup_master.sh" end (1..slave_cnt).each do |i| config.vm.define "slave#{i}" do | slave | slave.vm.hostname = "slave#{i}" slave.vm.network "private_network", ip: "192.168.33.4#{i}" slave.vm.provision :hosts, :sync_hosts => true slave.vm.provision :shell, path: "./create_solr.sh" slave.vm.provision :shell, path: "./setup_slave.sh" end end end
service solr stop \cp -f /home/vagrant/shared/master/solrconfig.xml /var/solr/data/examplecore/conf/solrconfig.xml service solr start
service solr stop \cp -f /home/vagrant/shared/slave/solrconfig.xml /var/solr/data/examplecore/conf/solrconfig.xml service solr start
D:\vagrant\solrmasterslave ┣Vagrantfile ┣shared ┃┣master ┃┃┗solrconfig.xml ┃┗slvae ┃ ┗solrconfig.xml ┣create_solr.sh ┣setup_master.sh ┗setup_slave.sh
Solr Cloud で可用性を検証した
前回 Solr Cloud を構築ました.
今回は Solr Cloud の可用性を検証しました.
node の管理
node が停止したら Failover するか,node が再起動したら Failback するか実験しました.
1個の node を停止する
全ての node が起動しているときの node 一覧とグラフです.
これらの node で shard1 の leader を停止してみます.
vagrant halt node2
node 一覧を見ると shard1 の leader が死んだことがすぐわかります.
グラフを見ると shard1 の leader が 192.168.33.22 -> 192.168.33.24 になりました.
このように node が停止したら自動で leader が切り替わることがわかりました.
1個の node を復旧する
こんどは停止した node を起動します.
また Cloud モードで起動します.
vagrant up node2 vagrant ssh node2 -c "sudo -u solr /opt/solr/bin/solr stop" vagrant ssh node2 -c "sudo -u solr /opt/solr/bin/solr start -cloud -s /var/solr/data -p 8983 -z 192.168.33.11:2181,192.168.33.12:2181,192.168.33.13:2181 -h 192.168.33.22"
node 一覧を見ると shard1 の node が復活したことがすぐわかります.
グラフを見ると node が shard1 に復帰しました.
このように node が再起動したら自動で Solr Cloud に復帰し,shard に割り当てられることがわかりました.
shard の管理
先ほどの例では 1個の node を消しました.
今度は 1個の shard を消したらどうなるか実験します.
1個の shard を停止する
現状の node 一覧とグラフです.
shard1 の node を全てを停止してみます.
vagrant halt node2 vagrant halt node4
shard1 が死んでますね. shard2 の node が shard1 の node になるなんてことは起こりませんでした.
この状態で検索すると shard1 がありませんと怒られます.
$ curl -X GET "http://192.168.33.21:8983/solr/examplecollection/select?q=*:*&fl=id&sort=id%20asc&wt=json&indent=on" --noproxy 192.168.33.21 { "responseHeader":{ "status":503, "QTime":0, "params":{ "q":"*:*", "indent":"on", "fl":"id", "sort":"id asc", "wt":"json"}}, "error":{ "metadata":[ "error-class","org.apache.solr.common.SolrException", "root-error-class","org.apache.solr.common.SolrException"], "msg":"no servers hosting shard: shard1", "code":503}}
shard が死ぬと検索にも影響を及ぼすことがわかりました.
shard を復旧する
先ほどのままではよろしくないので復旧します.
停止している shard に属する node を起動します.
また Cloud モードで起動します.
vagrant up node2 vagrant ssh node2 -c "sudo -u solr /opt/solr/bin/solr stop" vagrant ssh node2 -c "sudo -u solr /opt/solr/bin/solr restart -cloud -s /var/solr/data -p 8983 -z 192.168.33.11:2181,192.168.33.12:2181,192.168.33.13:2181 -h 192.168.33.22"
node 一覧を見ると shard1 の node が復活しましたが down と表示されています.
グラフを見ても shard1 の node が down しています.
この状態で検索しても shard1 がないと怒られてしまいます.
$ curl -X GET "http://192.168.33.21:8983/solr/examplecollection/select?q=*:*&fl=id&sort=id%20asc&wt=json&indent=on" --noproxy 192.168.33.21 { "responseHeader":{ "status":503, "QTime":0, "params":{ "q":"*:*", "indent":"on", "fl":"id", "sort":"id asc", "wt":"json"}}, "error":{ "metadata":[ "error-class","org.apache.solr.common.SolrException", "root-error-class","org.apache.solr.common.SolrException"], "msg":"no servers hosting shard: shard1", "code":503}}
ですがしばらく待つと node が生き返ります.
検索すると全件取得できました.
$ curl -X GET "http://192.168.33.21:8983/solr/examplecollection/select?q=*:*&fl=id&sort=id%20asc&wt=json&indent=on" --noproxy 192.168.33.21 { "responseHeader":{ "zkConnected":true, "status":0, "QTime":108, "params":{ "q":"*:*", "indent":"on", "fl":"id", "sort":"id asc", "wt":"json"}}, "response":{"numFound":32,"start":0,"docs":[ { "id":"0579B002"}, { "id":"100-435805"}, { "id":"3007WFP"}, { "id":"6H500F0"}, { "id":"9885A004"}, { "id":"EN7800GTX/2DHTV/256M"}, { "id":"EUR"}, { "id":"F8V7067-APL-KIT"}, { "id":"GB18030TEST"}, { "id":"GBP"}] }}
まとめ
- node が停止したら自動で leader が切り替わります.
- node が再起動したら自動で Solr Cloud に復帰し,shard に割り当てられます.
- shard に属する node が全て停止したら shard は機能しなくなり検索にも影響が出ます.
- node が全停止した shard で node が復旧すると最初は dead 状態だがしばらくすると復旧し検索できるようになります.
Solr Cloud を Vagrant で構築した
いよいよ Solr Cloud を構築します.
Solr Cloud を構築するには,Solr はもちろん ZooKeeper も必要です.
これまで Vagrant で Solr を Standalone で構築したり,ZooKeeper を Ensemble で構築してきたのもこのためでした.
Solr Cloud
イメージはこんな感じです.
- ZooKeeper : 3
- Solr node: 4
- shard : 2
- replica : 2
Zookeeper 3 個,Solr node 4 個の計 7 個の VM を構築します.
構成
構成を以下に示します.
D:\vagrant\solrcloud ┣Vagrantfile ┣create_zkensemble.sh ┗create_solrnode.sh
Vagrantfile
Zookeeper 3 個,Solr node 4 個の計 7 個の VM を構築します.
Solr を Cloud モードで起動する際に ZooKeeper の接続先を指定する必要があるので Vagrantfile で接続先文字列を用意するのがポイントです.
# ZooKeeper 数 zk_cnt = 3 # Solr node 数 node_cnt = 4 Vagrant.configure("2") do |config| config.vm.box = "centos/7" #--- proxy 設定 ---# no_proxy_address = "localhost,127.0.0.1" (1..zk_cnt).each do |i| no_proxy_address += ",zk#{i},192.168.33.1#{i}" end (1..node_cnt).each do |i| no_proxy_address += ",node#{i},192.168.33.2#{i}" end config.proxy.enabled = true config.proxy.http = "http://user_id:password@proxy_address:proxy_port" config.proxy.https = "http://user_id:password@proxy_address:proxy_port" config.proxy.no_proxy = no_proxy_address #--- /proxy 設定 ---# #--- ZooKeeper アンサンブル構築 ---# (1..zk_cnt).each do |i| config.vm.define "zk#{i}" do | zk | zk.vm.hostname = "zk#{i}" zk.vm.network "private_network", ip: "192.168.33.1#{i}" zk.vm.provision :hosts, :sync_hosts => true zk.vm.provision :shell, path: "./create_zkensemble.sh", args: "#{i} #{zk_cnt}" end end #--- /ZooKeeper アンサンブル構築 ---# #--- ZooKeeper アンサンブルの接続先文字列 ---# zkensemble = "" (1..zk_cnt).each do |i| zkensemble += "192.168.33.1#{i}:2181," end zkensemble.slice!(/,$/) #--- /ZooKeeper アンサンブルの接続先文字列 ---# #--- Solr Cloud 構築 ---# (1..node_cnt).each do |i| config.vm.define "node#{i}" do | node | node.vm.provider :virtualbox do |vb| # Solr node だけメモリ増やしてます vb.customize ["modifyvm", :id, "--memory", "2048"] vb.customize ["modifyvm", :id, "--cpus", "2"] end node.vm.hostname = "node#{i}" node.vm.network "private_network", ip: "192.168.33.2#{i}" node.vm.provision :hosts, :sync_hosts => true node.vm.provision :shell, path: "./create_solrcloud.sh", args: "192.168.33.2#{i} #{zkensemble}" end end #--- /Solr Cloud 構築 ---# end
create_zkensemble.sh
ZooKeeper Ensemble を構築したときと同じです.
# Java をインストールする yum install -y java-1.8.0-openjdk # ZooKeeper を構築する cd /var/tmp yum install -y wget wget http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz tar xvf zookeeper-3.4.14.tar.gz cp -ar zookeeper-3.4.14 /opt/ ln -s /opt/zookeeper-3.4.14 /opt/zookeeper # ZooKeeper 用のユーザを作成する groupadd zookeeper useradd -g zookeeper -d /opt/zookeeper -s /sbin/nologin zookeeper chown -R zookeeper.zookeeper /opt/zookeeper/* # ZooKeeperの設定ファイルを用意する cd /opt/zookeeper/conf cp zoo_sample.cfg zoo.cfg # dataDir を tmp から var に変更する mkdir /var/lib/zookeeper chown zookeeper.zookeeper /var/lib/zookeeper cd /opt/zookeeper/conf sed -i -e 's#dataDir=.*$#dataDir=/var/lib/zookeeper#' zoo.cfg # ZooKeeper アンサンブルの設定を追記する # $1 : ZooKeeper のノードID # $2 : ZooKeeper アンサンブルの構成ノード数 echo "# ZooKeeper Ensemble" >> /opt/zookeeper/conf/zoo.cfg for i in `seq 1 $2` do if [ $i = $1 ] ; then # ノードIDが自分のものと一致する場合は 0.0.0.0 とする. echo "server.${i}=0.0.0.0:2888:3888" >> /opt/zookeeper/conf/zoo.cfg else echo "server.${i}=192.168.33.1${i}:2888:3888" >> /opt/zookeeper/conf/zoo.cfg fi done # dataDir にノードIDが記載された myid ファイルを用意する必要がある echo $1 >> /var/lib/zookeeper/myid # ZooKeeper を起動する cd /opt/zookeeper bin/zkServer.sh start # Port を開放する systemctl enable firewalld.service systemctl start firewalld.service firewall-cmd --zone=public --add-port=2181/tcp --add-port=2888/tcp --add-port=3888/tcp --permanent firewall-cmd --reload # ZooKeeper の状態を確認する cd /opt/zookeeper bin/zkServer.sh status yum install -y nc echo ruok | nc localhost 2181 # 終了ステータスが 0 でない場合に Vagrant が異常終了とみなし VM が連続生成されないことがあるので明示する exit 0
create_solrcloud.sh
Solr を Cloud モードで起動します.
# Java をインストールする yum install -y java-1.8.0-openjdk # Solr を構築する cd /usr/local/src/ yum install -y wget wget http://ftp.jaist.ac.jp/pub/apache/lucene/solr/8.1.1/solr-8.1.1.tgz tar xzf solr-8.1.1.tgz ./solr-8.1.1/bin/install_solr_service.sh solr-8.1.1.tgz # Solr を Cloud モードで起動する # $1 : Solr node の IPアドレス # $2 : ZooKeeper アンサンブルの接続先文字列 sudo -u solr /opt/solr/bin/solr stop sudo -u solr /opt/solr/bin/solr start -cloud -s /var/solr/data -p 8983 -z $2 -h $1 # Port を開放する systemctl enable firewalld.service systemctl start firewalld.service firewall-cmd --zone=public --add-port=8983/tcp --permanent firewall-cmd --reload # 終了ステータスが 0 でない場合に Vagrant が異常終了とみなし VM が連続生成されないことがあるので明示する exit 0
Solr Cloud を構築する
cd D:\vagrant\solrcloud vagrant up
# Solr を停止する vagrant ssh node1 -c "sudo -u solr /opt/solr/bin/solr stop" vagrant ssh node2 -c "sudo -u solr /opt/solr/bin/solr stop" vagrant ssh node3 -c "sudo -u solr /opt/solr/bin/solr stop" vagrant ssh node4 -c "sudo -u solr /opt/solr/bin/solr stop" # Solr を Cloud モードで起動する vagrant ssh node1 -c "sudo -u solr /opt/solr/bin/solr restart -cloud -s /var/solr/data -p 8983 -z 192.168.33.11:2181,192.168.33.12:2181,192.168.33.13:2181 -h 192.168.33.21" vagrant ssh node2 -c "sudo -u solr /opt/solr/bin/solr restart -cloud -s /var/solr/data -p 8983 -z 192.168.33.11:2181,192.168.33.12:2181,192.168.33.13:2181 -h 192.168.33.22" vagrant ssh node3 -c "sudo -u solr /opt/solr/bin/solr restart -cloud -s /var/solr/data -p 8983 -z 192.168.33.11:2181,192.168.33.12:2181,192.168.33.13:2181 -h 192.168.33.23" vagrant ssh node4 -c "sudo -u solr /opt/solr/bin/solr restart -cloud -s /var/solr/data -p 8983 -z 192.168.33.11:2181,192.168.33.12:2181,192.168.33.13:2181 -h 192.168.33.24"-->
http://192.168.33.21:8983/solr/#/ にアクセスすると Cloud という項目が追加されていることがわかります.
Cloud をクリックすると node が一覧に表示されます.
4個 node を作成したので4個表示されています.
まだ collection を作成していないので Collections,Replicas は空です.
ZK Status をクリックすると ZooKeeper が一覧に表示されます.
3個 ZooKeeper を作成したので3個でアンサンブルが構成されています.
今回は 192.168.33.12 が leader に選出されたようです.
Graph をクリックすると Solr Cloud の構成が可視化されます.
まだ collection を作成していないので何も表示されません.
collection を作成する
早速 collection を作ります.
vagrant ssh node1 -c "sudo -u solr /opt/solr/bin/solr create_collection -c examplecollection -p 8983 -shards 2 -replicationFactor 2"
Dashboard を見ると collection が追加されていることがわかります.
collection を選択すると shard 2 replication 2 で構成されていることがわかります.
Cloud の Graph を見ると collection の shard に各 node が割り当てられていることがわかります.
インデクシング(文書登録)する
collection にインデクシングをおこないます.
vagrant ssh node1 -c "sudo -u solr /opt/solr/bin/post -c examplecollection -p 8983 /opt/solr/example/exampledocs/*.xml"
検索してみます.
$ curl -X GET "http://192.168.33.21:8983/solr/examplecollection/select?q=*:*&fl=id&sort=id%20asc&wt=json&indent=on" --noproxy 192.168.33.21 { "responseHeader":{ "zkConnected":true, "status":0, "QTime":7, "params":{ "q":"*:*", "indent":"on", "fl":"id", "sort":"id asc", "wt":"json"}}, "response":{"numFound":32,"start":0,"docs":[ { "id":"0579B002"}, { "id":"100-435805"}, { "id":"3007WFP"}, { "id":"6H500F0"}, { "id":"9885A004"}, { "id":"EN7800GTX/2DHTV/256M"}, { "id":"EUR"}, { "id":"F8V7067-APL-KIT"}, { "id":"GB18030TEST"}, { "id":"GBP"}] }}
32件ヒットしました.
rows を指定していないので id は 10 件のみの表示となっています.
これで構築が完了しました.
shard を指定した検索
まずは shard1 を指定して検索します.
$ curl -X GET "http://192.168.33.21:8983/solr/examplecollection/select?q=*:*&fl=id&shards=shard1&sort=id%20asc&wt=json&indent=on" --noproxy 192.168.33.21 { "responseHeader":{ "zkConnected":true, "status":0, "QTime":4, "params":{ "q":"*:*", "shards":"shard1", "indent":"on", "fl":"id", "sort":"id asc", "wt":"json"}}, "response":{"numFound":14,"start":0,"docs":[ { "id":"3007WFP"}, { "id":"EN7800GTX/2DHTV/256M"}, { "id":"GB18030TEST"}, { "id":"GBP"}, { "id":"IW-02"}, { "id":"MA147LL/A"}, { "id":"TWINX2048-3200PRO"}, { "id":"USD"}, { "id":"VDBDB1A16"}, { "id":"VS1GB400C3"}] }}
14件ヒットしました.
次に shard2 を指定して検索します.
$ curl -X GET "http://192.168.33.21:8983/solr/examplecollection/select?q=*:*&fl=id&shards=shard2&sort=id%20asc&wt=json&indent=on" --noproxy 192.168.33.21 { "responseHeader":{ "zkConnected":true, "status":0, "QTime":0, "params":{ "q":"*:*", "shards":"shard2", "indent":"on", "fl":"id", "sort":"id asc", "wt":"json"}}, "response":{"numFound":18,"start":0,"docs":[ { "id":"0579B002"}, { "id":"100-435805"}, { "id":"6H500F0"}, { "id":"9885A004"}, { "id":"EUR"}, { "id":"F8V7067-APL-KIT"}, { "id":"NOK"}, { "id":"SOLR1000"}, { "id":"SP2514N"}, { "id":"UTF8TEST"}] }}
18件ヒットしました.
各 shard から異なる結果が得られたことから,文書を登録すると自動で各 shard に文書が登録されることがわかりました.
今度は shard1 と shard2 両方指定して検索します.
$ curl -X GET "http://192.168.33.21:8983/solr/examplecollection/select?q=*:*&fl=id&shards=shard1,shard2&sort=id%20asc&wt=json&indent=on" --noproxy 192.168.33.21 { "responseHeader":{ "zkConnected":true, "status":0, "QTime":17, "params":{ "q":"*:*", "shards":"shard1,shard2", "indent":"on", "fl":"id", "sort":"id asc", "wt":"json"}}, "response":{"numFound":32,"start":0,"docs":[ { "id":"0579B002"}, { "id":"100-435805"}, { "id":"3007WFP"}, { "id":"6H500F0"}, { "id":"9885A004"}, { "id":"EN7800GTX/2DHTV/256M"}, { "id":"EUR"}, { "id":"F8V7067-APL-KIT"}, { "id":"GB18030TEST"}, { "id":"GBP"}] }}
shard を指定しない場合と同じ結果になりました.
つまりクエリで shard を全て指定しなくても全ての shard から検索することがわかりました.
まとめ
- collection を作成すると shard に各 node が自動で割り当てられます.
- Solr Cloud の collection に文書を登録すると自動で各 shard に文書が登録されます.
- Solr Cloud の collection を検索する際に shard を指定しなくても全 shard から検索されます.
ZooKeeper Ensemble を Vagrant で構築した
前回 ZooKeeper を Standalone で構築しました.
次は ZooKeeper も冗長構成を取れるように ZooKeeper Ensemble を構築します.
ZooKeeper のバージョンは 3.4.14 です.
ZooKeeper Ensemble
ZooKeeper の可用性を高めるために複数台で Ensemble 構成を取ります. ZooKeeper では Ensemble の過半数が動作していることがサービスの継続条件のため,奇数台で構築するのがベストなようです.
例
4 台のマシンで ZooKeeper Ensemble を構築した場合,1 台のマシンがダウンしても残り 3 台で過半数となるためサービスを継続することができますが, 2 台のマシンがダウンすると残り 2 台で過半数にならないためサービスを継続できなくなってしまいます. つまり 1 / 4,25 % のダウンを許容します.
5 台のマシンで ZooKeeper Ensemble を構築した場合,2 台のマシンがダウンしても残り 3 台で過半数となるためサービスを継続することができますが, 3 台のマシンがダウンすると残り 2 台で過半数にならないためサービスを継続できなくなってしまいます. つまり 2 / 5,40 % のダウンを許容します.
6 台のマシンで ZooKeeper Ensemble を構築した場合,2 台のマシンがダウンしても残り 4 台で過半数となるためサービスを継続することができますが, 3 台のマシンがダウンすると残り 3 台で過半数にならないためサービスを継続できなくなってしまいます. つまり 2 / 6,33 % のダウンを許容します.
このことから奇数台で構築したほうがマシンを有効活用できると言えそうです.
構成
相変わらずシェルでどうにかします.
D:\vagrant\zkcluster ┣Vagrantfile ┗create_zkcluster.sh
Vagrantfile
# ZooKeeper数 zk_cnt = 3 Vagrant.configure("2") do |config| config.vm.box = "centos/7" #--- Proxy設定 ---# no_proxy_address = "localhost,127.0.0.1" (1..zk_cnt).each do |i| no_proxy_address += ",zk#{i},192.168.33.1#{i}" end config.proxy.enabled = true config.proxy.http = "http://user_id:password@proxy_address:proxy_port" config.proxy.https = "http://user_id:password@proxy_address:proxy_port" config.proxy.no_proxy = no_proxy_address #--- /Proxy設定 ---# #--- ZooKeeper Ensemble 構築 ---# (1..zk_cnt).each do |i| config.vm.define "zk#{i}" do | zk | zk.vm.hostname = "zk#{i}" zk.vm.network "private_network", ip: "192.168.33.4#{i}", virtualbox__intnet: "solrcloud" zk.vm.provision :hosts, :sync_hosts => true zk.vm.provision :shell, path: "./create_zkensemble.sh", args: "#{i} #{zk_cnt}" end end #--- /ZooKeeper Ensemble 構築 ---# end
create_zkensemble.sh
ZooKeeper で Ensemble 構成をとるには以下の設定が必要です.
- zoo.cfg に Ensemble を構成するサーバーの情報を「server.id=host:port:port」の形式で記載する.
- dataDir 配下に id(1~255の数字) が記載されたファイル myid を用意する.
# Java をインストールする yum install -y java-1.8.0-openjdk # ZooKeeper を構築する cd /var/tmp yum install -y wget wget http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz tar xvf zookeeper-3.4.14.tar.gz cp -ar zookeeper-3.4.14 /opt/ ln -s /opt/zookeeper-3.4.14 /opt/zookeeper # ZooKeeper 用のユーザを作成する groupadd zookeeper useradd -g zookeeper -d /opt/zookeeper -s /sbin/nologin zookeeper chown -R zookeeper.zookeeper /opt/zookeeper/* # ZooKeeperの設定ファイルを用意する cd /opt/zookeeper/conf cp zoo_sample.cfg zoo.cfg # dataDir を tmp から var に変更する mkdir /var/lib/zookeeper chown zookeeper.zookeeper /var/lib/zookeeper cd /opt/zookeeper/conf sed -i -e 's#dataDir=.*$#dataDir=/var/lib/zookeeper#' zoo.cfg # ZooKeeper アンサンブルの設定を追記する # $1 : ZooKeeper のノードID # $2 : ZooKeeper アンサンブルの構成ノード数 echo "#ZooKeeper Ensemble" >> /opt/zookeeper/conf/zoo.cfg for i in `seq 1 $2` do if [ $i = $1 ] ; then # ノードIDが自分のものと一致する場合は 0.0.0.0 とする. echo "server.${i}=0.0.0.0:2888:3888" >> /opt/zookeeper/conf/zoo.cfg else echo "server.${i}=192.168.33.1${i}:2888:3888" >> /opt/zookeeper/conf/zoo.cfg fi done # dataDir にノードIDが記載された myid ファイルを用意する必要がある echo $1 | sudo tee /var/lib/zookeeper/myid # ZooKeeper を起動する cd /opt/zookeeper bin/zkServer.sh start # Port を開放する systemctl enable firewalld.service systemctl start firewalld.service firewall-cmd --zone=public --add-port=2181/tcp --add-port=2888/tcp --add-port=3888/tcp --permanent firewall-cmd --reload # 終了ステータスが 0 でない場合に Vagrant が異常終了とみなし VM が連続生成されないことがあるので明示する exit 0
ZooKeeper Ensemble を構築する
> pwd D:\vagrant\zkensemble > vagrant up
動作確認
ZooKeeper のステータスを見てみます.
> vagrant ssh zk1 -c "sudo /opt/zookeeper/bin/zkServer.sh status" ZooKeeper JMX enabled by default Using config: /opt/zookeeper/bin/../conf/zoo.cfg Mode: follower > vagrant ssh zk2 -c "sudo /opt/zookeeper/bin/zkServer.sh status" ZooKeeper JMX enabled by default Using config: /opt/zookeeper/bin/../conf/zoo.cfg Mode: leader > vagrant ssh zk3 -c "sudo /opt/zookeeper/bin/zkServer.sh status" ZooKeeper JMX enabled by default Using config: /opt/zookeeper/bin/../conf/zoo.cfg Mode: follower
leader や follower が自動で選出されました.
可用性
では leader を停止してみます.
> vagrant zk2 halt
再度 ZooKeeper のステータスを見ます.
> vagrant ssh zk1 -c "sudo /opt/zookeeper/bin/zkServer.sh status" ZooKeeper JMX enabled by default Using config: /opt/zookeeper/bin/../conf/zoo.cfg Mode: follower > vagrant ssh zk3 -c "sudo /opt/zookeeper/bin/zkServer.sh status" ZooKeeper JMX enabled by default Using config: /opt/zookeeper/bin/../conf/zoo.cfg Mode: leader
無事 leader が選出されました.
このように Ensemble 構成を取ることで ZooKeeper 自体の可用性が向上します.
ZooKeeper(Standalone) を Vagrant で構築した
Solr Cloud を構築するには ZooKeeper を構築する必要があります.
そこで Vagrant で ZooKeeper を構築しました.
ZooKeeper のバージョンは 3.4.14 です.
ZooKeeper???
Solr Cloud を構築する上で必須なサービスです. Solr Cloud では collection を 複数の shard に分けることでインデックスを分散し,さらに shard を replication により複製し冗長構成をとることで可用性を向上させます. ZooKeeper は分散アプリケーションの管理をおこない,Solr Cloud において shard の replica が格納された Solr の各 node の状態を管理します.
構成
ZooKeeper の構築はシェルでやることにしました.
D:\vagrant\zk ┣Vagrantfile ┗create_zk.sh
Vagrantfile
- OS は CentOS 7 を使用します.
- プロビジョナでシェルを実行します.
Vagrant.configure("2") do |config| config.vm.box = "centos/7" config.vm.network "private_network", ip: "192.168.33.10" config.proxy.enabled = true config.proxy.http = "http://user_id:password@proxy_address:proxy_port" config.proxy.https = "http://user_id:password@proxy_address:proxy_port" config.proxy.no_proxy = "localhost,127.0.0.1" config.vm.provision :shell, path: "./create_zk.sh" end
create_zk.sh
# Java をインストールする yum install -y java-1.8.0-openjdk # ZooKeeper を構築する cd /var/tmp yum install -y wget wget http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz tar xvf zookeeper-3.4.14.tar.gz cp -ar zookeeper-3.4.14 /opt/ ln -s /opt/zookeeper-3.4.14 /opt/zookeeper # ZooKeeper 用のユーザを作成する groupadd zookeeper useradd -g zookeeper -d /opt/zookeeper -s /sbin/nologin zookeeper chown -R zookeeper.zookeeper /opt/zookeeper/* # ZooKeeperの設定ファイルを用意する cd /opt/zookeeper/conf cp zoo_sample.cfg zoo.cfg # dataDir を tmp から var に変更する mkdir /var/lib/zookeeper chown zookeeper.zookeeper /var/lib/zookeeper cd /opt/zookeeper/conf sed -i -e 's#dataDir=.*$#dataDir=/var/lib/zookeeper#' zoo.cfg # ZooKeeper を起動する cd /opt/zookeeper bin/zkServer.sh start # Port を開放する systemctl enable firewalld.service systemctl start firewalld.service firewall-cmd --zone=public --add-port=2181/tcp --permanent firewall-cmd --reload
ZooKeeper を構築する
> pwd D:\vagrant\zk > vagrant up
これだけで Solr 導入済みの VM が完成しました.
動作確認
サービスの状態を確認します.
> vagrant ssh -c "sudo /opt/zookeeper/bin/zkServer.sh status" ZooKeeper JMX enabled by default Using config: /opt/zookeeper/bin/../conf/zoo.cfg Mode: standalone
nc(netcat) で ZooKeeper に Are you OK? と聞きます.
> vagrant ssh -c "yum install -y nc" > vagrant ssh -c "echo ruok | nc localhost 2181" imok
I'm OK と答えたので問題ないです.
Solr(Standalone) を Vagrant で構築する
Vagrant で Solr を Standalone で構築しました.
Solr のバージョンは 6.2.0 です.
構成
Solr の構築はシェルでやることにしました.
D:\vagrant\solr-standalone ┣Vagrantfile ┗create_solr.sh
Vagrantfile
- OS は CentOS 7 を使用します.
- プロビジョナでシェルを実行します.
Vagrant.configure("2") do |config| config.vm.box = "centos/7" config.vm.network "private_network", ip: "192.168.33.30" config.proxy.enabled = true config.proxy.http = "http://user_id:password@proxy_address:proxy_port" config.proxy.https = "http://user_id:password@proxy_address:proxy_port" config.proxy.no_proxy = "localhost,127.0.0.1" config.vm.provision :shell, path: "./create_solr.sh" end
create_solr.sh
# Java をインストールする yum install -y java-1.8.0-openjdk # Solr を構築する cd /usr/local/src/ yum install -y wget wget https://archive.apache.org/dist/lucene/solr/6.2.0/solr-6.2.0.tgz tar xzf solr-6.2.0.tgz ./solr-6.2.0/bin/install_solr_service.sh solr-6.2.0.tgz # Port を開放する systemctl enable firewalld.service systemctl start firewalld.service firewall-cmd --zone=public --add-port=8983/tcp --permanent firewall-cmd --reload
Solr を構築する
Vagrantfile があるディレクトリで vagrant up します.
> pwd D:\vagrant\solr-standalone > vagrant up
これだけで Solr 導入済みの VM が完成しました.
http://192.168.33.30:8983/solr/#/ にアクセスすると Solr の管理画面にアクセスできます.
まだ core がないので作成します.
core を作成する
core とは RDB でいう schema に相当します.
core には Solr の設定(solrconfig.xml)や Solr に取り込むデータの型やフィールド,tokenizer 等の設定(managed_schema または schema.xml)が含まれています.
core を作成するには Solr をインストールしたディレクトリにあるシェルを実行します.
> vagrant ssh -c "sudo -u solr /opt/solr/bin/solr create -c examplecore" Copying configuration to new core instance directory: /var/solr/data/examplecore Creating new core 'examplecore' using command: http://localhost:8983/solr/admin/cores?action=CREATE&name=examplecore&instanceDir=examplecore { "responseHeader":{ "status":0, "QTime":2701}, "core":"examplecore"}
/var/solr/data 配下に core が作成されたことが確認できます.
> vagrant ssh -c "sudo -u solr ls /var/solr/data" examplecore solr.xml > vagrant ssh -c "sudo -u solr ls /var/solr/data/examplecore" conf core.properties data
core.properties には core の名前が記載されています.
> vagrant ssh -c "sudo -u solr cat /var/solr/data/examplecore/core.properties" #Written by CorePropertiesLocator #Mon Jun 10 02:06:21 UTC 2019 name=examplecore
core のルートディレクトリの下にある conf を見ると managed-schema や solrconfig.xml がありました.
> vagrant ssh -c "sudo -u solr ls /var/solr/data/examplecore/conf" currency.xml elevate.xml lang managed-schema params.json protwords.txt solrconfig.xml stopwords.txt synonyms.txt
managed-schema を見てみます.
fieldType で Solr で扱うデータの型が定義されています.
analizer で tokenizer(文字列を解析して単語分割をおこなう) や filter(トークンを取り除いたり変換したりする処理をおこなう)が定義されています.
また analizer は fieldType ごとに設定できます.
field では core で扱うデータのフィールドを定義します.
dynamicField では field に定義していないフィールドを登録出来るようにします.
> vagrant ssh -c "sudo -u solr cat /var/solr/data/examplecore/conf/managed-schema" <?xml version="1.0" encoding="UTF-8"?> <!-- Solr managed schema - automatically generated - DO NOT EDIT --> <schema name="example-data-driven-schema" version="1.6"> <uniqueKey>id</uniqueKey> ... <fieldType name="int" class="solr.TrieIntField" positionIncrementGap="0" docValues="true" precisionStep="0"/> <fieldType name="ints" class="solr.TrieIntField" positionIncrementGap="0" docValues="true" multiValued="true" precisionStep="0"/> ... <fieldType name="text_ja" class="solr.TextField" autoGeneratePhraseQueries="false" positionIncrementGap="100"> <analyzer> <tokenizer class="solr.JapaneseTokenizerFactory" mode="search"/> <filter class="solr.JapaneseBaseFormFilterFactory"/> <filter class="solr.JapanesePartOfSpeechStopFilterFactory" tags="lang/stoptags_ja.txt"/> <filter class="solr.CJKWidthFilterFactory"/> <filter class="solr.StopFilterFactory" words="lang/stopwords_ja.txt" ignoreCase="true"/> <filter class="solr.JapaneseKatakanaStemFilterFactory" minimumLength="4"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType> ... <field name="id" type="string" multiValued="false" indexed="true" required="true" stored="true"/> ... <dynamicField name="*_txt_ja" type="text_ja" indexed="true" stored="true"/> ... </schema>
solrconfig.xml を見てみます.
version や キャッシュに関する情報等がありました(他にもいろいろありましたが今回は割愛させてください).
> vagrant ssh -c "sudo -u solr cat /var/solr/data/examplecore/conf/solrconfig.xml" <?xml version="1.0" encoding="UTF-8" ?> ... <config> ... <luceneMatchVersion>6.2.0</luceneMatchVersion> ... <dataDir>${solr.data.dir:}</dataDir> ... <query> ... <queryResultCache class="solr.LRUCache" size="512" initialSize="512" autowarmCount="0"/> ... </query> ... </config>
あっちこっちいきましたが 管理画面からも確認してみます.
http://192.168.33.30:8983/solr/#/ をリロードすると core が選択できるようになります.
今度は core にインデクシング(文書登録)をします.
core にインデクシングする
Solr で検索をするには文書のインデックスを core に登録する必要があります.
Solr では転置インデックスを使います.
転置インデックスにはどの単語がどの文書のどの位置にあるかが格納されています.
この転置インデックスを使うことで全ての文書をフルスキャンするより圧倒的に早く検索することができるというわけです.
Solr をインストールするとサンプルの文書が含まれているのでそれを利用します.
> vagrant ssh -c "sudo -u solr /opt/solr/bin/post -c examplecore /opt/solr/example/exampledocs/*.xml" java -classpath /opt/solr/dist/solr-core-6.2.0.jar -Dauto=yes -Dc=examplecore -Ddata=files org.apache.solr.util.SimplePostTool /opt/solr/example/exampledocs/gb18030-example.xml /opt/solr/example/exampledocs/hd.xml /opt/solr/example/exampledocs/ipod_other.xml /opt/solr/example/exampledocs/ipod_video.xml /opt/solr/example/exampledocs/manufacturers.xml /opt/solr/example/exampledocs/mem.xml /opt/solr/example/exampledocs/money.xml /opt/solr/example/exampledocs/monitor2.xml /opt/solr/example/exampledocs/monitor.xml /opt/solr/example/exampledocs/mp500.xml /opt/solr/example/exampledocs/sd500.xml /opt/solr/example/exampledocs/solr.xml /opt/solr/example/exampledocs/utf8-example.xml /opt/solr/example/exampledocs/vidcard.xml SimplePostTool version 5.0.0 Posting files to [base] url http://localhost:8983/solr/examplecore/update... Entering auto mode. File endings considered are xml,json,jsonl,csv,pdf,doc,docx,ppt,pptx,xls,xlsx,odt,odp,ods,ott,otp,ots,rtf,htm,html,txt,log POSTing file gb18030-example.xml (application/xml) to [base] POSTing file hd.xml (application/xml) to [base] POSTing file ipod_other.xml (application/xml) to [base] POSTing file ipod_video.xml (application/xml) to [base] POSTing file manufacturers.xml (application/xml) to [base] POSTing file mem.xml (application/xml) to [base] POSTing file money.xml (application/xml) to [base] POSTing file monitor2.xml (application/xml) to [base] POSTing file monitor.xml (application/xml) to [base] POSTing file mp500.xml (application/xml) to [base] POSTing file sd500.xml (application/xml) to [base] POSTing file solr.xml (application/xml) to [base] POSTing file utf8-example.xml (application/xml) to [base] POSTing file vidcard.xml (application/xml) to [base] 14 files indexed. COMMITting Solr index changes to http://localhost:8983/solr/examplecore/update... Time spent: 0:00:01.348
http://192.168.33.30:8983/solr/#/examplecore/query にアクセスして Execute Query を実行すると文書がヒットしました.
curl でこのように検索することもできます.
$ curl -X GET "http://192.168.33.30:8983/solr/examplecore/select?q=*:*&fl=id&wt=json&indent=on" --noproxy 192.168.33.30 { "responseHeader":{ "status":0, "QTime":0, "params":{ "q":"*:*", "indent":"on", "fl":"id", "wt":"json"}}, "response":{"numFound":32,"start":0,"docs":[ { "id":"GB18030TEST"}, { "id":"SP2514N"}, { "id":"6H500F0"}, { "id":"F8V7067-APL-KIT"}, { "id":"IW-02"}, { "id":"MA147LL/A"}, { "id":"adata"}, { "id":"apple"}, { "id":"asus"}, { "id":"ati"}] }}
これで Solr を構築して検索するところまで確認できました.
まとめ
Solr で検索できるまでにおこなう手順は以下.
- Solr を構築する.
- core を作成する.
- core にインデクシングをおこなう.
用語 | 内容 |
---|---|
Solr | ソーラー. 全文検索システム. 検索ライブラリ Lucene をベースにしている. |
core | RDBでいう shema に相当する. core には Solr の設定や Solr に取り込むデータ型,フィールド,tokenizer 等を設定する. |
managed-schema | Solr に取り込むデータ型,フィールド,tokenizer 等を定義する. |
fieldType | Solr で扱うデータ型を定義する. |
analizer | tokenizer や filter を定義する. fieldType ごとに analizer を定義できる. |
tokenizer | 文字列を解析して単語分割をおこなう. |
filter | トークンを取り除いたり,変換したりする処理をおこなう. |
filed | Solr で扱うデータのフィールドを定義する. |
dynamicField | filed に定義していないフィールドを登録出来るようにする. |
solrconfig.xml | データの格納先やキャッシュの設定等 Solr の設定を定義する. |
インデクシング | 文書を登録すること. Solr では core に文書を登録しインデックスを作成する. |
転置インデックス | どの単語がどの文書のどの位置にあるかが格納される. Solr は転置インデックスで検索することで全文書をフルスキャンするより圧倒的に早く検索できる. |
あとがき
core 作成,文書登録までやってしまいたい場合のシェルを以下に記載します.
# Java をインストールする yum install -y java-1.8.0-openjdk # Solr を構築する cd /usr/local/src/ yum install -y wget wget https://archive.apache.org/dist/lucene/solr/6.2.0/solr-6.2.0.tgz tar xzf solr-6.2.0.tgz ./solr-6.2.0/bin/install_solr_service.sh solr-6.2.0.tgz # core を作成する sudo -u solr /opt/solr/bin/solr create -c examplecore # 文書を登録する sudo -u solr /opt/solr/bin/post -c examplecore /opt/solr/example/exampledocs/*.xml # Port を開放する systemctl enable firewalld.service systemctl start firewalld.service firewall-cmd --zone=public --add-port=8983/tcp --permanent firewall-cmd --reload