noellabo's tech blog

@noellaboの技術ブログ

ElasticsearchをMastodonとは別のサーバに設置する

新しくVPSを借りたので、そこにElasticsearchのサーバをたてたい! という話がありました。

よい機会なので、Elasticsearchのインストールからsudachiを使えるようにする設定変更まで、ざっくり記録しておこうと思います。

Elasticsearchをインストールする

説明のために、サーバはUbuntu 18.04という前提で話を進めますが、動けばなんでもOKです。

まずはjavaの実行環境から

さくっとOpenJDK 11を入れます。

sudo apt install openjdk-11-jre

Elasticsearchを入れる

記事執筆時点では、elasticsearch 7.3.1が最新です。aptにパッケージの取得元と鍵を追加してinstallします。

wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
sudo apt install apt-transport-https
echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-7.x.list
sudo apt update
sudo apt install elasticsearch

環境変数JAVA_HOMEを設定しておきます。

echo "JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64" | sudo tee -a /etc/default/elasticsearch
echo "JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64" | sudo tee -a /etc/environment  
source /etc/environment 

elasticsearch.ymlに設定を追加します。

  • クラスタ名を何か付ける(例:mastodon-search
  • ノード名を${HOSTNAME}にする(識別できれば何でもOK)

複数のサーバを設置してクラスタを構成することで、負荷を分散したり、レプリカを持たせたりできますが、ここでは1台だけで構成します。

cat <<-_EOS_ | sudo tee -a /etc/elasticsearch/elasticsearch.yml
cluster.name: mastodon-search
node.name: ${HOSTNAME}
_EOS_

jvm.optionsにJVMのヒープサイズの設定をします。サーバのメモリの35%50%ぐらいを割り当てるのが目安だそうです。ここでは、2GBのVPSという想定で、既存の設定をコメントアウトして、新たに512MBずつ割り当てます。

sudo sed -i -E 's/-Xms/#&/;s/-Xmx/#&/' /etc/elasticsearch/jvm.options
cat <<-_EOS_ | sudo tee -a /etc/elasticsearch/jvm.options
-Xms512m
-Xmx512m
_EOS_

analysis-sudachiを入れる

必要なファイルを取ってきて、analysis-sudachiをインストールします。辞書を取ってきて/etc/elasticsearch/に展開。sudachi.jsonを作成して、使用する辞書の指定と、いくつかの補助設定をしておきます。

wget https://github.com/WorksApplications/elasticsearch-sudachi/releases/download/v7.3.1-1.3.0/analysis-sudachi-elasticsearch7.3.1-1.3.0.zip
sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install file://${PWD}/analysis-sudachi-elasticsearch7.3.1-1.3.0.zip
sudo apt install unzip
wget https://object-storage.tyo2.conoha.io/v1/nc_2520839e1f9641b08211a5c85243124a/sudachi/sudachi-dictionary-20190718-full.zip
unzip sudachi-dictionary-20190718-full.zip
sudo mv sudachi-dictionary-20190718/system_full.dic /etc/elasticsearch/
sudo chown root:elasticsearch /etc/elasticsearch/system_full.dic
cat <<-_EOS_ | sudo tee -a /etc/elasticsearch/sudachi.json
{
    "systemDict" : "/etc/elasticsearch/system_full.dic",
    "userDict" : [],
    "inputTextPlugin" : [
        { "class" : "com.worksap.nlp.sudachi.DefaultInputTextPlugin" },
        { "class" : "com.worksap.nlp.sudachi.ProlongedSoundMarkInputTextPlugin",
          "prolongedSoundMarks": ["ー", "-", "", "", ""],
          "replacementSymbol": "ー"}
    ],
    "oovProviderPlugin" : [
        { "class" : "com.worksap.nlp.sudachi.MeCabOovProviderPlugin" },
        { "class" : "com.worksap.nlp.sudachi.SimpleOovProviderPlugin",
          "oovPOS" : [ "補助記号", "一般", "*", "*", "*", "*" ],
          "leftId" : 5968,
          "rightId" : 5968,
          "cost" : 3857 }
    ],
    "pathRewritePlugin" : [
        { "class" : "com.worksap.nlp.sudachi.JoinNumericPlugin",
          "joinKanjiNumeric" : true },
        { "class" : "com.worksap.nlp.sudachi.JoinKatakanaOovPlugin",
          "oovPOS" : [ "名詞", "普通名詞", "一般", "*", "*", "*" ],
          "minLength" : 3
        }
    ]
}
_EOS_

ユーザー辞書を追加すると良いのですが、ここでは説明を割愛します。テーマサーバでは、語彙を増やしておきたいですね!

起動する

systemctlで起動します。

また、バージョンが変わる時にsudachi等のプラグインを再インストールする必要があるので、aptで勝手に更新されないようにロックしておきます。

sudo systemctl start elasticsearch
sudo apt-mark hold elasticsearch

外から繋げられるようにリバースプロキシとファイアウォールを設定する

elasticsearchには、ノード間の通信とREST APIにTLSを適用するオプションがありますが、REST APIについてはnginxなどリバースプロキシでhttps接続できるようにする方が設定が簡単で安全と思われます。

REST APIはポート9200を使いますが、伏せる意味でもあえて異なるポート(ここでは9100)で受け付け、ファイアウォールで接続元をMastodonサーバに限定します。

というわけで、ここではnginxとcertbot、ufwを使います。インストールからざっと説明しますが、既に入っている場合は設定だけ参考にしてください。

なお、ここではDNSにcloudflareを使っているという想定で、certbotのdnsプラグインも追加しておきます。ご自身の環境に合わせて選択してください。(このあたりについてはLet's Encrypt(certbot)で、TLS-SNI-01の削除により証明書の更新に失敗する問題への対応の中で説明しています)

ufwは、ssh(22)と、9100ポートへの接続を許可します。 sshに22以外を使っている場合は各自変更してください。他の必要なポートがある場合も忘れずに開放しておきましょう。

sudo apt install nginx certbot python3-certbot-dns-cloudflare ufw
sudo ufw allow 22/tcp
ufw enable

で、Mastodonを実行しているサーバからだけ、elasticsearchの9100ポートに接続を許可します。

sudo ufw allow from (Mastodonを実行しているサーバのIPv4アドレス) to any port 9100 proto tcp
sudo ufw allow from (Mastodonを実行しているサーバのIPv6アドレス) to any port 9100 proto tcp

certbotはdns-01で認証します。/etc/letsencrypt/.cloudflare_credentialsにアカウントとAPI Keyが保存されている想定です。

certbot certonly --dns-cloudflare --rsa-key-size 4096 --dns-cloudflare-credentials /etc/letsencrypt/.cloudflare_credentials -d example.com -m admin@example.com --manual-public-ip-logging-ok --agree-tos --no-eff-email

nginxの設定は、/etc/nginx/sites-available/elasticsearch.confに書いて、

cat <<-_EOS_ | sudo tee -a /etc/nginx/sites-available/elasticsearch.conf
map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

server {
  listen 9100 ssl http2;
  listen [::]:9100 ssl http2;
  server_name example.com;

  ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
  ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;

  ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

  location / {
    proxy_pass http://127.0.0.1:9200;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  location /statuses_ {
    if ( $request_method ~ ^(PUT)$ ) {
      rewrite ^(.+)$ $1?include_type_name=true break;
    }
    proxy_pass http://127.0.0.1:9200;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  location ~ _search$ {
    rewrite ^(.+)$ $1?rest_total_hits_as_int=true break;
    proxy_pass http://127.0.0.1:9200;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }
}
_EOS_

/etc/nginx/sites-enabled/elasticsearch.confにシンボリックリンクを張ります。

cd /etc/nginx/sites-enabled
sudo ln -s /etc/nginx/sites-available/elasticsearch.conf

nginxの設定に記述間違いがないかテストして

sudo nginx -t

nginxを再起動(起動してなければstart、起動していればreloadの方が良い)

systemctl restart nginx

動作確認

そもそもサービスが起動しているのか確認

sudo systemctl status elasticsearch

ダメっぽかったらjournalを見る

sudo journalctl -ru elasticsearch

リアルタイムにモニターする場合はこう

sudo journalctl -fu elasticsearch

ダメならログをみる

sudo less /var/log/elasticsearch/mastodon-search.log

ローカルからREST APIで状態を確認。バージョンとか色々でる。

curl http://localhost:9200/ 

リバースプロキシにhttpsで接続

curl https://example.com:9100/ 

インデックスの一覧をみる(今は何もないけど見出しだけ出る)

curl https://example.com:9100/_cat/indices?v

Mastodon側の設定

elasticsearchのサーバに繋がるかどうか真っ先にテストしておきましょう。

curl https://example.com:9100/ 

.env.production

.env.productionで、elasticsearchを有効にし、サーバとポートを指定し、プリフィックスを指定します。

プレフィックスを指定すると、elasticsearchが他のMastodonサーバの検索など他の役割を引き受けている場合にインデックスを区別できます。

ES_ENABLED=true
ES_HOST=https://example.com
ES_PORT=9100
ES_PREFIX=example

app/chewy/statuses_index.rbapp/services/search_service.rb

Mastodon本体のコードにも修正が必要です。

app/chewy/statuses_index.rbapp/services/search_service.rbを、sudachiを活用するように修正します。具体的な内容は、githubのコミットを参照してください。 https://github.com/noellabo/mastodon/commit/e2214ec9844d1e17c219065e1034814077b39119

こちらをgit cherry-pickするか、差分を見ながら自分で修正してください。

sudo -iu mastodon
cd ~/live
git remote add noellabo https://github.com/noellabo/mastodon.git
git fetch noellabo
git cherry-pick e2214ec9844d1e17c219065e1034814077b39119

この変更で、sudachiの力によって日本語を適切に扱えるようになります。また、IDでソートすることで、検索結果の表示を最新順にしています。

Mastodon側からdeployする

過去の記事をインデックスし、検索できるようにします。

tootctlに専用のコマンドがあります。mastodonのliveディレクトリにて、

bin/tootctl search deploy

を実行します。投稿が多数蓄積されているサーバでは時間がかかるので覚悟した方が良いです。tmux等でssh接続が切れても大丈夫なように対策しておきましょう。

設定に不備があったりすると早めにエラー終了するので、内容に応じて対処してください。

Mastodonの再起動

.env.productionとコードの変更を反映させるため、再起動します。deployの時点で書き間違え等によるエラーはあらかた出尽くしているかと思いますが、ちゃんと動くかどうか色々確かめてください。

sudo systemctl restart mastodon-*

【オマケ】elasticsearchをアップデートする

遠からず、elasticsearchのアップデートに対応する時が来ると思います。その際の手順の参考です。

  • elasticsearchのプラグインはバージョンチェックが厳密で、本体のバージョンと完全一致したものを使う必要があり、毎回削除して入れ直しが必要
  • なので、本体だけうっかりアップデートすると死ぬので、普段はapt-markでholdしておくこと
  • https://github.com/WorksApplications/elasticsearch-sudachi/releases からanalysis-sudachiの最新リリースをチェックし、対応版がでてから切り替えるのが吉
    • 自分でpom.xmlを書き換え、関連する箇所を修正してビルドしても良いが、ここでは割愛する

analysis-sudachiのv7.0.1-1.3.0にアップデートする例を挙げておきます。実際に適用する際は、zipファイルのファイル名nalysis-sudachi-elasticsearch7.0.1-1.3.0.zipとURLhttps://github.com/WorksApplications/elasticsearch-sudachi/releases/download/v7.0.1-1.3.0/analysis-sudachi-elasticsearch7.0.0-1.3.0.zipの記載箇所を入れ替えて実行してください。

sudo /usr/share/elasticsearch/bin/elasticsearch-plugin remove analysis-sudachi
sudo apt-mark unhold elasticsearch
sudo apt install elasticsearch
sudo apt-mark hold elasticsearch
wget https://github.com/WorksApplications/elasticsearch-sudachi/releases/download/v7.0.1-1.3.0/analysis-sudachi-elasticsearch7.0.1-1.3.0.zip
sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install file://${PWD}/analysis-sudachi-elasticsearch7.0.1-1.3.0.zip
sudo systemctl restart elasticsearch

謝辞・参考資料

MastodonでのElasticsearchの設定方法については、なんと言ってもクラゲ丼のぜまさんの記事が先駆けです。私も大変お世話になりました。本記事の設定部分の記述については、ほぼそのままお借りしています。

また、nginxの設定の箇所で採用した、Elasticsearch 7.xに対応するための互換オプションをnginxでrewriteするという手法は、Balさんが編み出した手法をお借りしています。めっちゃスマートな解法です。