FESS (Windows, Docker) でファイルシステムを全文検索できた

# はじめに
「FESS を構築してみた」というブログ記事は検索すればそこそこ見つかるけど、「Windows上でDocker Desktop上に構築した FESS を使ってファイルシステムを検索できるようにしてみた。うまくいった」という明確な報告例を見つけることができなかった。ので、書く。

※ 注意：この記事はあくまで「やってみた」「できた」という報告を簡易にまとめたものに過ぎないので、技術的な詳細は FESS や Docker 等の公式ドキュメントを参照してください。ブログ記事を先に鵜呑みにするとかえって効率が悪いので、公式の情報を最初に見た方が良いです。あと公式ドキュメント読むときは自分が触っているソフトウェアのバージョンに合わせたものを読むべし。。。いや当たり前すぎるんですが、自分が横着してその辺でめちゃくちゃハマったので。。。

# FESSについて
普段作業しているWindowsマシン上の様々なファイルを全文検索できたら便利だなと思って、検索エンジンの導入を試してみることにした。
ググってみると FESS という名前を見つけた。FESSはCodeLibs社によるオープンソースの検索サーバー(エンジンではない？)。Apache ライセンス。導入がお手軽らしい。Web、ファイルシステム、Windows共有フォルダ、データベースをクロールさせることができる。

外部連携用のAPIもあるらしいし、今後いろいろ活用できそうなので FESS を選択することにした。
CodeLibs社さん、ありがとうございます！
https://codelibs.co/ja/product/fess.html

FESS のバックエンドは OpenSearch や ElasticSearch らしい。例えば FESS と OpenSearch が連携して動作する。今回は OpenSearch を使う。

# 実験環境
- Windows 11 Pro 23H2 (22631.5039)
- Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz   2.20 GHz
- RAM	16.0 GB (15.8 GB 使用可能)
- Docker Desktop
- docker-fess-14.17.0

# やったこと
## Docker Desktop を導入
今回は Windows 上で Docker コンテナとして FESS (と OpenSearch) を動かしたいので Docker Desktop を導入する。
下記より入手してインストールする。
https://www.docker.com/ja-jp/get-started/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95847/0dcd7e34-e3a8-4ee0-bdfa-001f038fe225.png)
インストール時には"Use the WSL 2 based engine" オプションを有効にしておく(後からでも設定可)。

## Docker for FESS を入手する
Docker イメージや docker-compose 用の YAML ファイルも CodeLibs 社が GitHub 上で提供している。
下記よりソースコードのzipをダウンロードしてローカルに展開する。
https://github.com/codelibs/docker-fess/releases/tag/v14.17.0
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95847/4fe689b5-8771-40cd-a96d-c70a2c16ef97.png)
zipの展開先はどこでもOKだけど、ストレージの空き容量が十分あるところをおすすめする。
自分は適当に D ドライブ直下に配置した。

## Docker for FESS の compose.yaml と compose-opensearch2.yaml を編集する
### compose.yaml
下記のように volumes 設定を追記する。
```
services:
  fess01:
    image: ghcr.io/codelibs/fess:14.17.0
    # build: ./playwright # use Playwright
    container_name: fess01
    environment:
      - "SEARCH_ENGINE_HTTP_URL=http://search01:9200"
      - "FESS_DICTIONARY_PATH=${FESS_DICTIONARY_PATH:-/usr/share/opensearch/config/dictionary/}"
      # - "FESS_PLUGINS=fess-webapp-semantic-search:14.17.0 fess-ds-wikipedia:14.17.0"
    ports:
      - "8080:8080"
    networks:
      - search_net
    depends_on:
      - search01
    volumes:
      - D:/docker-fess-14.17.0/data/fess:/usr/share/fess/data
      - <クロールさせたいフォルダのパス>:/usr/share/docs
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "5"
    restart: unless-stopped

networks:
  search_net:
    driver: bridge
```
クロールしてほしいフォルダもマウントさせることをわすれずに。マウント先のパス名はなんでもいい。自分は "/usr/share/docs" にしておいた。適切なお作法は知らん。
Dockerコンテナとして動作するFESSは、コンテナとしてマウント済みのストレージ領域にしかアクセスできない。つまりクロール先もマウントしておかないといけない。ここで自分はめちゃくちゃハマったので注意が必要。

### compose-opensearch2.yaml
こちらも同様にvolume設定を追加する。
```
services:
  search01:
    image: ghcr.io/codelibs/fess-opensearch:2.17.0
    container_name: search01
    environment:
      - node.name=search01
      - discovery.seed_hosts=search01
      - cluster.initial_cluster_manager_nodes=search01
      - cluster.name=fess-search
      - bootstrap.memory_lock=true
      - node.roles=cluster_manager,data,ingest,ml
      - "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
      - "DISABLE_INSTALL_DEMO_CONFIG=true"
      - "DISABLE_SECURITY_PLUGIN=true"
      - "FESS_DICTIONARY_PATH=/usr/share/opensearch/config/dictionary"
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65535
        hard: 65535
    volumes:
      - D:/docker-fess-14.17.0/data/es:/usr/share/opensearch/data
      - D:/docker-fess-14.17.0/data/es/dictionary:/usr/share/opensearch/config/dictionary
    ports:
      - 9200:9200
    networks:
      - search_net
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "5"
    restart: unless-stopped

volumes:
  search01_data:
    driver: local
  search01_dictionary:
    driver: local
```

## Docker for FESS を動かす
README.md にも書かれているが、下記コマンドで動かす。
```
$ docker compose -f compose.yaml -f compose-opensearch2.yaml up -d
```
Docker Desktop のダッシュボードで下スクショのような見た目になっていればOK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95847/a103ac1d-5891-47c0-80cb-5d25390b64b3.png)
起動してからしばらくの後、赤丸で囲ったところをクリックするとWebブラウザで FESS の検索画面が表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95847/91c9a7ad-105b-4bad-815d-b0d05ec9eeab.png)
各コンテナが立ち上がってからすぐは上記画面が表示されないので注意。コーヒーとか飲んで待つのが良い。

## FESS にファイルシステムをクロールさせる
この時点では上記画面から何か検索クエリを投げても何もヒットしない。クロールさせないといけない。
まずは FESS 公式ドキュメントを読むのがよいです (今回は FESS 14.17 相当を使っているので、対応するバージョンのドキュメントを参照しましょう)。
https://fess.codelibs.org/ja/14.17/admin/fileconfig-guide.html#id25

下記管理画面からクロール設定を行う。
http://localhost:8080/admin
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95847/3dcc9cb8-b651-4e6b-b9cd-90fcbb9e51aa.png)
新規作成ボタンを押すとクロール設定画面が表示されるので、適当に名前をつけてパスを指定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95847/521078d2-8296-449e-833d-b1e8028410ff.png)
公式ドキュメントにも書かれているが、ここで指定するパスには `file:/` を頭につける必要があるので注意。
自分は FESS の compose.yaml でマウントした `/usr/share/docs` をパスとして指定した。このパスでマウントされている Windows 側のフォルダはなんでもいい。ドキュメントフォルダでもいいし普段使いのWebブラウザのダウンロードフォルダとかでもいい。
設定できたら管理画面右上の「クローラーの実行」ボタンをクリックして、いい感じにクロールしてもらう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95847/dac394ce-9370-4cf3-9d86-a819cf181415.png)
うまくいけばインデックスが作成される。

## インデックスが作成されたか確認する
管理画面のシステム情報 --> クロール情報から最近のクロール結果を確認できる。自分はインデックスのサイズが0より大きければ基本的にうまくいったと判断した。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95847/b6c583b1-3d34-4344-8464-8f8800f9bd5a.png)
実際に検索でヒットするか試してみる。
http://localhost:8080/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95847/07085556-e362-4678-815e-afb33c230ec8.png)
イェイ。IA-32のマニュアル(pdf)が全文検索でヒットしたぜ。成功です。

※ おまけ："intel"を検索語としてAPI経由でクエリを投げるなら以下のようにする。
```
$ curl http://localhost:8080/api/v1/documents?q=intel 
```
いい感じのレスポンスが返ってきたので、これをいろいろすればよさそう。詳細は公式ドキュメント
https://fess.codelibs.org/ja/14.18/api/api-search.html

# その他ハマったところ
- Docker じゃなくて直接 FESS と Elastic Search で環境構築しようとしたけど全然うまくいかなかった。よほどの理由が無ければ最初から Docker を使うべき
- Docker コンテナとして動作する FESS はマウント済みのストレージしかクロールできない。ということに気づくまでちょっと時間がかかった。何度設定を変えてクロールしてもインデックスサイズが 0 のままだったので発狂しました
- 公式ドキュメントを読むべし。ブログをつまみぐいすると本当によくない。あと参考にするなら利用ソフトウェアのバージョン情報を明示している記事と、最後まで(読者が知りたい)結果を明記してくれている記事を読みましょう
- クローラが動いている最中に、クロール先のドライブを誤って切断したところ次回の`docker-compose up`から`OpenSearchException[failed to bind service]; nested: AccessDeniedException[/usr/share/opensearch/data/nodes];`というエラーが出てOpenSearchが起動しなくなった。マウント先のvolumeへのアクセス権の原因と考え、マウント先のアクセス権を変更したりUIDやGIDをホスト側ユーザと揃えたり……といろいろやってみたが解決せず。最終的にはDocker DesktopのResources設定上からWSL2のディスクイメージの場所を特定し、ここにあるvhdxを削除してホスト側を再起動することで解決した。不正な終了のせいでWSL2側に変な情報が残っちゃったのかも。