チーム内のメモや検証結果をWikiに集約しても、ページ数が増えると「どこに書いたか分からない」という問題が起きます。Wiki側の検索だけでは検索条件を細かく指定しにくく、複数のデータソースを横断したいケースでは別の検索基盤も必要です。
そこで今回は、Docker Composeを使ってローカルに以下の環境を構築しました。
- Wiki.js:ドキュメント管理
- PostgreSQL:Wiki.jsのデータベース
- OpenSearch:全文検索エンジン
Wiki.jsとOpenSearchは役割を分離し、まずは検索用インデックスへAPIで文書を登録する構成です。ローカル検証や個人用ナレッジベースの土台として、比較的少ない設定で動かせます。
構成と前提
今回の構成は次の通りです。
ブラウザ
├─ Wiki.js : http://localhost:8080
└─ OpenSearch : http://localhost:9200
Wiki.js ── PostgreSQL
Docker DesktopとDocker Composeが使える環境を前提にしています。OpenSearchはメモリを使用するため、Docker Desktopには最低でも2〜4GB程度のメモリを割り当てておくと安定します。
プロジェクト用のディレクトリを作成します。
mkdir local-wiki
cd local-wiki
Docker Composeを作成する
compose.yamlを作成します。
services:
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: wiki
POSTGRES_USER: wikijs
POSTGRES_PASSWORD: wikijs-password
volumes:
- postgres-data:/var/lib/postgresql/data
wiki:
image: ghcr.io/requarks/wiki:2
restart: unless-stopped
depends_on:
- db
ports:
- "8080:3000"
environment:
DB_TYPE: postgres
DB_HOST: db
DB_PORT: 5432
DB_USER: wikijs
DB_PASS: wikijs-password
DB_NAME: wiki
search:
image: opensearchproject/opensearch:2.17.1
restart: unless-stopped
environment:
discovery.type: single-node
DISABLE_SECURITY_PLUGIN: "true"
DISABLE_INSTALL_DEMO_CONFIG: "true"
bootstrap.memory_lock: "true"
OPENSEARCH_JAVA_OPTS: "-Xms512m -Xmx512m"
ports:
- "9200:9200"
volumes:
- opensearch-data:/usr/share/opensearch/data
ulimits:
memlock:
soft: -1
hard: -1
volumes:
postgres-data:
opensearch-data:
今回のOpenSearchはローカル検証用としてセキュリティプラグインを無効にしています。本番やネットワーク越しで利用する場合は、認証・TLS・アクセス制御を必ず設定してください。
起動します。
docker compose up -d
docker compose ps
wiki、db、searchの3サービスがrunningになっていれば起動完了です。
Wiki.jsを初期設定する
ブラウザでhttp://localhost:8080を開くと、Wiki.jsの初期設定画面が表示されます。
管理者メールアドレスとパスワードを入力し、データベース設定はComposeの環境変数と同じ内容にします。
Database Type: PostgreSQL
Host: db
Port: 5432
User: wikijs
Password: wikijs-password
Database: wiki
Composeネットワーク内では、接続先にlocalhostではなくサービス名のdbを指定する点がポイントです。
初期設定が完了したら、テスト用のページを作成します。私は次のようなページを用意しました。
タイトル: Docker Composeのメモ
本文:
Docker Composeでは複数のコンテナをサービスとして定義できる。
docker compose up -dでバックグラウンド起動する。
Wiki.js単体の動作確認としては、ページの作成、編集、検索ができれば問題ありません。
OpenSearchに検索インデックスを作る
次に、OpenSearchへWikiページ用のインデックスを作成します。
日本語を扱うため、今回は形態素解析プラグインを追加せず、2文字単位のngramトークナイザーを使います。小規模なローカルWikiでは、設定が簡単で部分一致にも対応しやすい方法です。
まずOpenSearchが応答するか確認します。
curl http://localhost:9200
続いてインデックスを作成します。
curl -X PUT http://localhost:9200/wiki-pages \
-H "Content-Type: application/json" \
-d '{
"settings": {
"analysis": {
"tokenizer": {
"jp_bigram": {
"type": "ngram",
"min_gram": 2,
"max_gram": 2
}
},
"analyzer": {
"jp_text": {
"type": "custom",
"tokenizer": "jp_bigram",
"filter": ["lowercase"]
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "jp_text"
},
"body": {
"type": "text",
"analyzer": "jp_text"
},
"path": {
"type": "keyword"
},
"tags": {
"type": "keyword"
}
}
}
}'
インデックスが作成できたら、Wikiページに相当する文書を登録します。
curl -X PUT http://localhost:9200/wiki-pages/_doc/docker-compose \
-H "Content-Type: application/json" \
-d '{
"title": "Docker Composeのメモ",
"body": "Docker Composeでは複数のコンテナをサービスとして定義できる。docker compose up -dでバックグラウンド起動する。",
"path": "/docker-compose",
"tags": ["docker", "compose"]
}'
登録した文書を検索します。
curl -X GET http://localhost:9200/wiki-pages/_search \
-H "Content-Type: application/json" \
-d '{
"query": {
"multi_match": {
"query": "バックグラウンド起動",
"fields": ["title", "body"]
}
}
}'
レスポンスのhits.hitsに、タイトルや本文、検索スコアが含まれていれば成功です。
Wiki.jsと検索インデックスを同期する
Wiki.jsのページ変更をOpenSearchへ反映する流れは次の通りです。
今回の構成では、Wiki.jsにページを作成しただけではOpenSearchへ自動登録されません。ここは意識して設計する必要があります。
最初の検証では、検索APIの確認を優先して手動登録しました。実際に運用する場合は、次のような同期処理を追加します。
- Wiki.jsのページ更新を検知する
- ページのタイトル、本文、パス、タグを取得する
- OpenSearchのドキュメントを作成または更新する
- ページ削除時は検索インデックスからも削除する
Wiki.jsにはGraphQL APIがあるため、アクセストークンを使ってページ情報を取得する同期スクリプトを作れます。ページ数が少ないうちは、Markdownをエクスポートして定期的にインデックスを作り直す方法でも十分です。
同期処理では、Wiki.js側のページIDをOpenSearchのドキュメントIDとして使うと、更新時に同じ文書を上書きできます。
OpenSearch document ID = Wiki.js page ID
検索結果からWikiページへ戻るために、pathも必ず保存しておくと便利です。
実運用で気を付けた点
まず、OpenSearchのインデックス設定は作成後に簡単には変更できません。ngramのサイズやフィールド構成を変えたい場合は、新しいインデックスを作成してデータを移行します。
また、ngramは検索しやすい一方で、通常の単語分割よりインデックスサイズが大きくなります。数万ページ規模になった場合は、kuromojiなどの日本語解析を検討した方がよいでしょう。
データを初期化したい場合は、次のコマンドでボリュームごと削除できます。
docker compose down -v
このコマンドを実行すると、Wiki.jsのデータベースとOpenSearchのインデックスも削除されます。検証環境では便利ですが、残したいデータがある場合は実行前にバックアップしてください。
まとめ
Docker Composeを使うと、Wiki.js、PostgreSQL、OpenSearchをローカルだけでまとめて起動できます。
今回のポイントは次の3つです。
- Wiki.jsと検索エンジンをコンテナ単位で分離する
- 日本語検索には
ngramを使って部分一致を実現する - Wiki更新とOpenSearch更新を同期処理として設計する
まずは手動登録で検索の動きを確認し、その後にWiki.jsのAPIを使った同期処理を追加すると、段階的に構築できます。個人の技術メモからチーム内のナレッジベースまで、用途に合わせて拡張しやすい構成です。