はじめに
Djangoでサイトを作っていた際に、検索機能を手軽に設置したいなぁと思い、
OSSの検索エンジン Fess
を使って実現したので方法を共有します。
Fessについて知りたい方は こちら をご参照ください。
想定読者
- Djangoでのアプリケーション開発手法を理解している
- Docker / docker-compose の最低限の使用法について理解している
- Nginxの最低限の設定方法について理解している
構成
docker-composeにて以下コンテナを起動します。
-
nginx
コンテナ -
django
コンテナ -
postgresql
コンテナ -
fess
コンテナ -
elasticsearch
コンテナ
以下は docker-compose.yml
の例です。
なお、fessとelasticsearchの設定についてはこちらのGithubを参考にさせていただきました。
また、ディレクトリ構成などは各開発状況に依存します。
version: '3.7'
services:
nginx:
image: nginx:1.19.3-alpine
ports:
- "80:8000"
volumes:
- ./nginx/conf:/etc/nginx/conf.d
- ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
- ./nginx/log:/var/log/nginx
- ./app/static:/static
depends_on:
- django
django:
build: ./app
command: uwsgi --socket :8001 --module app.wsgi --py-autoreload 1
volumes:
- ./app/:/usr/src/app/
ports:
- 8001:8001
depends_on:
- postgres
- fess
postgres:
image: postgres:12.4-alpine
volumes:
- ./postgres/data:/var/lib/postgresql/data
fess:
image: ghcr.io/codelibs/fess:13.12.0
environment:
- "ES_HTTP_URL=http://es:9200"
- "FESS_DICTIONARY_PATH=/usr/share/elasticsearch/config/dictionary/"
ports:
- "8080:8080"
depends_on:
- es
es:
image: ghcr.io/codelibs/fess-elasticsearch:7.12.0
environment:
- node.name=es01
- discovery.seed_hosts=es01
- cluster.initial_master_nodes=es01
- cluster.name=fess-es
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms1g -Xmx1g"
- "FESS_DICTIONARY_PATH=/usr/share/elasticsearch/config/dictionary"
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65535
hard: 65535
volumes:
- ./elasticsearch/data:/usr/share/elasticsearch/data
- ./elasticsearch/dictionary:/usr/share/elasticsearch/config/dictionary
ports:
- 9200:9200
ただし、これで docker-compose up
すると
「elasticsearch
が立ち上がってないから fess
が落ちたよ!」
と言われることがあります。
depends_on
で指定はしていますが、内部の起動タイミングが合わないことがあるようです。
先にelasticsearchだけ立ち上げておけばよいのですが、よい解決法がありましたら教えてください。
Djangoのテンプレートへの検索窓追加
こちらのサンプルに従って検索窓をtemplateに配置します。
以下はサイトのURLが https://yoursite.co.jp
の場合の例です。
<form id="searchForm" method="get" action="https://yoursite.co.jp/search/">
<input required pattern=".*\S+.*" id="query" type="text" name="q" maxlength="1000" autocomplete="off">
<input type="submit" name="search" value="検索">
</form>
サンプルとの差分としては、required pattern=".*\S+.*"
の部分です。
これがないと空、またはスペースのみの状態で検索ボタンが押せるのですが、
空で検索するとなぜか私の環境の場合はエラー画面となってしまったので追加しています。
Nginxの設定ファイル修正
フロントからのリクエストパスに応じて振り分けていきます。
以下の例では、/static
, /admin/
などのDjangoのパスをまず設定し、
Fess関連のパスをfessコンテナに転送しています。
(とりあえず見つけた範囲を設定していますが他にもあるかもしれません)
最後にそれ以外のパスをdjangoコンテナに転送しています。
(Fess関連のパスがDjangoのパスと重複しないようにする必要があります)
upstream django {
ip_hash;
server django:8001;
}
server {
listen 8000;
server_name 127.0.0.1;
charset utf-8;
location /static {
alias /static;
}
location /admin/ {
deny all;
}
location /search/ {
proxy_pass http://fess:8080/search/;
}
location /css/ {
proxy_pass http://fess:8080/css/;
}
location /js/ {
proxy_pass http://fess:8080/js/;
}
location /images/ {
proxy_pass http://fess:8080/images/;
}
location /go/ {
proxy_pass http://fess:8080/go/;
}
location /cache/ {
proxy_pass http://fess:8080/cache/;
}
location / {
uwsgi_pass django;
include /etc/nginx/uwsgi_params;
}
}
server_tokens off;
Fessの設定
ブラウザで http://[IP address]:8080
にアクセスするとFessの画面が表示されます。
ログインすると管理画面が表示されますので、クロールの設定やスケジューラの設定をしてください。
詳細はFess公式サイトに記載されていますので、自分がハマったところだけ記載しておきます。
(以下はすごく素人的なハマりだと思いますので、熟練者の方は生暖かく見守りください)
Fessからのクロール先のURLはlocalhost
では機能しません。
なぜなら、fessコンテナから見たlocalhost
は、通常fessコンテナ自身を表すからです。
また、クロール先を http://django:8001
や http://nginx:8000
にすると一見インデックスが作成されているように見えます。
しかし、検索結果のリンクURLも上記アドレスとなってしまうため、検索結果からサイトにアクセスできません。
こちらコメントにて「パスマッピング」の設定をすればよいとのご指摘をいただきました!
なので、クロール先は https://yoursite.co.jp
にしましょう。
この場合、クロールのリクエストは一旦外に出ることになります。
マシンのFW等でIP制限をしている場合は、マシン自体のグローバルIPを指定しないとはじかれてしまうので注意が必要です。
その他はまりどころ
以下の設定(Ubuntu 20.04の例)をしないとelasticsearchコンテナが落ちます。
$ sudo sysctl -w vm.max_map_count=262144