新しいメールフィルタを開発 (Dovecot LDA, Postfix alias/PIPE)

新手のスパムが増えてきたので、追加のスパムフィルタを適用しようとしたのだが、従来のスパムフィルタがちょっと力技過ぎて(まぁ、2時間で作ったものだから)拡張が難しかったので、新しいフレームワークを作った。

GitHubで公開しているけれど まだREADMEを書いていないので利用は難しいだろう。

これ自体は大した話ではないのだが、部分的にとても苦戦したところがあったので、その話をしよう。

旧フィルタはPostfix aliasのPIPEで起動し、通過したものはsendmailでキューに戻すという仕様だった。 これは問題なかったのだが、フォルダへの振り分けも行いたい(特にJunkフォルダに対して振り分けたい)という理由でDovecot LDAを使うことにした。 これはArch Linuxだと/usr/lib/dovecot/dovecot-ldaである。

だが、ここで問題があった。Dovecot LDAはメールボックスに配送する。 一方Postfix sendmailはキューに入れるだけで、メールボックスへの配送はPostdropが行う。

だが、PostfixはPIPEをnobody:nobodyで起動する。 だから、メールボックスに対するアクセス権がなく、Dovecot LDAでメールボックスに配送することができない。

PIPEのユーザーを指定して起動する方法を検討したのだが、公式Wikiに「suidしろ」とか書いてあって絶望した。

ここまで特定するのに随分苦労したが、結局Postfix Sendmail同様、サーバーに対して送信する、という仕組みにすることにした。 rootで起動されているサーバーがDovecot LDAを呼ぶのでroot権限で動かすことができる。

これを実現するためにRubyのUNIXServerインスタンス(socketファイル)を777にしてあげる必要があるのだが、 UNIXServer.openにその機能がないため、

という感じ。

また、ログファイルもnobodyで書き込める必要がある。

Mimir Yokohamaに「いいね機能」「コメント機能」を追加

概要

Weekly 10000PVを達成して機能強化に力の入っているMimir Yokohamaのウェブサイト。

連続の機能強化でついに「いいね機能」と「コメント機能」が追加された。

実は先日の「お問い合わせフォームの実装」は単にその機能を実装する最小限ではなく、簡単なアプリケーションを実装できるプラットフォームになっており、 アプリケーションを追加する条件が整っていたのだ。 また、そのためのテストもしてあった。

そのため、実は今回コード追加はわずかで、両方合わせても23行ほどにとどまる。 ごく簡単だが、確証が持てないためにテストと本番環境のための修正を行ったりして結構な時間がかかった。

これに関してはみるべきところはあまりない。 受け取ったパラメータをファイルに書き込めば良いだけだからだ。

ちなみに、連打しやすいアプリケーションを入れるために連打の対策もサーバーにしてあった。 実は先のパフォーマンスチューニングはこの対策によってパフォーマンスが低下してしまったため、これをカバーするついでに行ったものだった。

いいね機能の設計

いいね機能はごくシンプルだ。

Pandocテンプレートを使ってページタイトルを埋め込むことができるので、これだけ使うのであれば単にテンプレートの中にフォームを書けばいいだけ、ということになる。

実際はリファラ(Rack::Request#referer)及びユーザーエージェント(Rack::Request#user_agent), IPアドレス(Rack::Request#ip)も特定に使用している。

ポイントは「一度送信したら表示を変更、送信を無効化する」ということだ。

inputタグのdisabled属性を使うことで送信ボタンを無効化している。

そう、この機能はHTML上でインラインで書かれているのだ。 このような書き方はW3C的には推奨されないのだが、Googleは推奨している。 実際、これだけのためにJavaScriptファイルをロードさせることをしたくなかったので、インラインにした。

ポイントは

  • Legacy DOMにおいてフォーム部品は連想配列のようにアクセスできるようになっている
  • submitボタンのラベルはvalueである
  • disabledによってフォーム部品を無効化できる

である。

これらの処理は送信の「前に」行われる。 これは正しいことではないが、問題はない。 なぜならば

  • 送信できなかったからといってユーザーが修正するなどの手を入れる余地はない
  • 特に返信を必要とするものでもないので、送信失敗はクリティカルな問題でもない
  • サーバーエラーなどは表示されるようになっている

からだ。

また、画面変遷せずに送るだけ…というと、Ajaxで非同期に送るしかないように考えるかもしれないが、 実際は現代のブラウザは基本的に2XXステータスで空コンテンツを返すとページ変遷しないようになっている。

このような用途のために204(No Content)が用意されているため、204を返す仕様だ。 できるだけブラウザの標準機能に頼るようにしている。

このボタン、めっちゃgooglebotが押してくる…

コメント機能の設計

公開されるものではなく、sanitizeしなくても問題が発生しない設計になっている(単に文字列として扱う以上の取り扱いがなされる条件で使用しない)ため、非常に楽だ。

難しく考えるよりも、シンプルで挙動をちゃんと把握できていて、余計なことをしない方法をとるのが最も楽に、確実に、バグなく設計できる。

ただ、この機能に関しては「コメントフォームの表示」などが必要になり、JavaScriptが必要になった。 また、コメントするというあまりとらない行為のための部品であり、常にロードすることは非常に好ましくない。

そこでとった方法は「JavaScriptの遅延ロード」であり、「クリック時にelementを追加してJavaScriptを読み込む」という方法だ。

これでscript要素を追加している。

では呼び出されたあとどうしているのか…というと、こんな感じだ。

そう、コメントフォームは標準でHTMLに含まれていない。

パフォーマンス的にみても親切機能のためにもう重くなりつつあるドキュメントをこれ以上重くしたくないので、 「フォーム部分はJavaScriptがロードされたときにinnerHTMLで書く、という方法をとっている。

「クリックされたときにはじめて必要とされるのでドキュメントノードを追加してロードする」という手法は稀に使われるが、さすがにそれでHTMLドキュメントそのものを生成するというのはまず見ない手法になっている。

連打されたときのことを考えて、clickイベントを発生する値に特殊なプロパティを埋め込み、何度も実行されないようにしている。 なお、これは本当に連打したときだけ機能するもので、JavaScriptシングルスレッドなので一回このスクリプトに入っていまえばこのスクリプト中に割り込まれることはない。 だが、連打されるとイベントがキューに入ってしまうので何度も実行されてしまうことから、そうしたことがないようにしている。 シングルスレッドなので、このスクリプトが実行されているのにプロパティが設定されないうちにまた実行されるということはない。

元になるフォーム自体は存在していて、submitボタンを配置すればフォームの送信は可能だ。 そのため、submitイベントに対するイベントリスナーを設定するものはHTML上に静的に存在している。

その上で「フォームはやっぱり閉じられたほうがいいな」ということでボタンクリックに対するイベントの変更、及びボタンラベルの変更を行っている。

送信を行った場合はもうコメントフォームは使わないので、コメントフォームは非表示にして(削除はしていない)ラベルを変更している。

ラベル変更だが、input部品とは違ってbutton要素は子要素テキストノードがラベルになっているので、dataプロパティの書き換えによって変更している。

もともとHTML上でLevel0 DOMイベントを使っているので、スクリプト上でもLevel0 DOMによってイベントを変更している。 いつもaddEventListenerを使っていたので、珍しい。 イベントの削除はイベントにnullを代入するだけだ。

なお、HTMLで全て含めてしまえばJavaScriptは排除できるのだが、これ以上余計な要素を組み込むことはできれば避けたいためこのような仕様になっている。既にDOMコンパイルは割と重い。

また、デザインポリシーからいって、多くの場合余計なコメントフォームを常に表示させておくというのは美しくないとも思っている。 JavaScriptを使わずCSSでオンオフするようにもできるが、そうすると今度はボタンのテキストを変更するのが難しいし、連打や再送信を防ぐのも難しい。

このことから、なにがなんでもJavaScriptを使わないのが正義、ということでなければ、 このような付加コンテンツはユーザービリティの点からも素直に使うべきだと思う。

こちらもいいね機能同様、送信する前に状態を変更してしまい、成功すれば204を返す仕様。

Legacy DOM と DOM Level0 Eventに関して

Legacy DOMやDOM Level0 Eventについて意見をもとめられることがたまにあるのだが、私としてはあまり勧められないと考えている。

非常に簡単なので学習にはいいが、Legacy DOMはHTMLの構造に依存する。例えば

として、

とかやってしまうと、ドキュメントの構造が変わるたびに修正だ。

だが、「フォームにのみnameで使い」「フォームはW3C DOMで特定する」のであれば悪くない。

DOM Level0 Eventはイベントリスナーが1つしか登録できないためモジュール設計になっている場合や、なんらかのライブラリを使っている場合は使ってはいけない。 イベントを「追加」する場合はLevel2で、イベントを「設定」する場合はLevel0という使い分けも考えられる。 HTMLに直接書く場合はLevel0しか書けないので、その部品の基本的な動作と定義されているならLevel0でもいい。

ただし、その場合でもできればLevel2 Eventを「後から追加する」のが適切なので、DOM Level0 Eventの使いどころは今回のようなものが唯一だと思う。

ConoHaのCPUは本当に速いのか試してみた

ConoHaの5周年イベントで@hironobu_sさんが「ConoHaはCPUが速いので」という話をしていたので、「本当に速いんだったら使いみちが増えるなぁ」と思って試してみた。

だいたいこういうときはLinuxカーネルをビルドするものだけど、それはちょっと私のConoHaインスタンスには辛いものがあるので、zsh-gitをビルドしてみた。 ビルドはzsh-gitを一旦ビルドしたあと、パッケージを削除して再度makepkgする方式だ。

1GBしかメモリのないConoHaではtmpfsに置くのは難しいため、公平を期すためストレージ上に配置してビルドした。 ただし、Proliantに関してはIDE HDDで低速であるため、tmpfs上に置くこととした。

マシン CPU メモリ ストレージ 結果
ThinkStation P720 (Performance) Xeon Silver 4114 *2 16GB DDR4 (tmpfs) 58.85s user 16.15s system 63% cpu 1:57.77 total
ConoHa 1GB SSD Vdisk 68.20s user 18.65s system 67% cpu 2:08.76 total
ThinkPad X1 Carbon Core i5-7200U 8GB DDR4 SATA3 SSD +LUKS 85.58s user 31.33s system 73% cpu 2:38.57 total
自作 A10 7870K 32GB DDR3 SATA3 SSD +LUKS 86.29s user 27.51s system 73% cpu 2:34.90 total
Z400 Xeon W3565 20GB DDR3 SATA2 SSD +LUKS 94.57s user 27.29s system 74% cpu 2:44.22 total
ThinkStation P720 Xeon Silver 4114 *2 16GB DDR4 NVMe SSD +LUKS 101.57s user 49.95s system 80% cpu 3:08.84 total
Proliant microserver Turion II Neo N54L 4GB DDR3 (tmpfs) 146.95s user 46.53s system 84% cpu 3:50.16 total

えぇぇぇ…

まず、超高性能マシンであるところのP720が異様に遅い。 CPU利用率は高くても8%、周波数は800MHzのままで、凄まじい余裕を見せつける…のはいいけれど、とにかく遅い。

そこでもう我慢ならぬとcpupower frequency-set -g performanceしてtmpfsを解禁してやってみた結果が1位のものだ。 この場合でもCPU利用率は0.5%未満で、辛いものがある。

さて、X1 Carbonが積んでいるのは前世代のCore i5で、ラップトップとしては標準的な性能といっていいと思う。 A10 7870Kは今のデスクトップ用Core i3と同じくらい、Xeon W3565もだいたい同じ感じだ。

この感じだとConoHaと同等というと、6コアのi7でないと厳しいのではないか、というくらいConoHaが速い。

P720とProliantが採用するのはサーバー用プロセッサだ。ProliantのTurionは激安サーバーに使われているもので、イマドキちょっと見ない。 一方、P720が使用しているのは最新のサーバー用プロセッサで、4114を2基搭載するサーバーというと、結構な高級サーバーである。例えばHPE ProLiant DL360 Gen10がXeon Silver 4114 *2という構成だが、776,000円である。

つまりConoHaはちょっと自分では持つのは厳しいコンピュータくらいの性能がある。 コンピュータがマニアックな趣味の人でなければちょっと買えない、実用的なコストパフォーマンス的には買わないようなコンピュータ並の計算力を持っているということになる。

これまでConoHaが「あれ、なんか速いな」と思うことはあったのだが、それでもそんなに計算リソースが割り当てられているとは思っていなかったので、あまり負担をかけないようにしていた。 ぶっちゃけ、AURパッケージはローカルでビルドしてパッケージをアップロードしていたくらいだ。

しかしここまで速いと、AURパッケージのビルドくらいなんということはない(メモリ的に厳しいところもあるが)、どころか、「計算用のノードとしてConoHaインスタンスを立てる」ということが成り立つレベルである。

こうなると使いようがすごく広がるし、感覚的にも全然違う。

おみそれしました…

メッセージフォームのサポート (Nginx + FastCGI + spawn-fcgi + Rack + Ruby)

あらまし

Mimir Yokohamaでついにお問い合わせ方法として「メッセージフォーム」が追加された。

なにがついになのか、なにをドヤっているのかと思うかもしれない。 まぁ、ドヤってはいないのだが。

実は私はかなり長い間ウェブアプリケーションをほとんど作っていない。 そして、今まで私が作ったウェブアプリケーションは、専用サーバーを持つサーブレットタイプか、もしくはCGIだった。

馬鹿にされがちなCGIだが、利便性は高く、頻繁にアクセスする性質を持たないアプリケーションには適している。

そして、そもそもウェブアプリケーションを作っていなかったのは、私が「事前生成戦略」の研究と実験に注力していたからで、 どちらかといえばウェブアプリケーションからは離れる方向にあった。 そして、ウェブアプリケーションを必要とするとしても大部分は静的ページとして提供できる方式を目指していたため、CGIで十分事足りたのである。

ちなみに、これまでウェブサーバーは

  • Apache
  • lighttpd
  • delegate
  • Nginx

という経過をたどっている。 Apacheは言うに及ばずlighttpdとdelegateはApacheよりもCGIが簡単だったので、「ほぼCGI」だった。

だが、時代は変わった。NginxはCGIをそもそもサポートしない。 私も新しい時代に対応する必要がある。

ちなみに、この作業は次の仕事のための実戦テストという意味合いもあった。

方針を考える

最も話が速いのはFastCGI Wrapである。

NginxはFastCGIをサポートしている。 FastCGIはプログラムをデーモンのように起動しっぱなしにする。

だが、通しで実行するプログラムとデーモンではそもそもの前提が違う。 そのためCGIプログラムをFastCGIとして動かすのはそれなりにハードルが高い。

そこでFastCGI Wrapの登場である。 FastCGIとして利用されるプログラムをFastCGI Wrapにする方式だ。 このラッパープログラムは要求に合わせて都度CGIプログラムをCGIインターフェイス経由で起動する。 結果的にFastCGIの意図は無視して従来型CGIを動作させるようにするというものだ。

この方法は結構出てくるのだが、基本的には既存のCGIプログラムを動作させる話である。

個人的な感覚としては、無駄なプロキシを噛ませるような方法を使ってまでCGIに固執したくない…というか、実はfcgi-wrapってそれなりにめんどくさい。

だったらFastCGI直というのもありかなぁ、と考えるわけだ。

ところが、やっぱりFastCGIはデーモン状のプログラムを想定しているわけで、やはり前提が違う。 要求として割と複雑なのか、デーモン化に関してはspawn-fcgiに担ってもらって、さらにRackを使う、というのがどうやら主流らしい。

だいぶ話が複雑になってきた。

サーバーはNginxである。NginxはFastCGIインターフェイスを経由してFastCGIプログラムにパラメータを渡し、応答を受け取る。

FastCGIプログラムはデーモンである。 Rubyでは次のようにしてFastCGIプログラムを書くことができる。

あるいは、CGIライブラリ互換インターフェイスを使うことで、#each_cgiの中身はまるっきりCGIと同じにすることもできる。

spawn-fcgiはこのデーモン部分を担う。 つまりeachしてる部分を担ってくれるわけだ。

プロセスとしてCGIインターフェイスで起動するわけではないので、fcgiwrapほどの互換性はない。 感覚はCGIに近いが、インターフェイスは意識する必要がある。

Rackはミドルウェアと呼ばれている。これはまずFastCGI抜きで話そう。

Rackはインターフェイスを担っている。 今までプログラムはCGIなり、あるいはFCGIなり、さらには各種フレームワークやサーブレットの様式(例えばSinatraとか)で書いていた。

Rackはこれらの違いを吸収するモジュール設計のものだ。 Rackに準拠したプログラムを書いておけば、たとえ愛用のフレームワークがディスコンになっても、サーバーが変わっても安心、というわけだ。

だが、Rack自身はサーバーではないからサーバーがいるのだが、Rack組み込みのサーバーというのはもう完全にRuby世界の住人だ。 だってRackはRubyのWebアプリケーションインターフェイスだから。

Passengerというソフトウェアがあって、これはwebサーバーのモジュールとしてRackに対応する。 Apacheでは比較的簡単だけれど、Nginxだと結構きつい。

そこでRackに対応したサーバーを立ててサーバーとサーバーでやりとりさせる、という方式がすごく現代的。 直接にRack経由でプログラムとやりとりするのはRackに対応したサーバーだけれど、Rackに対応したサーバーにwebサーバーとしての機能を持たせると大変なので、「本物のwebサーバーに矢面に立ってもらって、RackサーバーはあくまでRack対応に特化」というわけである。

Rackに特化したサーバーとしては(別にRackだけではないんだけど)、Webrick, Mongrel, Puma, Thin, Unicornあたりがある。

しかしRackでやりとりする方法があればいいので、FastCGI + Rackという方法もある。 それはRack側でFastCGI経由で受け取って、応答するためのハンドラが用意されている。

つまり、Unicornのようなサーバーを立てる代わりの手段としてFastCGIが使える。 FastCGIもデーモンを必要とするので別にFastCGIにすることで間に挟まってるものを減らす効果はない。 ただ話が楽になるだけである。

Unicornはむちゃくちゃ速いので、UnicornでUnixドメインソケットを使えば形式とししてはspawn-fcgiでUnixドメインソケットを使っているのと一緒だし、やっていることははるかに高度になる。 これが超モダンなやり方である。

が、あえてのFastCGI。 理由は管理する要素数を減らすためである。必要がないのにいかついものを使うことはしない。 これはサーバー運用のコツでもある。

なお、Rackに関してはかなり情報が少ない。 なんらかのフレームワーク…というか、ほぼRailsのバックエンドとしてのRackの話だけで、Rack単独の話ってない。 そして、FastCGIを使う話もない。これもだいたいなんらかのアプリケーションが「使ってる」あるいは「使わせる」話になる。

なんというか、みんなそんなに自分でプログラム作るってことをしてないのか… 世の中エンジニアたくさんいるのに、WordPressとRailsだけで満足なのか…

そんなわけで情報が猛烈に足りていない中、FastCGIとRackについて勉強することになったわけだ。

なお、Nginxでアプリケーションとやりとりする方法に関してはDiscourceで散々やったので経験済みだ。

なぜRackなのか

もちろんこのことからもわかるようにRackはなくても構わない。 spawn-cgiも使用せず単独のFastCGIアプリケーションを開発するのは容易である。

私が気にしたのはRubyのfcgiライブラリは2013年から更新が止まっているとい点だ。 また、Arch LinuxではfcgiライブラリはAURにもなく

# gem install --no-user-install fcgi

とするよりない。

ベーシックな機構であるFastCGIそのものが廃止になるようなことは考えにくいが、NginxのCGIの扱いのように消極的なサポートへと変遷する可能性はある。 その場合にアプリケーションの書き直しが発生してしまう。

Rackは現在主流であり、新規採用例も多い。 Rackが廃止になると影響を受ける範囲も非常に広いので今後10年は安泰だと思われる。

そこでFastCGI+Rackという構成にしたわけだ。 この場合でもRackはFastCGIをネイティブサポートしているわけではく、fcgiライブラリを使ったハンドラを同梱しているだけなのでfcgiライブラリは必要となる。実はこれを回避したかったのだが、結局はできなかった形だ。

とはいえ、この状態であればFastCGIを捨ててUnicornに移行するのも難しくはない。

とりあえずやってみる

Nginx

location / {
    root /var/www/testapp;
    fastcgi_pass /var/run/fcgi-testapp.sock
    fastcgi_index testapp.rb;
    include fastcgi_params;
}

Rack Application

Requestのほうはインターフェイスに絡むけれど、 Responseは単純に#finishでRackに沿った配列を返すための便利クラス。なくてもいい。

spawn-fcgi

# spawn-fcgi -U http -s /var/run/fcgi-testapp.sock /var/www/testapp/testapp.rb -n

試してるうちは-nつきにしてフォアグラウンドで実行するのが楽

実用的にする

起動スクリプト

forkingなので停止・再起動の制御のためPIDファイルを作る。

Systemd Unit

[Unit]
Description = FastCGI Rack Test Application
After = nginx.service

[Service]
Type = forking
PIDFile = /var/run/fcgi-testapp.pid
ExecStart = /usr/local/sbin/fcgi-testapp.bash
ExecStop = kill $MAINPID

[Install]
WantedBy = multi-user.target

forkingなので$MAINPIDがそのままでは使えないため、PIDFileで指定しておく。 Nginxのあとに起動しておいたほうがいいような気がしたけど、なくても構わない。 アクセスが激しい場合は逆にNginxの前に起動したほうがいいだろう

spawn-fcgi自体にはアプリをリロード、再起動するような機能はない。

おまけ

S-NailがSubjectも本文も、UTF-8をちゃんとエンコードしてくれるのですごくびっくりした。

「mailxとは違うのだよ!!!」ってことか。 さすがSMTPやPOPやIMAPにも対応しているだけのことはある。

ここの部分(MIMEエンコーディング)も自分でやるつもりだったので、かなり省力化された形。

今回の構築は他にも色々やったのだけれど、共有して意味のある部分はこれくらいのものだろう。

一連のサーバー&SSH技 応用・実用編

このブログは多くが検索によって解決法を探してたどり着いた読者に支えられている。

このような場合、その人が向いている方向としては二種類ある。

ひとつは、なにをしようとしているかが(適切かはどうかはともかく)決まっている場合だ。 ポートフォワーディングしたい、とか、SSHでなんとかならないか、とか、自分の中で絞り込みができている場合である。

もうひとつは自分の現状だけを認識している場合だ。 例えば「ストレージがいっぱいなった」とか、「家のデータに外からアクセスできなくて不便だ」とか。 どのような解決方法があるのか、あるいは解決可能なのかどうかから探している状態だ。

このふたつは明確に異なる。

前者であればポイントを絞った答が欲しい一方、自分の中にある前提が間違っていると解決まで回り道をすることになる。 後者であれば何をどう考えて調べればいいのか絞り込んでいくほうが難しい。

しかし前者の問題は解決できるのだが、後者の問題は解決できない、という人が割と多い。 問題を定義し、設計し、手法を構築することは地力が求められ、知識の体系化が進んでいないために発想力が求められると難しい、というケースだ。

そこで、今回はそのあたりに踏み込んで解説していく。

個別ケースや個々の人の理解に合わせた内容は仕事としてやっているので、興味があればのぞいてみてほしい

Overview

Definition

親と同居しているごく普通のLinuxer少女が、日々を便利にするために奮闘する姿でも描けばよい。

…と思ったのだが、VPSを用意するにあたりクレジットカードが必要になるあたり、やはりペット同居のごく普通の乙女、もちろんLinuxerである、ぐらいにとどめておくのが無難か。

phase 1

ストレージの不足を解消するためのNAS導入と、監視カメラの設置を行う。

スマートフォンはきっと愛猫の写真と動画でいっぱいなのだろう。 これに愛猫が勝手にツナ缶を空けて食べてしまわないかチェックしておく必要がある。

phase 2

NASに写真を保存するようにしたはいいが、このままでは仕事中に愛猫の写真を堪能できない。

24時間愛猫とたわむれるためには家にあるデータにもアクセスできなくてはならない。

phase 3

もし愛猫がトラに進化しそうなときには急いで家にかえりこれを阻止しなくてはならない。

また、愛猫がNASからハードディスクを取り出そうとしたときにはデータ保護のためにNASを緊急停止する必要があるはずだ。

構成と発展

予めお断り

コピペでできるようにすることを目標としているわけではないので、詳しい手順などは各自学んでほしい。

難しい部分は過去の記事で解説している。

家用コンピュータを導入する

この手順において一番最初に、そして独立して効果を発揮するのはNASなのだが、設定なども面倒なので、とりあえずコンピュータを用意しよう。

この段階でラップトップがあるのであれば、NASがさき、デスクトップは後でも構わないし、 この場合必ずしもデスクトップである必要はない。 だが、総合的に考えてよりよいコンピュータ環境を構築するのが目標なので、このような余剰部分も今回は入れていく。

さて、やはりコストパフォーマンスを考えても、デスクトップPCは性能が高い。 そこでデスクトップを導入することにしたいが、おしゃれに気を使う乙女としては部屋にあまりに無骨な機械を導入するのは気が引ける。 かといってせっかくコンピュータを導入するならいろいろしてみたい。 悩みに悩んだ彼女は神に問うた。天の声(はるか)はこう答えた

「Pavilion Waveあたりにしとけば?」

まじめに答えて、まじめに構築する話はそのうちMimir Yokohamaでやることにしよう。

NASを導入する

NASの導入と構築もそのうち向こうでやることにする。 今回はLinuxでのテクニック中心なので、簡単に。

ReadyNASあたりが鉄板だろうし、WebDAV(HTTP)で共有すればスマートフォンのデータを転送するのも簡単だ。

もう少しスマートな話をすると、まずはReadyNASでストレージ環境を作り、共有の作成まで進める。 共有をSMBにして、

# mount -t cifs //nas-aa-bb-cc/shared1 /srv/nas

のようにマウントする。

次にスマートフォンをLinuxデスクトップに接続し、MTP経由でデータをコピーする。 あとは

$ rsync -rv ~/Pictures/20180602 /srv/nas/

のようにして(/の有無に注意)コピーすればバックアップが確保された状態になる。

監視カメラを導入する

これはどちらかといえばSocks Proxyの説明のためなので詳しくは省くけれども、ウェブカムとmotionを使うのが定番。動体検知もできる。

これについては結構設定が色々ややこしい。 興味がある人は“Linux motion カメラ”で検索すれば色々でてくるので参考にすれば良い。 設定ファイルを眺めるのも悪くない。

ネットワークに接続する

NASを導入する時点で有線でのネットワークは構築されているはずだが、無線LANルーターを含めて、さらに 有線での インターネット接続環境を構築する。

VPSを契約する

私はConoHaのとServerMan@VPSのユーザーだが、さくらのVPSも結構定番。

今回はゲートウェイとして使うだけなので、最低限でも大丈夫でSSH接続だけできるようにすればよいが、発展的にはあまりおすすめできない。

VPSを契約したらサーバーを設定し、SSHログイン可能な状態にしておく。

ラップトップを買う

外からアクセスできるコンピュータが必要である。 ラップトップを買おう。ネットカフェのような環境からアクセスすることを考えるべきではない。

SSHを設定する

ラップトップで2つのSSH鍵を作り、それぞれデスクトップとサーバーのauthorized keyとして登録する。

制限をかけてもいいが、今回の場合鍵のパスワード保護が可能なため、特にかけないほうが良いだろう。

ラップトップ側の~/.ssh/configは設定が必要。

Host vps
  User jrh
  Port 22
  HostName vps.example.com
  IdentityFile ~/.ssh/vpslogin_rsa

Host target-proxy
  User jrh
  Port 10000
  HostName localhost
  IdentityFile ~/.ssh/proxylogin_rsa
  ProxyCommand ssh -CW %h:%p vps

この場合、VPSのログインしてからリバースプロキシを介してデスクトップにログインする。

localhostはVPSから見たlocalhost(VPSのlo)だが、デスクトップにログインするためのSSHを実行するのはラップトップである。

単純にログインしたあとログインする場合、デスクトップにログインするSSHを実行するのはVPSなので、これがProxyCommandの特徴となる。

単純にSSHを張るだけであればデスクトップからVPSにログインできるようにして、

$ ssh -R 22:localhost:10000 vps

のようにすればよいのだが、経験ではそれはうまくいかないのでそれは記事にした

外から家のデータにアクセスする

これでSSH経由でアクセスできるようになったので、デスクトップでNASをマウントしておけばデータは利用可能な状態になる。 Nemo, Thunarならばアドレス欄にsftp://target-proxy/と入力すればアクセス可能。NautilusならConnect to Serverから。 Dolphin/Konquerorにも同等の機能はあるのだが、うまく動作しない場合がある。

VPSのコンソールやmotionのストリーミングにアクセスする

VPSのコンソールなどWebインターフェイスをもつものについてはインターネットに公開するべきではなくインターネットに閉じておくべきだが、これでは外出中にコントロールできない。

だが、SSHでアクセスできる状態であれば、簡単に解決できる。

$ ssh target-proxy -NCD 8888

Chromiumが簡単。

$ chromium --proxy-server="socks://localhost:8888"

Vivladiでも良い。

$ vivaldi-stable --incognito --proxy-server="socks://localhost:8888"

Firefoxなら専用プロファイルか

$ firefox -P proxylogin

ArchLinux * Nginx * php-fpm * MariaDB * WordPress

慣れている人にとっては常識レベルなのだろうけれど、つまずきどころ満載だったので、メモ。

Nginxのインストール

# pacman -S nginx

PHP / php-fpmのインストール

# pacman -S php-fpm

MariaDBのインストール

“MySQL”と認識している人も多いだろうけれども、 現在は圧倒的にMySQLよりMariaDBが一般的である。

MySQL自体は継続しているが、MySQLを提供しているディストリビューションは非常に少なく、 そもそもアプリケーションが想定しているデータベースもMySQLよりMariaDBのほうが一般的だ。

MariaDBはMySQL5.5から派生したコミュニティベースのデータベースである。

# pacman -S mariadb

有効化する前に 初期設定を行う。

# mysql_install_db --user=mysql --basedir=/usr --datadir=/var/lib/mysql

そしてセキュアな設定を行う。 (省略しても構わない)

# mysql_secure_installation

リモートホストからのアクセスを拒絶しておく。 /etc/mysql/my.cnfskip-networkingをアンコメントすればOK。この状態でもローカルホストからは接続できる。

これでMariaDBを起動・有効化できる。

PHPの設定

PHPのモジュールが無効になっているので有効にしておく。 これがなければデータベースに接続できず、500 Internal Server Errorになる。

/etc/php/php.iniを編集する。bz2, mysqli, pdo_mysqlを有効にする必要がある。

これをしていないとWordPressの初期設定でデータベース設定後に500エラーになることになり困惑することになる。

これでphp-fpmを起動・有効化できる。

Nginxの設定

まずはサーバー設定を書いておく。 設定を書かない場合、WordPressにアクセス自体できない。

どこに何を書くかという解説はしない(Nginxについて説明をはじめると長い)ので、それを理解しているという前提で進める。 もしろん、コピペでなく値は合わせること。

最低限だとこんな感じ。

server {
  listen 80;
  listen [::]:80;
  server_name blog.example.com;
  root /srv/http/blog/example.com;

  access_log /var/log/nginx/blog-example.log;

          location / {
               index  index.html index.htm index.php;
          }
          location ~ \.php$ {
               fastcgi_index  index.php;
               include        fastcgi.conf;
          }

}

公式の設定から引用するとこんな感じ。

server {
  listen 80;
  listen [::]:80;
  server_name blog.example.com;
  root /srv/http/blog/example.com;


  location / {
    try_files $uri $uri/ /index.php?$args;
  }

  rewrite /wp-admin$ $scheme://$host$uri/ permanent;

  location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
    expires 24h;
    log_not_found off;
  }

  location ~ \.php$ {
    try_files $uri =404;
    
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    include fastcgi_params;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
#   fastcgi_intercept_errors on;
    fastcgi_pass php;
  }

}

公式の場合サイト別ではなく上流で次のような設定も必要になる。

upstream php {
  server unix:/run/php-fpm/php-fpm.sock;
}

php-fpmのソケットファイルが間違っている場合、502 Bad Gatewayになる。

WordPressのインストール

「公式パッケージはあるけどWordPress公式からインストールしたほうがいいよ」というのがArchの姿勢。

$ cd /srv/http
$ wget https://wordpress.org/latest.tar.gz
$ tar xvzf latest.tar.gz
$ sudo chown -R http:http wordpress
$ mv wordpress blog/example.com

パーミッションが間違っていると403 Forbiddenになる。 これは主に所有者で、Nginxプロセスの所有にする必要がある。 現在のArch LinuxではNginxプロセスはhttp:httpで動作する。

起動と有効化

# systemctl start nginx
# systemctl enable nginx
# systemctl start mariadb
# systemctl enable mariadb
# systemctl start php-fpm
# systemctl enable php-fpm

php-fpmが起動されていない場合は502 Bad Gateway、MariaDBが起動されていない場合は500 Internal Server Errorになる。

別のWordPressから移行する

WordPressのファイル側にもファイルがあるらしい(どうも画像データはこっちらしい)ので、MySQLのエクスポートと同時にファイルもとっておく必要がある。

取得したファイルはWordPressインストールの代わりに展開し、パーミッション(所有者)を変更する。 パーミッションの変更は忘れがち。

SiteGuardでログインアドレスを変更している場合は、この機能がApacheに依存しているためログインできなくなってしまうので、wp_content/plugins/site-guardを削除しておく。あるいは、エクスポート前にこの機能を無効にしておいてもいい。 ログインページをNginx上で変更したい場合は他のプラグインを仕様する必要がある。私の場合は“Login Rebuilder”を利用した。

従来が第三者サービスなどによって提供されていたWordPressで、データベース名やユーザー名が好ましくない場合、あるいは複数サイトをインストールするために指定したい場合はwp_config.phpも編集。

データベースをインポートする前にデータベースを用意しておく。

# mysql
> create database <database_name>
> create user '<username>'@'localhost'
> grant all privileges on <database_name>.* to '<username>'@'localhost' identified by '<password>'
> quit

そしてインポート

# mysql <database_name> < wordpress_database_file.sql

そんなわけでブログ引っ越し完了

ブログはXdomainの付属サービスから自サーバーに移動した。

これによりパフォーマンスが改善し、またスマートフォンでの閲覧において広告がでなくなった。

パフォーマンスは最善ではない。これはこのサーバーでWordPressだけが動いているわけではないため、リソースをWordPressに大量に割くことができないからだ。 必要ならば将来的にWordPressを独立したサーバーにしてパフォーマンスを向上させたり(Kasanagi導入が妥当だろう)、リソースを増強してパフォーマンスチューニングを行うかもしれない。

現状ではこのChienomiのほうのデイリーPVは1500から6000程度である。 特に広告などを出して収益があるわけでもないので、今のところあまり考えていない。 そうね、ヒューマンユニークがデイリー1500を越えるか、月間1000円以上の広告収入が発生するようなことがあれば考えようかな。

いずれ広告を出す可能性があるにせよ、それはChienomi側で選定しコントロールした話であり、プロバイダー広告が出るのは(広告を出さないというポリシーに基づく)ユーザービリティ的にもパフォーマンス的にもデメリットしかないので、Chienomiがそれなりにアクセスがあることを踏まえて対応した。

ConoHaへのサーバー引っ越しレジュメ (DeleGate-Nginx/Postfix/Dovecot, Let’s Encrypt)

概要

作業概要は以下の通りだ

  • サーバーをDTI(@ServerMan)からConoHaに変更する
  • サーバーOSをCentOS (6.9)からArch Linuxに変更する
  • WebサーバーをDeleGateからNginxに変更する
  • WebコンテンツシステムをPureBuilder2からPureBuilder Simplyに変更する
  • Mimir YokohamaのWebコンテンツを別サーバーのWordPressから新サーバーのPureBuilder Simplyに変更する
  • Webメニューを完全JavaScriptフリーにする
  • Postfix/Dovecotを引っ越しする
  • DNSサーバーをConoHaとし、DNSの設定を変更する
  • 一部のドメインを廃止とする
  • 一部のドメインの役割を変更する
  • Let’s EncryptによるSSL証明書を取得し、HTTPSに対応する
  • 同証明書にメールも対応させる

なお、あまり詳細な解説はしていない。 Linuxやサーバーに関する基本的な事項に対して理解していないまま実施するのは危険であるため、 「コピペ仕様」にはしていない。 詳細に対してエラーが生じるなどの理由でより情報が欲しい場合はあるだろうが、 「言っていることが理解できない」のであれば、サーバー構築をするにはまだ危険なレベルにあると思ったほうが良いだろう。

承前: 開発

PureBuilder Simplyの開発

今回のために新しいコンテンツシステムであるPureBuilder Simplyを開発した。 この詳細は別に譲ろう。

テンプレートの開発

PureBuilder SimplyはPandocテンプレート+eRubyテンプレートというテンプレートを扱うことができる。 PureBuidler Simplyにテーマファイルがあるわけではないので自分で書く前提である。

今回は複数の表示デザインがあるのだが(現在のところは表示されるのはアーティクルモードとプロモーションモードの2つだけだが)これは全てPandocテンプレートとCSSで実現している。

もちろん、このようなテンプレートの開発はWeb屋の腕の見せ所だろう。

CSS

サイトはそれほど華やかなデザインではないが、技術的に劣っているわけではない。 その最たるものがCSSのみで書かれたハンバーガーメニューとドロワーだろう。

ポイントを簡単に言うと

  • ドロワーはfixedで上部に最初からある。display: noneで、高さは比較的新しい単位であるvhである
  • ハンバーガーメニューはtransitionを使っている
  • z-indexでメニューのほうが上になるようにする
  • 状態変遷は次のような方法でコントロールしている
    • 表示されないチェックボックスを用意し、操作対象をlabelで関連づける。これによりlabelで囲まれた要素をクリックするとチェックボックスがトグルする
    • CSSの+セレクタは同じ親要素を持つ次の兄弟要素に適用される
    • チェックボックスにはチェックが入っているときに適用される疑似要素:checkedがある
    • そこで、チェックボックス本体の直後に対象となるセクションコンテンツを置くことでチェック状態によって状態をトグルできる
    • ドロワーはfixedなのでどこに書いても構わないので、このセクションコンテンツの中におく。場所としてはメニューの位置に基づくのが良いだろう

見た目はWordPress時とあまり変わっていないが、性能は大幅に向上した。

速度

キャッシュの効かない初回アクセスで従来のWordPressページが5.57s、新しいページは約400msと10倍程度高速化した。

ConoHaサーバーのスタートアップ

立ち上げ

ConoHaのサインアップは10分もあれば可能で、即座にサーバーを使いはじめられる。

サーバー仕様は東京リージョンの1GBプランである。

512GBプランは安いが、性能が十分でなく、またマイグレーションができないため、1GBプランとしている。

この時点でサーバー選択が可能なので、Arch Linuxを選択する。 また、この時点である程度のセットアップが可能だ。 ここでSSH公開鍵の登録が可能である。

のようにして専用に生成しておく。 そしてconoharoot.rsa.pubを登録する。 名前からもわかるように、ここで登録する鍵はroot鍵である

また、開放するポートも選択できる。 ここで開放するポートはサーバーよりも上流でフィルタリングされる。 ここで透過していないポートに対するアクセスはサーバーに到達しない。

ただし、結果的にSSHポートの変更はサーバーに負担がかかるものになっている。 少なくともパスワードログインは禁止すべきだろう。

現時点ではSSHのみを通過させる。

初期設定

ConoHaのArch Linuxは標準インストールではなく、ある程度調整されている。 主にSSHログインが可能で、SSH公開鍵が登録され、パスワード認証不可となっている、といったことだ。 こうした内容は/etc/cloud/conoha-init/conoha-init.shによって確認可能だ。

複数のサーバーを接続する予定であれば変更すべき点は多いが、そうでなければ作業は平凡だ。

アップデート

まずはアップデートする。 20MB/sも出るため、超早い。

Zsh

私としてはZshがないと話にならないので入れておく。

これ移行はZshでの作業

一般ユーザーの追加

もちろん、一般ユーザーは登録されていない。

この時点で一旦再起動しないとパスワード設定ができないので、再起動しておく。

パスワードの設定

その上でvisudoを使って%wheelに対するsudoを許可する。

pacaurとyaourtのインストール

AURパッケージも扱う予定であるため、pacaurを入れておく。

# su - jrh
% gpg --recv-keys 487EACC08557AD082088DABA1EB2638FF56C0C53
% sudo pacman -S git

%git clone --depth=1 "https://aur.archlinux.org/cower.git"
%cd cower
%makepkg -si
%cd ..

%cower -d pacaur
%cd pacaur
%makepkg -si

これでAURからのパッケージインストールが可能になったので、Yaourtを入れておく。 作業が明確に自動化されていたり、システマチックに行えるのならYaourtはいらないかもしれないが、 まとめて検索するためには欲しい。

% pacaur -S yaourt

必要なソフトのインストール

ここでいう「必要なソフト」は私の取り扱い上の話だ。

% yaourt -S ruby openbsd-netcat rsync mercurial postfix dovecot nginx fcgiwrap certbot certbot-nginx vim

一般ユーザーでの鍵ログインの準備

サーバーで受け入れの準備をする

% mkdir .ssh

ローカルで鍵を生成しておく。

% ssh-keygen -f ~/.ssh/conoha.rsa

~/.ssh/configに設定する。 これは名前によるアクセスを可能にするためである。 (同一ホストに対する異なる鍵でのアクセスのため、鍵を指定せずに済むようにでもある) もちろん、読み替えること;

Host conoha-root
  User root
  Port 22
  HostName conoha.site.static.cnode.io
  IdentityFile ~/.ssh/conoharoot.rsa

Host conoha
  User jrh
  Port 22
  HostName conoha.site.static.cnode.io
  IdentityFile ~/.ssh/conoha.rsa

rootでは作業できるので、転送する。

% ssh conoha-root 'cat > /home/jrh/.ssh/authorized_keys' < ~/.ssh/conoha.rsa.pub

サーバー側でパーミッションの設定を行う

% sudo chown -R jrh:jrh .ssh
% chmod go-a -R .ssh
% chmod go-a .

そしてsshdリロード

% sudo systemctl reload sshd

DNS設定

ドメインを複数持っていない場合はいきなり完全移行できなかったりするので、 一時的にサブドメインを作るなどする必要がある。

ConoHaコントロールパネルのDNSからDNSの設定を行う。 (DNSの設定ってなんだ、という者はサーバーを立てるにはまだ早い。修行してくるべし)

これは本番サーバーのものを含む。 ただし、稼働中の本番サーバーのアドレスをこの時点で変更してはならない

そして、DNSサーバーをConoHaのものに変更する。

今回の場合従来はドメインサービス提供のDNSを使用していたため、同サービスのメニューから変更を行った。

WebとLet’s Encrypt

Nginxの立ち上げとテスト

従来がDeleGateで、Nginxにするため互換性は全くない。

Nginxは単純にstartすればアクセス可能な状態だが、移行のための準備をしていく。

まずは、Archの記述に合致するようにserver-avilableディレクトリを読むようにしたほうが良いだろう。

http {
    include       mime.types;
    include       /etc/nginx/servers-avilable/*;
    ...
}

これで/etc/nginx/server-avilableを作ればそこに配置したファイルを読むようになった。 この時点でrestartすると…

Dec 19 18:45:42 archlinux nginx[9339]: 2017/12/19 18:45:42 [warn] 9339#9339: could not build optimal types_hash, you should increase either types_hash_max_size: 1024 or types_hash_bucket_size: 64; ignoring types_hash_bucket_size

とのことなので増やす。

http {
    include       mime.types;
    include       /etc/nginx/servers-avilable/*;
    default_type  application/octet-stream;

    server_names_hash_bucket_size       128;
    types_hash_max_size                 4092;
    ...
}

配置するファイルはArchwikiに従ったもので大丈夫だ。

server {
  listen 80;
  listen [::]:80;
  server_name domain.tld;
  root /usr/share/nginx/html;
  location / {
    index index.htm index.html;
  }

  # ACME challenge
  location ^~ /.well-known/acme-challenge/ {
    default_type "text/plain";
    root /var/lib/letsencrypt;
  }
}

domain.tldは適切なドメインに置き換えること。

ここで、あまり書かれていない重要なこと。

rootというのはドキュメントルートで、locationの起点となる。 つまり、

location /foo {
  root /usr/share/nginx/html;
}

であるとき、/foo/bar.htmlにアクセスすると読まれるファイルは/usr/share/nginx/html/foo/bar.htmlであって、/usr/share/nginx/html/bar.htmlではない

そのような単純なマッピングを行いたい場合はrootではなくaliasを使う。

ACME challengeはhttp://domain.tld/.well-known/acme-challenge/randomnumberに対して行われるため、 rootで正しい。

だが、このディレクトリがないので作っておいたほうが良いようだった。

% sudo mkdir -p /var/lib/letsencrypt/.well-known/acme-challenge

あとはcertbotを使えば良いのだが…

% sudo certbot certonly --email webmaster@domain.tld --webroot --webroot-path /var/lib/letsencrypt/ -d domain.tld

その前にConoHaのサーバー設定でHTTPをあけておかなくてはならない。 そして、IPv4とIPv6両方をあけておくこと。 別々になっていることに気づかず、ACME Challengeのリクエストが到達せずに(/var/log/nginx/access.logを見ても記録されていない)随分とハマってしまった。

移行作業

各ドメインごとのドキュメントルートを作り、ファイルを配置、 さらに対応したドメインごとの設定ファイルを作る。

今回の場合、Mimir Yokohamaのページは既にPureBuilder Simplyによる静的ファイルへのビルドが完了しているし、 移行対象のものに関してもPureBuilder2で静的ファイルにビルドされているものなので、単純にファイルを配置するだけの簡単なお仕事である。

Aki SIE関連のアドレスはdiscontinuedなので、301を返す。

server {
        listen 80;
        listen [::]:80;
        server_name aki-sie.com akisie.com aki-sie.yokohama akisie.yokohama;

        return 301 http://www.mimir.yokohama/;
}

http://journal.reasonset.net/に関しては301でリダイレクトしていたので、これも反映する

server {
        listen 80;
        listen [::]:80;
        server_name journal.reasonset.net;
        return 301 http://chienomi.reasonset.net$request_uri;
}

NginxのLet’s encryptの対応

メールサーバーの移行まで完了した時点で作業を行うのだが、 certbotで必要なドメインをすべて署名してもらったら、1SSL対応化を行う。

設定例は以下

server {
  listen 80;
  listen [::]:80;
  listen 443 ssl http2;
  listen [::]:433 ssl http2;

  ssl_certificate /etc/letsencrypt/live/domain.tld/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/domain.tld/privkey.pem;
  ssl_trusted_certificate /etc/letsencrypt/live/domain.tld/chain.pem;
  ssl_session_timeout 1d;
  ssl_session_cache shared:SSL:50m;
  ssl_session_tickets off;
  ssl_prefer_server_ciphers on;
  add_header Strict-Transport-Security max-age=15768000;
  ssl_stapling on;
  ssl_stapling_verify on;

  server_name domain.tld;

  index index.html;
  location / {
    root /var/www/domain.tld/doc;
  }

  # ACME challenge
  location ^~ /.well-known/acme-challenge {
    root /var/lib/letsencrypt;
  }
}

メールサーバーの移行

機能させるために一旦古い証明書で作業しているため、Let’s Encryptの証明書が取れたら証明書ファイルを変更してreload/restartすること。

Postfix

Postfixに関してはCentOS 6とArch Linuxではファイル配置が異なり、 バージョンの違いから設定ファイルの違いも大きく互換性に乏しい。

そこで、CnetOSの設定ファイルを/etc/postfix.centとしてコピーする。 このほか、/etc/alias*も忘れずに移行する必要がある。 また、一旦古い証明書ファイルもコピーした。

設定ファイルの違いを見るため、diffを取る。

% diff /etc/postfix.cent/main.cf /etc/postfix/main.cf > main.diff
% diff /etc/postfix.cent/master.cf /etc/postfix/master.cf > master.diff

このdiffファイルをkwriteで2開く。 今回はNemoのsftp機能を使って開いた。

このdiffを元に手作業で変更箇所を反映していく。 主にssl, auth, virtual関連の変更を反映する必要があった。

virtual関連のファイルは/home/mailuserにあったため、これをコピーする。 ただし、これはvirtualメールボックスを含むため、移行完了までは継続的にアップデートする必要がある。 この前にvirtual用に設定していたmailuserを設定する必要もあった。

% sudo useradd --no-create-home --uid=20000 --shell /bin/nologin --system -U -M mailuser

だが、これだけではグループが適正な値にならない。 そこで、グループIDを変更した上で、ユーザーの所属する主グループIDも変更する。

% sudo vigr
% sudo vipw

Postfixをrestartして完了。

Dovecot

Dovecotに関しては設定ファイルをコピーしてそのまま使うことができた。

Dovecotの設定ファイルとして/home/mailを使っていたので、これをコピーする。

Dovecotをrestartして完了。

上流

DNSを変更してもDNSキャッシュの関係でまだ旧サーバーにもメールが届く。 ある程度はaliasを使って転送しても良いのだが、素直にPostfixで受け取りつつ、Maildirのファイルを同期していくのが良いだろう。

DeleGateを使っていたため、DeleGateのSMTP転送機能を使う方法もあるのだが、うまく動作しなかったのと、TLS接続を受け付けることができないため、諦めた。

セキュリティ関連

複雑な話になるので省略。

そこまで難しいことをしているわけではないが、 普通の対策と、らしい対策とで簡単には突破できないようにしている。

後処理(旧サーバー)

DovecotとDeleGateは停止してしまう。

Postfixは5日程度稼働したら停止してしまっていいだろう。3

その後、バックアップ(主にログや設定など)をとったらサーバー破棄である。

おまけ: ConoHaについて

DNS

ConoHaのDNSは高速である上に非常に設定しやすい。

これまで使用していたお名前.comのもの、XDomainのものは非常に設定しづらかったので、 これだけでもConoHaにする価値はあるレベルだ。

料金

コンピューティングリソースはDTIとあまり変わらないが、料金は約倍になった。

ただし、ConoHaの柔軟性やメリットを考えればそう悪くない。 両方持つと少々負担は大きいが4、一気に完全移行したためDTIを解約でき5、許容すべきコストアップだろう。

速度

速い。

「コンピューティングリソースは大きく変わらない」といったが、速度は明らかにあがった。 特にネットワークの高速化とストレージの高速化の恩恵は大きく、Webの応答性は明確に向上した。

柔軟性

DTI VPSは色々と柔軟性が足りず、困った。 特に、OSのバージョンをあげようにもサーバーがひとつしかないので、できない。 ConoHaなら

  • インスタンスが立てられるのでサーバー仕様変更もできる
  • OSが選べる。さらにカスタムイメージのアップロードも可能
  • サーバーのアップグレードが簡単にできる(512MBプランを除く)

ファイアウォール

上流でフィルタリングしてくれるのはとても嬉しい。 設定が楽になる、というのもあるが、サーバーの耐障害性が勝手にあがる上に、まあまあ重いiptablesでの処理量が減るため、パフォーマンスが改善する。 リソースが限られている中では非常に嬉しい。


  1. Let’s Encryptの証明書はSAN(Subject Alternative Name)に 対応したもので、fullchain.pemが全てのドメインに対応したものになる

  2. Kwrite/Kateにはdiffのハイライト機能があるからだ

  3. TTLは3600だったため、それに従うならば1時間待てば良いのだが…

  4. DTIが月額500円ほど(年間6000円程度), ConoHaが900円ほど(年間10800円程度)である

  5. といっても、私はDTI SIMの契約があるため、DTIの契約そのものが終了するわけではない

サーバーを使ったConoHaを使うと幸せになれるnの理由

はじめに

この記事はConoHa Advent Calenderとして書かれたものである。

Qiitaのみなさん、ConoHa愛好家のみなさん、はじめまして。 コンニチハ、ハルカデス。

Qiitaをよく見ている人ならこのブログをご覧頂いたこともあるかもしれない。 技術的な記事を期待しているかもしれないが、今回はカテゴリからしてもおわかりの通り、ビジネス的なお話である。

ConoHaについて

ConoHaはGMOグループが提供しているVPSサービスである。 以前はVPSではなくレンタルサーバーを名乗っていたような気がするが、最近はVPSだと言っている。

「三雲このは」というマスコットキャラクターで有名なサービスになる。

そのためにサービス自体については軽視されがちなのだが、サービスも特徴的である。

ホスティングサービス, VPS

まず、かなり難しい区分なのだが、web基準で考えると次のような段階がある。

  • 用意されているアプリを使うだけ
  • 用意されているスペースにファイルが配置できる
  • サーバーを操作できる
  • サーバーを構築できる

アプリを使うだけ、というのはブログやECなどの特定の機能を持ったアプリケーションによるサイト公開が可能になっているものだ。 主立ってはamebloやSeeSaaなど、ConoHaと同じGMOグループではGMOペパボのJUGEMや、GMOメディアのYaplogなどがある。

さらに、GMOペパボには「ホームページ作成サービス」系のGoopeもある。 Qiitaもこの形態のひとつであるといえる。

用意されているスペースにファイルが配置できるのは、かつてのGeocitiesが有名で、 これを有料のサービスで行うというのはかつてはひとつの頂点であったとも言える。 最近は減ったがかつては多彩なサービスがあった。 GMOペパボのLolipopはその中でも有名にして定番のものだろう。私も以前は使用していた。また、同社はHetemlという上位サービスも用意している。 GMOインターネットもドメインサービスのお名前.comの一環として提供していたりする。

残る2者はいわゆるroot権限がとれるタイプのものだ。 前者はサーバーの再インストールや、サーバーのOS選択などは基本的にできない。後者はサーバーのインストールや追加なども可能なものだ。

VPS

VPSという語に明確な定義というか、区別はないようだ。 VPSという語が登場した当初のことを考えると、基本的に「rootがとれる従量課金のサーバー」という意味合いであったように思われる。 実際、VPSと名乗るもので、従来のホスティングサービスとの違いが「従量制課金である」ということ以外にあまり明瞭なものがなかったのである。

VPSで当初よりAmazon AWSは非常に強力なサービスであったが、 通信料など外部的要因によって変動する事象によって従量課金となるため、軽い気持ちでサーバーを立てた人が月に数十万円の請求をされる事案も少なくなかった。

そのために、金銭的にかなり余裕があるのでない限りVPSは選択肢にはならなかったのが実情だ。

ConoHaは当初その中間のような存在だったと言える。 VPSのようにいくつものサーバーを立てることができるが、料金はサーバー台数*プランというシンプルなもので、過大な請求がかかることなく安心して使用することができた。

現在のConoHa

当初と比べConoHaは幾分VPSらしくなった。 例えば計算資源からは独立したストレージ容量や、時間従量制などだ。

時間従量制というのは、携帯電話料金のような上限つき従量制と近いものだが、 例えば512MBプランは月額630円で、1時間あたり1.0円。24時間*31日だと744円であるため、それよりも安く収まることになる。

普通は従来型ホスティングサービスが月額制なのだから、現代では珍しい純粋なユーザーメリットによる料金体系である。

このために一時的な用途やテストサーバーなどを含め気軽かつ柔軟にサーバーを用意できることがConoHaの大きな特長である。

ライバル

このようなサービスはあまり類をみないように思うが、最大のライバルはやはりさくらインターネットによる「さくらのVPS」だろう。

このサービスも月額制で複数インスタンスが利用可能だ。 人気はこちらのほうが高く、周囲にも利用者が多い。

クーポンをイベントで配布していた、という点でも類似である。 ただし、ConoHaはクーポン配布はやめてしまったようだ。

お客様のサーバーにConoHaを

私が代表を務めるMimir Yokohamaでは、サーバーを使用するサービスを提供する際には指定がない限りConoHaを使用している。

これは、私がConoHaからコミュニティ支援を受けているという事情もあるが、 別にこれは他サービスの利用を制約するものではない。 ConoHaを使用しているのは、それによってエンジニアとお客様が幸せになるためだ。

サーバーを伴うサービスの展開戦略

自社で使用している巨大なインスタンスにすべて格納する「共有サーバー」スタイルを採用している事業者も多いだろう。

実際のところ、Mimir Yokohamaのお客様のご要望は、サーバー一台用意するのはコスト的に見合わないタイプ(アクセスの少ないウェブサイトなど)と、 共有させるのは難しいタイプ(XMPPサーバーを立てたり、アプリケーションサーバー必要とするケース)が混在している。

ひとつの仮想ホストですべてを賄おうとするのは、管理面を考えてもあまりうれしいことはない。 パフォーマンス低下によってお客様にもストレスをかけてしまうかもしれないし、 セキュリティ的に分離したいケースがあることも普通に考えられるのだ。

つまり、多少アップグレードしてでも複数の環境をホスティングする共有タイプのサーバーを提供してコストを抑えるべきケースと、 性能は控えめでもサーバーを専有すべきケースがある。

これをroot権限があるタイプのレンタルサーバーで行うのは、費用がかさむ上に管理も大変だ。 以前そのようなサービスを提供していたこともあるが(Mimir Yokohamaができるより前の話だ)、正直労力に見合わないものであった。

ConoHaならば仕様の異なる複数のインスタンスを立てることができる。 事業の拡大とともにインスタンスをどんどん増やしていっても構わないのだ。

2GBプランであれば共有webサーバーにも十分耐える。 上手に構成すれば1GBプランでも十分だ。 (分散できるのであれば1GBプランのホストを2つ用意するほうが負荷は厳しくない)

負荷の少ない環境で専有サーバーが必要なケースは512MBプランのインスタンスを使うといいだろう。

性能面

私は個人サイトはDTIのサーバーで運営しているのだが(このブログはまた違う)、速度的にはConoHaにとてもかなわない。 最大の理由は「すべてSSDで提供している」というストレージバックボーンだろう。 Mimir YokohamaのWebプランは可能な限り静的ページとして提供する構造をしている。 そのため、ほとんどストレージ性能と帯域で速度が決まる。「高速なウェブページ」を売りにする以上、インフラが遅いことによる限界というのは悲しいものがある。

もちろん、湯水のようにお金を使えば大幅なパフォーマンスアップも可能なのだが、 「リソースが足りてさえいればコストを抑えても性能が確保できる」というのは非常に嬉しいポイントだ。 仕方なくサーバーを使われるお客様というのは、なるべくなら費用は抑えたいとしつつも、性能はストレスのないものだと信じているものだ。 あまり性能が劣っているとお客様が悲しまれてしまう。

ConoHaは最小の512MBプランを、ちゃんと「多くのリソースは使わない」というだけの理由で選ぶことができるのだ。

以前はConoHaよりもさくらのVPSのほうが安かったのだが、現在は最小プランではほぼ同等の内容でConoHaのほうが少し安い。 1GBプランはConoHaのほうが安いにもかかわらず、SSDの容量はConoHaのほうが大きい。 Mimir Yokohamaは一般のお客様が中心であるため、これらのプランが中心となる。

この「費用の小さいプランでも満足度が高い」ということは、サービスを提供する際の価格設定を抑えられるということにもなる。 サーバー側の金額が安いとあまり大きな利益を載せにくいという面はあるが、安い価格から提供可能なサービスは競争力を向上させるだろう。

イメージ保存機能

サーバーを「凍結したい」という場合や、「移行したい」「サーバーは廃止するがデータは損失したくない」というケースは意外と多い。 サービスを開始する際にサービスを終了することを考えたくない気持ちはわかるのだが、 良いサービスのためには「お客様が気持ちよく完了できること」は不可欠な要素なのだ。

イメージ保存機能はこうしたケースにおいて極めて便利だ。 イメージ保存機能の存在によってお客様に柔軟な終了戦略を提示することができる。

API

ConoHaにはAPIがある。 これはさくらのVPSに対するアドバンテージでもある。

APIを使用することでサーバーの管理を容易にすることができる。

多くのサーバーを展開する場合において手作業の量を減らせば価格設定も抑えることができ、 それは事業の強みとしても活かせるはずだ。

しかも、APIはシンプルなJSONであるため、書くのも簡単。 このような機能をスマートに活用することは、優れたサービスとして欠かせない作法であろう。

カスタムISOからのインストール

重要なのはISOイメージによるインストールが可能なことだ。

Mondo RescueによってシステムバックアップのISOイメージ化か可能であるため、ローカルな仮想環境で練った(アップデートも済ませた)サーバーイメージをテンプレートとして使用することで、スタートアップの時間と労力を減らすことができる。

これは納期を急がれるお客様のご要望にお応えできることに加えて、 コストの大幅な削減にもつながる。

無駄な手間をかけることで価格を釣り上げるような戦略はとるべきではない。 お客様が本当に必要とされているもののためにより多くの労力をかけるべきなのだ。

これは、VPSでなければ難しい(場合によってはVPSですらも難しい)Arch Linuxによるサーバーによって 少ないリソースでもパフォーマンスを発揮するという点でも活用されている。

海外リージョン

今は日本でも海外の顧客を相手にする企業は大変多い。

B2Bサービスを提供する場合、お客様は海外向けにサービスを提供される場合は多いのだ。 実のところ海外とのトランジットが割と少ない日本のサーバーというのは、海外から見るとあまりうれしくない。 どこかの果てにある得体のしれないサーバーであり、実際に応答が遅い。 これは我々が日本からベルギーやデンマークあたりのサーバーにアクセスすれば体験できる事象だ。

私もお客様が海外と商売をされているケースというのは「意外なほど多い」という印象を受けている。 このようなケースにおいてはやはりサーバーも日本ではなく適切な場所に配置したいところだ。

ConoHaにはアメリカとシンガポールという、「適切な場所」としやすい2箇所が用意されている。

このおかげでよりお客様にとってかゆいところに手が届くサービスを展開しやすくなっている。

提灯記事?いやいや…

なんといってもこの記事はConoHaのAdvent Calenderのために書いたものだし、 私がコミュニティ支援を受けているということもあり、PR記事疑惑は拭えないところだろう。

だが、そんなことはない。 なんといっても、特にこの記事作成にあたってConoHaから見返りを得ていないのだ!!!!! (もちろん、見返りをいただけるのなら大歓迎である!!!!!)

実際にMimir Yokohamaのサービスというのは、「ConoHaがある」ということが先にあった。 つまり、Mimir Yokohamaにラインナップされているサーバーを利用するタイプのサービスは、 いずれも私が「ConoHaがあるからこんなこともできる」という発想によって設定したものであり、 ConoHaがなければこれらのサービスは存在しなかった可能性が高い。 実際、ConoHaとの関係がなかった初期にはサーバーを使うサービスというものは非常に限定的であった。

もちろん、ConoHaがなくてさくらのVPSがあるという状況であれば、 いずれそのようなサービスを考えてさくらのVPSを使って展開していた可能性は高いが、 ConoHaを使うことによってお客様にメリットを提供しつつ、 それを事業の強みとして活用することができているのだ。

それどころか、一時期「ConoHaを活用できる」ということは、事業の価値において非常に大きな割合を占めていた。 そのようなVPSの利用があまり一般的でなく、圧倒的に低価格に、かつ柔軟にサービスを構成することが可能だったからだ。

Mimir Yokohamaはお客様の笑顔のために、ConoHaを利用しています。

WordPressでリバースプロキシを介すると301ループする/X-Forwarded-Forが反映されない

301ループ

リバースプロキシ(DeleGate)の向こうにあるWordPressが301ループするという問題だ。

これはWordPressのサイトアドレスをDeleGateがアクセスするものにしておけば問題は発生しない。 ただし、リバースプロキシを置く以上、バックエンドのアドレスは隠蔽したいわけで、これは望ましくない。

一見、MOUNTオプションとしてvhostでなくnvhostを使用することで解決できるかに思えたが、実際は解決しない。 問題がHostヘッダにあるのは間違いない。

調べると、requested_urlredirect_urlが異なる場合に301でリダイレクトする、ということがわかる。 DeleGateはどうしてもHostヘッダは自分で生成してしまう(スルーしてくれない)ため、Hostを元に生成されるrequested_urlと、サイトアドレスを元に生成されるredirect_urlが一致しない。

そこで、起動設定に以下を加えた

HTTPCONF="add-qhead:X-Forwarded-Host:%I"

だが、WordPressがX-Forwarded-Hostを拾ってくれないのか、それともjournal.reasonset.net:80とポートを含めるためなのか、これだけでは動作しない。

そこで、wpconfig.phpに以下の記述を加えた

$_SERVER['HTTP_HOST'] = "journal.reasonset.net";

Hostが違う場合は無視することになってしまうが、まぁ良いとしよう。 ちなみに、X-Forwarded-Hostの値を代入しても、やはりポートを含むためなのか、動作しない。

X-Forwarded-For

DeleGateでX-Forwarded-Forを設定する方法は

HTTPCONF="add-qhead:X-Forwarded-For:%a"

であるが、これで正しく動作しない。 X-Forwarded-Forの値をとるとどうしてもDeleGateの値になってしまう。

どうも、ブログを設置しているXDomain側もリバースプロキシをもっていて、そこでX-Forwarded-Forを設定しているようだ。

仕方ないので、別の値も与えておく

HTTPCONF="add-qhead:X-Forwarded-Delegate-For:%a"

wpconfig.phpでこちらを使わせる

$_SERVER['HTTP_X_FORWAREDED_FOR'] = $_SERVER['HTTP_X_FORWARDED_DELEGATE_FOR'];

これで実際のアドレスを拾わせることができるようになった。

サーバーをManjaro i3に

サーバー(ProLiant Microserver, Manjaro Linux LXQt)が、起動中にコケるようになっていたので、Manjaro i3で作りなおしてみた。

バックアップ作業

単純にsystemdユニット、そこから呼ばれるスクリプトと設定ファイルをバックアップした。
後にfstabもバックアップする必要があったことに気づく。

i3 Window Manager

i3 window managerは、タイル型ウィンドウマネージャだ。

タイル型ウィンドウマネージャというと、AwesomeやXMonadが有名だが、Manjaroで最も活発なのはi3だ。i3, XMonad, bspwmの比較を見ても、最も人気があるのはi3であるようだ。

Awesomeと比べると利点は多い。マルチディスプレイに対応しているというのが大きいのではないか。

bspwmもManjaroではCommunity buildのあるウィンドウマネージャだ。bspwmのほうが、ウィンドウに隙間があってスタイリッシュだが、i3のほうがとっつきやすい。ちなみに、Gnome3をi3で使っている人もいるようだ。なかなか変態的だ。けれど結構使いやすいのではないかという気もする。
ここまでカスタマイズすれば作業環境は快適だろう。

机でちゃんとした作業スペースがあるわけでもなく、膝の上の小さなキーボードの上でも扱いやすい、また複数のモニタープログラムを並べ、状況を把握したい場合にGUIを立ち上げてコンソールを叩くようなケースも考えられるのだし、サーバーにi3というのはベストな選択なのではないか。

i3やbspwmというとArchばかり出てくるが、それはArchであれば環境構築が楽なGUIとしての選択肢にもなりうるし、そもそもArchでもなければなかなかパッケージもないからだろう。

非常に機能的で使いやすいウィンドウマネージャだ。

Manjaro i3

Community buildではあるが、Manjaroにはi3 Editionが存在する。
ウィンドウマネージャの操作感こそ違うが、全体的な印象としては変わらない。Thusインストーラもそのままだ。インストール手順も、LUKSやbtrfsに関するソフトウェアも変わらない。

Manjaro Welcomeはいきなりフローティングウィンドウで表示される。
フローティングウィンドウはタイルとは完全に独立として表示され、マウスでフォーカスすることになる。ちなみに、クリックフォーカスではなく、オーバーフォーカスである。

ModはSUPER(Windows)に設定されている。Mod+Enterでターミナルエミュレータを開くことができる。基本的な操作方法は画面に表示されているし、mod+Shift+hでヘルプを表示できる。

なお、フリードライバで起動する必要があった。

アップデート

キーリングがおかしなことになってしまうので

# pacman-key --init
# pacman-key --populate archlinux manjaro
# pcaman-key --refresh
# pacman -Syuu

その間にavahi-daemonとsshdをenableしておく。mdns_minimalは既にエントリに記載されており、問題ない。

さらに、/etc/default/grub にデフォルトオプションとしてsystemd.unit=multiuser.targerを追加しておく。
グラフィカル起動する時は、Grubでオプションを編集して削る。

再起動したら、

# mhwd-kernel -i linux45

Linux 4.5はまだRC1だが、問題なく動作した。

設定

退避したファイルを戻すのだが、tarパイプでファイルとして保存したものを、tarパイプで戻そうとするとパーミッションを書き換えられてしまうので要注意。一度これでやり直しになった。

/etc/fstabを書いて完成だ。

i3は良いけれど

Synapticsによる設定をちゃんとしていれば結構快適だったりするし、Cinnamon/Plasma/XFceはタイリングもでき、便利なアプレットもあったりするので、実際に選択する機会は少ないかも。