Docker Composeを試してみたので、これを使ってApache Solrのレプリケーションを構成してみたいと思います。
Index Replication | Apache Solr Reference Guide 6.6
Solrのレプリケーションは、マスターとスレーブがあって、それぞれの設定をsolrconfig.xmlにすればよいみたいです。
参考)
https://cwiki.apache.org/confluence/display/solr/Making+and+Restoring+Backups+of+SolrCores
tree-tips: solrのReplicationHandlerで簡単レプリケーション! | Apache Solr
moco(beta)'s backup: Solrのレプリケーション機能を試す (1)
このあたりを参考にSolrを設定します。
Dockerfile
で、Docker Composeを使って構成すると言いましたが、とりあえず普通にDockerfileを書いてDockerイメージをビルドします。
Dockerfile
FROM ubuntu:latest ENV JAVA_HOME /usr/lib/jvm/java-8-oracle ENV SOLR_VERSION 5.3.0 ENV SOLR_ARCHIVE solr-${SOLR_VERSION}.tgz ENV SOLR_INSTALL_DIR /opt ENV SOLR_DATA_DIR /var/solr ENV SOLR_PORT 8983 ENV SOLR_CORE_NAME mycore ## Oracle JDK 8インストール RUN echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | debconf-set-selections && \ apt-get install -y software-properties-common && \ add-apt-repository -y ppa:webupd8team/java && \ apt-get update && \ apt-get install -y oracle-java8-installer ## ツールインストール RUN apt-get install -y lsof \ curl \ vim ## 日本語環境サポート RUN apt-get install -y language-pack-ja-base \ language-pack-ja \ ibus-mozc \ man \ manpages-ja && \ update-locale LANG=ja_JP.UTF-8 LANGUAGE=ja_JP:ja ENV LANG ja_JP.UTF-8 ENV LC_ALL ja_JP.UTF-8 ENV LC_CTYPE ja_JP.UTF-8 ## Solrインストール RUN wget -q http://ftp.jaist.ac.jp/pub/apache//lucene/solr/${SOLR_VERSION}/${SOLR_ARCHIVE} && \ tar -zxvf ${SOLR_ARCHIVE} solr-${SOLR_VERSION}/bin/install_solr_service.sh && \ solr-${SOLR_VERSION}/bin/install_solr_service.sh ${SOLR_ARCHIVE} -i ${SOLR_INSTALL_DIR} -d ${SOLR_DATA_DIR} -p ${SOLR_PORT} EXPOSE ${SOLR_PORT} ## コア作成 RUN service solr restart && \ sudo -u solr ${SOLR_INSTALL_DIR}/solr/bin/solr create -c ${SOLR_CORE_NAME} && \ service solr stop && \ rm /var/solr/data/${SOLR_CORE_NAME}/conf/managed-schema ADD solrconfig.xml /var/solr/data/${SOLR_CORE_NAME}/conf/solrconfig.xml ADD schema.xml /var/solr/data/${SOLR_CORE_NAME}/conf/schema.xml ADD entrypoint.sh entrypoint.sh RUN chmod a+x entrypoint.sh ## 実行コマンド ENTRYPOINT ["./entrypoint.sh"]
いくつか設定ファイルのコピーやシェルスクリプトが登場しますが、それは後でまた載せます。
今回は、こんな名前でDockerイメージとして作成しました。
$ docker build -t kazuhira/apache-solr-replication:5.3.0 .
レプリケーション向けの設定
Dockerfileが置かれているカレントディレクトリの内容は、このようになっています。
$ ls -l 合計 84 -rw-rw-r-- 1 xxxxx xxxxx 1843 9月 22 17:16 Dockerfile -rw-rw-r-- 1 xxxxx xxxxx 520 9月 22 17:32 entrypoint.sh -rw-rw-r-- 1 xxxxx xxxxx 10784 9月 22 20:44 schema.xml -rw-rw-r-- 1 xxxxx xxxxx 62463 9月 22 20:43 solrconfig.xml
Dockerfileの中でSolrのコアを作成するようにしているのですが、今回のコアの名前は「mycore」としています。
ENV SOLR_CORE_NAME mycore
で、schema.xmlとsolrconfig.xmlの2つのファイルは、ここで作ったコアに対して反映するようにDockerfileを書いています。
schema.xmlの内容はお好みで…ですが、今回はこのくらいの少ない内容で。
※いろいろ端折っています
<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" /> <field name="_version_" type="long" indexed="true" stored="true"/> <field name="name" type="text_ja" indexed="true" stored="true"/> <!-- Field to use to determine and enforce document uniqueness. Unless this field is marked with required="false", it will be a required field --> <uniqueKey>id</uniqueKey>
Kuromojiは、ユーザー定義辞書を含めるようにしています。
<fieldType name="text_ja" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="false"> <analyzer> <!-- <tokenizer class="solr.JapaneseTokenizerFactory" mode="search"/> --> <tokenizer class="solr.JapaneseTokenizerFactory" mode="search" userDictionary="lang/userdict_ja.txt"/>
solrconfig.xmlですが、schema.xmlを使うようにClassicIndexSchemaFactoryを使うようにしているのと、レプリケーション向けに以下のような設定を入れています。
<requestHandler name="/replication" class="solr.ReplicationHandler"> <lst name="master"> <str name="enable">${solr.repl.master:false}</str> <str name="replicateAfter">startup</str> <str name="replicateAfter">commit</str> <str name="replicateAfter">optimize</str> <str name="confFiles">schema.xml,stopwords.txt,synonyms.txt,lang/stopwords_ja.txt,lang/userdict_ja.txt</str> <str name="backupAfter">optimize</str> </lst> <int name="maxNumberOfBackups">3</int> <lst name="slave"> <str name="enable">${solr.repl.slave:false}</str> <str name="masterUrl">http://solr-master:8983/solr/mycore/replication</str> <str name="pollInterval">00:00:05</str> <str name="httpConnTimeout">5000</str> <str name="httpReadTimeout">10000</str> </lst> </requestHandler>
こちらを参考に、システムプロパティでマスターとスレーブを切り替える設定しています。
tree-tips: solrのReplicationHandlerで簡単レプリケーション! | Apache Solr
マスター側の設定は、レプリケーションのトリガーは起動時、コミット時、オプティマイズ時
<str name="replicateAfter">startup</str> <str name="replicateAfter">commit</str> <str name="replicateAfter">optimize</str>
の3つとし、設定ファイルは以下の内容をレプリケーションします。
<str name="confFiles">schema.xml,stopwords.txt,synonyms.txt,lang/stopwords_ja.txt,lang/userdict_ja.txt</str>
最後に、オプティマイズ後にバックアップを取るように設定。
<str name="backupAfter">optimize</str>
バックアップの数は3つまで。
<int name="maxNumberOfBackups">3</int>
で、マスターとして機能させるかどうかはシステムプロパティで設定します、と。
<str name="enable">${solr.repl.master:false}</str>
スレーブ側は、有効/無効をシステムプロパティで切り替えるとともに、タイムアウトなどの設定を入れています。
<lst name="slave"> <str name="enable">${solr.repl.slave:false}</str> <str name="masterUrl">http://solr-master:8983/solr/mycore/replication</str> <str name="pollInterval">00:00:05</str> <str name="httpConnTimeout">5000</str> <str name="httpReadTimeout">10000</str> </lst>
Solrのレプリケーションは、スレーブからマスターに問い合わせにいく形なので、そのエンドポイントをまずは指定。
<str name="masterUrl">http://solr-master:8983/solr/mycore/replication</str>
ホスト名は、「solr-master」としています。あと、ポーリング間隔は5秒で。
<str name="pollInterval">00:00:05</str>
で、この両者の切り替えなんですけど、DockerのENTRYPOINTに設定するシェルスクリプト内で、どうこうしてしまおうという作戦。シェルスクリプトの中身は、こんな感じです。
entrypoint.sh
#!/bin/bash if [ "$1" == "master" ]; then grep 'SOLR_OPTS="$SOLR_OPTS -Dsolr.repl.master=true"' /var/solr/solr.in.sh || \ echo 'SOLR_OPTS="$SOLR_OPTS -Dsolr.repl.master=true"' >> /var/solr/solr.in.sh elif [ "$1" == "slave" ]; then grep 'SOLR_OPTS="$SOLR_OPTS -Dsolr.repl.slave=true"' /var/solr/solr.in.sh || \ echo 'SOLR_OPTS="$SOLR_OPTS -Dsolr.repl.slave=true"' >> /var/solr/solr.in.sh else echo "require argument 'master' or 'slave'" exit 1 fi service solr start && tail -f /dev/null
「master」と入力されると/var/solr/solr.in.shに「-Dsolr.repl.master=true」を追加し、「slave」と入力されると「-Dsolr.repl.slave=true」を追加するようにしました。
ここまでで、Dockerの準備はおしまい。
Docker Composeの設定
あとは、作成したDockerイメージを使ってレプリケーションを構成するための、Docker Composeの設定を書きます。
docker-compose.yml
master: image: kazuhira/apache-solr-replication:5.3.0 ports: - "8983:8983" container_name: solr-master hostname: solr-master command: "master" slave: image: kazuhira/apache-solr-replication:5.3.0 ports: - "8983" links: - master command: "slave"
同じDockerイメージを使って、「command」で追加引数を入れます。あと、マスターのみホスト側から参照するポートは固定にしました。
動作確認
それでは、起動してみます。「docker-compose.yml」が配置されているディレクトリで、以下のコマンドを実行。
$ docker-compose up -d
Creating solr-master...
Creating solrreplication_slave_1...
コンテナが2つ起動します。
利用しているポートの割り振りは、こんな感じで。
$ docker-compose ps Name Command State Ports ---------------------------------------------------------------------------------- solr-master ./entrypoint.sh master Up 0.0.0.0:8983->8983/tcp solrreplication_slave_1 ./entrypoint.sh slave Up 0.0.0.0:32820->8983/tcp
この場合、それぞれ以下のURLでSolrの管理画面にアクセスできます。
マスター http://localhost:8983/solr/#/mycore
スレーブ http://localhost:32820/solr/#/mycore
マスター側にデータ登録。対象データは、とりあえずこんな感じ。
data1.json
[ { "id": 1, "name": "[改訂新版] Apache Solr入門 ~オープンソース全文検索エンジン" }, { "id": 2, "name": "Apache Lucene 入門 〜Java・オープンソース・全文検索システムの構築" } ]
実行。
$ curl -H 'Content-Type: application/json' 'http://localhost:8983/solr/mycore/update?commit=true' --data-binary @data1.json
マスターで検索。
$ curl 'http://localhost:8983/solr/mycore/select?wt=json&indent=true' -d '{ "query": "*:*" }'{ "responseHeader":{ "status":0, "QTime":21, "params":{ "indent":"true", "json":"{ \"query\": \"*:*\" }", "wt":"json"}}, "response":{"numFound":2,"start":0,"docs":[ { "id":"1", "name":"[改訂新版] Apache Solr入門 ~オープンソース全文検索エンジン", "_version_":1513016793776521216}, { "id":"2", "name":"Apache Lucene 入門 〜Java・オープンソース・全文検索システムの構築", "_version_":1513016793951633408}] }}
スレーブで検索。
$ curl 'http://localhost:32820/solr/mycore/select?wt=json&indent=true' -d '{ "query": "*:*" }' { "responseHeader":{ "status":0, "QTime":32, "params":{ "indent":"true", "json":"{ \"query\": \"*:*\" }", "wt":"json"}}, "response":{"numFound":2,"start":0,"docs":[ { "id":"1", "name":"[改訂新版] Apache Solr入門 ~オープンソース全文検索エンジン", "_version_":1513016793776521216}, { "id":"2", "name":"Apache Lucene 入門 〜Java・オープンソース・全文検索システムの構築", "_version_":1513016793951633408}] }}
OKそうですね。
スレーブを追加してみる
ここで、Docker Composeでスレーブを3台にしてみます。
$ docker-compose scale slave=3 Creating and starting 2... done Creating and starting 3... done
ポートの状態。
$ docker-compose ps Name Command State Ports ---------------------------------------------------------------------------------- solr-master ./entrypoint.sh master Up 0.0.0.0:8983->8983/tcp solrreplication_slave_1 ./entrypoint.sh slave Up 0.0.0.0:32820->8983/tcp solrreplication_slave_2 ./entrypoint.sh slave Up 0.0.0.0:32821->8983/tcp solrreplication_slave_3 ./entrypoint.sh slave Up 0.0.0.0:32822->8983/tcp
追加したスレーブからも、同じようにデータが検索できます。
$ curl 'http://localhost:32821/solr/mycore/select?wt=json&indent=true' -d '{ "query": "*:*" }' { "responseHeader":{ "status":0, "QTime":34, "params":{ "indent":"true", "json":"{ \"query\": \"*:*\" }", "wt":"json"}}, "response":{"numFound":2,"start":0,"docs":[ { "id":"1", "name":"[改訂新版] Apache Solr入門 ~オープンソース全文検索エンジン", "_version_":1513016793776521216}, { "id":"2", "name":"Apache Lucene 入門 〜Java・オープンソース・全文検索システムの構築", "_version_":1513016793951633408}] }}
ここでさらに、マスターにデータ登録。
data2.json
[ { "id": 3, "name": "高速スケーラブル検索エンジン ElasticSearch Server" }, { "id": 4, "name": "Beginning Java EE 6 GlassFish 3で始めるエンタープライズJava" }, { "id": 5, "name": "Javaエンジニア養成読本" } ]
登録。
$ curl -H 'Content-Type: application/json' 'http://localhost:8983/solr/mycore/update?commit=true' --data -binary @data2.json
追加した3台目になるスレーブに対して、検索を実行。
$ curl 'http://localhost:32822/solr/mycore/select?wt=json&indent=true' -d '{ "query": "*:*" }' { "responseHeader":{ "status":0, "QTime":25, "params":{ "indent":"true", "json":"{ \"query\": \"*:*\" }", "wt":"json"}}, "response":{"numFound":5,"start":0,"docs":[ { "id":"1", "name":"[改訂新版] Apache Solr入門 ~オープンソース全文検索エンジン", "_version_":1513016793776521216}, { "id":"2", "name":"Apache Lucene 入門 〜Java・オープンソース・全文検索システムの構築", "_version_":1513016793951633408}, { "id":"3", "name":"高速スケーラブル検索エンジン ElasticSearch Server", "_version_":1513017253929418752}, { "id":"4", "name":"Beginning Java EE 6 GlassFish 3で始めるエンタープライズJava", "_version_":1513017253932564480}, { "id":"5", "name":"Javaエンジニア養成読本", "_version_":1513017253937807360}] }}
OKそうですね。
最後に、スレーブは減らしておきます…。
$ docker-compose scale slave=1 Stopping solrreplication_slave_2... done Stopping solrreplication_slave_3... done Removing solrreplication_slave_3... done Removing solrreplication_slave_2... done $ docker-compose ps Name Command State Ports ---------------------------------------------------------------------------------- solr-master ./entrypoint.sh master Up 0.0.0.0:8983->8983/tcp solrreplication_slave_1 ./entrypoint.sh slave Up 0.0.0.0:32820->8983/tcp