2020-03-04

ブログシステム(続)

昨日のブログで言及したMovable Typeを導入しようとして頑張ったが、うまく行かなかった。

Movable TypeはPerlで実装されているCGI(Common Gateway interface) であり、動的コンテンツ(っていうのか?)を使いたいならさらにPHPを入れる必要ありとのこと。

CGI + NGINX

うちではNGINXを使っているのでNGINXとCGIを組み合わせる必要があるのだが、ここで躓いた。

CGI雑考

そもそもCGIとは何かというと、動的なコンテンツ(って言っていいのか?少なくとも、前述とは意味が違う)をサポートするWebサーバを実現するための仕組みである。普通の(従来の、ただの)Webサーバは予め用意しておいたHTMLファイル等を見せるだけであり、コンテンツの内容が変化しないという意味で静的なコンテンツと表現するのだが、CGIでは見せるファイルそのものではなく、実行することで見せたいコンテンツを標準スクリプトに書き出すスクリプトを置く。そのスクリプトへのリクエストが来たら、スクリプトの内容をレスポンスするのではなく、スクリプトを実行して出力された結果を出力する。その結果、毎回異なる内容をユーザに見せることができるので動的なコンテンツが実現できるというわけだ。

ググるとどこもかしこもperlのスクリプト(と何故かPHP!?!?)ばかり出てくるが、原理的にはシェルスクリプトでもいいし、pythonでもいいし、何ならCのようにコンパイルしたバイナリを置いても動作するはずである(試してはいない)。実際、HaskellでCGIプログラムを作成するためのライブラリ(ズバリcgi) もある。

ぶっちゃけるとある程度Linuxを触った人なら誰でも思いつく仕組みだと思うがこれを体系立てて今日まで使われている仕組みに昇華されたことは大変素晴らしいと思う。が、perlやPHPばっかりググって出てくるあたり仕組みを知らないでとりあえず使ってみたってパターンが多そうだ。中身を理解するのは容易ではないがとりあえず使ってみる、というアプローチは成功しやすいということだろうか。Worse is better(参考1, 参考2)に通ずる話かもしれない。

FastCGI

で、肝心のNGINXとCGIをどう組み合わせるかという話だが、これを実現するには NGINXがCGIスクリプトを起動する機能が必要だ。が、なんとその機能が存在しない。

いや、広義には存在する。NGINXはFastCGI はサポートしている。

FastCGIとはプロトコルの一つで、CGIの遅さを解決する仕組みのようだ。CGIの仕組みを考えると、1リクエストごとに1プロセスを起動し終了するので、その分のコストが掛かる。また、そのたびにいちいち設定ファイルを読み込んだり、DB に聞きに行ったり等の処理が馬鹿にならない。平たく言えばリクエストごとにメモリの内容が完全にリセットされるような環境なので、とんでもなく遅いことは想像に難くないだろう。

FastCGIはその問題を解決するため、常駐するプロセスを立てておき、リクエストごとのプロセスの使い捨てをやめて高速化を実現するということらしい。が、ここで疑問が生じる。前述の通り、CGIは本来実行可能なファイルであればなんでもいい(はず)である。逆に言うと、CGIとして作成されたスクリプトはプロセスが使い捨てにされることを前提に作成されているはずである。そのため、仮に何かのプロセスが常駐したところで、それ経由で起動されるCGIスクリプトは結局使い捨てのプロセスとして使わざるを得ないのだから、高速化につながらないのではないか。

この疑問の答えは見つからなかったが、それらしい仮説を立てることはできた。 CGIはPerlスクリプトで主に実装されているので、Perlのスクリプトに限り、インタープリタを起動しっぱなしにすることでプロセスの使い捨てコストをなくせるということなのかもしれない。つまりFastCGIはCGIのサブセットのみサポートするのかもしれない。(本当に高速化する実装であれば)

調べても調べても何故かPerlとPHP絡みの古いニュース記事やブログしか出てこず原理の理解には至らなかった。

fcgiwrap

で、既存の純粋なCGIをFastCGIにラップするプログラムもある。fcgiwrapというプログラムである。

どうもMovable Typeはここでいう純粋なCGIとして作成されているっぽかったのでfcgiwrapを使用するアプローチが正しいだろうと思って選択。

当然環境を汚したくなかったのでDockerを使っていろいろやった。 nginx:mainlineイメージから開始し、必要なPerlモジュールを perl -MCPAN -e 'install <モジュール名>'でインストールし、MariaDBを設定し、NGINXの設定を済ませ、といろいろやったのだが、結局fcgiwrapからスクリプトのパスが分からんだかなんだかのエラーメッセージが出て解決できなかった。

セキュリティ雑考

投げた一番の理由はセキュリティの懸念である。動作させられなかったのであくまで推測だが、Movable Typeは管理者用アカウントを作ってWebUIで設定管理を行うようだ。ということは設定用のURLを外部に公開するということになる。そのような機能は典型的な攻撃対象であり、今まで気にしていなかった、ひっきりなしに来るWebサーバへの攻撃の対策を本気で考えなければいけなくなる。

強いパスワードを考える以前に外部から管理用URLに接続できない仕組みにするのがいいのだが、NGINXの設定をいじるかリバースプロキシを組むかになると思ったものの、とんでもなくめんどくさくなってしまったのであった。

結局手作りで

当面は手作りでMarkdownをHTMLに変換する仕組みを使い続けることになりそうだ。 Haskellで実装されているpandoc を使ってみたところなかなかいい感じだったので、これを使って.mdファイルを.htmlファイルに変換する簡単なHaskellプログラムとシェルスクリプトを書いた。

コードブロック内のキーワードに<span>要素を付けたり、class属性を付けたりしてくれるのでCSSさえ用意できればそれっぽい見た目にできる。大変ありがたい。

あと、Markdownは古のプログラマーごっこで72文字で改行して書いているため、 HTMLファイルにしてから見ると本文の途中に謎の空白が入ってしまっていた。段落内の改行は空白1文字になる。これは英語圏の文法に合わせた仕様だが、日本語とは相性が悪い。このスペースが入らない設定もpandocならできた。(正確にはできてない。日本語の場合「だけ」スペースが入らないようにする必要がある。まあ、Haskellのライブラリなので、自力で本文データをいじってスペースを取ることもできるだろう。)

お問い合わせはこちらまで: @matil019 リプにブログのリンクを含めていただけるとスムーズに会話できます。
記事一覧