うなすけとあれこれ

2015年09月23日

middlemanとh2oとVPSによるブログ構築

h2oのbuild

wordpressからmiddlemanへ

wordpressからmiddlemanへの移行は、今まで(はてなダイアリーからtumblrへ、tumblrからwordpressへ)のお引越しとは違う、極めて大規模な物になることが予想されました。理由は、 「記事の移行が発生する」 からです。

むしろ何故いままでのブログ移転では記事の引っ越しをしていないか、という話ではあります。しかし、今回の移転は記事の移行が必要になる理由が2つありました。

維持費がかかる

wordpressはさくらのレンタルサーバー スタンダードを使って運営していました。これを残しておくとなると、月額515円の維持費が発生します。なので、新しいブログに移転するタイミングでサーバーを解約してしまいたかったのです。

アクセス数の多い記事がある

Slackに関する記事、windowsとubuntuのdualbootに関する記事などは定期的なアクセスが発生しており、検索流入もそれなりにあるのでぜひとも残したいという気持ちがありました。

移行にあたって考慮した点

以前の記事を残しつつ、新しくブログを作るにあたって考慮したのが、以下の項目です。

以前のURLからアクセスできるようにする

ありがたいことに、記事へのリンクを記載していただいているwebページもありました。それらのリンクを変更することなく新しいブログへ誘導させたいと思いました。

記事はmarkdownで書く

wordpressの頃は、markdownモードがあるにも関わらず、直接htmlを書くか、あるいはmarkdownで書いてからhtmlに変換して編集エリアにペーストしていました。それをやめ、すべてmarkdownで書くことにしました。

ブログの外見は自分で書く

すでに存在するテーマ等は使わず、自らSassを書いて一からデザインすることにしました。

最新の技術を取り入れていく

最新の技術を勉強する上で重要なのは、実際に使ってみることだと思っています。h2oの使用を決めたのも、この理由によります。

ブログ移行

それでは、ブログ移行までの道のりを綴っていきます。

現状確認

worpressで運営していたunasuke.comは、78個の記事、309の画像、日ごとに100前後のアクセス数をもつブログでした。ドメインはお名前.comで取得したものをさくらに登録して使用していました。主な流入は検索によるものでした。使用しているwordpressのバージョンは4.2系、有効になっているプラグインは13個ありました。

wordpressから、なにに移行するのか

静的サイト構築ツールはいくつも存在します。その多さはStatic Site Generatorsを見ていただけると分かります。僕が候補として考えたのは、以下の5つです。

Octopressは、その特徴的なfaviconで使用しているブログがすぐ分かります。あのfaviconが好きになれなくて使わないことにしました。Hugoは、記事生成のスピードが凄まじいと話題ですが、Goで書かれていることもあり、Goのエコシステムが未知なことと、今後の記事生成で不満が出てきたら乗り換え先として考えることにし、今回は使わないことにしました。SpinaはRails製CMSとのことで少し触ってみましたが、(unasuke/spina-test)ドキュメントが殆ど無いこと、そもそもCMSであることから使わないことにしました。最終的に残ったJekyllとmiddlemanでだいぶ悩みましたが、会社や所属するコミュニティでmiddlemanを使っていること、先輩にmiddlemanを勧められたことにより、middlemanを使用することに決定しました。

さくらのレンタルサーバーから、なにに移行するのか

レンタルサーバーをやめ、自由度の高いVPSに移行することは決めていましたが、その先は3つ候補があると思っています。

ConoHaは不安定であることが友人の使用例からわかっていたので使わないことにしました。AWSとさくらで迷いましたが、これまで使っている会社であること、信頼性に関して実績があることからさくらのVPSを使用することに決定しました。

Apacheから、なにに移行するのか

これについても3つの選択肢がありました。

これについては、最新の技術を取り入れていくという目標があるので、迷わずh2oを使用することに決定しました。

wordpressから記事と画像のエクスポート

記事の移行のために、wordpressから記事を移行する作業をしました。移行にはmdb/wp2middlemanを使用しました。エクスポートしたXMLを、

% wp2mm wordpress.2015-07-25.xml

とし、すべての記事について手作業でmarkdownに変換しました。markdownに変換する、以下の様なオプションもありましたが、試したところ画像の処理がうまくいっておらず、やはり手作業は必要でした。

% wp2mm wordpress.2015-07-25.xml --body_to_markdown true

また、画像も撮影した時点のファイル名となっていたのを、意味のある名前に変更、年ごとにディレクトリを分割などの作業を行いました。

体感では、この作業が一番長く時間がかかったように思います。markdownized · unasuke/blog@e0658de

middlemanの設定を整える

middlemanは素で使うのではなく、ブログ用の拡張であるmiddleman-blogを適用させて使うことにしました。記事のURLをwordpressの頃とほとんど変えないなどの設定をconfig.rbに書いていきました。他にも、livereloadの設定や、layoutの設定などを整えていきました。History for config.rb - unasuke/blog

サイトのデザインをしていく

デザインをする上で、以下の条件をつけました。

これは、色が増えると調和を取るために考えなければいけないことが増えるのを避けるためと、画面サイズ、解像度になるべく依存しないようにするためです。Sassは慣れているし、bootstrapもSassに移行するとのことでAltCSSのデファクトスタンダードとなりつつある(ような気がする)からです。

また、会社のデザイナーさんに教わったことですが、白と黒(#fffと#000)の組み合わせはコントラストが強くて良くないそうなので、純粋な白、黒は使用しないことにしました。

結果的に現在のようなものになりましたが、これで決まったわけではなく、今後も修正は続けていきます。

web fontをどうにかする

web fontも興味があったので使ってみることにしました。どのフォントを使うにせよ、サブセット化(使う文字だけ収録されたフォントファイルをつくる)は日本語を使う以上は避けられないと考えました。3846masa/japontを使って動的にfontを生成しようかとも考えましたが、とりあえずはM+Web FONTS Subsetterを使うことにしました。それにあたって、ブログ内で使われている文字を抽出する必要があり、以下の様なスクリプトを使って文字を抽出し、web fontを作成しました。

VPS serverの初期設定

server OSにはUbuntu 14.04 LTS amd64を選択しました。

まずはOSのインストール、SSHの設定、iptablesの設定を行いました。

% sudo vim /etc/ssh/sshd_config
% sudo service ssh restart
% sudo apt-get install iptables-persistent
% sudo invoke-rc.d iptables-persistent save

h2oのビルド

以前の記事URLから、新しい記事のURLにリダイレクトさせるために、正規表現が必要になると考えました。具体的には、

# old url
http://unasuke.com/diary/2015/yapc-asia-tokyo-2015/

# new url
http://blog.unasuke.com/2015/yapc-asia-tokyo-2015/

このようになります。categoryがすべてtagに統一されたので、URLからcategoryを除いたものへとリダイレクトさせてやりたいのです。

まずは、h2oが素で正規表現を用いたroutingを行えるかどうか確認します。

# h2oのビルド
% sudo apt-get install locate git cmake build-essential checkinstall autoconf pkg-config libtool python-sphinx wget libcunit1-dev nettle-dev libyaml-dev libuv-dev
% sudo apt-get install unzip zlib1g-dev bison
% wget https://github.com/h2o/h2o/archive/v1.5.0-beta4.zip
% unzip v1.5.0-beta4
% cd h2o-1.5.0-beta4
% cmake -DWITH_BUNDLED_SSL=on .
% make
% sudo make install
% h2o -v
h2o version 1.5.0-beta4
OpenSSL: LibreSSL 2.2.2

はいりました。ひとまずexampleの設定を使って立ち上げてみましょう。

% pwd
/home/hoge/h2o-1.5.0-beta4
% h2o -c ~/h2o-1.5.0-beta4/examples/h2o/h2o.conf # conf内のpath指定があるので注意

これで8080番ポートにアクセスするといい感じになにか見れると思います。ちなみに、example/h2o/h2o.confを以下のように書き換えると、URLにポートを指定せずに接続することができますが、h2oの起動に管理者権限が必要となります。

# to find out the configuration commands, run: h2o --help
# 秘密鍵とかも消す
listen: 80
hosts:
  "127.0.0.1.xip.io:80":
    paths:
      /:
        file.dir: examples/doc_root
    access-log: /dev/stdout

では、このyamlでワイルドカードが使えるかどうか試してみます。

# to find out the configuration commands, run: h2o --help
# 秘密鍵とかも消す
listen: 80
hosts:
  "127.0.0.1.xip.io:80":
    paths:
      /:
        file.dir: examples/doc_root
      "/test/test":
        file.dir: examples/doc_root
      "/*/test":
        file.dir: examples/doc_root
    access-log: /dev/stdout

この設定でh2oを立ち上げたところ、/test/testにはアクセスができましたが、/hoge/testにはアクセスできませんでした(not found)。なので、mrubyなどでなんとかしてやる必要があると感じました。

ひとまず、mrubyをインストールします。

% sudo apt-get install mruby libmruby-dev

mrubyのインストールができたら、h2oをmrubyを使えるようにしてもう一度buildします。

% cmake -DWITH_BUNDLED_SSL=on -DWITH_MRUBY=on .
% make
% sudo make install
% h2o -v
h2o version 1.5.0-beta4
OpenSSL: LibreSSL 2.2.2
mruby: YES

なにも考えずにh2oの最新版Release H2O version 1.5.0-beta4 · h2o/h2oをbuildしたら、どうやらmruby拡張のAPIがRackベースのものに変更になったらしく、そのサンプルコードを動かしてみてもどういう動作をするのかが全く読めなかったので、Rack based APIに変わる前でvulnerability修正がされているRelease H2O version 1.4.5 · h2o/h2oを使うことにしました。

ひとまずexamples/h2o_mruby/h2o.confを以下のように変更します。

# to find out the configuration commands, run: h2o --help

listen: 80
# host名はサーバーのドメインに設定してもよい
hosts:
  "127.0.0.1.xip.io:80":
    paths:
      /:
        mruby.handler-file: examples/h2o_mruby/hello.rb
    access-log: /dev/stdout

そしてアクセスすると、次のような文字列が返ってきます。

hello from h2omruby. User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10105) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.99 Safari/537.36 New User-Agent:new-Mozilla/5.0 (Macintosh; Intel Mac OS X 10105) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.99 Safari/537.36-h2omruby path:/ host:hoge method:GET query:

では、mrubyをつかって旧URLから新URLへのリダイレクトをさせるように、やっていきます。

まず、旧URLの形式ですが、次のようになっています。

http://unasuke.com/#{カテゴリー(diary,howto,info,review,about-me)}/#{記事公開年(2013から2015)}/#{記事タイトルっぽい英文}/

これに一致する正規表現を以下のように作成しました。

reg = Regexp.compile("(diary|howto|info|review|about-me)(/.*)")

さて、mrubyはrubyとは違い、組み込み向けなので正規表現は標準の状態では使用できません。mgemというmruby用のgemを組み込んでmrubyをコンパイルし直す必要があります。しかしh2oがbuildするmrubyには既にmattn/mruby-onig-regexpが組み込まれているので、正規表現が使えます。(気付かずに自前でbuildしてた)

class HelloApp
  def call(env)
    h = "hello"
    m = "from h2o_mruby"

    ua = env["HTTP_USER_AGENT"]
    new_ua = "new-#{ua}-h2o_mruby"
    path = env["PATH_INFO"]
    host = env["HTTP_HOST"]
    method = env["REQUEST_METHOD"]
    query = env["QUERY_STRING"]

    # この辺を追記
    reg = Regexp.compile("(diary|howto|info|review|about-me)(/.*)")
    result = "#{reg =~ path}\n $1=#{$1}\n $2=#{$2}\n"

    msg = "#{h} #{m}.\n User-Agent:#{ua}\n New User-Agent:#{new_ua}\n path:#{path}\n host:#{host}\n method:#{method}\n query:#{query}\n"

    [200,
     {
       "content-type" => "text/plain; charset=utf-8",
       "user-agent" => new_ua,
     },
     ["#{msg}\n\n#{result}"]
    ]

  end
end

HelloApp.new

正規表現の結果

できました。これをドメインを見てリダイレクトさせるかどうか判断させ、さらに実際にリダイレクトさせればいいわけです。リダイレクトさせるには、301を返してやればいいので、このようになります。

class HelloApp
  def call(env)
    path = env["PATH_INFO"]
    reg = Regexp.compile("(diary|howto|info|review|about-me)(/.*)")
    result = "#{reg =~ path}\n $1=#{$1}\n $2=#{$2}\n"

    [301,
      {
        "Location" => "https://blog.unasuke.com#{$2}"
      },
      []
    ]
  end
end

HelloApp.new

無駄な部分もありますが、実際にこれでリダイレクトができました。

このmrubyを使用した最終的なh2o設定ファイルは次のようになりました。

listen: 80
listen:
  port: 443
  ssl:
    certificate-file: /hoge/path
    key-file: /hoge/path
hosts:
  "unasuke.com:80":
    paths:
      /:
        mruby.handler-file: /home/blog/redirect.rb
  "unasuke.com:443":
    paths:
      /:
        mruby.handler-file: /home/blog/redirect.rb
  "blog.unasuke.com:80":
    paths:
      /:
        redirect: https://blog.unasuke.com/
  "blog.unasuke.com:443":
    listen:
      port: 443
      ssl:
        certificate-file: /hoge/path
        key-file: /hoge/path
    paths:
      /:
        file.dir: /hoge/path
    access-log: /hoge/path

access-log: /hoge/path
error-log: /hoge/path
pid-file: /hoge/path

あとは、middlemanでbuildした記事を適当なディレクトリに配置してやれば完成となります。(疲れたのでまた後で書きます)

参考

middleman

ubuntu

h2o

mruby

SSL

2015年09月23日