カテゴリー: テクノロジー

SolrCloudをDockerで動かす

前回に引き続き、SolrをDockerで動かしてみます。今回はSolrとZooKeeperを別コンテナに分けてSolrCloudを構成します。

docker-compose.yml作成

まず、以下の内容の docker-compose.yml を作成します。

ZooKeeperの公式コンテナイメージの情報はこちらです。

正しく動作させるためのポイントがいくつかあります。

Solrの起動オプションを使ってコンテナのZooKeeperを指定

そのまま起動するとSolrは同梱のZooKeeperを使ってしまうので、docker-compose でコマンドラインオプションを指定してコンテナのZooKeeperを参照するように指定しています。

ZooKeeperの4lw.commands.whitlistの指定

“ZOO_4LW_COMMANDS_WHITELIST: mntr,conf,ruok”を指定します。これをしないと、管理GUIの”Cloud” → “ZK Status”の画面で以下のエラーになりZooKeeperの状態を確認できません。

Could not execute ruok towards ZK host zoo1:2181. Add this line to the 'zoo.cfg' configuration file on each zookeeper node: '4lw.commands.whitelist=mntr,conf,ruok'. See also chapter 'Setting Up an External ZooKeeper Ensemble' in the Solr Reference Guide.

ZooKeeperのバージョンを3.5.7で指定する

ZooKeeperの最新イメージは3.6系になっていますが、これを使うと、先程の”Cloud”→”ZK Status”の画面で4lw.commands.whitelistの指定をしていても以下のエラーになります。

For input string: "null"

いろいろ調べてみると Solr 8.5 と ZooKeeper 3.6 の組み合わせで発生するという情報が見つかりました。この問題を回避するために ZooKeeper のバージョン 3.5.7 を指定しています。

起動

$ docker-compose up -d

http://localhost:8983/solr/ で管理GUIにアクセスできます。

起動後

いつもの動作確認をしてみます。

設定ファイルアップロード

$ (cd configsets/_default/conf && zip -r - *) | curl -X POST --header "Content-Type:application/octet-stream" --data-binary @- "http://localhost:8983/solr/admin/configs?action=UPLOAD&name=test"

コレクション作成

$ curl -s "http://localhost:8983/solr/admin/collections?action=CREATE&name=test&maxShardsPerNode=2&numShards=2&replicationFactor=3&collection.configName=test"

ドキュメント投入

$ bin/post -c test example/exampledocs/manufacturers.xml

検索

$ curl -s "http://localhost:8983/solr/test/select?q=*%3A*&rows=1"
{
  "responseHeader":{
    "zkConnected":true,
    "status":0,
    "QTime":64,
    "params":{
      "q":"*:*",
      "rows":"1"}},
  "response":{"numFound":11,"start":0,"maxScore":1.0,"docs":[
      {
        "id":"apple",
        "compName_s":"Apple",
        "address_s":"1 Infinite Way, Cupertino CA",
        "_version_":1670655179588894720}]
  }}

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なりで管理することができます。

Raspberry Piでかんたん監視カメラ

新しい生活様式が今年の流行語大賞受賞となることを願っています。
マエダです。

 

前の記事でRaspberry Pi 4を購入して遊んでみたと紹介させていただきましたが、眠っていたRaspberry Pi2をなんか使えないかなとかんたん監視カメラを作ってみました。

材料

・OSインストールし直した新鮮でまっさらなラズパイ2
・ウェブカメラ
・ラズパイを雨から守ってくれそうなケース
・USB延長ケーブル
・融着テープ

作り方

① Motion をインストール

sudo apt-get install motion

② Motionを設定

$ sudo vi /etc/motion/motion.conf

daemon on
width 640
height 480
output_pictures off
ffmpeg_output_movies off
webcontrol_localhost off
stream_localhost off
$ sudo vi /etc/default/motion

start_motion_daemon=yes

そっと起動
/etc/init.d/motion start

③ 外に置いても大丈夫風にする

・セリアで購入したおしゃれプラケースに入れる

・セリアで購入したUSB延長ケーブルをダイソーで購入したアルミテープと融着テープで接続部分の浸水を防止する

・設置してみる

あとは同じネットワークに接続されたスマホ等からラズパイ2にブラウザからアクセス。
http://192.168.11.xxx:8081/

まとめ

使ってないラズパイも使い方次第ではまだまだ活かせます。
そしてセリアさんダイソーさんをはじめ100円ショップは偉大であることを再認識しました。

そして監視カメラは普段から監視することはあまりないことも認識しました。。

プログラミング言語とは?

こんにちは。開発担当のマットです。

以前も言ったことがありますが、開発はとても楽しい仕事です。
何らかのアイディアを分解してパソコンに文字を書けば、面白いものを作ることができます。

では、その文字とは何でしょう?

その前に、機械語を・・・

パソコンの中心にはCPU(中央処理装置)が存在します。
このCPUはとても忠実で、ユーザやアプリケーションからの命令を受けてその命令に従います。

つまり、何かのアプリケーションを作りたい場合、プログラマーはCPUへの命令をCPUが理解できる言語で書く必要があります。

いや、ちょっとまって・・・

CPUが理解できる言語のことを「機械語」と言います。
機械語はバイナリです。(つまり、0と1の列)

01100011 01101111 01100100 01100101


CPUの構成バージョンによって、コードの書き方は異なります。
人間が機械語を直接書くことはどう考えても不可能です。

CPUにわかってもらうことってなかなか大変です。

では、どうしたらいい?

人間は0と1を直書きすることはできませんが、英語のような文章は書くことができます。

例えば、

answer = 1+1;

とか、

name = "山田太郎";

なら、人間は理解できます。

ならば、こういう文章を、機械語(010010…)に翻訳できるものがあればいいのです!
そしてその翻訳できるものは存在します。コードの解釈・翻訳機をしてくれるものを「コンパイラ」と言います。

コンパイラの種類は多くてコンパイラによって、翻訳対象となる「人間が書くコードの書き方」が異なり、それぞれの書き方を「言語」と言います。

言語の特徴

それぞれの言語には特徴があり、使われる場面は異なります。
例えば、iOSアプリの場合、SwiftやObjective-Cを使わなければなりません。
3Dゲームの場合、C#やJavaが人気です。
ウェブサイトならPHPやRubyを使うことが多いです。

いくつかの言語と使われる場面と特徴をピックアップして紹介したいと思います。なお、各言語で、「Hello World!」というメッセージを画面に出力する例も挙げたいと思います。

JavaScript

JavaScriptはちょっと特別な言語です。
一番使われる場面はウェブサイトのブラウザーです。
ChromeやFirefoxはこのJavascriptを解釈して、ブラウザで実行します。

つまり、メモ帳でコードを書いて、ブラウザで開くだけでプログラムを書くことができるのでとても簡単です。
その反面、ブラウザによって、動作が異なるデメリットもあり、ブラウザに解釈される前のコードは丸見えので、JavaScriptで面白いものを作っても、パクられちゃうことも考えられます。

console.log("Hello World!");

実は、ブラウザ側だけではなく、2009年にnode.jsというフレームワークも開発され、ウェブサイトのサーバー側でも使えるようになりました。

Python

Pythonは多くの開発者に愛されている言語です。
理由としては、書き方が簡単で読みやすいのが特徴だからです。しかも、多くの場面で使えます。

ここ数年ではディープラーニングのアプリケーションにもよく使われており、これからもずっと愛されるでしょう。

print("Hello World!")

C

Cはとても代表的な言語です。

1973年に発明されて、現在使われているほとんどの言語に大きな影響を及ぼしました。
あまりにも影響を及ぼしたため、C++、C#、Perl、PHP、Swift、Javaなど、50種類以上の言語が「C系言語」と呼ばれています。

現在、直接C言語で作られているプログラムは少なくなってきていますが、Cに極めて近いC++(シー・プラス・プラス)やC#(シー・シャープ)はまだ多くの開発者が利用しているかと思います。

#include 
int main()
{
   printf("Hello World!");
   return 0;
}

かなり古い言語のため、「Hello World」を出力させるだけで、入力・出力を対応するstdio.hライブラリーを明示的にロードしなければなりません。現代の言語のほとんどは、こういうことを勝手にやってくれます。

Java

Javaは良い言語だと思いますが、一番の特徴は、別にダウンロードできる Java Run Environment というものです。 コンパイラーは人間のコードを機械語に翻訳するものですが、Windowsにしかコンパイルできないコンパイラーを使っていると、折角書いたプログラムをMacやLinuxで実行できません。

しかし、Javaでコンパイルしたコードは、直接OSではなく、上記のJava Run Environmentに実行されます。そして、そのJava Run EnvironmentのWindows版、Mac版、Linux版も存在しますので、Javaで一度プログラムを書くだけで、多くの環境で利用できることが一番の特徴だと思います。 実は、AndroidアプリもJavaで書くことができます。

class HelloWorld {
  static public void main(String args[]) {
    System.out.println("Hello World!");
  }
}

ちなみに、JavaとJavaScriptの名前は似ていますが、一切関係がありません。

PHP

世界のウェブサイトの約8割はPHPで書かれています。

PHPは初心者にも極めて優しい言語で、ウェブ開発者の慣れのHTMLの中に混じったりすることもできますし、他の言語だったらエラーになるという間違えたコードを書いても、PHPは動き続けます。

PHPはもともと、Rasmus Lerdorfn氏が自分のホームページに作ったものです(PHP = Personal Home Pageの略)。こんなに普及されるものと思われていなかったので、他の言語に比べて、PHPのファンクション(機能)名にルールがなく、ちょっと雑な言語と思われがちです。

しかしとても便利なため、これからもずっと多くの開発者に利用されるでしょう。

echo "Hello World!";

SQL

SQLはデータベースの言語です。

多くのウェブサイトでは、自分の名前やプロフィールを入力できる機能があります。
その情報をデータベースに保存することは一般の手段です。

データベースの構成はExcelのスプレッドシートのようなものです。
カラム名があり、データを一行一行保存していくイメージです。
そのデータを挿入、削除、または検索するのはSQLの役割です。

「Hello World!」を出力することができませんが、「山田太郎」さんを検索できます。

SELECT * FROM users WHERE name = '山田太郎';

Ruby

Rubyは日本で発明された言語です。用途が幅広くて、Rubyで色々開発できます。

当初は日本でしかあまり使われていなかったですが、2004年発のRuby on Rails によって、ウェブサイト開発にも便利な言語になり、海外でも使われる様になりました。

これからもRubyは多くの開発者に愛されるでしょう。

puts "Hello World!"

Swift

iOSのアプリを開発したい場合、Objective-CまたSwiftで作らなければなりません。
Objective-Cは割と古くて少しわかりにくいですが、Swiftは新しくてわかりやすいと思います。

Swiftの一番の特徴はオプショナルの対応だと思います。
通常、データを操ろうとする時、そのデータが存在しないとエラーになってしまいます。他の言語では、わざわざ「もし存在する」という文を書かなければなりませんが、Swiftでは「?」の一文字だけでできますので、開発者にとってはとても楽です。

最近、Android開発に使えるようになったKotlin言語にもある特徴ですが、今後発明される言語に是非入って欲しい特徴です。

println("Hello World!")

まとめ

プログラミング言語は道具のようなものです。
仕事に対して、適切な道具を選ぶことが大事です。
そのため、どの開発者でも複数の言語を覚えざるを得ない場面がでてくるとは思います。

しかし、特徴がありながら、現代のプログラミング言語は共通点が多くて、一つ覚えれば、他の言語にもチャレンジできるかと思います。

今後、私はまだ触ったことがなく、聞いたこともない、言語で何かを作ることが出てくるでしょう。
それは私にとって、とても楽しみなことです。

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を上手に使えば、稀に発生するクラスタ異常にもうまく対処することができそうです。