CLOVER🍀

That was when it all began.

Ubuntu Linux 14.04 LTSで始めるDocker Compose

今までDockerを使う時はdockerコマンド単体で扱っていたのですが、最近少し複数コンテナを扱いそうな機会が増えてきまして。これを機に、Docker Composeを試してみようかと思います。

Overview of Docker Compose

Docker Composeを使うと、複数のコンテナをまとめて管理できるのだとか。

早速使ってみます。

参考)
docker-composeを使うと複数コンテナの管理が便利に - Qiita
Docker Compose チュートリアル - ようへいの日々精進XP

インストール

ドキュメントに沿って、まずはインストール。Docker自体は、あらかじめインストールが済んでいるものとします。

Install Docker Compose

なお、当方の環境はUbuntu Linux 14.04 LTSです。

で、ドキュメントのインストールコマンドだと

curl -L https://github.com/docker/compose/releases/download/VERSION_NUM/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose

と書いているのですが、「VERSION_NUM」のところは自分で置き換えてあげる必要があります。

どのバージョンを使うかは、こちらを見て選ぶことになるのでは。

https://github.com/docker/compose/releases

今回は、1.4.1を使用します。

また、このコマンドは

/usr/local/bin/docker-compose

というファイルに書き込めと言っているのですが、rootで実行しているわけでもないのでsudo越しになります。

というわけで、結果このようなコマンドになりました。

$ sudo sh -c 'curl -L https://github.com/docker/compose/releases/download/1.4.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose'

権限変更。

$ sudo chmod +x /usr/local/bin/docker-compose

オプションな手順ではありますが、コマンド補完ができるようにbashの設定も追加しておきます。こちらも案内されている手順は

curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose --version | awk 'NR==1{print $NF}')/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose

ですが、やはりこのままだとroot権限がないので、以下のようにsudo越しになりました。

$ sudo sh -c "curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose --version | awk 'NR==1{print $NF}')/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose"

バージョン確認。

$ docker-compose --version
docker-compose version: 1.4.1

以上で、インストールはおしまいです。

アップグレードする時は、削除して再度インストールするみたいです。

Upgrading

Uninstallation

動かしてみる

では、Docker Composeを動かしてみます。Docker Composeを使うには、「docker-compose.yml」というYAMLファイルを作成することになるみたいです。

適当なディレクトリを作成して、そちらに移動。

$ mkdir docker-compose-getting-started
$ cd docker-compose-getting-started

ドキュメントに載っているDjangoRailsWordpressにチャレンジするのもちょっと微妙な気がしたので、ここは自分で書いてみました。

コンテナは、MySQL

https://hub.docker.com/r/library/mysql/

それから簡単なWebアプリケーションとしてGroovyでスクリプトを書いて試すことにします。

https://hub.docker.com/r/webratio/groovy/

今回は、自分でDockerイメージは作りません。docker-compose.ymlを書いて、その中に「build:」があれば以下のコマンドでDockerイメージのビルドが

$ docker-compose build

またDockerイメージのpullは以下のコマンドで行うそうなのですが、今回は使いません。

$ docker-compose pull

で、用意したdocker-compose.ymlはこちら。
docker-compose.yml

web:
  image: webratio/groovy:2.4.4
  ports:
    - "18080:8080"
  volumes:
    - ./web/script:/source
    - ./web/graperoot:/graperoot
  links:
    - mysql
  command: "rest-server.groovy"
mysql:
  image: mysql:5.6.26
  ports:
    - "3306"
  environment:
    MYSQL_ROOT_PASSWORD: my-secret-pw

webと付けた方のコンテナは、後述しますがgroovyコマンドで実行するスクリプト(ここでは「rest-server.groovy」)でサーバーが立ち、ポート8080でリッスンします。これをホスト側の18080ポートにあてています。また、いくつかVOLUMEを設定し(webratio/groovyイメージ参照)、mysqlとリンク設定を入れています。

最後のcommandは、DockerfileのENTRYPOINTの引数となります。

mysqlは、ポート3306を定義していますが、ホスト側の定義を書いていないので、ホスト側の適当なポートにマッピングされるようになります。パスワードは、環境変数で設定。

で、用意したGroovyスクリプトはこちら。
web/script/rest-server.groovy

@Grab('mysql:mysql-connector-java:5.1.36')
@GrabConfig(systemClassLoader = true)
import groovy.sql.Sql

@Grab('org.jboss.resteasy:resteasy-jackson2-provider:3.0.13.Final')
@Grab('org.jboss.resteasy:resteasy-jdk-http:3.0.13.Final')
import javax.ws.rs.*
import javax.ws.rs.core.*

import org.jboss.resteasy.plugins.server.sun.http.HttpContextBuilder

import com.sun.net.httpserver.HttpServer

class Entry {
  int id
  String name
}

@Path("mysql")
class MySqlResource {
  @GET
  @Path("select")
  @Produces(MediaType.APPLICATION_JSON)
  def select() {
    def sql =
      Sql.newInstance('jdbc:mysql://mysql:3306/test', 'root', 'my-secret-pw', 'com.mysql.jdbc.Driver')

    def result = sql.rows("SELECT * FROM entry ORDER BY id").collect { row ->
      new Entry([id: row.id, name: row.name])
    }

    sql.close()

    result
  }

  @PUT
  @Path("insert")
  @Consumes(MediaType.APPLICATION_JSON)
  @Produces(MediaType.APPLICATION_JSON)
  def insert(Entry entry) {
    def sql =
      Sql.newInstance('jdbc:mysql://mysql:3306/test', 'root', 'my-secret-pw', 'com.mysql.jdbc.Driver')

    sql.execute("INSERT INTO entry(id, name) VALUES(${entry.id}, ${entry.name})")

    sql.close()
  }
}

def server = HttpServer.create(new InetSocketAddress(8080), 10)
def contextBuilder = new HttpContextBuilder()

[MySqlResource].each {
    contextBuilder.deployment.actualResourceClasses.add(it)
}

def context = contextBuilder.bind(server)
server.start()

println("[${new Date()}] RestEasyJdkHttpd startup[${server.address}].")

作りはかなり適当ですが、ご愛嬌…。

JDBCの接続URLでのホスト名は、Dockerでリンクを貼った物にしています。

      Sql.newInstance('jdbc:mysql://mysql:3306/test', 'root', 'my-secret-pw', 'com.mysql.jdbc.Driver')

このスクリプトを、先ほどVOLUMEで定義した「./web/script」配下に置いています。

では、docker-compose.ymlファイルをカレントディレクトリに置いた状態で、「up」。

$ docker-compose up
dockercomposegettingstarted_mysql_1 is up-to-date
dockercomposegettingstarted_web_1 is up-to-date
Attaching to

起動したまま、待ち状態になります。「docker run -d」のようにするには、同じく「-d」オプションを付与します。

いったんCtrl-Cで終了して、コンテナ削除。

$ docker-compose rm

「-d」付きで起動。

$ docker-compose up -d
Creating dockercomposegettingstarted_mysql_1...
Creating dockercomposegettingstarted_web_1...

今度は、端末に制御が戻ってきます。

「docker-compose ps」で、コンテナの一覧が見れます。

$ docker-compose ps
               Name                              Command               State            Ports          
------------------------------------------------------------------------------------------------------
dockercomposegettingstarted_mysql_1   /entrypoint.sh mysqld            Up      0.0.0.0:32795->3306/tcp 
dockercomposegettingstarted_web_1     groovy -Dgrape.root=/grape ...   Up      0.0.0.0:18080->8080/tcp

ログを確認するには、「docker-compose logs」。

$ docker-compose logs

停止。

$ docker-compose stop
Stopping dockercomposegettingstarted_web_1... done
Stopping dockercomposegettingstarted_mysql_1... done

先ほど1度出しましたが、「docker-compose rm」でコンテナ削除。

$ docker-compose rm
Going to remove dockercomposegettingstarted_web_1, dockercomposegettingstarted_mysql_1
Are you sure? [yN] y
Removing dockercomposegettingstarted_web_1... done
Removing dockercomposegettingstarted_mysql_1... done

また、各コマンドは後ろにコンテナ名の指定ができるので、以下のように特定のコンテナのみを起動することも可能です。

$ docker-compose up -d mysql
Creating dockercomposegettingstarted_mysql_1...

複数指定も可。

コンテナ単位に起動していなくても、ログを個別に見れます。

$ docker-compose logs mysql

おお、便利だ!!

動作確認

で、コンテナ操作をしただけで、上に乗っているものを何も動かしていないので、ちょっと動かしてみます。

まずは起動。

$ docker-compose up -d
Starting dockercomposegettingstarted_mysql_1...
Creating dockercomposegettingstarted_web_1...

MySQLにテーブルなどが何もないので、コンテナに入って作成。

$ docker exec -it dockercomposegettingstarted_mysql_1 /bin/bash

データベース、テーブル作成。

# mysql -uroot -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.6.26 MySQL Community Server (GPL)

Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> CREATE DATABASE test;
Query OK, 1 row affected (0.05 sec)

mysql> USE test;
Database changed
mysql> CREATE TABLE entry(id INT, name VARCHAR(255), PRIMARY KEY(id));
Query OK, 0 rows affected (0.06 sec)

ホスト側から確認してみます。

$ curl http://localhost:18080/mysql/select
{}

とりあえず、エラーにはなりません、と。

データ登録。

$ curl -XPUT -H 'Content-Type: application/json' http://localhost:18080/mysql/insert -d '{ "id": 1, "name": "Taro" }'
$ curl -XPUT -H 'Content-Type: application/json' http://localhost:18080/mysql/insert -d '{ "id": 2, "name": "Jiro" }'

確認。

$ curl http://localhost:18080/mysql/select
[{"id":1,"name":"Taro"},{"id":2,"name":"Jiro"}]

OKそうです。

インスタンス数を増やしてみる

Docker Composeでは、scaleコマンドで実行中のインスタンスを増やすことができるようです。

scale

webを3つにしています。

$ docker-compose scale web=3
The "web" service specifies a port on the host. If multiple containers for this service are created on a single host, the port will clash.
Creating and starting 2... error
Creating and starting 3... error

ERROR: for 2  Cannot start container bb19e6f7acab86eaad22eafe51c3ba39e25f1d525c796f276a07a1053ee98afa: Bind for 0.0.0.0:18080 failed: port is already allocated 
ERROR: for 3  Cannot start container 6bab879c70bcb455e5d48329d5d3ba00141b9df456b7358a1a584cbf05d35d80: Bind for 0.0.0.0:18080 failed: port is already allocated 
Removing dockercomposegettingstarted_web_2... done
Removing dockercomposegettingstarted_web_3... done

まあ、ホスト側に18080でバインドしようとするので、失敗しますよね…。

いったん停止。

$ docker-compose stop
Stopping dockercomposegettingstarted_web_1... done
Stopping dockercomposegettingstarted_mysql_1... done

仕方がないので、web側のポートをいったんホスト側で固定するのをやめます。

web:
  image: webratio/groovy:2.4.4
  ports:
    - "8080"

再度起動。

$ docker-compose up -d
Starting dockercomposegettingstarted_mysql_1...
Recreating dockercomposegettingstarted_web_1...

web側も適当なポートに割り当てられました。

$ docker-compose ps
               Name                              Command               State            Ports          
------------------------------------------------------------------------------------------------------
dockercomposegettingstarted_mysql_1   /entrypoint.sh mysqld            Up      0.0.0.0:32800->3306/tcp 
dockercomposegettingstarted_web_1     groovy -Dgrape.root=/grape ...   Up      0.0.0.0:32801->8080/tcp

webを3つにしてみます。

$ docker-compose scale web=3
Creating and starting 2... done
Creating and starting 3... done

確認。

## ポート確認
$ docker-compose ps
               Name                              Command               State            Ports          
------------------------------------------------------------------------------------------------------
dockercomposegettingstarted_mysql_1   /entrypoint.sh mysqld            Up      0.0.0.0:32800->3306/tcp 
dockercomposegettingstarted_web_1     groovy -Dgrape.root=/grape ...   Up      0.0.0.0:32801->8080/tcp 
dockercomposegettingstarted_web_2     groovy -Dgrape.root=/grape ...   Up      0.0.0.0:32802->8080/tcp 
dockercomposegettingstarted_web_3     groovy -Dgrape.root=/grape ...   Up      0.0.0.0:32803->8080/tcp

## web#1
$ curl http://localhost:32801/mysql/select
[{"id":1,"name":"Taro"},{"id":2,"name":"Jiro"}]

## web#2
$ curl http://localhost:32802/mysql/select
[{"id":1,"name":"Taro"},{"id":2,"name":"Jiro"}]

## web#3
$ curl http://localhost:32803/mysql/select
[{"id":1,"name":"Taro"},{"id":2,"name":"Jiro"}]

できてそうです。

なるほど、確かにこれは便利そうですね。覚えておきましょう。

Apache Solr 5.xのレプリケーションを、Docker Composeを使って試す

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)

第5回 Solr4でレプリケーションを構築する

LINE Engineering Blog

Solrをイジった備忘録その3(レプリケーション編)

このあたりを参考に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  922 17:16 Dockerfile
-rw-rw-r-- 1 xxxxx xxxxx   520  922 17:32 entrypoint.sh
-rw-rw-r-- 1 xxxxx xxxxx 10784  922 20:44 schema.xml
-rw-rw-r-- 1 xxxxx xxxxx 62463  922 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

まとめ

というわけで、Apache Solrでレプリケーションの設定をして、Docker Composeでレプリケーションを構成するのと、スレーブの増減をやってみました。

いろいろ確認とかしやすくなるので、使い捨てられる割り切った構成が取れるの、便利ですね。

※設定ファイルの反映は、確認しましたがこのエントリ上は端折りました…