はじめに
出先や移動中にメモ書きが必要になったときのメモ用にGROWIを使い始めてから、この記事を最初に書いた時点(2021年1月)でほぼ1年が経ちました。
メモ書き用GROWIのサーバは本Webサイトとは別のところで動いていますが、個人(+α)用ということと、保存するデータもテキストデータのみということでデータ量もそれほど増えない見込みのため、仮想サーバ上で普通に稼働させています。
GROWIで編集中の記事をブラウザで表示しているときに画像ファイル等をそのブラウザにdrag & dropすることでそれらのファイル等のアップロードができて、かつ編集中の記事にリンクを貼り付けることができるのを割と最近知りました(※この記事を最初に書いた時点(2021年1月)の情報です)。
…というか、socket.io関連の設定をnginx.confに追加するのを忘れていて、ファイルのアップロード機能が使える状態になっていませんでした。(´・ω・`)
貼り付けたい画像ファイル等が大量にある場合に有効に使えそうな機能の予感がします。
この機能はできれば大々的に使いたいところですが、画像ファイルなどの保管のためにはストレージの容量が大量に必要になってくるので、気が向いた時だけ稼働させている自宅サーバ上に画像ファイル等の保存用のGROWIのサーバ(以下「GROWIサーバ」と書きます。)を構築することで、ストレージの容量の問題についてはとりあえず解決とすることにしました。
GROWIサーバはこの記事に書いた方法でサクサクと構築できますので、GROWIサーバが構築できたところで、次の課題としてこのGROWIサーバにインターネット側からストレスなくアクセスさせる方法を考えることにしました。
解決すべき課題
幸いなことにGROWIはWebサーバとは別のアプリケーションであり、Webサーバをリバースプロキシ(以下、単に「Webサーバ」と書きます。)として使用するため、GROWIが動いていない場合には”504 Gateway Timeout”をはじめとした何らかのメッセージを返すことができます。
ただ、前節のGROWIサーバは「気が向いた時だけ」電源がONにされて稼働する運用を予定しています。この「気が向いた時だけ」というのがかなりの曲者です。
WebサーバはGROWIサーバが動いているどうかを知るためには実際にGROWIサーバへの接続を試みる必要があります。ところがアクセスを試みたタイミングではGROWIサーバは動いていることは保証されておらず、タイムアウト待ち(*1)が発生するかもしれません。
タイムアウトが発生した場合には、その発生までに要した時間はブラウザからのアクセスを開始してレスポンスを受け取るまでの所要時間にそのまま加算されます。したがって、ブラウザを使っているユーザはその分待たされることになります。また、得られる結果もおそらくはユーザが求めるものではないため、待たされた末に注文したものとは違うものが出てくるという、ユーザ体験(UX)的には最悪の結果となってしまいます。
そこで、
- (*1)のタイムアウト待ちが入り込む余地をなくすことと、
- 原因が「サーバが気が向いた時だけ稼働しているから」であることをユーザに知らせること。
が課題として浮かび上がってきます。
解決策のようなもの
スポンサーリンク
例によって前置きが長くなりましたが、前節の課題を解決するのに最も手っ取り速そうな策として思いつくのは、
- タイムアウト待ちが入り込む余地をなくす → GROWIサーバの開始/終了時にGROWIサーバからその旨教えてもらえばよくね?
- 「サーバが気が向いた時だけ稼働しているから」であることをユーザに知らせること → 専用のエラーページを作って表示させればいいんじゃね?
といったような方向性の策です。
GROWIサーバの開始/終了の情報については、それを記録するための入れ物でGROWIサーバ及びWebサーバの両方からアクセス可能なものをWebサーバ側に作れば良さそうです。ここではMariaDBのデータベース上にテーブルを1個作成し、入れ物として使用します。
入れ物を決めてしまえば、上記の解決策を実現するシステムはこの入れ物を境に少なくとも2個の疎結合なサブシステムに分割することができます。そこで、この記事ではこの入れ物からGROWIサーバの稼働状況を取得してそれをWebサーバ(nginx)の設定情報に反映させるための仕組みについて考えることにします。
サブシステムの構築
データベースのテーブルの作成
MariaDBのデータベース上で以下のSQL文を実行し、データベース名がserverstatusで、かつserverstatusという名前のテーブルを持つデータベースを作成します。
statusカラム(10行目)に稼働状況が格納されます。
GROWIサーバの電源がON→OFFになったときに0が、OFF→ONになったときに1がそれぞれ新規レコードとして書き込まれることを想定しています(レコードの上書きを想定していないことに注意。以下同じです)。
稼働状況を取得するためのPerlのmodule
事前準備
次に、前節で作成したデータベースのテーブルに格納された稼働状況を読み出して、nginxの設定ファイル(nginx.conf)で使用される変数に渡すためのPerlのプログラムを作成する… のですが、Fedora 33のnginxではPerlを読み込むためのnginxの動的モジュールがnginx本体とは別のパッケージになっていますので、以下のコマンドを実行することでインストールします。
また、PerlからMariaDBのデータベースへのアクセス用のパッケージもインストールしておきます。
Perlのプログラム例
事前準備が終わったところで、以下のようなPerlのプログラムを書いて、nginxからアクセス可能な然るべき場所に置きます。
Perlのモジュールとして作成するところがポイントで、パッケージ名を”ServerStatus”としています。
なお、上記のプログラムではnginxから渡されるデータは使用しませんので、
は記述していません。
nginxの設定
Perlのmoduleを起動するための設定
Webサーバのnginx.confのhttpセクションに以下の設定を追加します。
# 前略
perl_modules /opt/wiki/perl;
perl_require ServerStatus.pm;
perl_set $annex_server_status ServerStatus::getStatus;
# 草々
}
perl_modules, perl_require及びperl_setの各ディレクティブには以下の値をそれぞれ指定します。
- perl_modulesディレクティブ: 前節で作成したPerlのプログラムのあるディレクトリを指定します。
- perl_requireディレクティブ: Perlのプログラムのファイル名を指定します。
- perl_setディレクティブ: 戻り値をセットする変数の変数名及びその戻り値を返すための関数(Perlのプログラムに実装されているもの。)を指定します。
専用のエラーページを表示するための設定
専用のエラーページを表示するために、Webサーバのnginx.confのserverセクションに以下の設定を追加します。
$is_power_offに1がセットされた場合に限り専用のエラーページが表示されるよう設定しています。
…
charset UTF-8;
…
error_page 503 504 @error503;
set $is_power_off 0;
if ($annex_server_status = 0) {
set $is_power_off 1;
return 503;
}
…
location @error503 {
internal;
expires 0;
if ($is_power_off = 1) {
rewrite ^(.*)$ /503_power_off.html break;
}
if ($is_power_off = 0) {
rewrite ^(.*)$ /503_server_unavailable.html break;
}
}
何らかの理由でデータベースの稼働状況が更新されず、稼働状況が”電源ON”を示しているのに実際には電源が切れている場合には、特に何も設定しない場合にはHTMLのステータスコードとして504が返されますが、その場合も503を返す(ただし、エラーメッセージは「一般的な」503のメッセージとします。)よう設定しています。
設定の変更ができたら、nginxを再起動します。
動作確認
ここで動作確認です。
$is_power_offが1の場合
$is_power_offが1の場合、すなわち稼働状況が”電源OFF”を示している場合に、ここまでの設定を行ったWebサーバにアクセスすると…
表示できていますね。
$is_power_offが0の場合
一方、$is_power_offが0の場合、すなわち稼働状況が”電源ON”を示している場合に、ここまでの設定を行ったWebサーバにアクセスすると…
問題なく表示できているようです。👍
まとめ
Python3でプログラムを書き始めて以降、ワンライナー以外のPerlのコードは新規には作成しないことにしていたのですが、nginx.confの設定のために使えるプログラミング言語で、かつモジュールをソースコードからビルドする必要のないものがPerl以外には存在しなかった(Luaについてはこの記事を最初に書いた時点(2021年1月)ではソースコードからモジュールをビルドする必要があります。)ので、とりあえず仕組みを完成させることを優先させるためにPerlでプログラムを書くこととしました。
データベースからSQL文を使って稼働状況を読み出して、それを戻り値として返すC言語で記述されたnginxのモジュールがもし作成できるようであれば、そちらもコード例として公開する予定です。
稼働状況をMariaDB上の入れ物に書き込むまでの部分、すなわちGROWIサーバの開始/終了をWebサーバに伝える仕組みについては考察を後回しとし、かつ別記事に書きましたので、よろしければご覧ください。
この記事は以上です。