LoginSignup
2
0

More than 1 year has passed since last update.

SambaでSpotlightを使えるようにした

Last updated at Posted at 2022-01-01

Sambaで公開しているボリュームをSpotlight検索できるようにしたので、備忘録を書いておく。

なぜSpotlightを動かそうと思ったのか
TimeMachineをsamba上で動かそうとしていた所、うまく動作しない理由にSpotlightが関係しているらしいと思われた。これを検証する為に動かしてみた。下のリンク先はそのドタバタ記である。

前提

  • 「Spotlightが動作した」と言うだけ1であり、それ以上の検証は行っていない。
  • 筆者はJavaやelasticsearchでは ど素人 なので、elasticsearchやfscrawlerの設定には ほぼ確実に 不適切な所があると思われる2。ご指摘頂ければ幸いである。
  • elasticsearchへのアクセスにはパスワードすら設定できない。これはsamba4.1.13でパスワード等を指定する手段が無い為。おそらく近い将来のバージョンでサポートされると思われる。
  • 以下の例では、sambaサーバ上の/share1, /share2と言うディレクトリを、それぞれShare1, Share2と言う名前で公開している。

環境

  • OS: FreeBSD 12.2-RELEASE-p11 amd64
  • samba: 4.13.14

samba

sambaのコンパイル

ports/pkg の sambaは、Spotlight非対応である。よってportsをSpotlight対応に設定した上で、コンパイル&インストールする必要がある。

# cd /usr/ports/net/samba413
# make config
      (SPOTLIGHTにチェックを入れる)
# make clean all deinstall reinstall

smb4.conf

最低限必要な設定は、[global]セクションに

[global]
    # Spotlight
    spotlight backend = elasticsearch 
    elasticsearch:address = 127.0.0.1
    elasticsearch:port = 9200

を追加し、Spotlightをサポートするボリュームに、

[Share]
    spotlight=yes

を加えれば良い。
参考までに筆者が運用しているsmb4.confの抜粋を以下の折りたたみ部分に示す。

`smb4.conf`の例
smb4.conf
[global]
    workgroup = WORKGROUP
    security = USER
    server min protocol = SMB2
    server string = <your server>
    server role = standalone server
    dos filemode = yes
    store dos attributes = yes
    map archive = no
    unix extensions = no

    # veto files
    delete veto files = yes
    veto files = /._*/.DS_Store/

    # Spotlight
    spotlight backend = elasticsearch 
    elasticsearch:address = 127.0.0.1
    elasticsearch:port = 9200

    # vfs
    # ZFSの場合
    vfs objects = zfsacl catia fruit streams_xattr
    # UFSの場合
    # vfs objects = acl_xattr catia fruit streams_xattr

    # vfs_zfsacl
    nfs4:chown = yes

    # vfs_fruit
    fruit:zero_file_id = yes
    fruit:metadata = stream
    fruit:veto_appledouble = no
    fruit:resource = xattr
    fruit:model = PowerMac

    # vfs_streams_xattr
    streams_xattr:store_stream_type = no
    streams_xattr:prefix = user.

[Share1]
    path = /share1
    read only = no
    valid users = <yourname>
    browseable = yes
    writable = yes
    spotlight = yes

[Share2]
    path = /share2
    read only = no
    valid users = <yourname>
    browseable = yes
    writable = yes
    spotlight = yes

elasticsearch

インストール

# pkg install elasticsearch7 openjdk15

設定

elasticsearch本体の設定
/usr/local/etc/elasticsearch/elasticsearch.yml
cluster.name: samba
node.name: node1
path.data: /var/db/elasticsearch
path.logs: /var/log/elasticsearch
network.host: 127.0.0.1
http.port: 9200
xpack.ml.enabled: false
discovery.type: single-node
xpack.security.enabled: false
JVMの設定
/usr/local/etc/elasticsearch/jvm.options
####################################
## IMPORTANT: JVM heap size
####################################
       :
     (略)
       :
##
## -Xms4g
## -Xmx4g
##

を、以下のように変更(heap sizeを2GByteにする場合)

-Xms2g
-Xmx2g

elasticsearchは、デフォルトでは物理メモリの半分を確保してしまう。Spotlightのみに使うなら、2Gbyte程度で十分である。
以下のscript(esmem.py)でelasticsearchが消費しているheapの割合を調べられるので、割り当て量が妥当か確認できる。

スクリプト: `esmem.py`
esmem.py
#!/usr/bin/env python3

import requests
import json
import time

r = requests.get('http://localhost:9200/_nodes/stats/jvm')
j = json.loads(r.text)

for node in j['nodes'].items():
    id, properties = node
    timestamp = time.strftime('%Y-%m-%d %H:%M:%S %z',
                              time.localtime(properties['timestamp']/1000))
    name = properties['name']
    total = properties['jvm']['mem']['heap_used_percent']
    percent = properties['jvm']['mem']['heap_used_percent']
    print(timestamp, name, percent, sep=',')
    break

/etc/rc.conf

以下を追加

# elasticsearch
elasticsearch_enable="YES"
elasticsearch_java_home="/usr/local/openjdk15"
起動
$ sudo service elasticsearch start
起動後の設定

以下の設定3 により、クラスタヘルスが yellow → greenとなった。

# 新しく作られるインデックスのレプリカ数を 0 に設定する。
$ curl  -H "Content-Type: application/json" -XPUT localhost:9200/_template/template_1 -d '
{
    "template" : "*",
    "settings" : {
        "number_of_shards" : 1, "number_of_replicas" : 0
    }
}'

# 既にインデックスが存在する場合、レプリカ数を 0 に変更する。
$ curl -H "Content-Type: application/json" -XPUT localhost:9200/*/_settings -d '{"number_of_replicas":0}'

fscrawler

インストール

こちらから適切なパッケージをダウンロードし、適当なディレクトリに展開する。
ここでは例として、fscrawler-es7-2.9.zipをダウンロードし、/usr/local/fscrawler-es7-2.9/に展開、/usr/local/fscrawler -> fscrawler-es7-2.9と言うシンボリックリンクを張った。

$ cd /usr/local
$ sudo unzip <somewhere>/fscrawler-es7-2.9.zip
$ sudo ln -s fscrawler-es7-2.9 fscrawler

設定

実行時の設定

fscrawlerの実行時の設定ファイルは、defaultでは実行ユーザの$HOME/.fscrawler/以下に置かれるが、ここでは /usr/local/fscrawler/config/以下に置く事とする。

以下を実行する。

$ sudo JAVA_HOME=/usr/local/openjdk15 /usr/local/fscrawler/bin/fscrawler job_share1 --config_dir /usr/local/fscrawler/config/ 

その結果、/usr/local/fscrawler/config/job_share1/_settings.yaml が生成されるので、これを編集する。

/usr/local/fscrawler/config/job_share1/_settings.yaml
---
name: "job_share1"
fs:
  url: "/share1"
  update_rate: "15m"
  excludes:
  - "*/~*"
  - "*/*~"
  - "*/[.]*"
  json_support: false
  filename_as_id: false
  add_filesize: true
  remove_deleted: true
  add_as_inner_object: false
  store_source: false
  index_content: true
  attributes_support: false
  raw_metadata: false
  xml_support: false
  index_folders: true
  lang_detect: false
  continue_on_error: true
  ocr:
    language: "eng"
    enabled: true
    pdf_strategy: "ocr_and_text"
  follow_symlinks: false
elasticsearch:
  nodes:
  - url: "http://127.0.0.1:9200"
  bulk_size: 100
  flush_interval: "5s"
  byte_size: "10mb"
  ssl_verification: false

変更したのは、name(セッション名), url(インデックスを作る対象のpath), excludes(インデックス対象から除外するpath), continue_on_error(エラー発生時に続けるか), ssl_verification(SSL証明書の検証を行うか)のみである。

最初excludes:でdotで始まるファイルを除外するために、- "*/.*"としていたが、これでは正常動作しない。
- "*/[.]*"とするのが正しい。

上記と同様に、

$ sudo JAVA_HOME=/usr/local/openjdk15 /usr/local/fscrawler/bin/fscrawler job_share2 --config_dir /usr/local/fscrawler/config/ 

を実行し、/usr/local/fscrawler/config/job_share2/_settings.yamlを適宜編集する。

cron

fscrawlerはsambaサーバ上に常駐させる事もできるが、ここではcronで定期的に動作させる事にする。

/usr/local/sbin/fscrawler.sh
#!/bin/sh
export JAVA_HOME=/usr/local/openjdk15
export FS_JAVA_OPTS="-DLOG_DIR=/var/log/fscrawler"

args="--loop 1 --config_dir /usr/local/fscrawler/config"

OPTS=""
JOBS=""

for OPT in "$@"; do
    case $OPT in
        --*)
            OPTS="$OPTS $OPT"
            ;;
        *)
            JOBS="$JOBS $OPT"
            ;;
    esac
done

for job in $JOBS; do
    /usr/local/fscrawler/bin/fscrawler $job $OPTS $args
done

fscrawler.shでは環境変数FS_JAVA_OPTSによりログを/var/log/fscrawler以下に保存するように設定しているが、fscrawler-es7-2.7ではcronから実行した場合ログが正常に保存されなかった。なおfscrawler-es7-2.9にバージョンアップする事で解決した。

/etc/cron.d/fscrawler.cron
# fscrawler
#%M %H  %d  %m  %w  user  command
0   4	*   *   1-6 root  /usr/local/sbin/fscrawler.sh --silent job_share1 job_share2
0   4	*   *   0   root  /usr/local/sbin/fscrawler.sh --silent --restart job_share1 job_share2
その他

fscrawlerにインデックスを作らせたくないディレクトリには、.fscrawlerignore と言うファイルを作っておく。

$ cd somewhere-you-want-to-hide-from-spotlight
$ touch .fscrawlerignore

なお上記設定では、.(dot)で始まるか、~(tilde)が先頭か最後に付いているファイル・ディレクトリを無視するように設定している。

ログ4の整理

上記設定では、elasticsearchは/var/log/elasticsearch以下にログファイルを作る。特にsamba-YYYY-MM-DD-1.{json,log}.gzと言うファイルが毎日作成される。
同じようにfscrawlerは/var/log/fscrawler以下にdocuments-YYYY-MM-DD.log.gzfscrawler-YYYY-MM-DD-[0-9]+.log.gzと言うファイルが作成される。

これらの古い物を消したいが、UNIXで使われる newsysloglogrotateでは無理である。その為Pythonで簡単なスクリプトを書いて、cronで動かしている。

スクリプト: /usr/local/sbin/elasticlog.py
/usr/local/sbin/elasticlog.py
#!/usr/bin/env python3
#
# log cleaner for elasticsearch
# usage:
#       elasticlog.py [-d <days>] <path>
#
# log file name
#   samba-%Y-%m-%d-1.{log,json}.gz

from datetime import datetime, date, timedelta
import argparse
import os
import re
import sys

parser = argparse.ArgumentParser()
parser.add_argument("-d", "--days", help="expire if file is older than days", ty
pe=int, default=7)
parser.add_argument("path", help="path to log files", type=str, default='.')
parser.add_argument("-n", "--dry_run", help="check files but do not remove", act
ion="store_true")
args = parser.parse_args()

if not os.path.isdir(args.path):
    print(args.path, "is not a directory", file=sys.stderr)
    exit(1)

if re.match(r'.*/$', args.path):
    p = args.path
else:
    p = args.path+'/'

reg=re.compile('(^[A-Za-z][A-Za-z0-9]*)\-([0-9]{4}\-[0-9]{2}\-[0-9]{2})')
lastdays=timedelta(days=args.days)
today=date.today()

for f in os.scandir(path=p):
    m=reg.match(f.name)
    if m:
        fdate=date.fromisoformat((m.group(2)))

        if (today-fdate) > lastdays:
            if args.dry_run:
                print("rm", p+f.name, file=sys.stderr)
            else:
                try:
                    os.unlink(p+f.name)
                except (IsADirectoryError,FileNotFoundError,PermissionError) as f:
                    print(f, file=sys.stderr)
        else:
            if args.dry_run:
                print(f.name, "is newer than", args.days, "days",
                      file=sys.stderr)

exit(0)

cron設定

/etc/cron.d/elasticlog.cron
# elasticlog.cron

#%M  %H %d %m %U user command
0    11  *  *  * root /usr/local/sbin/elasticlog.py /var/log/elasticsearch
5    11  *  *  * root /usr/local/sbin/elasticlog.py /var/log/fscrawler

なおelasticsearch本体を使って行う方法もあるようだが、
よく分からなかったので使っていない。

参考

  1. 実際Spotlightの動作がMac本体に比べてやや遅いような...

  2. elasticsearchの用語はデータベース分野の物?のせいか、理解しづらいのが一因である。

  3. Elasticsearchが1ノードでConditionがYellowな時の直し方」を参考にした。

  4. ここで言うログとは、UNIX文化でのログ(/var/log/ 以下に作られるファイル)を指している。データベース文化での(トランザクション)ログとは異なるので注意。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0