タグ: Solr

SolrをDockerで動かす

SolrをDockerで稼働させるにあたっては、公式に提供されているコンテナイメージを利用することができます。ただし、このページの説明は非常に簡素なので、利用法については GitHub の README を参照した方が分かりやすいと思います。

起動

docker run -d -p 8983:8983 --name my_solr solr solr-precreate gettingstarted

サンプルデータの投入

docker exec -it my_solr post -c gettingstarted example/exampledocs/manufacturers.xml

データの確認

$ curl 'http://localhost:8983/solr/gettingstarted/select?q=*%3A*&rows=1'
{
  "responseHeader":{
    "status":0,
    "QTime":1,
    "params":{
      "q":"*:*",
      "rows":"1"}},
  "response":{"numFound":11,"start":0,"docs":[
      {
        "id":"adata",
        "compName_s":"A-Data Technology",
        "address_s":"46221 Landing Parkway Fremont, CA 94538",
        "_version_":1670098040008998912}]
  }}

コンテナの中はどうなっているか

コンテナに入って中を確認してみます。

$ sudo docker exec -it my_solr /bin/bash
solr@ef3c76ff156a:/opt/solr-8.5.2$ pwd
/opt/solr-8.5.2
solr@ef3c76ff156a:/opt/solr-8.5.2$ ls
CHANGES.txt  LICENSE.txt  LUCENE_CHANGES.txt  NOTICE.txt  README.txt  bin  contrib  dist  example  licenses  server

Solr は /opt/solr-8.5.2 にインストールされていて、solr ユーザのホームディレクトリになっていることが分かります。

solr@ef3c76ff156a:/opt/solr-8.5.2$ cat /etc/os-release 
PRETTY_NAME="Debian GNU/Linux 10 (buster)"
NAME="Debian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
solr@ef3c76ff156a:/opt/solr-8.5.2$ dpkg --list|grep solr

OS は Debian buster です。Solr パッケージを使っている訳ではないです。

solr@ef3c76ff156a:/opt/solr-8.5.2$ find /var/solr -type d
/var/solr
/var/solr/data
/var/solr/data/gettingstarted
/var/solr/data/gettingstarted/data
/var/solr/data/gettingstarted/data/snapshot_metadata
/var/solr/data/gettingstarted/data/tlog
/var/solr/data/gettingstarted/data/index
/var/solr/data/gettingstarted/conf
/var/solr/data/gettingstarted/conf/lang
/var/solr/data/userfiles
/var/solr/data/filestore
/var/solr/logs

データは /var/solr 以下に格納されます。

solr@ef3c76ff156a:/opt/solr-8.5.2$ ls /opt/docker-solr/scripts/
docker-entrypoint.sh  init-var-solr  oom_solr.sh  precreate-core  run-initdb  solr-create  solr-demo  solr-fg  solr-foreground	solr-precreate	start-local-solr  stop-local-solr  wait-for-solr.sh  wait-for-zookeeper.sh

コンテナ外からの操作で使われるスクリプトが /opt/docker-solr/scripts 以下に置かれています。起動時に使った solr-precreate はここに置かれているものです。

solr-precreate の中身は、/opt/solr/server/solr/configsets/_defaults の設定ファイルを指定されたコアのディレクトリ(上の例なら /var/solr/data/gettingstarted/conf)にコピーしてコアを作成してから Solr を起動するというものになっています。

SolrのデータをホストOS側に置く

データサイズが気になる場合や、インデックスの使い回しをしたい場合などには、SolrのデータをホストOS側に置くこともできます。

$ mkdir solrdata
$ sudo chown 8983:8983 solrdata/
$ sudo docker run -d -v "$PWD/solrdata:/var/solr" -p 8983:8983 --name my_solr2 solr solr-precreate gettingstarted

起動前に、ホスト側に作ったデータディレクトリの所有者をコンテナのsolrユーザ(8983:8983)に合わせておくことが必要です。

起動後

起動後はホストOS側から localhost:8983 でアクセスできるので、管理GUIなりウェブAPIなりで管理することができます。

SolrCloudのリーダー再選出の動作を確認する

はじめに

前回の記事ではシャードを構成する複数のレプリカの中からリーダーが選出される仕組みを解説しました。この記事では、実際に動いているSolrCloudを使って、特定のノードがダウンしたときにリーダーが切り替わる動作を確認してみます。

SolrCloudの構成

  • サーバ3台、サーバ毎に Solr 1プロセス
  • コレクション名 test のコレクションを作成
  • test コレクションは shard1 と shard2 の2つのシャードを含む
  • 各シャードはそれぞれ3つのレプリカ(それぞれ別のSolrノードで動く)を含む
コア名ノードシリアル番号備考
core_node3172.19.0.7:898512リーダー
core_node5172.19.0.6:898314
core_node7172.19.0.5:898413
shard1
コア名ノードシリアル番号備考
core_node9172.19.0.7:898512リーダー
core_node11172.19.0.6:898314
core_node12172.19.0.5:898413
shard2

リーダーがダウンしたとき

ノード 172.19.0.7:8985 (core_node3, core_node9)を落としてみます。

それまでのリーダー(シリアル番号12)の次に若い番号(13)を持つcore_node7およびcore_node12が新しいリーダーに選ばれています。

コア名ノードシリアル番号備考
core_node3172.19.0.7:898512ダウン
core_node5172.19.0.6:898314
core_node7172.19.0.5:898413リーダー
shard1
コア名ノードシリアル番号備考
core_node9172.19.0.7:898512ダウン
core_node11172.19.0.6:898314
core_node12172.19.0.5:898413リーダー
shard2

ダウンしたノードが復帰したとき

ダウンさせていた 172.19.0.7:8985 (core_node3, core_node9)を復帰させます。

リーダーに変更は無く、core_node3とcore_node9は新しい番号15をそれぞれ割り当てられます。

コア名ノードシリアル番号備考
core_node3172.19.0.7:898515復帰
core_node5172.19.0.6:898314
core_node7172.19.0.5:898413リーダー
shard1
コア名ノードシリアル番号備考
core_node9172.19.0.7:898515復帰
core_node11172.19.0.6:898314
core_node12172.19.0.5:898413リーダー
shard2

リーダーを意図的に変更する

まずcore_node3をpreferredLeaderに指定します。

curl 'http://172.19.0.6:8983/solr/admin/collections?action=ADDREPLICAPROP&shard=shard1&collection=test&replica=core_node3&property=preferredLeader&property.value=true'

REBALANCELEADERSを実行。

curl 'http://172.19.0.6:8983/solr/admin/collections?action=REBALANCELEADERS&collection=test'
{
  "responseHeader":{
    "status":0,
    "QTime":3053},
  "Summary":{
    "Success":"All active replicas with the preferredLeader property set are leaders"},
  "successes":{
    "shard1":{
      "status":"success",
      "msg":"Successfully changed leader of slice shard1 to core_node3"}}}

shard1のリーダーがcore_node3に変更されました。

FORCELEADER

FORCELEADERは障害等何らかの理由でリーダー不在の状態ができてしまった場合に強制的にリーダーを割り当てるためのコマンドです。リーダーが居ない状態を意図的に作るのは難しいので、正常な状態のシャードに対して FORCELEADER を実行するとどうなるか試してみました。

curl 'http://172.19.0.6:8983/solr/admin/collections?action=FORCELEADER&collection=test&shard=shard2'
{
  "responseHeader":{
    "status":500,
    "QTime":35},
  "error":{
    "metadata":[
      "error-class","org.apache.solr.common.SolrException",
      "root-error-class","org.apache.solr.common.SolrException"],
    "msg":"The shard already has an active leader. Force leader is not applicable. State: shard2:{
(略)
    "code":500}}

指定されたシャードにはリーダーが居るのでFORCELEADERの実行はできませんと怒られてしまいました。

おわりに

SolrCloudのクラスタはZooKeeperと連携していて、リーダーがダウンしたら自動的に新しいリーダーが選ばれてなるべくダウンタイムが小さくなるように工夫されている、という漠然とした理解から一歩進むために、具体的なリーダー選出のロジックを調べました。ZooKeeperの分散アプリケーションのコーディネート機能を使って案外シンプルなロジックで実装されていることが分かりました。この理解を持った上でリーダー調整用のAPIを上手に使えば、稀に発生するクラスタ異常にもうまく対処することができそうです。

SolrCloudのリーダー選出の仕組み

はじめに

SolrCloudではコレクションを複数のシャードに分け、各シャードを複数のレプリカによる冗長構成にできます。レプリカの中から1台リーダーが選出されてインデックス更新更新の責任を持ちます。
旧来のマスタースレーブの構成では、マスターがダウンしたときには復旧するまでは更新処理が停止してしまいますが、SolrCloudではリーダーがダウンした場合には自動的に別のレプリカがリーダーに選出されてなるべく更新処理が停止しないように工夫されています。
この記事では、リーダー選出の仕組みを解説します。

リーダー選出の仕組み

リファレンスでは以下のように説明されています。

SolrCloud にはマスターもスレーブもありません。その代わりに各シャードは最低1台の物理的レプリカから構成され、その中の1台がリーダーとなります。
リーダーは自動的に選出されます。最初は先着順で、以後は
http://zookeeper.apache.org/doc/r3.5.5/recipes.html#sc_leaderElection
に記述されている ZooKeeper を使った手順に基づきます。

リーダー選出の手続きは org.apache.solr.cloud.LeaderElector クラスで実装されています。このクラスのコメントでもリーダー選出の仕組みについて触れられています。

これらのドキュメントとコードを参考に、リーダー選出の仕組みをまとめました

  • 冗長構成においては各レプリカが同じ状態を持つことが重要だが、リーダーを選ぶためには何らかの「違い」を作り出す必要がある
  • 違いを作り出すために ZooKeeper の Ephemeral & Sequential ノードを使う
    • ZooKeeper のクライアント(Solrのノード)は ZooKeeper 上の木構造にノードを作れる
    • ZooKeeper の Ephemeral ノードは、そのノードを作ったクライアントがダウンしたときに自動的にそのノードが無くなるので、クライアントのダウンを検知できる
    • ノードに Sequential プロパティを付与すると、兄弟ノード間で作られた順に一意のシーケンシャルな番号が振られる
    • 一番若い番号を持つノードのクライアントをリーダーとする
  • リーダーがダウンしたとき
    • ZooKeeper のクライアントは ZooKeeper 上のノードを監視することができる
    • 各レプリカは自分より1個若い番号のノードを監視しておく
    • リーダーがダウンしたらそのノードを監視していたレプリカが新しいリーダーになる
  • 全体のコーディネートは SolrCloud の Overseer が担当する
    • Overseer は SolrCloud のクラスタを構成する各 Solr ノードから選ばれるリーダー
    • Overseer 選出の手続きはシャードを構成するレプリカからリーダーを選出する手続きとほぼ同じ

Solrのパッケージ管理機能を使って自作プラグインを設定する

はじめに

Solr 8.4 で追加されたパッケージ管理機能を使って自作のプラグインを動的に設定してみます。ただし、Solr 8.4 では自作プラグインのインストールの際にキー読み込み関係のエラーが発生したので、今回の動作確認は Solr 8.5 で行っています。

作ったプラグイン

その名の通り、何もしないリクエストハンドラです。”Hello World!” のメッセージとバージョン番号を応答するだけです。これをコンパイルして plugin-nop-1.0.jar というファイルを作成しました。

リポジトリ登録

キー作成

$ openssl genrsa -out my_key.pem 512
$ openssl rsa -in my_key.pem -pubout -outform DER -out publickey.der
$ openssl dgst -sha1 -sign my_key.pem ../build/plugin-nop-1.0.jar | openssl enc -base64 | tr -d \\n
KzDSHoBGofrRbN2iRMZEnQEGArN7evEOELDsEPuFsYKRw7eQ11veFUvo5y3r0msqBPpCs8Fga4eJMxjDJyOzIA==

repository.json

Solr のソースコードに含まれる solr/core/src/test-files/solr/question-answer-repository/repository.json を参考にして作成しました。

上で作成したメッセージダイジェストの文字列を artifacts の sig に記載します。

ウェブサーバへ配置

Jarファイル、publickey.der、repository.json をウェブサーバに配置します。publickey.der と repository.json はこのファイル名である必要があります。

$ ls $DOC_ROOT/solr/repo2
plugin-nop-1.0.jar  publickey.der  repository.json

リポジトリをSolrに登録

$ bin/solr package add-repo nop http://localhost/solr/repo2/
$ bin/solr package list-available
Available packages:
-----
nop 		No Operation plugin
	Version: 1.0.0

パッケージのインストールとデプロイ

インストール

$ bin/solr package install nop:1.0.0
Posting manifest...
Posting artifacts...
Executing Package API to register this package...
Response: {"responseHeader":{
    "status":0,
    "QTime":11}}
nop installed.

デプロイ

$ bin/solr package deploy nop:1.0.0 -collections test
Executing {"add-requesthandler":{"name":"/nop","class":"nop:jp.co.splout.solr.plugins.NopRequestHandler"}} for path:/api/collections/test/config
Execute this command (y/n): 
y
Executing http://localhost:8983/api/collections/test/config/requestHandler?componentName=/nop&meta=true for collection:test
{
  "responseHeader":{
    "status":0,
    "QTime":0},
  "config":{"requestHandler":{"/nop":{
        "name":"/nop",
        "class":"nop:jp.co.splout.solr.plugins.NopRequestHandler",
        "_packageinfo_":{
          "package":"nop",
          "version":"1.0.0",
          "files":["/package/nop/1.0.0/plugin-nop-1.0.jar"],
          "manifest":"/package/nop/1.0.0/manifest.json",
          "manifestSHA512":"794a594729c8772073338aa03bbe41866d68bf76527d2a9568779c00ca4802bf362841073d323d16ce9f44cd38757371b591c5afe96876fe65a4e7c6a3f1eb89"}}}}}

Actual: 1.0.0, expected: 1.0.0
Deployed on [test] and verified package: nop, version: 1.0.0
Deployment successful

動作確認

$ curl http://localhost:8983/solr/test/nop
{
  "responseHeader":{
    "status":0,
    "QTime":0},
  "message":"Hello, World!",
  "version":"1.0"}

パッケージのアップデート

NopRequestHandler.java のバージョン文字列を “1.1” に変更して plugin-nop-1.1.jar を作成しました。これをリポジトリに追加してデプロイします。

リポジトリへの追加

まず電子署名を作成します。

$ openssl dgst -sha1 -sign my_key.pem ../build/plugin-nop-1.1.jar | openssl enc -base64 | tr -d \\n
zlEAXf6hKNbmjAW48noLyMiilESOuPXmsA4xZ5L+zYwYOX3aIuW1SnmFa+VrRMSKwk76xG+PomTX2ELmHiOvtg==

plugin-nop-1.1.jar をウェブサーバに配置して repository.json に以下を追加します。

      {
        "version": "1.1.0",
        "date": "2020-04-21",
        "artifacts": [
          {
            "url": "plugin-nop-1.1.jar",
            "sig": "zlEAXf6hKNbmjAW48noLyMiilESOuPXmsA4xZ5L+zYwYOX3aIuW1SnmFa+VrRMSKwk76xG+PomTX2ELmHiOvtg=="
          }
        ],
        "manifest": {
          "version-constraint": "8 - 9",
          "plugins": [
            {
		"name": "request-handler",
              "setup-command": {
                "path": "/api/collections/${collection}/config",
                "payload": {"add-requesthandler": {"name": "${RH-HANDLER-PATH}", "class": "nop:jp.co.splout.solr.plugins.NopRequestHandler"}},
                "method": "POST"
              },
              "uninstall-command": {
                "path": "/api/collections/${collection}/config",
                "payload": {"delete-requesthandler": "${RH-HANDLER-PATH}"},
                "method": "POST"
              },
              "verify-command": {
                "path": "/api/collections/${collection}/config/requestHandler?componentName=${RH-HANDLER-PATH}&meta=true",
                "method": "GET",
                "condition": "$['config'].['requestHandler'].['${RH-HANDLER-PATH}'].['_packageinfo_'].['version']",
                "expected": "${package-version}"
              }
            }
          ],
          "parameter-defaults": {
            "RH-HANDLER-PATH": "/nop"
          }
        }
      }
$ bin/solr package list-available
Available packages:
-----
nop 		No Operation plugin
	Version: 1.0.0
	Version: 1.1.0

インストールとデプロイ

後の流れはほぼ同じです。

$ bin/solr package install nop:1.1.0
Posting manifest...
Posting artifacts...
Executing Package API to register this package...
Response: {"responseHeader":{
    "status":0,
    "QTime":7}}
nop installed.
$ bin/solr package deploy nop:1.1.0 -collections test --update
Executing http://localhost:8983/api/collections/test/config/requestHandler?componentName=/nop&meta=true for collection:test
{
  "responseHeader":{
    "status":0,
    "QTime":0},
  "config":{"requestHandler":{"/nop":{
        "name":"/nop",
        "class":"nop:jp.co.splout.solr.plugins.NopRequestHandler",
        "_packageinfo_":{
          "package":"nop",
          "version":"1.1.0",
          "files":["/package/nop/1.1.0/plugin-nop-1.1.jar"],
          "manifest":"/package/nop/1.1.0/manifest.json",
          "manifestSHA512":"794a594729c8772073338aa03bbe41866d68bf76527d2a9568779c00ca4802bf362841073d323d16ce9f44cd38757371b591c5afe96876fe65a4e7c6a3f1eb89"}}}}}

Actual: 1.1.0, expected: 1.1.0
Deployed on [test] and verified package: nop, version: 1.1.0
Deployment successful
$ curl http://localhost:8983/solr/test/nop
{
  "responseHeader":{
    "status":0,
    "QTime":0},
  "message":"Hello, World!",
  "version":"1.1"}

無事にバージョン1.1にアップデートできました。

Solrのパッケージ管理機能

はじめに

Solr 8.4 からパッケージ管理機能が追加されました。リファレンスによると、ここでいうパッケージは1つまたは複数のプラグインを1つにまとめたものという意味のようです。
Solr におけるパッケージ管理について調べました。

パッケージ管理機能

従来、プラグインを利用するためには
・jar ファイルを特定の場所に置く
・solrconfig.xml の lib ディレクティブで jar ファイルの場所を指定する
という作業が必要でした。

SolrCloud環境では、複数のノードでこの作業を行う必要があります。 jar ファイルのアップデートの際に異なるノードでのバージョン違いが発生するリスクもあります。

パッケージ管理機能により、複数ノードへのプラグインのインストールやアップデートを一括で操作できるようになります。

パッケージ管理を有効にする

デフォルトではパッケージ管理機能は無効になっているので、起動時のオプションを指定して有効にします。

bin/solr -c -Denable.packages=true

リポジトリを追加する

パブリックなSolrパッケージリポジトリというものは存在していないようなので、Solr のソースに含まれていた、以下のサンプルプラグインをローカルのウェブサーバでホストして試してみました。

$ ls solr-8.4.1-src/solr/core/src/test-files/solr/question-answer-repository/
publickey.der  question-answer-request-handler-1.1.jar.tmp
question-answer-request-handler-1.0.jar.tmp  repository.json
$ cp solr-8.4.1-src/solr/core/src/test-files/solr/question-answer-repository/* $DOC_ROOT/solr/repo/

ウェブサーバ上の特定のディレクトリがパッケージリポジトリであるとSolrに認識されるためには repository.json ファイルが必要であるようです。

$ cat solr-8.4.1-src/solr/core/src/test-files/solr/question-answer-repository/repository.json
[
  {
    "name": "question-answer",
    "description": "A natural language question answering plugin",
    "versions": [
      {
        "version": "1.0.0",
        "date": "2019-01-01",
        "artifacts": [
          {
            "url": "question-answer-request-handler-1.0.jar.tmp",
            "sig": "TTzgh5/usbyWg7oZ7lRwz4eQfh1FeXWvv4U85tsVthVz0MRDz9t7SmonDkegZ7OyqeoiQ4I207pifpVW+DRd9Q=="
          }
        ],
        "manifest": {
          "version-constraint": "8 - 9",
          "plugins": [
            {
              "name": "request-handler",
              "setup-command": {
                "path": "/api/collections/${collection}/config",
                "payload": {"add-requesthandler": {"name": "${RH-HANDLER-PATH}", "class": "question-answer:fullstory.QA\
RequestHandler"}},
                "method": "POST"
              },
              "uninstall-command": {
                "path": "/api/collections/${collection}/config",
                "payload": {"delete-requesthandler": "${RH-HANDLER-PATH}"},
                "method": "POST"
              },
              "verify-command": {
                "path": "/api/collections/${collection}/config/requestHandler?componentName=${RH-HANDLER-PATH}&meta=tru\
e",
                "method": "GET",
                "condition": "$['config'].['requestHandler'].['${RH-HANDLER-PATH}'].['_packageinfo_'].['version']",
                "expected": "${package-version}"
              }
            }
          ],
          "parameter-defaults": {
            "RH-HANDLER-PATH": "/mypath"
          }
        }
      },
      {
        "version": "1.1.0",
        "date": "2019-01-01",
        "artifacts": [
          {
            "url": "question-answer-request-handler-1.1.jar.tmp",
            "sig": "LypqlmbJ76AWa5jx0XhjKxO4lrcQAvpSuYddfzcE6TnX0VDPFhrlQHSSX6cZLtvNbQ+74xUMKgsoX1IUnEnKYw=="
          }
        ]
      }
    ]
  }
]

同じパッケージのバージョン違いがrepository.json で定義されています。

Solr稼働中に以下のコマンドでリポジトリを追加します。

$ bin/solr package add-repo question-answer http://localhost/solr/repo/

利用できるパッケージのリスト

インストール対象となるパッケージのリストを表示します。

$ bin/solr package list-available
Available packages:
-----
question-answer                 A natural language question answering plugin
        Version: 1.0.0
        Version: 1.1.0

インストール

$ bin/solr package install question-answer:1.0.0
Posting manifest...
Posting artifacts...
Executing Package API to register this package...
Response: {"responseHeader":{
    "status":0,
    "QTime":42}}
question-answer installed.

インストール済みパッケージの確認

$ bin/solr package list-installed
Installed packages:
-----
{
  "name":"question-answer",
  "version":"1.0.0"}

デプロイ

コレクションを指定してデプロイします。

$ bin/solr package deploy question-answer -collections test
Executing {"add-requesthandler":{"name":"/mypath","class":"question-answer:fullstory.QARequestHandler"}} for path:/api/\
collections/test/config
Execute this command (y/n):
y
Executing http://localhost:8983/api/collections/test/config/requestHandler?componentName=/mypath&meta=true for collecti\
on:test
{
  "responseHeader":{
    "status":0,
    "QTime":0},
  "config":{"requestHandler":{"/mypath":{
        "name":"/mypath",
        "class":"question-answer:fullstory.QARequestHandler",
        "_packageinfo_":{
          "package":"question-answer",
          "version":"1.0.0",
          "files":["/package/question-answer/1.0.0/question-answer-request-handler-1.0.jar.tmp"],
          "manifest":"/package/question-answer/1.0.0/manifest.json",
          "manifestSHA512":"a91ab5a2c5abd53f0f72c256592c2be8b667cecb8226ac054aeed4d28aac9d743311442f2d58539bb83663a19bd\
1efb310aaadfd77bea458f3d475161721a114"}}}}}

Actual: 1.0.0, expected: 1.0.0
Deployed on [test] and verified package: question-answer, version: 1.0.0
Deployment successful

repository.json の記述にしたがって /mypath で QARequestHandler が動くように Config API が呼ばれたことが分かります。
/mypath をリクエストしてみます。

$ curl http://localhost:8983/solr/test/mypath
{
  "responseHeader":{
    "status":0,
    "QTime":0},
  "responseHeader":[
    "version","1.0"]}

残念ながら QARequestHandler のソースを見つけることができず、具体的にどんなパラメータでどんな機能が使えるのかが不明ですが、デプロイできていることは確認できました。

パッケージのアップデート

$ bin/solr package install question-answer:1.1.0
Posting manifest...
Posting artifacts...
Executing Package API to register this package...
Response: {"responseHeader":{
    "status":0,
    "QTime":4}}
question-answer installed.
$ bin/solr package list-installed
Installed packages:
-----
{
  "name":"question-answer",
  "version":"1.0.0"}
{
  "name":"question-answer",
  "version":"1.1.0"}

同じパッケージの2つのバージョンがインストールされたことが分かります。

新しい方のバージョンをデプロイします。

$ bin/solr package deploy question-answer:1.1.0 -collections test
Package {
  "name":"question-answer",
  "version":"1.0.0"} already deployed on test. To update to {
  "name":"question-answer",
  "version":"1.1.0"}, pass --update parameter.
Already Deployed on [test], package: question-answer, version: 1.1.0
Deployment failed

失敗しました。
リファレンスを確認すると、上書きデプロイのときは –update オプションが必要とのことです。

$ bin/solr package deploy question-answer:1.1.0 -collections test --update
Executing http://localhost:8983/api/collections/test/config/requestHandler?componentName=/mypath&meta=true for collecti\
on:test
{
  "responseHeader":{
    "status":0,
    "QTime":1},
  "config":{"requestHandler":{"/mypath":{
        "name":"/mypath",
        "class":"question-answer:fullstory.QARequestHandler",
        "_packageinfo_":{
          "package":"question-answer",
          "version":"1.1.0",
          "files":["/package/question-answer/1.1.0/question-answer-request-handler-1.1.jar.tmp"],
          "manifest":"/package/question-answer/1.1.0/manifest.json",
          "manifestSHA512":"be22126df3fbb84284cdb94469fa0f83753961c066451f0b9107312d076f3f016e60fd279951656ee7ff4b17bc2\
57fbd2ce8950533c7d394b1080122f6461606"}}}}}

Actual: 1.1.0, expected: 1.1.0
Deployed on [test] and verified package: question-answer, version: 1.1.0
Deployment successful

今度は成功しました。
/mypath にリクエストを投げて、バージョン1.1にアップデートされたことを確認します。

$ curl http://localhost:8983/solr/test/mypath
{
  "responseHeader":{
    "status":0,
    "QTime":0},
  "responseHeader":[
    "version","1.1"]}

デプロイ中のパッケージをリストするコマンドもあります。

$ bin/solr package list-deployed -c test
Packages deployed on test:
{
  "name":"question-answer",
  "version":"1.1.0"}

おわりに

Solrのパッケージ管理機能の使い方を見てきました。次回はリポジトリを設定して自分で作ったパッケージを扱えるようにしてみます。