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

WSL上でSolrが動くかどうか試してみた

SolrはJavaで書かれているのでもちろんWindowsでも動くわけですが、ふとWSL上ではどうなのかが気になって試してみました。

まずは WSL 上でいつもの手順通りSolrを動かしてみます。ディストリビューションはUbuntu 18.04.2 LTSです。

Solrをダウンロードして展開

$ wget https://www-eu.apache.org/dist/lucene/solr/8.2.0/solr-8.2.0.tgz
tar zxf solr-8.2.0.tgz

起動

$ cd solr-8.2.0
$ bin/solr -e cloud

Welcome to the SolrCloud example!

This interactive session will help you launch a SolrCloud cluster on your local workstation.
To begin, how many Solr nodes would you like to run in your local cluster? (specify 1-4 nodes) [2]:
1
Ok, let's start up 1 Solr nodes for your example SolrCloud cluster.
Please enter the port for node1 [8983]:
8983
Creating Solr home directory solr-8.2.0/example/cloud/node1/solr

Starting up Solr on port 8983 using command:
"bin/solr" start -cloud -p 8983 -s "example/cloud/node1/solr"

*** [WARN] ***  Your Max Processes Limit is currently 7823.
 It should be set to 65000 to avoid operational disruption.
  If you no longer wish to see this warning, set SOLR_ULIMIT_CHECKS to false in your profile or solr.in.sh
  Waiting up to 180 seconds to see Solr running on port 8983 [|]  bin/solr: line 660:   177 Aborted                 (core dumped) nohup "$JAVA" "${SOLR_START_OPTS[@]}" $SOLR_ADDL_ARGS -Dsolr.log.muteconsole "-XX:OnOutOfMemoryError=$SOLR_TIP/bin/oom_solr.sh $SOLR_PORT $SOLR_LOGS_DIR" -jar start.jar\
 "${SOLR_JETTY_CONFIG[@]}" $SOLR_JETTY_ADDL_CONFIG > "$SOLR_LOGS_DIR/solr-$SOLR_PORT-console.log" 2>&1
   [\]  ^C
   ERROR: Failed to start Solr using command: "bin/solr" start -cloud -p 8983 -s "example/cloud/node1/solr" Exception : org.apache.commons.exec.ExecuteException: Process exited with an error: 130 (Exit value: 130)

エラー終了してしまいました。
実行ログを調べると、以下のようなJava VMレベルでのエラーが発生していました。

$ cat example/cloud/node1/logs/solr-8983-console.log
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  Internal Error (g1PageBasedVirtualSpace.cpp:49), pid=177, tid=185
#  guarantee(is_aligned(rs.base(), page_size)) failed: Reserved space base 0x00007f77bc420000 is not aligned to requested page size 2097152
#
# JRE version:  (11.0.4+11) (build )
# Java VM: OpenJDK 64-Bit Server VM (11.0.4+11-post-Ubuntu-1ubuntu218.04.3, mixed mode, aot, sharing, tiered, compressed oops, g1 gc, linux-amd64)
# Core dump will be written. Default location: core.177 (may not exist)
#
# An error report file with more information is saved as:
# solr-8.2.0/server/hs_err_pid177.log
#
# If you would like to submit a bug report, please visit:
#   https://bugs.launchpad.net/ubuntu/+source/openjdk-lts
#

WSLのバグレポートによるとJavaのプロセスで似たようなエラーが出ることはあるようで順次修正はされているものの、今回のエラーと同じものは見つかりませんでした。

Java VMの起動オプションを変更すれば起動するのではないかと考えて、まずはヒープサイズをいろいろ変えてみましたが駄目でした。
次にJMX関連のオプションを外してみたところ問題なく起動するようになりました。具体的には起動用のSolrスクリプトを以下のように変更しました。

--- solr.bak    2019-10-22 23:25:45.830541600 +0900
+++ solr        2019-10-27 19:05:03.758148800 +0900
@@ -1964,6 +1964,7 @@
 fi

 # These are useful for attaching remote profilers like VisualVM/JConsole
+ENABLE_REMOTE_JMX_OPTS=false
 if [ "$ENABLE_REMOTE_JMX_OPTS" == "true" ]; then

   if [ -z "$RMI_PORT" ]; then

開発中の動作確認ではWSL側でSolrが動かせた方が都合が良い場合もあるので、謎のエラーを回避する方法を見つけることができて良かったです。


SolrCloudにおけるインデックスのリストア

はじめに

前回の記事で Collection API を使ってインデックスのバックアップを取る方法を説明しました。この記事ではバックアップからのリストアの方法を説明します。

リストアの実行

リストアは以下のように実行します。

$ curl 'http://localhost:8983/solr/admin/collections?action=RESTORE&name=backup1&collection=backup_test2&location=/tmp/solr_backup'

基本パラメータ

パラメータ説明
actionリストアコマンドとして”RESTORE”を指定
nameバックアップの名前
collectionリストア先のコレクション名
locationバックアップ出力先ディレクトリ

name と location はバックアップのときと同じものを指定します。

設定上書き用のパラメータ

パラメータ説明
collection.configNameconfigsetを変更
replicationFactorシャードあたりのレプリカ数を変更
ntrReplicas,tlogReplicasそれぞれの種類のレプリカ数を変更
maxShardsPerNodeノードあたりの最大シャード数を変更
autoAddReplicasレプリカの自動追加を許可するかどうかを変更

バックアップ/リストアに備えた運用

リストアが実行されると、バックアップ元とは異なるコレクション名で新しいコレクションが作成されます。コレクション名が変更されると外部で参照している箇所にも影響が出てしまうので、Solr のリファレンスではエイリアスの利用を推奨しています。

外部から参照するコレクション名を mycollection とすると、実際のコレクション名を mycollection20190915 という風に付けてエイリアスを mycollection とします。バックアップ→リストアによって新しく mycollection20190920 が作られたら mycollection の実体をそちらに切り替える、という形で運用します。


SolrCloudにおけるインデックスのバックアップ

はじめに

SolrCloudにおいては、Collection API の BACKUP コマンドを使ってインデックスのバックアップを取ることができます。複数のノード、シャード、レプリカが存在する場合に具体的にどのような形でバックアップが残るのかを調べてみました。

対象のコレクション

実験用に backup_test というコレクションを作成しました。シャード2個がそれぞれレプリカを2個持つ構成です。

コレクションシャードレプリカ
backup_testshard1localhost:8983
backup_testshard1localhost:7574
backup_testshard2localhost:8983
backup_testshard2localhost:7574

ここに、以前実験に利用した大阪の施設情報を投入しました。

バックアップの実行

バックアップは以下のように実行します。

$ curl 'http://localhost:8983/solr/admin/collections?action=BACKUP&name=backup1&collection=backup_test&location=/tmp/solr_backup'
パラメータ説明
actionバックアップコマンドとして”BACKUP”を指定
nameバックアップの名前。以前指定したものとは衝突しないようにする
collectionバックアップ対象のコレクション
locationバックアップ出力先ディレクトリ

一つ注意が必要なのは、クラスタが複数のサーバから構成される場合には、バックアップの出力先は共有ドライブでなければならないということです。

バックアップの内容

出力先を調べると、以下の内容がバックアップに含まれていることが分かります。

  • backup.properties
  • コレクションの設定情報
  • シャード毎のインデックスファイル
$ find /tmp/solr_backup/backup1
/tmp/solr_backup/backup1
/tmp/solr_backup/backup1/backup.properties
/tmp/solr_backup/backup1/zk_backup
/tmp/solr_backup/backup1/zk_backup/configs
/tmp/solr_backup/backup1/zk_backup/configs/backup_test
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/solrconfig.xml
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/protwords.txt
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/stopwords.txt
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/params.json
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/synonyms.txt
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/lang
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/lang/stopwords_tr.txt
(略)
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/lang/contractions_fr.txt
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/configoverlay.json
/tmp/solr_backup/backup1/zk_backup/configs/backup_test/managed-schema
/tmp/solr_backup/backup1/zk_backup/collection_state.json
/tmp/solr_backup/backup1/snapshot.shard2
/tmp/solr_backup/backup1/snapshot.shard2/_3.fdt
/tmp/solr_backup/backup1/snapshot.shard2/_1_Lucene50_0.tim
/tmp/solr_backup/backup1/snapshot.shard2/segments_2
(略)
/tmp/solr_backup/backup1/snapshot.shard2/_0_Lucene50_0.pos
/tmp/solr_backup/backup1/snapshot.shard2/_2_Lucene80_0.dvm
/tmp/solr_backup/backup1/snapshot.shard2/_1_Lucene50_0.doc
/tmp/solr_backup/backup1/snapshot.shard1
/tmp/solr_backup/backup1/snapshot.shard1/_3.fdt
/tmp/solr_backup/backup1/snapshot.shard1/_1_Lucene50_0.tim
/tmp/solr_backup/backup1/snapshot.shard1/segments_2
(略)
/tmp/solr_backup/backup1/snapshot.shard1/_0_Lucene50_0.pos
/tmp/solr_backup/backup1/snapshot.shard1/_2_Lucene80_0.dvm
/tmp/solr_backup/backup1/snapshot.shard1/_1_Lucene50_0.doc

backup.properties は以下のような内容のファイルです。

#Backup properties file
#Sun Sep 19 14:21:47 UTC 2019
collection.configName=backup_test
collectionAlias=backup_test
startTime=2019-09-29T14\:21\:47.122280Z
collection=backup_test
backupName=backup1
index.version=8.1.0

おわりに

コレクションのバックアップを作成したときに具体的にどういうファイルが生成されるのかを調べました。次回はリストアの動作を確認してみます。


VSCodeでEmmet入門

コーディングはやり始めるときは楽しいのですが、どんどん進めていくうちに、だんだんと入力が面倒になってきますよね。
そこで、コーディングを効率的に進める上で欠かせないのが「Emmet」です!

Emmetというのは、HTMLやCSSの入力をサポートするツールです。

「そんなのエディタの自動補完を使ってもいいじゃん。」という声も聞こえてきそうですが、Emmetに慣れてくるとコーディングのストレス軽減に大きな違いを実感できると思います。

入力例として

「height:100px」を入力するときは、
「h100」と入力→「Tab」キーを押す

「<div class=”wraper”></div>」を入力するときは、
「div.wraper」と入力→「Tab」キーを押す
というような感じになります。

慣れれば慣れるほどその効果を実感できると思います。

それではVSCodeでEmmetを使って行きましょう。

「Tab」キーでEmmet省略記法が展開されるようにする

①基本設定>設定から「設定」を開きます。
②上部の検索窓から「emmet」を入力
③「trigger Expansion On Tab」にチェックをいれます。
これで準備が整いました。

HTMLの雛形を出力

まずは、HTMLの雛形を出力していきましょう。
VSCodeを開いてtest.htmlというファイルを作成して保存します。

それでは「html:5」と入力して「Tab」キーを押してください。 下記のように展開されましたね。
html:5
↓↓↓
<!DOCTYPE html>
<html lang="en">
<head>
  
  
  Document
</head>
<body>
  
</body>
</html>

このままでは、「lang=”en”」になっているので、「基本設定>設定」から「emmet」を入力して、「settings.jsonで編集」をクリックします。
そして下記の1行を追加します。
{
  "emmet.variables": {"lang" : "ja"}
}
「html:5」と入力して「Tab」キーを押すと、先程enになっていたところがjaになります。

EmmetのHTMLサンプル

基本的には「○○+Tab」なので、下記を参考に一度試してみていただければと思います。

aタグ

a
↓↓↓

class名

a.top
↓↓↓

ID名

a#header
↓↓↓

リスト

ul>li
↓↓↓

リストタグを並べる場合

ul.test_list>li.test_item*3
↓↓↓

EmmetのCSSサンプル

height

h10
↓↓↓
height: 10px;

width

w100%
↓↓↓
width: 100%;

border-radius

bdrs4
↓↓↓
border-radius: 4px;
上記はほんの一例で、下記のリンクに各スタイルの記述する方法が書かれています。
https://docs.emmet.io/cheat-sheet/

これらをすべて覚えるのは難しいので、よく使うスタイルやhtmlだけでも覚えるとコーディングの負荷を軽減できると思います。
ぜひ活用してみてください!

NGINXでキャッシュを行う

NGINXでリバースプロキシする際、キャッシュを行いパフォーマンスを上げることができます。

メリット
・upstreamサーバへのリクエストを減らすことができる。
・NGINXとupstreamサーバ間のトラフィック削減
・レスポンス速度が早くなる

デメリット
・キャッシュの更新を考えた場合に処理が複雑になる可能性がある。
・NGINXサーバでディスク使用量の増加
・NGINXサーバでIO負荷がかかる

設定方法

現在の一時ファイルについての設定確認

# nginx -V 2>&1 | sed 's/ --/\n --/g' | fgrep proxy-temp-path
# fgrep -nr proxy_temp_path /etc/nginx/

設定

http {
  #proxy_temp_path /var/cache/nginx/proxy_temp;
  proxy_cache_path /var/cache/nginx/cache_1 levels=1:2 keys_zone=cache_zone_1:16m max_size=100m inactive=120m;

  upstream backend {
    server 192.168.0.101;
    server 192.168.0.102;
  }

  server {
    listen 80;
    server_name _;

    location / {
      proxy_pass http://backend;

      proxy_set_header Host $http_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;

      proxy_ignore_headers X-Accel-Expires Cache-Control Expires;

      proxy_cache cache_zone_1;
      proxy_cache_key "$scheme$proxy_host$request_uri";
      proxy_cache_valid 200 60m;
      proxy_cache_valid any 5m;

      add_header X-Cache-Status $upstream_cache_status;
    }
  }
}

proxy_cache_pathディレクティブ

/var/cache/nginx/cache_1:キャッシュファイル保存場所
キャッシュを行う場合、一旦レスポンスをproxy_temp_pathで指定した一時ファイルに保存してからのrenameとなるため同じデバイスになるよう注意
実ディレクトリは/var/cache/nginxまで存在していればcache_1は自動で作成されます。

levels=1:2:キャッシュを保存する階層
キャッシュファイル名はproxy_cache_keyをmd5したものになり、階層はファイル名の一部が利用されます。
md5が3858f62230ac3c915f300c664312c63fだった場合のファイルパス
/var/cache/nginx/cache_1/f/63/3858f62230ac3c915f300c664312c63f

keys_zone=cache_zone_1:16m:キャッシュゾーンの名前とメモリサイズ。
メモリサイズは1つのキャッシュにつき128バイト使われる(64bit環境)
この例だと16mになっているので約130000ファイル保存できます。
※よく使われるサイズの単位
g:ギガバイト
m:メガバイト
k:キロバイト

max_size=100m:キャッシュファイルの総容量
nginxは定期的にcache managerと呼ばれるプロセスを起動し、容量を監視しています。この時最大容量を超えていた場合は最も長い期間アクセスされなかったファイルから削除されます。
※最大容量を超えた瞬間ではなく、定期的にチェックされる=一時的に最大容量を超えた状態になる。

inactive=120m:最後にアクセスされてからキャッシュファイルが削除されるまでの時間
120mの場合、最後にアクセスされてから120分アクセスがないと削除されます。
※よく使われる時間の単位
M:月
d:日
h:時間
m:分
s:秒

proxy_cacheディレクティブ

proxy_cache_pathで設定したキャッシュゾーンの名前を指定

proxy_cache_keyディレクティブ

キャッシュのキーとして使用する値を指定

proxy_cache_validディレクティブ

ステータスごとのキャッシュの有効期限の指定
ステータスを指定しない場合は200,301,302のレスポンスのみがキャッシュされる。anyを指定した場合は明示的に指定していないすべてのステータスコードに設定することができる。

proxy_ignore_headersディレクティブ

backendからのレスポンスで無視するヘッダーを指定する
Cache-Control及びExpiresを指定することでproxy_cache_validの設定が生きる
キャッシュ設定の優先順位
X-Accel-Expiresヘッダ > Cache-Controlヘッダ > proxy_cache_valid

※Set-Cookieヘッダがある場合、通常はキャッシュされませんがproxy_ignore_headersで無視してキャッシュする事も可能です。
ただしこの場合proxy_hide_headerも指定しておかないと他人にもcookieがセットされてしまいます。

add_headerディレクティブ

サンプルの場合X-Cache-Statusというレスポンスヘッダ名でキャッシュのステータスを返しています。

簡単にNGINXのキャッシュ設定について説明しました。
NGINXの設定は奥が深いので色々試してみましょう。