読者です 読者をやめる 読者になる 読者になる

PythonでWebアプリケーション作る練習をした

最近流行っている雰囲気があるPythonだけど、僕も年末にふと気分が高まったので、練習をしてみた。

自分にとってはPerlやRubyよりも先に勉強した、初めてのスクリプト言語がPythonだったので、ちょっとだけ思い入れがある。とはいえ、何年も前に初めてのPythonで勉強した後は、稀に使うくらいだったのでPythonならではの良い書き方とか、良いライブラリの知見とかは全然持ってなかった。

そこで、Pythonに入門しなおしてPython流を思い出した後、自分が気になっているWeb開発をやってみてどういうもんなのかを一通りやってみた。

このエントリは、GoでWebアプリケーション作る練習をしたの続編です(さらに続きはないでしょう)。

入門 Python3 を読んだ

入門 Python 3

入門 Python 3

一通りの文法を思い出そうということでとりあえず読んでみた。他の言語を一通りやっていれば新しい概念はほとんどないのでサクサク読める。僕は3,4日くらいでだいたい目を通せた。

ざっと読んだことで、Pythonならではのリスト内包表記とか、関数デコレータの話題とかを思い出せることが出来てよかった。PythonってリテラルにSet型があるんだなーとか。

Pythonの文法の話題だけにとどまらず、テストやデータベースへのアクセス、HTTPクライアントやサーバ(WSGI)の話題について一通り教えてくれるのも良い。最近の本らしく、NoSQL(Redis)やメッセージングキュー(ZeroMQ)あたりの話題も取り上げられていた。各章に練習問題がついてるので実際に手を動かすこともできる。

はてなインターンの事前課題

はてなインターン参加者は、はてなオフィスにやってくるまでの予習として、事前課題というのをやることになっている。ちょっと古いけど公開されていて、Perl版とScala版がある。その課題をPythonで解いた。ソースコードの python-Intern-Exercise はこちら

この事前課題は、プログラムの基本的な書き方、ファイルI/O、テストなどを網羅しているので、同じ内容の課題をPerlやScala以外の言語でやると良い練習になっておすすめ。ちょっと前に id:shiba_yu36 さんがやっていた、 Java版はこちら

Webアプリケーションの実装

さらに練習として、PythonでWebアプリケーションを作ってみることにした。あれこれ調べながら、自分がよくやるスタイルでWebアプリケーションを書くとどういう感じになるかを調べてみた。

github.com

結論としては、Pythonでどういう書き方をすると良いのかとか、どのライブラリを使うのかといったことを調べるのには時間がかかったものの、それさえわかれば、だいたいやりたいことはできるということがわかった。

普段はPerlを使っているのだけど、Perlで使っているテクニックやライブラリはだいたい対応するものがあって同じやり方が通用する感じ。例えば、テストの感じとかは、いつも書いているPerlのコードに近いインターフェースのサポート関数を利用できるようにできた。

もしかすると何かの参考になるかもしれないので、実装の方針を紹介しておいてみる。

PerlでWebアプリケーションを作るときにもよくやっている、薄いフレームワークの上になるべくシンプルにアプリケーションを構築するという方針でつくっている。複雑なしくみは使わず見通しの良さを重視してる。使っているライブラリは最低限で以下の様な感じ。

  • Webフレームワーク: Flask
  • データベース: ORMは使わずに mysqlclient をそのまま利用
  • テスト: nose (追記: 識者によると今開発止まってるのであんまり使わないほうがいいらしかった!)

ファイルツリーは以下のような感じになっていて、それぞれの役割も書いた。

.
├── db/
│   └── schema.sql
├── hakoblog/
│   ├── config.py  # アプリケーション設定用のクラス定義
│   ├── model/     # アプリケーションのモデル定義(BlogやEntryなど)
│   ├── loader/    # モデルオブジェクトをデータベースから取得するクラスが入ってる
│   ├── action/    # アプリケーションに対して破壊的な操作を行うクラスが入ってる
│   ├── db.py      # データベース接続を抽象化したクラスの定義
│   └── web.py     # Webアプリケーションのルーティングとコントローラ
│   ├── templates/ # HTMLテンプレート
└── tests

ORMは使わないので、modelの定義は単純なPythonのクラスになっている(例: Entryクラス)。

モデルオブジェクトをデータベースから取得するにはloader内で定義されたクラスを使う。クラスはただのネームスペースで、クラスメソッドとして処理を定義する。例えば、UserLoaderは以下のような雰囲気になる。

from hakoblog.model.user import User


class UserLoader():
    @classmethod
    def find_by_name(cls, db, name):
        with db.cursor() as cursor:
            cursor.execute(
                '''
                SELECT id, name
                FROM user
                WHERE name = %s
                LIMIT 1
                ''',
                (name, )
            )
            row = cursor.fetchone()

            if row is None:
                return None

        return User(**row)

SQL内に%sが出て来るのでドキッとするけど、これでちゃんとplaceholderになっている(mysqlclientの仕様で%sを使う)。

破壊的な操作をするときはactionに同様のクラスを定義することに、ざっくり責任をわけておく。こうすることでデータを読み込みつつ破壊的な操作を定義しにくくなる狙いがあったりする。

db.pyはDBクラスが定義してあってmysqlclientライブラリを薄くラップしてある。接続のための設定を読み込んだり、ちょっとしたユーティリティ関数(例: uuid_short関数)を定義するのに便利。

web.pyでは、HTTPのルーティングとコントローラを定義してある。アプリケーションのエントリポイントはここになる。

実装を試すのが主目的なので実用性はまったくない。記事書いたり記事を読んだりはできるけど、ユーザ認証などはないし、スタイルも書いてなくてみためは限界に近くしょぼい。

感想

ちょっとPythonに入門したあと、あれこれ調べながらPythonでWebアプリを0から書いてみた。

当然、普通にWebアプリケーションは書けるわけだけど、Perlでいつもよく使う設計スタイルも無理なく採用することができることがわかったのは良かった。コードを書いてみると、標語*1のとおりで、一つのことをやるやり方がだいたい決まっているので、迷わずにどんどん書き進められるのは心地よかった。

あんまり凝ったことをしない文化っぽいけど、必要になったらやっていくという感じがあるのもなんとなくわかってきた。例えば、テストのために時間をとめるライブラリ(freezegun)とかはちゃんとあったりして、柔軟性も大事にされていそう。

スクリプト言語としてなかなか良いバランスだし、Web開発がうまくできそうなことも体験できた。そうでなくても、機械学習やデータサイエンス分野で勢いのある言語だと思うので、もうちょっと練習して使えるようにしておいても良さそう。

*1:There should be one-- and preferably only one --obvious way to do it.

無線機を購入した

9月ごろに第一級アマチュア無線技士の資格を取得したのだけど、今の今まで無線機を持ってなかった。資格を取ったのが引越をした直後のお金を使いまくっていた時期だったので、しばらく高価なものを買う気になれなかった...。

引越後の生活も落ち着き、せっかく資格をとったのだからちゃんと実際の電波の送受信をやってみたいという気持ちも復活してきたので、ついに昨日、自分へのクリスマスプレゼントとして無線機を購入した。

購入したのは 八重洲無線のFT-817NDという機種。

自分の家の電波の状況は今のところわからないので、最悪まったく電波が受信できないない可能性がある。なので、まずは移動運用できる機種にしておいて、最悪電波が受信できなくても、野外の広いところにいって使うという選択肢を残して起きたかった。また、技術的にもコミュニティの層的にもおもしろいと聞く、HF帯に出れる機種にはしたかった。

この条件に見合う機種はほとんどなくて、購入したお店の人によると今回買ったFT-817NDくらいとのこと。自分で調べたところだとKX3も候補にあげられるのだけど、こっちは高価だし、技適に通ってないので開局の手続きのハードルが高そうであったのでやめておいた。

前に一度覗いたアマチュア無線の専門店に出向いて、FT-817NDほしいんですと言うとお店のおじさんがおすすめのアンテナなんかも教えてくれて、あれこれ一式をスムーズに購入できた。結構値引きもしていただいて、古き良き電化製品購入プロセスの体験をしてきたという感じ。

購入しただけではまだ自由に使うことはできなくて、無線機から電波を出すには、総務省に無線局の開局の申請をして免許される必要がある。手続きも時間がかかるのでのんびり取り組んでみようと思ってる。気の長い感じだけど趣味としてはじっくり楽しめて良いと思う。

第一電波工業 ダイヤモンド  7-50MHz帯ポータブル無線機用ハンディアンテナ コネクターBNC-P RHM8B

第一電波工業 ダイヤモンド 7-50MHz帯ポータブル無線機用ハンディアンテナ コネクターBNC-P RHM8B

次に何を勉強するかを決めるための作戦

Webエンジニアが学ぶべき技術範囲はとても広く、いったい何をどこから勉強していくかは難しい問題です。僕も試行錯誤を繰り返しています。

そんな試行錯誤の中で、新しく何を勉強するか決めるときに使ってる作戦がいくつかありそうだなと思うようになりました。そこでこの記事では、僕が次に勉強すべきテーマに困ったときに使っている作戦を紹介してみようと思います。

各作戦の例のコーナーでは実際に僕がその作戦を使って勉強したトピックなどを紹介しています。

このエントリは、はてなエンジニアアドベントカレンダー2016の20日目の記事で、担当はid:hakobe932です。昨日の担当は id:masayoshi さんでLinuxのARPとL2スイッチのお話という記事でした。

作戦1: 新しいプログラミング言語を学ぶ

新しいプログラミング言語を学ぶのは、比較的手を出しやすい作戦です。プログラミング言語を学ぶことで自分が使えるプログラミング言語が増えるのはもちろんですが、これまでとは違ったプログラムの捉え方や考え方も学ぶことができます。

また、プログラミング言語を学ぶ過程で様々な技術をつまみ食いできるのがお得なポイントです。例えば、ファイルの扱い方、プロセスやスレッド、ネットワーク、正規表現、リストや辞書などのデータ構造などの話題は言語の入門本にはよく登場します。詳細に学ぶことはできませんが、とっかかりには悪くありません。複数の言語を学ぶことで、これらの技術を多様な角度から学ぶこともできます。

勉強用の資料には分量が多くて詳しく書いてありそうなものを選ぶのがおすすめです。著者が言語の作者やそれに準ずる人である資料であればなお良いと思います。そのほうが表面的な文法や使い方だけでない考え方や背景についても教えてくれますし、いろんな技術がつまみ食いできます。

例: Scalaスケーラブルプログラミング第3版 / すごいErlangゆかいに学ぼう! / 初めてのPerl 第6版 / 続・初めてのPerl 改訂第2版 / すごいHaskellたのしく学ぼう! など

Scalaスケーラブルプログラミング第3版

Scalaスケーラブルプログラミング第3版

  • 作者: Martin Odersky,Lex Spoon,Bill Venners,長尾高弘,羽生田栄一,水島宏太
  • 出版社/メーカー: インプレス
  • 発売日: 2016/09/20
  • メディア: Kindle版
  • この商品を含むブログを見る

Dave Thomas先生も一年に一個は新しい言語を学ぶべしとおっしゃってるので、丁度良いんではないでしょうか。この作戦ではいろんな技術をつまみ食いできるのが良いのですがつまみ食いでは満足できなくなってきたら作戦2に移りましょう。

作戦2: 計算機科学分野からテーマを選んで勉強する

計算機科学の分野にあげられるような比較的基礎的な技術を学ぶという作戦です。計算機科学の分野といっても幅広いですが、Webエンジニアに関係が深いのは例えば、OSやネットワークの仕組み、アルゴリズムや、計算理論、ソフトウェア設計、並行プログラミング、データベースなどでしょうか。計算機科学の分野はWikipediaにざっくり載っているので、これを眺めたりしてもいいかもしれません。この中から必要性や自分の興味のバランスで分野を選びましょう。得意分野から攻めていくとモチベーションが続きやすい気がします。

一度勉強すれば長く役立つ、基礎的な技術を学ぶのが目的です。特定のソフトウェアやライブラリの使い方のような知識はある瞬間はとても役立ちますが、ライブラリのバージョンが変化してしまったり、そのライブラリが廃れてしまうと役に立たなくなります。「寿命が長い技術」を学ぶのが費用対効果が良い作戦です*1

本を探すときには、自分の得意分野ではちょっとがんばって一番分厚いバイブル的な本を読むと効率が良いと思います。不慣れな分野では、近くのひとに聞いたりインターネットを探したりして、がんばって良い入門書を見つけましょう。

例: オブジェクト指向入門 / エリック・エヴァンスのドメイン駆動設計 / アルゴリズムイントロダクション / プログラミング言語の基礎概念 / スクリプト言語の作り方 / Java並行処理プログラミング / アジャイルサムライ / データベース技術実践入門 / マスタリングTCP/IP 入門編 第5版 など

オブジェクト指向入門 第2版 原則・コンセプト (IT Architect’Archive クラシックモダン・コンピューティング)

オブジェクト指向入門 第2版 原則・コンセプト (IT Architect’Archive クラシックモダン・コンピューティング)

さらなる基礎を抑えるという意味で、数学の復習をしてみたりも良いでしょうが、深みにハマっていくときりはなさそうです。

作戦3: お隣の分野に入門する

自分の専門ではないけれど、近い領域で役立ちそうな技術を見繕って勉強してみるという作戦です。僕の場合は、機械学習や電子工学といった技術は今すぐには必要になってはいませんが、そのうち役立つだろうと、ちょっと入門してみたりします。最近はこの作戦3を使っていることが多いです。

例: 機械学習 / 電子工学 / など

ちょっと別の話題として、作戦1や作戦2を繰り返していると、徐々に自分がもっている知識が増えてきて学習曲線が緩やかになってきます。それ自体は結構なことですが、やっぱり勉強をはじめたところの、高速に成長してる感覚を取り戻したくなるときがあります。そういうときは作戦3で勉強ネタを選ぶと、成長している感が得られて楽しかったりします(ちょっと逃避っぽい側面ですが)。

番外編: 自分がやりたい仕事ができそうな技術を学ぶ

純粋に技術的ではないですが、ある意味王道の作戦です。おもしろい仕事ができそうな技術を学びます。

技術をもっていないとその技術を使う仕事ができませんから、当然学ぶ必要があります。加えて、自分が勉強しているトピックというのものは、なんだかんだで会社の人にも見られていますから、その技術に関係ある仕事を任せられる可能性があったりなかったりするかもしれません。

高難易度の技としてはイケてる技術学んで、その技術を使った仕事を自分で作りだすというやりかたもありそうです。これはできたらかっこいいですね。

まとめ

僕が新しく何を学ぶかを決めるときに使っている作戦を紹介しました。勉強し続けるモチベーションを維持するには、常に勉強したいことをストックしておくのも大事です。Webエンジニアとして勉強をしていく上で、このエントリで紹介した作戦が何かの役にたてばうれしいです。

明日のアドベントカレンダーは id:syou6162 さんです!

*1:このあたりはid:stanakaさんの受け売りです

AtomからPerlのテストを直接実行するくん

チームで流行ってるので最近はAtomを使ってる。普段仕事ではPerl書くのでAtom上からテストをサクッと実行できるくんを作った。

atom.io

既存のscriptの拡張として動作する。本家のscriptでもPerlのファイルの実行はできるけど、以下のような点で便利に使えるようになっている。

  • carton exec -- proveしてくれる
  • カーソルがある位置が含まれている Test::Class 形式のテストケースを個別に実行できる

手元だけで使ってたんだけど、最近 hitode くんにも使ってもらって、ちゃんと動いてそうということが判明したので、apmにあげた。どうぞご利用ください。そこそこ雑。

このときにscriptの不具合発見してdiff 1byteのPullRequestに成功して笑顔です。

GoでWebアプリケーション作る練習をした

GoでWebアプリケーションを書いてみる練習として RequestBin ぽいものを試しに作ってみた。gomibakoという名前であまりひねりはない。以下のURLで試せます。

https://gomibako.douzemille.net/

ソースコードもGitHubに公開してある。

github.com

何ができるか

HTTPリクエストを受け付ける用のURLを作ることができて、そのURLに対するHTTPリクエストのログをWeb上で確認することができる。ちょっとしたWebHookの動きのチェックとかリバースプロキシの設定確認とかに使えて便利。

具体的には以下の様にして使える

  1. https://gomibako.douzemille.net/ にアクセスして "New Gomibako" ボタンを押す
  2. https://gomibako.douzemille.net/g/deadbeaf123/inspect みたいなURLが作られてリダイレクトされる。このページにログが表示される。
  3. 画面に表示されている、https://gomibako.douzemille.net/g/deadbeaf123 みたいなURLに別のタブやcurlとかでHTTPリクエストを送る
  4. 2で表示したページに今アクセスしたHTTPリクエストが表示される

ログ画面は以下のような感じになる。

f:id:hakobe932:20161125120132p:plain

HTTPアクセスがあるとリアルタイムにログが更新されるようになっていてるのが、ちょっとおもしろい。TwitterにURLをながすといろんなクローラが次々とやってくるのがリアルタイムに楽しめる。

なんか怖いのでHTTPリクエストのログは永続化していない。URLごとに最新10リクエストしか保持しないし、URLを作ってから一時間経つとそのURLは破棄されて使えなくなる。

ちょっとリクエストの様子を調べられるところが欲しかったので、自分としては満足してる。これまではリクエストログをダンプするだけのちっちゃいWebアプリケーション(app.psgi 3行くらい)を使ってたけど、いまいち使いづらかった。

技術的見どころ

習作という感じであまり見どころはないけど、せっかくなので紹介します。

Webアプリケーションの構成するGoのライブラリ

GoのWebアプリケーションフレームワークは多種多様で選択肢は豊富なものの、Goの net/http パッケージがしっかりしているので、あんまり分厚いフレームワークは使わずに済ませたほうがシンプルで良さそう。ということで最低限のライブラリで構成してる。

  • httptreemux
    • さすがに net/http のルーティングは機能が貧弱なのでつかってる
    • HTTPルータの実装もいろいろあるけど、速度やメモリフットプリントのパフォーマンスを優先して機能が足りないのも多い
    • httptreemuxはまぁまぁバランスが良さそう
  • negroni
    • 一番うすそうなミドルウェア実装かな、と思って選んだ
    • ミドルウェアのインターフェースも独自の型とか使ってなくてリーズナブルな感じ
  • secure
    • セキュリティ関係のヘッダをいい感じにつけてくれる汎用のミドルウェア

これくらいでだいたい困ってないし、とくていのライブラリへの依存度も高くないので結構いいんではと思ってる。セッションとか、データベースへのコネクションとかのグローバルステートの管理とかしたくなってくると、もうすこしいろいろ入れたくなってくるのかもしれない。もっと複雑なGoのWebアプリケーション開発している人の知見も知りたい

GoによるSever Sent Event の実装

リアルタイムにHTTPのログをページに出すために Server Sent Event を使ってる。Goの net/http はevent-streamのエンドポイントもちゃんと実装できるようになっている。

実際のコードはこのあたり:
https://github.com/hakobe/gomibako/blob/master/main.go#L148-L194

HTTPのハンドラの最後で無限ループを作って、その中でeventを書き込むようにしてしまえばコネクションは切断されない。ブラウザ側にもevent-streamってわかるヘッダをはいてやればコネクションは維持される。ただし、リバースプロキシがコネクションをタイムアウトさせたりということはあるので、コネクションが途中できれても良いようにはする。

コネクションははりっぱなしなので、書き込みバッファリングのフラッシュのタイミングはある程度制御してやる必要がある。http.ResponseWriterをキャストして、http.Flusherを作ってeventの書き込みごとにフラッシュしてやると良い。

おなじくhttp.ResponseWriterをキャストして、http.CloseNotifierのオブジェクトを取得すると、HTTPコネクションが切れたときに何かしらの処理をすることができる。

コネクションの管理はchannelを使ってがんばってやってる。リクエストを受け付けるURLに対応するオブジェクトが直接channelを管理するという雰囲気になっていて迫力がある(このあたりのコード >
https://github.com/hakobe/gomibako/blob/master/lib/gomibako.go#L32 )。mutex使っててこのへんのコードはむずくなっている。

channelはGoにおいてはファーストクラスのオブジェクトだけど、ドメインレイヤで扱うのはよくなさそうな感触。かなり実装の都合に依存するので、インフラストラクチャレイヤで管理したほうが良さそう。まぁこのプロジェクトでは、リポジトリっぽいレイヤしかなくてごちゃごちゃしている。interfaceとかおしゃれに使いたい。

まとめ

GoでWebアプリケーション書く練習をしてみて何かしら便利なものはできた。また、界隈にどういうフレームワークあったりするのかとか、net/httpの便利具合などはよくわかった。

一方、DBにリクエストしてテンプレートにいろいろレンダリングするみたいな、普通っぽいWebアプリケーションを題材に選ばなかったので、DBの管理の感じとかレイヤの設計とかあんまり気にせずにベロっとつくってしまった。Sever Sent Event を実装するところとかを主にがんばったけど、その知見の使い所は少なそう。interfaceをいいかんじに使って関心事を分離したい人生だった。

次は日記システムとかを作るといいんだろうかな。GoでWebアプリばりばり作ってる人いたら知見教えて欲しい。

立ち居振る舞い: チームのエンジニアに話しかける

ひとでくんがエンジニア立ち居振舞いお題を作っていたので参加します。

時々同じチームのエンジニアに話しかけるようにしてる。各エンジニアがやっているタスクはGitHubのissueの説明をみればだいたいわかるという設定だけど、話しかけて何やってるかを教えてもらうと良いことがある。

話しかけた時、だいたい相手はうまくいってるか、うまくいってない状態になっている。うまくいってる場合は、よく書けたコードとか工夫した設計とかについて教えてもらえて、なるほどな〜と勉強になる。うまくいってない場合は、聞き役になって困りごとを説明してもらえれば問題の整理に役立つかもしれないし、運がよいとアドバイスすることもできる。

当然、集中しているときに声を書けるのはご法度なのでタイミングを見計らうのが必要になる。狙い目は昼休みが終わった直後とか、終業間際とか仕事に一段落ついてそうなときが良い。相手が席をたって帰ってきたときとか。とにかく集中を乱さないようにする。相手が普段集中しているときはどういう雰囲気かとかを観察しておくと安全度が高まる。わざと自分が共有スペースをうろついて逆にスキを見せるという技もある。

基本的にエンジニアをインタラプトしてはいけないけど、何とかうまいことやって会話すると知見共有ができたり問題解決に役立つかもしれないし、会話していくのは大事ですよねという普通の話でした。もしめっちゃ邪魔になってたらこれまですいませんでした... > チームメンバー

みなさまの立ち居振舞いを教えてください!

お題「エンジニア立ち居振舞い」

HTTPSのWebサーバを設定した (h2o + Let's Encrypt)

最近、ハイパフォーマンスブラウザネットワーキングを読んでいて、HTTPSについてちょっと勉強しています。勉強にあたっては、実際に試せる場所があったら便利そうなので、自分のさくらVPSにHTTPSのWebサーバを設置してみることにしました。この次はHTTP2の実験もしたいので、先進的なHTTP2の機能が実装されていそうなh2oを使ってみることにしました。

環境

今回の作業は以下のような環境でやりました。

$ uname -a
Linux douzemille 4.4.0-36-generic #55-Ubuntu SMP Thu Aug 11 18:01:55 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.1 LTS"

準備

このあとの作業のために以下くらいのパッケージをいれておきます。h2oのmruby機能を使わないのであればruby系とbisonは不要。rotatelogsコマンドのためにapach2-utilとかもいれてます。

  • cmake
  • build-essential
  • libyaml-dev
  • ruby
  • ruby-dev
  • bison
  • letsencrypt
  • apache2-utils

h2oのビルドとインストール

オフィシャルのドキュメントが丁寧なのでその通りにやりましょう。この手順で、/usr/local以下にインストールされます。

$ ghq get h2o/h2o
$ ghq look h2o/h2o
$ git checkout -b build v2.0.4
$ cmake -DWITH_BUNDLED_SSL=on .
$ make
$ sudo make install

h2oの基本的な設定の準備

Let's Encrypt でHTTPSの鍵を取得するためにstaticなファイルを配信するためのHTTPサーバが必要なので、そのためのh2oの設定を用意します。/usr/local/etc/h2o/h2o.conf.ymlとか好きなところに置きます。

pid-file: /run/h2o.pid
error-log: "| rotatelogs /var/log/h2o/error.%Y%m%d 86400"
access-log: "| rotatelogs /var/log/h2o/access.%Y%m%d 86400"
listen: 80
hosts:
  "hakobe.douzemille.net:80": # 自分のドメインにする
    paths:
      /:
        file.dir: /home/yohei/web/hakobe # 配信されるファイルのおいてあるパスを指定

h2oをデーモンとして起動するためのsytemdのunit定義を準備

最近のUbuntuはsystemdというやつでデーモンの管理をしているそうです。h2oをsystemdの管理下で動作させるために、/lib/systemd/system/h2o.service に以下のような内容のファイルを置きます。h2o systemd service file作った - うま味がない をめっちゃ参考にしてます、というかだいたいコピペです。

[Unit]
Description=H2O the optimized HTTP/1, HTTP/2 server
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/run/h2o.pid
ExecStartPre=/usr/local/bin/h2o -c /usr/local/etc/h2o/h2o.conf.yml -t
ExecStart=/usr/local/bin/h2o -c /usr/local/etc/h2o/h2o.conf.yml -m daemon
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

ファイルをおいたら以下のようなコマンドで有効化します。

$ sudo systemctl enable h2o.service
$ sudo systemctl start h2o.service
$ sudo systemctl status h2o.service # これでログが見れる

ここまでで80番ポートでHTTPのサーバが起動している状態になっています。設定ファイルに書いたディレクトリ(この手順では/home/yohei/web/hakobeにファイルをおいてうまく配信されているか確認しましょう。

Let's Encrypt からHTTPSの鍵をもらう

決められた手順でLet's Encryptとやりとりすることで、Let's Encryptによって署名されたHTTPSの鍵を取得できます(How It Works - Let's Encrypt - Free SSL/TLS Certificates で詳しい仕組みが紹介されています)。自動化されていているので、コマンドを実行するだけですぐに完了します。

ドメインの所有者であることを証明するために、そのドメインでアクセスできるHTTPサーバから鍵を配信する必要があります(そしてもちろんドメイン名から今設定しているサーバのIPが引けるように設定されている必要があります)。ここまででh2oがstaticファイルを配信できるように設定してあるので、コマンドラインのオプションから -wオプションでファイル配信元のディレクトリを指定します。

$ sudo letsencrypt certonly --webroot -w /home/yohei/web/hakobe -d hakobe.douzemille.net

これで /etc/letsencrypt 以下に鍵や設定が保存されます。よくできてますね。

追記: ちなみにここではletsencrypt というコマンドを使っていますが、最新版では名称が変わってcertbotというコマンドになっています。certbotのほうが高機能なようですが、基本的な動きは変わらないようです。certbotのページから自分の使っているOSを選択するとインストール方法を紹介してくれます。Ubuntuの場合はapt-get install letsencryptせよと言わるのでしたがっています。

Let's Encrypt の鍵を定期更新する

Let's Encrypt の鍵は3ヶ月でexpireします。必要に応じて更新する手順も自動化されているので sudo crontab -eとかして以下のように書いておきます。

0 4 * * * letsencrypt renew

h2oでHTTPSのサーバを起動する設定

はじめに作って、/usr/local/etc/h2o/h2o.conf.ymlとかにおいてあった設定を書き換えてHTTPSで動作するようにします。

pid-file: /run/h2o.pid
error-log: "| rotatelogs /var/log/h2o/error.%Y%m%d 86400"
access-log: "| rotatelogs /var/log/h2o/access.%Y%m%d 86400"
listen:
  port: 443
  ssl:
    certificate-file: /etc/letsencrypt/live/hakobe.douzemille.net/fullchain.pem
    key-file: /etc/letsencrypt/live/hakobe.douzemille.net/privkey.pem
    cipher-suite: "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"
    cipher-preference: server

hosts:
  "hakobe.douzemille.net:443":
    paths:
      /:
        file.dir: /home/yohei/web/hakobe

あれこれ書いてますが、

ということをしています。省略してますが80番ポートもlistenして、httpsのURLにリダイレクトしたり Strict-Transport-Security ヘッダを付けて返したりすると良いです。

できました

f:id:hakobe932:20160923180015p:plain

とくに何か情報のあるWebサイトではないのですが、とにかくHTTPSで配信できるようになりました。h2oを使っているのでHTTP2も喋れているようです。(HTTP/2 and SPDY indicator - Chrome Web Store で確認しています)

https://hakobe.douzemille.net/

SSL Labs によると A くらいの評価の設定にはなっているようです。がんばればA+になるらしいけどまずはこんなもんで。

f:id:hakobe932:20160923180137p:plain

これでとにかく現代的なインターネット基盤を実験してみる環境が整いました。めでたいですね。

ハイパフォーマンス ブラウザネットワーキング ―ネットワークアプリケーションのためのパフォーマンス最適化

ハイパフォーマンス ブラウザネットワーキング ―ネットワークアプリケーションのためのパフォーマンス最適化