Solr(Master-Slave) を Vagrant で構築する

Vagrant で Solr を Master-Slave で構築しました. Solr のバージョンは 6.2.0 です.

f:id:mt9116:20190613162005p:plain

構成

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 一覧とグラフです.

f:id:mt9116:20190613090607p:plain

f:id:mt9116:20190613090626p:plain

これらの node で shard1 の leader を停止してみます.

vagrant halt node2

node 一覧を見ると shard1 の leader が死んだことがすぐわかります.

f:id:mt9116:20190613090643p:plain

グラフを見ると shard1 の leader が 192.168.33.22 -> 192.168.33.24 になりました.

f:id:mt9116:20190613090702p:plain

このように 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 が復活したことがすぐわかります.

f:id:mt9116:20190613090835p:plain

グラフを見ると node が shard1 に復帰しました.

f:id:mt9116:20190613090921p:plain

このように node が再起動したら自動で Solr Cloud に復帰し,shard に割り当てられることがわかりました.

shard の管理

先ほどの例では 1個の node を消しました.
今度は 1個の shard を消したらどうなるか実験します.

1個の shard を停止する

現状の node 一覧とグラフです.

f:id:mt9116:20190613090835p:plain

f:id:mt9116:20190613090921p:plain

shard1 の node を全てを停止してみます.

vagrant halt node2
vagrant halt node4

shard1 が死んでますね. shard2 の node が shard1 の node になるなんてことは起こりませんでした.

f:id:mt9116:20190613091027p:plain

f:id:mt9116:20190613091042p:plain

この状態で検索すると 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 と表示されています.

f:id:mt9116:20190613091103p:plain

グラフを見ても shard1 の node が down しています.

f:id:mt9116:20190613091142p:plain

この状態で検索しても 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 が生き返ります.

f:id:mt9116:20190613091223p:plain

f:id:mt9116:20190613091306p:plain

検索すると全件取得できました.

$ 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 を構築します.

f:id:mt9116:20190611154658p:plain

構成

構成を以下に示します.

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 という項目が追加されていることがわかります.

f:id:mt9116:20190611154730p:plain

Cloud をクリックすると node が一覧に表示されます.
4個 node を作成したので4個表示されています.
まだ collection を作成していないので Collections,Replicas は空です.

f:id:mt9116:20190611154741p:plain

ZK Status をクリックすると ZooKeeper が一覧に表示されます.
3個 ZooKeeper を作成したので3個でアンサンブルが構成されています.
今回は 192.168.33.12 が leader に選出されたようです.

f:id:mt9116:20190611154758p:plain

Graph をクリックすると Solr Cloud の構成が可視化されます.
まだ collection を作成していないので何も表示されません.

f:id:mt9116:20190611154813p:plain

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 が追加されていることがわかります.

images/solr_dashborad_collection.png

collection を選択すると shard 2 replication 2 で構成されていることがわかります.

images/solr_collection_overview.png

Cloud の Graph を見ると collection の shard に各 node が割り当てられていることがわかります.

images/solr_cloud_graph_collection_created.png

インデクシング(文書登録)する

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 % のダウンを許容します.

f:id:mt9116:20190611100033p:plain

5 台のマシンで ZooKeeper Ensemble を構築した場合,2 台のマシンがダウンしても残り 3 台で過半数となるためサービスを継続することができますが, 3 台のマシンがダウンすると残り 2 台で過半数にならないためサービスを継続できなくなってしまいます. つまり 2 / 5,40 % のダウンを許容します.

f:id:mt9116:20190611100042p:plain

6 台のマシンで ZooKeeper Ensemble を構築した場合,2 台のマシンがダウンしても残り 4 台で過半数となるためサービスを継続することができますが, 3 台のマシンがダウンすると残り 3 台で過半数にならないためサービスを継続できなくなってしまいます. つまり 2 / 6,33 % のダウンを許容します.

f:id:mt9116:20190611100057p:plain

このことから奇数台で構築したほうがマシンを有効活用できると言えそうです.

構成

相変わらずシェルでどうにかします.

D:\vagrant\zkcluster
┣Vagrantfile
┗create_zkcluster.sh

Vagrantfile

  • OS は CentOS 7 を使用します.
  • VM 間で通信できるように hosts や no_proxy を設定します.
  • 任意の数 ZooKeeper を作成できるようにします.
# 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 をインストールします.
  • ZooKeeper のアーカイブを取得して展開します.
  • ZooKeeper で使用する Port を開放します.
# 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 をインストールします.
  • Solr のアーカイブを取得して展開しシェルを実行します.
  • Solr で使用する Port を開放します.
# 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 の管理画面にアクセスできます.

f:id:mt9116:20190610155006p:plain

まだ 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 が選択できるようになります.

f:id:mt9116:20190610155025p:plain

f:id:mt9116:20190610155037p:plain

今度は 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 を実行すると文書がヒットしました.

f:id:mt9116:20190610155053p:plain

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

いまさら Vagrant に入門した

いまさら Vagrant に入門しました.

Vagrant って?

Virtual Box 等の仮想化ソフトのラッパーツールです.
通常は Virtual Box で仮想マシン(以下 VM)作って,OSインストールして,Guest Addition入れて,...って使えるようにするまで色々手順を踏む必要があると思いますが,Vagrant を使うとそんなオペレーションから開放されます.

どう使うの?

ざっくり説明するとこんな感じです.

すると VM ができちゃいます.

Vagrant をはじめるには?

まず,仮想化ソフトが必要です.
私は Virtual Box を使っています.
あと Vagrant が必要です.

Vagrant を使えるようにする

VitrualBox については https://www.oracle.com/technetwork/server-storage/virtualbox/downloads/index.html?ssSourceSiteId=otnjp からインストーラーを取得して実行してください.
Vagrant については https://www.vagrantup.com/downloads.html からインストーラーを取得して実行してください.

Vagrant の動作確認

まずはバージョンを表示してみます.

> vagrant --version
Vagrant 2.2.4

プラグインを導入する

すぐ VagrantVM を作りたくなるところですが,Guest Addition を入れたり,Proxy 設定したりと色々設定があるので便利なプラグインたちをまず導入します.

Proxy関係

※ Proxy に苦しめられていないかたは読み飛ばしてください.

私は Proxy に阻まれているので社外につなぐためには Proxy 設定が必須です.
そんなときはホストに vagrant-proxyconf プラグインをインストールします.
このプラグインによってゲストの Proxy 設定を自動でおこなってくれます.

> vagrant plugin install vagrant-proxyconf

Vagrantfile で proxy を設定する例です.

Vagrant.configure("2") do |config|
  config.vm.box = "centos/7"
  # proxy 設定
  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"
end

Guest Additions

ホストとゲストでファイルを共有するために必要です.
ホストに vagrant-vbguest プラグインをインストールするとゲストに自動でGuest Additions をインストールしてくれます.
Vagrantfile で特に設定は不要です.

> vagrant plugin install vagrant-vbguest

hosts

vagrant-hosts プラグインをインストールすると VM のホスト名を解決できるようになります.

> vagrant plugin install vagrant-hosts

Vagrantfile でホスト名解決を可能にする設定例です.
プロビジョニングで hosts の設定を有効にしています(プロビジョニングは vagrant up を実行する際にシェルを実行したりVMの設定をしたりする機能のことです).

Vagrant.configure("2") do |config|
  config.vm.box = "centos/7"

  config.vm.define :vm1 do |vm1|
    vm1.vm.hostname = "vm1"
    vm1.vm.network "private_network", ip: "192.168.33.10"
    vm1.vm.provision :hosts, :sync_hosts => true
  end

  config.vm.define :vm2 do |vm2|
    vm1.vm.hostname = "vm2"
    vm2.vm.network "private_network", ip: "192.168.33.11"
    vm2.vm.provision :hosts, :sync_hosts => true
  end
end

Vagrant を使って CentOS7 の VM を作成する

プラグインを入れていよいよ準備が整いました.
まず任意のディレクトリを用意してそこに移動します.

> mkdir D:\vargrant\centos7
> mkdir D:\vagrant\centos7\shared
> cd D:\vagrant\centos7

続いて Vagrantfile を作成します.

> vagrant init -m

Vagrantfile を以下のように編集します.

Vagrant.configure("2") do |config|
  config.vm.box = "centos/7"
  config.vm.network "private_network", ip: "192.168.33.10"
  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"
end

構成はこのようになりました.

D:\vagrant\centos7
┣Vagrantfile
┗shared

最後に vargant up すれば VM が起動します.

> vagrant up

VagrantSSH 接続する

Vagrantfile を置いたディレクトリで vagrant ssh すれば VM に接続できます.

> pwd
D:\vagrant\centos7
> vagrant ssh
Last login: Mon Jun 10 00:19:01 2019 from 10.0.2.2
[vagrant@centos7 ~]$ pwd
/home/vagrant

VM 作成時にシェルスクリプトを実行する

Vagrantfile を作成したディレクトリに start.sh を作成します.

yum update

Vagrantfile に シェルを実行するように設定します.

Vagrant.configure("2") do |config|
  ...
  config.vm.provision :shell, path: "./start.sh"
  ...
end

構成はこのようになりました.

D:\vagrant\centos7
┣Vagrantfile
┗start.sh

あとは vagrant up すればシェルが実行されます.

> vagrant up

Guest Addition インストール済みの CentOS7 の box を作る

毎回新しく VM を作成する際に Guest Addition をインストールしていると時間がかかるので Guest Addition インストール済みの box を作ってみます.

D:\vagrant\centos7ga
┗Vagrantfile
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.provision "shell", inline: <<-SHELL
    yum update
  SHELL
end

まずは VM を作成して停止します.

> cd D:\vagrant\centos7ga
> vagrant up
> vagrant halt

box を作成します. --output で box 名を指定します.

> vagrant package default --output centos7ga.box

作成した box を使用するには box を登録する必要があります.

> vagrant box add centos7ga centos7ga.box

これ以降 Vagrantfile で以下のように作成した box を使うことができます.

Vagrant.configure("2") do |config|
  config.vm.box = "centos7ga"
end