カメラ初心者がカメラ沼に入ってみた。(長文)

会社を辞めたので、旅行なんかしたいと思ったことないのだけど、海外旅行をしてみようと思いたち、衝動的にExpediaとAirbnbでバリ島への航空券と宿泊先を予約した(出発8日前のチケットとか、取れるんですね)。で、どうせ海外行くならいい機会だからちゃんとしたカメラ買って風景とか撮りたいなとなり、以前からうっすら欲しかったカメラを買うことにした。私と同じカメラわからん人の助けになるように、購入に至るまでの検討過程をここに記す。また参考にしたWebサイトなども可能な限り添付していく。何を買ったかだけ知りたい人は末尾までスクロールしてくれ。ちなみに今これを書いている時点でカメラを箱から出していない。旅行までに練習する時間があるだろうか…。というか旅行の準備なにもしてない…。

やりたいこと

  • 旅先で写真やムービーを撮りたい
  • 日常でカメラを持ち歩いて、面白い構図とか撮りたい。
  • いい感じにボケてる写真とか撮りたい。
  • 予算は最大でコミコミ20万まで出してもいいかもしれないけど、できれば10万円前後に抑えたい。

ボディ要件

やりたいことから導き出される要件はこんな感じ

コンパクトであること

旅にせよ日常にせよ、「サッと取り出して取る」性は確保しておきたい。ガジェットの軽さは心の軽さに繋がる。あくまで日常の延長に写真の撮影を組み込みたいのだが、一眼レフやフルサイズミラーレスのように大きなカメラは、構えた瞬間に「カメラマン」になってしまうような気がして気後れしてしまう。そのため、今回は上級者向けであるフルサイズのカメラは除外し、マイクロフォーサーズAPS-Cのミラーレスとした。

関係ないけど、一見そうは見えない小さなガジェットで大きなパフォーマンスを発揮するガジェットが私は好きだ。そこらへんは私がゲーミングPCとして Intel NUCを好んだのと似ているかもしれない。

matope.hatenablog.com

ムービー撮影機能が強力であること(特に4K動画)

せっかく高品位レンズとセンサーを買うので、必要とあらばムービーにも使いたい。日常ではムービーの撮影機会はなかなか無いかもしれないが、少なくとも旅では歩きながら撮ったりもするだろう。そしてそのムービーはいい感じでなくてはならない(いい感じじゃなくて良いならスマホ使った方が便利なので)。

4K録画であることはそれほど必須ではないが、撮れるに越したことはない。

www.youtube.com

強力な手ぶれ補正

デジカメの手ぶれ補正は、写真を撮るときには単なる暗所でのブレ防止というだけでなく、一部の機種では夜景を手持ちだけで撮れるなど、表現の広がりをになう。これは特に旅行では使いどころがありそうである。

5軸手ぶれ補正 E-M5 Mark II | デジタル一眼カメラ OM-D | オリンパス

また、旅でのムービー撮影は歩きながらの撮影が想定されるが、その歩行による見苦しい手ブレを取り除くことはムービーを「いい感じ」にする上で極めて重要であろう。デジカメの手ブレ補正には、ボディ内手ぶれ補正レンズ内手ぶれ補正、そしてその両方を組み合わせた方式がある。一般的にはレンズ内手ブレ補正のみのものは手ブレ補正の効果が少ないようだ。

手ぶれ補正の効果については以下の動画が分かりやすい。

レンズ内手ぶれ補正のみのα6400の手ブレを検証する動画

自撮り対応

ムービー撮影では自分を被写体にするケースが想定されるので、構図の確認のために自撮りに対応しているとうれしい。デジカメにおける自撮り対応とは、液晶ファインダーが縦(180度チルト)か横(バリアングル)かに180度回転するもののことを言う。

バリアングル液晶とチルト液晶の違い - フォトスク

hoboshuhu.com

hoboshuhu.com

防塵防滴

デジタルカメラは極めて繊細なガジェットで、旅先での急な雨や砂浜などでの扱いは注意を要すると聞く。一部の機種は「防塵防滴」を謳っており、ラフな扱いが許されるようだ。それは良いのでチェック項目に追加した。

第一ラウンド:ネット調査

そう言うわけで、「コンパクト」「4K動画撮影機能」「自撮り対応」「手ぶれ補正機能の充実」を中心に見ていきたい。とはいえカメラ全然わからんので、この辺から読み始めて、出てくる機種の価格コムと公式ページを回ってスペックを確認していった。

キヤノン

EOS Kiss M

キヤノン:EOS Kiss M|概要

  • コンパクト:本体重量 351g(軽量)
  • 4K動画:対応
  • 自撮り:対応(バリアングル液晶)
  • 手ぶれ補正:レンズ内手ぶれ補正のみ
  • 防塵防滴:非対応

キヤノン EOS KISS シリーズ初のミラーレス EOS Kiss M。EF-M規格レンズの選択肢が少ないことに否定的な意見が多いが、それは自分に問題になるかは分からなかった。店頭で触ってみた感じは、操作がわかりやすくて好印象だった。しかし、手ぶれ補正が「レンズ内手ぶれ補正」のみで弱かったり 、動画撮影中のAFが弱いらしいというレビューがあったので除外した。

ニコン

ヨドバシの売り場でニコンの機種は全て大きくて重かったので「プロ用なのかな」と考えあまり調査せずスルーした。

ソニー

ソニーのαシリーズは差別化のために機能を出し惜しみしているような…。

オリンパス

オリンパスのレンズはパナソニックとともにマイクロフォーサーズ規格を採用しており、選択肢が多いらしい。また、強力なボディ内手ぶれ補正による暗所や夜景の撮影能力をアピールする機種が多くみられた。

OM-D E-M10 Mark III

OM-D E-M10 Mark III | デジタル一眼カメラ OM-D | オリンパス

  • コンパクト: 本体重量 362g(軽量)
  • 手ぶれ補正:強力なボディ内手ぶれ補正。
  • 4K動画:対応(4K@30p)
  • 自撮り:非対応
  • 防塵防滴:非対応

E-M10はコンパクトだし4Kも撮れるけど自撮りができないので除外。惜しい。

OM-D E-M5 Mark II

OM-D E-M5 Mark II | デジタル一眼カメラ OM-D | オリンパス

  • コンパクト: 本体重量 417g(そこそこ軽量)
  • 手ぶれ補正:強力なボディ内手ぶれ補正
  • 自撮り:対応(バリアングル式)
  • 4K動画:非対応
  • 防塵防滴:対応

E-M5 Mk2 は自撮りができるのに4K動画に対応していないのが惜しい。しかし、オリンパス的には動画専用に強化された手ブレ補正やAF機能から「シネマクオリティ」と推しているので、4K撮影こそできないものの「動画の品質」自体には信頼をおけそうだ。公式の作例動画も良さそうである。この機種はすぐに後継機が出るという噂があり、そちらでは4Kに対応するのではないか。私は旅行が来週なので待てないが…。

www.youtube.com

参考にしたレビュー

dent-sweden.com

dc.watch.impress.co.jp

www.youtube.com

www.youtube.com

パナソニック

オリンパスとならんでマイクロフォーサーズ陣営のパナソニックオリンパスはスチルに強いが、パナソニックは動画につよいという定評があるらしい。

LUMIX DC-GF10/GF90/GF9

DC-GF10/GF90 | Gシリーズ 一眼カメラ | デジタルカメラ LUMIX(ルミックス) | Panasonic

  • コンパクト:本体重量 269g(とても軽量)
  • 自撮り:対応(180度チルト)
  • 手ぶれ補正:なし
  • 4K動画:対応
  • 防塵防滴:非対応

ちいさくてかわいい入門機。キットにパンケーキレンズとかついてる。かわいいのに4K動画も取れたりする。USB充電機能とかもうれしい。しかし手ブレ補正はついていないので除外。他の機種にはだいたいついている、スマホと連携してGPS情報を設定する機能がないのも旅の記録としては不便かもしれない。

LUMIX DC-GX7 Mark III

DC-GX7MK3 | Gシリーズ 一眼カメラ | デジタルカメラ LUMIX(ルミックス) | Panasonic

  • コンパクト:本体重量 407g(そこそこ軽量)
  • 自撮り:非対応
  • 手ぶれ補正:ボディ内蔵
  • 4K動画:対応(4k@30p)
  • 防塵防滴:非対応

良さそうなカメラだが自撮り非対応なので除外。あと外部マイク端子がないようだ。ムービーとして使うなら、やはり小型でもいいのでマイクは装着しておきたいところだ。

www.youtube.com

新製品レビュー:Panasonic LUMIX GX7 Mark III (外観・機能編) - デジカメ Watch

LUMIX DMC-G8

DMC-G8|デジタルカメラ LUMIX(ルミックス)|Panasonic

  • コンパクト:本体重量 453g (ちょっと重く大きい)
  • 4K動画:対応(4K@30p)
  • 自撮り:対応(バリアングル式)
  • 手ぶれ補正:ボディ内蔵+レンズ内蔵式
  • 防塵防滴:対応

4K動画、バリアングル、強力な手ぶれ補正と機能スペックが揃ったが、コンパクトさの観点ではでちょっと尻込みしてしまう。しかし有力候補だ。「生活の面白い視点を切り取る」がやや薄れ、「お仕事のムービーを撮影する」に近づいていく、のをどこまで許容するか、である。

www.youtube.com

LUMIX DC-G9 PRO

DC-G9 | Gシリーズ 一眼カメラ | デジタルカメラ LUMIX(ルミックス) | Panasonic

  • 4K動画:対応(4K@60p)
  • 自撮り:対応(バリアングル式)
  • 手ぶれ補正:ボディ内蔵+レンズ内蔵式
  • 防塵防滴:対応
  • 総重量:本体重量 586g(重い)

4K@60p対応かつ自撮り対応と、動画機能のかなりの充実を感じる。LUMIXマイクロフォーサーズカメラとしては最上位機種である。機能的には申し分ないが、いかんせん旅に持って出るには大きく、重い。価格的にもボディのみでおよそ12万と、予算オーバーだ。

www.youtube.com

LUMIX DC-G99

DC-G99 | Gシリーズ 一眼カメラ | デジタルカメラ LUMIX(ルミックス) | Panasonic

  • 4K動画:対応(4K@60p)
  • 自撮り:対応
  • 手ぶれ補正:ボディ内蔵
  • 防塵防滴:対応
  • 総重量:484 g(やや重い)

G8とG9 PROの後継機らしい。実物は見ずにG9の後継機なら重いだろうとスルーしてしまったのだが、今見ると重量はG8に近く、後述する瞳AFの改良も入っており、検討に値する機種だったかもしれない。しかし、いずれにせよG8の時点でサイズに気後れしてはいたし、中古価格もこなれておらず、レンズキットで13万からあるとはいえキットレンズに不満がある(後述)ので、やはり予算オーバーではあった。

e-m-wonderful.com

第一ラウンド総評

上記に挙げた機種のうち、まず「タッチAF」「内蔵手ブレ補正」は必須と考え、これらを装備していない機種を除外していった。その結果…「小さい」「自撮り可能」「4K動画撮影可能」の三つの要件を同時に満たすカメラが、なんと存在しないことがわかった。トリレンマ!

オリンパスOM-D E-M10 Mark III オリンパス OM-D E-M5 Mark II パナソニック LUMIX DC-G7 Mark III パナソニック LUMIX DCM-G8
小型軽量
自撮り × ×
4K動画 ×
防塵防滴 × ×

そのため3要件のうち、一つを諦めて二つを選ぶことで購入する製品を確定させる必要が出てきた。

もっとも優先しておきたい要件は「自撮り可能」だったので、まず E-M10 Mark III と DC-G7 Mark III が脱落した。ムービーで自分を被写体にできないと不便、というのは想像しやすい。次に優先したい要件は「コンパクトさ」で、もっとも優先度が低いのは4K動画の撮影能力なので、「小さい+自撮り可能」のE-M5 Mark IIで確定…かと思われたが、スペック上のサイズ・重量の差が体感として許容可能であれば、4K動画が撮影できるG8 LUMIXの方が魅力的な選択肢だ。ここから先は実際に実機を触ってみないと判断がつかないだろう。

店頭でのインプレッション

そんなわけで、E-M5 Mark II と LUMIX DCM-G8 まで絞り込んだうえで、Twitterで教えてもらった中野のフジヤカメラ本店で店員さんに相談のうえ、機種を決定することにした。フジヤカメラさんは新品・中古のカメラを扱うお店で、初心者だと言うと店員さんがとても丁寧に教えてくれる。「F値ってなんですか?」とか「ダイヤルのAとかPとかって何ですか?え、Aってオートだと思ってました…」「パンケーキレンズとかどうなんですか」とか色々教えてもらった。

で、E-M5 Mark II と LUMIX G8 の中古品を触らせてもらった。

  • フォームファクタ
    • たしかにG8の方が少し大きいが、両者を持ち比べてみるとE-M5の方は中身が詰まっている感じでズッシリした感じ。しかし、重量に関しては標準ズームレンズを装着してしまうと殆ど関係なくなると感じた。ガジェットのコンパクトさを維持するには短い単焦点レンズが必要なようだ。
    • G8はグリップがついてる分確実にホールドしやすいと感じた。大きいのも悪いことばかりではない。しかし、自撮りすると、グリップが逆向きになるのでとても持ちにくいという問題が発生した。(やはり歩き自撮りはスマホの領分だろうか…)
    • E-M5の瞳AFの方が強力なようだ。自撮りモードにした時、E-M5はすぐに瞳を認識して私にフォーカスを合わせてくれたが、G8はうまくフォーカスしてくれず、わたわたとタッチAFで調整しなくてはいけなかった。自撮り中はカメラを持ちにくいので、オートフォーカスの信頼性が重要なことがわかった。G9やG99では瞳AFが強化されているようだ。
  • 手ぶれ補正は、明確にオリンパスの方が優れているとのことだ。本体のみで超強力というのが重要らしい。パナソニックはレンズ内手ぶれ補正に対応したレンズを選ぶ必要ができてしまう。
  • 4K動画はまだまだ観る環境も整ってないし、そこはフルHDで妥協しても良さそう。

以上のインプレッションから、 E-M5 Mark IIに決定した

レンズ編

さて、ここで話は終わらない。一緒に使うレンズを決めないといけない。わからないので、とりあえずGCM-G8とE-M5 Mark II のキットレンズを起点に説明してもらった。二つのカメラはどちらもマイクロフォーサーズなので、どれを選んでも良い。

標準ズームレンズ

LUMIX G VARIO 12-60mm / F3.5-5.6 ASPH/ POWER O.I.S.

G8のキットレンズ。いわゆる標準ズームレンズ。防塵防滴。ズームレンズとしてはサイズが小さいのが魅力。レンズ内手ぶれ補正あり。一本で35mm換算で広角側24mm相当から望遠側120mm相当までカバーできる(マイクロフォーサーズ焦点距離は、2倍すると一般的な画角の数字になる)。しかし、F3.5-F5.6というのは、結構暗くてボケが作れないらしい。ボケを作るには、明るい=F値が小さいレンズを選ばないといけない。一回帰って価格コムでネットで作例を見たが、確かにVARIOの作例は他と比べて霞がかかったような印象があり、正直「こんな写真が撮りたい!」と心がときめくものはあまり見かけなかった(プロが撮ったものはさすがに良いのだが、アマチュアの作品の方が参考になるだろう)。ときめきは大事なのである。

M.ZUIKO DIGITAL ED 12-40mm/F2.8

EM-5 Mark IIのキットレンズ。同じく防塵防滴の標準ズームレンズだ。EM-5のレンズなので、手ぶれ補正は実装されていないことに注意。望遠側が80mm相当までと、上の12-60(120mm相当まで)より少し少ないが、覗かせてもらうとほとんど分からない程度の差だった。サイズが12-60より一回り大きい点を敬遠したが、しかしF2.8通し(望遠側まで全部F2.8)というF値の小ささからくる画面の明るさ、価格コムで作例を見ても明らかに優れていた。これはときめく。

家に帰って、似たスペックのパナライカ12-60/2.8-4.0も検討したが、F2.8通しの方が良い、と判断してM.ZUIKO DIGITAL ED12-40/F2.8に決定した。

sims-lab.com

追記: id:DocSeri さんの記事がとても参考になったのだった。 docseri.hatenablog.jp

単焦点レンズ

標準ズームレンズがあれば広角から望遠までカバーできるが、やや大きいのがネックだ。単焦点レンズはズームができない代わりに小さく、日常的にカメラを持ち出してスナップを撮るには適している。また、F値が小さく明るく撮れる(F1.4〜1.8とかある)特徴がある。一つくらい持っていてもバチは当たらなさそうである。

単焦点レンズはズームができないので、まず自分が一番撮りたい画角を理解しておく必要がある。下記サイトが参考になった。

logcamera.com

検討の結果、自分がカッコいいなー、こういうスナップ撮りたいなー、と思う写真は50mmっぽい雰囲気があるように思った。作品として締まっている感じがする。あと35mmとかだとあんまりボケを作って楽しむ感じじゃなさそうだし。俺はボケが作りたいんだ!スマホじゃできないから!

と言うわけで比較対象としたのが以下の3製品である。評価メモは全て価格コムの作例のみで判断している。

  • M.ZUIKO DIGITAL 25mm F1.8
    • 3万円くらい。個人的に好きなボケ味
  • LUMIX G 25mm/F1.7 ASPH
  • LEICA DG SUMMILUX 25mm/F1.4 ASPH.
    • 4万円くらい。いわゆるパナライカ。F1.4ですんごいボケる。とろけるようなボケ味…ほしい…

と、これもここまで絞り込んでからフジヤカメラで触らせてもらった。ZUIKOはかなり小さく、同じオリンパスのE-M5 Mark IIとデザインの親和性が高かった。パナライカも触らせてもらった。中古価格差は小さかったのだが、やや大きめであることと、カメラ内部の機構のせいでAFが駆動するたびにカチャカチャと耳障りな音がするため諦め、今回は M.ZUIKO DIGITAL 25mm F1.8 に決定した。

パナライカの方がやや明るく撮れるしボケの綺麗さも上ですが、まあ相当厳密に比較しないと違いは分からないですね、とのことであった。

お買い上げ

はいというわけで、購入したのは E-M5 Mark II + M.ZUIKO DIGITAL ED 12-40mm/F2.8 レンズキット(新品)と、M.ZUIKO DIGITAL 25mm F1.8 単焦点レンズ(中古)の組み合わせでした。

本体キットは中古で買ってもよかったのだけど、価格差が10%程度だったのと、バッテリーは消耗品なので不安があったこと、フラッシュの欠品もあったため新品に。中古でもお店の半年保証はつきます。単焦点は中古でコストを抑えました。

周辺機器含めたコストを書いておきますね。液晶保護フィルムは店員さんに熟練の技で貼ってもらいました。

品目 価格
本体キット ¥101,400
単焦点レンズ ¥24,840
レンズプロテクター(ズームレンズ用) ¥2,680
プロテクター(単焦点レンズ用) ¥1,680
SDカード(64GB) ¥1,680
予備バッテリー ¥5,630
液晶保護フィルム ¥840
合計 ¥138,750

今回Twitterレンズ沼の人たちに色々教えてもらったり、店員さんにみっちり教えてもらったり、いい沼や…となりました。ありがとうございます。楽しみます。

ほめられた。やったぜ。

gorilla/csrf で安全なWebフォームを作る

こんにちは。GoでWeb開発していますか?私はしていません。Goに限らず、既成のWebアプリケーションフレームワークを使わずに自前でWebフォームを作る場合、なにも考えずに書くと CSRF (Cross Site Request Forgery) 脆弱性を作りこみ、不正なユーザー操作を実行されてしまう可能性があります。

ダメな例

例えば以下のGoコードで作成されるフォームにはCSRF脆弱性があります。SubmitSignupForm ハンドラは、受け取ったリクエストが自分のサイト上のフォームからサブミットされたものかチェックしていないので、攻撃者が他のサイト上のフォームを使い、第三者のユーザーのブラウザで任意の操作を実行させることができてしまいます。

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/signup", ShowSignupForm)
    r.HandleFunc("/signup/post", SubmitSignupForm).Methods("POST")
    http.ListenAndServe(":8080", r)
}

func ShowSignupForm(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, `
<html>
  <body>
      <form method="POST" action="/signup/post" accept-charset="UTF-8">
          <input type="text" name="name">
          <input type="text" name="email">
          <input type="submit" value="Sign up!">
      </form>
  </body>
</html>`)
}

func SubmitSignupForm(w http.ResponseWriter, r *http.Request) {
    _ = r.ParseForm()
    fmt.Fprintf(w, "%v\n", r.PostForm)
    // TODO: ユーザー登録
}

CSRFについては「はまちちゃん事件」が有名です。mixiにログイン済みのユーザーにCSRF脆弱性のあったmixiの発言投稿用URLリンクを踏ませることで、ユーザーの意図しない発言を投稿させたというものです。CSRF対策にはいくつかの手法がありますが、権威あるセキュリティ団体であるOWASPが各手法の解説とメリット・デメリットを網羅しています。詳しく知りたい方はそちらを参照してください。

github.com

gorilla/csrf

信頼できるWebアプリケーションフレームワークではCSRFへの対策実装され、デフォルトで有効になっていますが、Goの net/http はWAFではないので、別途WAFを導入しないのであれば、自分でセキュリティ対策を導入する必要があります。そこで、今回は gorilla/csrf を使ってCSRF対策を実装します。

github.com

gorilla/csrf は net/http だけでなく、Echo や Gin といった人気のWAFと協調して動作することを目的としたCSRF対策パッケージです。

Cookieの二重送信(gorillaの場合)

gorilla/csrf が採用する対策手法は、OWASP の分類で言えば "Double Submit Cookie" (Cookieの二重送信) に分類されます。 Double Submit Cookieは、要点だけ言えばセッション開始時に暗号的に強い乱数でトークンを生成しておき、それをブラウザのCookieとフォームのhidden input要素の両方に送信するというものです。もちろん、サブミット時にはサーバーでCookieとフォームのトークンの一致を検証し、一致しなければエラーとします。この手法は、トークン情報をステートとして保持する必要がないため、実装・運用コストが低いのが特徴です。

一方、Cookieの二重送信には問題点も指摘されています。

要約すると、一定条件のもとではCookieを不正に書き換えることは可能であり、その場合に Double Submit Cookie の安全性は成立しないというものです。これについて、gorilla/csrfCookie に署名つき暗号化を施すことで回避しています。OWASPチートシートにも、Cookieの暗号化はうまく動作すると書かれています。

Including the token in an encrypted cookie - often within the authentication cookie - and then at the server side matching it (after decrypting authentication cookie) with the token in hidden form field or parameter/header for ajax calls mitigates both the issues mentioned above. This works because a sub domain has no way to over-write an properly crafted encrypted cookie without the necessary information such as encryption key.

また、gorilla/csrf では、リクエスト時に Unique-per-Request な one time padを生成し、CookieのtokenをXORでマスクしたものをフォームに送信する工夫を加えることで、BREACH攻撃を緩和しています。

プレーンな Double Submit Cookie の弱点を回避して、概ね安全なように思います。強いていえばユーザー自身が外部のWebページにフォームを送信してしまえば偽のフォームを作成可能ですが、そのケースはほぼ意図的な漏洩ですし、そこまで守るかどうかはポリシー次第(それも防ぎたかったらパスワード入力を求めるとか)じゃないでしょうか。

gorilla/csrfCSRF対策の流れを紹介します。

初回訪問時

最初にユーザーがWebサイトに訪れたとき、サーバーは暗号的に強い乱数生成器から basetoken を乱数生成します。そして basetoken を auth_key で署名つき暗号化したものを、レスポンスの _gorilla_csrf Cookie に格納します。この暗号は署名つき暗号なので、改竄に対する耐性があります。

f:id:ono_matope:20190605011412p:plain
初回訪問時

フォーム表示時

次に、サーバーが _gorilla_csrf Cookie を持ったクライアントにフォームを表示する際、サーバーはまず OTP (one-time-pad) を乱数生成します。この OTPと、basetokenをOTPでマスクした結果を結合し、CSRFトークンを生成します( csrfToken = OTP + XOR(basetoken, OTP) )。このCSRFトークンは、フォームのhidden inputとしてユーザーにレスポンスされます。

f:id:ono_matope:20190605092057p:plain
フォーム表示時

サブミット時

ユーザーがフォームをサブミットしてきたとき、サーバーはフォームのCSRFトークンからOTPを取り出し、Cookieのbasetokenを使って先ほどと同じ操作( OTP + XOR(basetoken, OTP)) をおこない、その結果がフォームのCSRFトークンと一致するか確かめることで、リクエストが正しく要求されたものか判別します。

f:id:ono_matope:20190605102640p:plain
サブミット時

How to use gorilla/csrf

さて、原理はわかったので使ってみましょう。

package main

import (
    "fmt"
    "html/template"
    "net/http"

    "github.com/gorilla/csrf"
    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/signup", ShowSignupForm)
    r.HandleFunc("/signup/post", SubmitSignupForm).Methods("POST")

    // Protect ミドルウェアは、非冪等なHTTPメソッドの場合にサブミットされたフォーム
    // をチェックし、CSRFトークンが一致しているか検証する。
    //
    // auth-key はコードに書き込まず、/dev/urandom したものを外部から与えること。
    // キーが変わるとそれまでに発行したトークンの検証に失敗する。
    //
    // HTTP な開発環境では opt に csrf.Secure(false) 指定が必要。本番では外すこと。
    h := csrf.Protect([]byte("32-byte-long-auth-key"), csrf.Secure(false))(r)
    http.ListenAndServe(":8080", h)
}

func ShowSignupForm(w http.ResponseWriter, r *http.Request) {

    // {{.CSRFField}} にCSRFトークンの隠しinputを埋め込む。
    t, _ := template.New("form").Parse(`
<html>
  <body>
      <form method="POST" action="/signup/post" accept-charset="UTF-8">
          <input type="text" name="name">
          <input type="text" name="email">
          {{.CSRFField}}
          <input type="submit" value="Sign up!">
      </form>
  </body>
</html> `)
    t.Execute(w, map[string]interface{}{
        "CSRFField": csrf.TemplateField(r),
    })
}

func SubmitSignupForm(w http.ResponseWriter, r *http.Request) {
    _ = r.ParseForm()
    fmt.Fprintf(w, "%v\n", r.PostForm)
    // TODO: ユーザー登録
}

csrf.ProtectCSRF保護機能を提供するHTTPミドルウェアを作成します。このミドルウェアは、POSTなどの非安全リクエストを受けた時にリクエストボディのフォームを自動的にチェックし、CSRFトークンが適格であれば次のハンドラを実行し、適格でなければエラー画面を表示して終了します。そのため、アプリケーション側のHTTPハンドラは(サブミットについては)CSRF検証済みであることを前提に開発することができます。OWASPチートシートにも、セキュリティ対策で怖いのはうっかりミスなので自動的な対策をせよと書かれているので、この仕様は歓迎できます。

一方、csrf.TemplateField(r *http.Request) template.HTML は、リクエストのCSRFトークンのhidden inputタグをリターンします。これをtemplateで置換してHTMLフォームを表示すると、gorilla.csrf.Token という名前の 隠し inputタグが埋め込まれます。

f:id:ono_matope:20190605144134p:plain
出力されるHTMLフォーム

同時に、Cookieにも _gorilla_csrf が記録されているのがわかります(内容は暗号化されているので確認も改竄もできません)。

f:id:ono_matope:20190605142544p:plain
Cookie

そこで、わざとフォームの値を書き換えてからSubmitすると、ちゃんとエラーになります。

f:id:ono_matope:20190605141731p:plain
シンプルなエラー画面

ところで、csrf.Protectはトークン不一致に簡単なエラー画面を出力するのですが、オプションでカスタムのエラーハンドラを指定させることができます。以下のように、何らかのセキュリティ対応を取れるようなログを落とすと良いと思います。

   h := csrf.Protect([]byte("32-byte-long-auth-key"),
        csrf.ErrorHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            log.Println("CSRF攻撃の疑いのあるリクエストが発行されました")
            fmt.Fprintf(w, "ニャーン (%s)", csrf.FailureReason(r).Error())
        })),
        csrf.Secure(false),
    )(r)

f:id:ono_matope:20190605142904p:plain
にゃーん

以上です。

外付け(として使う)SSDって今何買えばいいのか調べたメモ

昨日の記事で言ったようにうっかりSSD+ケースを買ってしまったのだが、最近はUSB3.1 Gen2もThunderbolt3も普及してきてデータ転送帯域がどんどん広がっているし、m.2?NVMe?とかもなんかちっちゃくてかっこいいし、SSDも価格爆下がり中って聞くし、「最近の外付けSSDはどうなの?」みたいなところをちょっと調べてみた。買ってから調べるという。容量は1TBを前提に調べた。

外付けSSDについての調査だが、単品のSSDにケースを調達して外付けSSDに自作する、ということを選択肢に入れて調べたのでほとんど内蔵SSDについてです。あとNANDとかTLCとかには触れません。

まず、SSDの製品を分類するには、サイズ規格インターフェイスに注目する必要がある。

サイズ規格は以下のふたつがある。

覚えておく必要があるインターフェイスも二つだけだ。

  • SATA3.0
  • PCI-Express (PCIe 3.0)
    • 1レーンで転送速度8Gbps (1GB/s). 最大4レーン32Gbps (4GB/s)での転送が可能
    • コントローラのプロトコルはNVMe。NVMeとはPCIe上でブロックデバイスを効率よく動作させるために導入されたプロトコルである。

つまり、SSD市場には、以下の3カテゴリが存在していることになる。

外付け化のためのケーブルインターフェイス

PCIeはSATAの6倍以上でるのかーすごいなという感じだが、私は私のMacに外付けして使いたいので、つなげるやつをさがす。

2.5インチ + SATA3.0用のUSB 3.1 Gen2変換ケース

Amazonで大量に見つかる。どれも2,000円から3,000円程度と、安価。

Amazon.co.jp: USB3.1 gen2 ssd 2.5 sata ケース: パソコン・周辺機器ストア

m.2 + SATA3.0用のUSB 3.1 Gen2 変換ケース

m.2の変換ケースは、「Bキー、Mキー」などサポートするカードに相性があるので注意。

m.2 + PCIe3.0x4変換ケース

Thunderbolt3-PCIe3.0変換ケース

変わり種で、Thunderbolt3-PCIe3.0変換ケースというのもあった。ただ、書き込み性能の表記がないがレビューによると900MB/sしかでないらしい。

外付けSSD製品

USB接続の外付けSSD製品は、基本的にSATA3.0構成のようで、USB 3.1のGen1を搭載した安価なものとGen2を搭載した高価なものに二極化している。 Gen1の製品はリード性能300MB/s〜400MB/sでGB単価が14円/GBから、Gen2の製品はリード性能が560MB/sでGB単価が23円から、となっているようだ。

kakaku.com

フォームファクタに関しては、実はいまは外付けSSDは2.5インチ製品は少なくなっており、ほとんどがコンパクトなm.2仕様のようだ。2.5インチ: 価格.com - インターフェイス:USBのSSD 人気売れ筋ランキング (規格サイズ:2.5インチ)

考察・あなたが何を買うべきか

関連するパフォーマンス指標を書き起こしてみた。まず、PCにUSBで接続したい場合、データ転送速度はUSBの転送速度である500MB/s(USB 3.1 Gen1)や1GB/s(USB 3.1 Gen2)で律速する。この場合、ドライブにUSBの転送速度を超えたあまり高性能なものを買っても活かせないことに注意。

f:id:ono_matope:20190503055433p:plain

その上で、獲得したい転送速度に応じて最適な購入ルートを考えたみた。

転送速度を気にしない人向け (Gen1コース)

外付けSSDのGen1のものなら1,3000円/1TBくらいで買えるので、転送速度が300MB/s〜400MB/sでいいよという人はわざわざ内蔵ドライブとケースで自作せず、普通に外付けを買うといいと思います。

転送速度は560MB/sくらいはほしいという人(SATA+Gen2コース)

単価の安いSATA SSDに、安いUSB 3.1 Gen2-SATA変換ケースをつけると手頃に560MB/s帯のポータブルSSDになる。Gen2の製品は外付けドライブだと23円/GB程度するが、以下の構成は15GB/円で作れて嬉しい。

代表構成: 12,339 + 2,999 = 15,338円 / 1TB。WDのSSDTranscendのケースとのセットバージョンもある。

パッケージ品で良さそうなのはこれとか。でも23,763円/1TB。丈夫にできてそうってメリットはある。

転送速度は1000MB/sほしいよという人向け(PCIe+Gen2コース)

わかる。MacBook Pro 2018の内蔵SSDの転送速度が3,200MB/sに対して、300MB/sや500MB/sではなにか物足りない。もうちょっとハイスペックを楽しみたい。そんなあなたは m.2/PCIe3x4のSSDに、USB 3.1 Gen2変換ケースをの組み合わせで1000MB/sなポータブルストレージを作りましょう。既製品の外付けドライブではこの選択肢はそもそも存在しないのかな。ちょっと見つかんなかったです。

代表構成:¥15,980 + ¥5,680 = 21,660円 / 1TB

Crucial SSD M.2 1000GB P1シリーズ Type2280 PCIe3.0x4 NVMe 5年保証 CT1000P1SSD8JP

Crucial SSD M.2 1000GB P1シリーズ Type2280 PCIe3.0x4 NVMe 5年保証 CT1000P1SSD8JP

USB-C付属バージョン

akiba-pc.watch.impress.co.jp

金に糸目をつけない人向け (Thunderbolt 3プロ向け機材コース)

Thunderbolt3 SSD変換ケースとか、

そもそもThunderbolt3 SSDとか買うといいんじゃないでしょうか。

blog.livedoor.jp

聞いたことないメーカーだけど3万円で 1,600MB/s でる謎の Thunderbolt3 1TB SSD とかある

容量が十分あるにも関わらずAfter Effects がディスクキャッシュを保存する容量がないと言い張るときはTimeMachineローカルスナップショットを消せ

TL;DR: TimeMachineローカルスナップショットを消せ。

macOS Mojave / APFS 環境で、たまにAfter Effectsを起動すると「ディスクキャッシュフォルダーが存在するドライブには、環境設定で指定されている容量を全て保存できるだけの空き容量がありません。空き容量を増やすか、メディア&キャッシュ環境設定でフォルダーまたは最大ディスクキャッシュサイズを変更してください。」と警告ダイアログが出現する。

f:id:ono_matope:20190502224711p:plain

しかし、私はディスクキャッシュサイズをMacintosh HDに最大40GB程度に設定しており、Finderによるとそのドライブには十分な容量がある。

f:id:ono_matope:20190502225117p:plain

f:id:ono_matope:20190502225040p:plain

何の気なしに df してみたところ、'/' のファイルシステムに空きが18GBしかないと出てきた。Finderの空き容量表示と全く違うではないか。

f:id:ono_matope:20190502225318p:plain

ところで、High Sierra 以降のAPFSが有効なシステムでは、TimeMachineが自動的にMacintosh HDにローカルスナップショットを作成する。

ローカルスナップショットが消費しているストレージ容量について心配する必要はありません。ファイルのダウンロード、ファイルのコピー、新しいソフトウェアのインストールなどのタスクに必要な容量が使われてしまうことはないからです。

スナップショットが消費している容量は、Mac では空き容量に算入されます。その上さらに、Time Machine は空き容量の多いディスクにしかローカルスナップショットを保存しないようになっているほか、スナップショットが古くなったり、ほかのことに容量が必要になったりした場合は、自動的にスナップショットを削除してくれます。

Time Machine のローカルスナップショットについて - Apple サポート

作成されるローカルスナップショットは、OSのディスクキャッシュのように「ファイルシステムの向こう側で」動的に管理されるように設計されているようだが、怪しい。ためしにTimeMachineローカルスナップショットを削除した。

blog.h-wd.info

f:id:ono_matope:20190502230318p:plain

f:id:ono_matope:20190502230342p:plain

すると、df のAvailが129GBまで回復し、AEのディスク容量警告も出なくなった。めでたし。

f:id:ono_matope:20190502230501p:plain

容量が本当に足りないのかと思ってSSD買っちゃったんだけど、返品しようかな…。ちょっと考える。

MacBook Pro 2018 13" + Intel NUC Bean Canyon + eGPU で Mac/Win グラボ共有環境を構築した話

今年もよろしくお願いします、小野マトペです。去年の10月、MacBook Pro 2018 13インチを購入しました。

今まで使っていた2013モデルでは性能重視で15インチを使用していましたが、今年のモデルはCPUのコア数が増え、13インチでも去年の15インチよりも高いCPUパフォーマンスが出るということだったので、モビリティのために13インチをチョイス。やっぱりノートパソコンは持ち運んでなんぼですね。軽くて最高。

japanese.engadget.com

13インチを購入するにあたって最後まで躊躇した要素はグラフィック性能でした。15インチには専用のdGPUが搭載されていますが、13インチは性能の低いiGPUしか搭載されていません。僕はたまに After Effects での動画制作作業をするので、この点はちょっと都合が悪かったです。

そこで目をつけたのがeGPU(外付けGPU)です。macOSではHigh Sierraから Thunderbolt 2/3 での eGPU 接続に対応したことで、Mac環境でのeGPUカテゴリが昨今盛り上がりを見せています。ともあれこれを使えば一台のMacを、外出時には軽量ラップトップ、自宅ではデスクトップレベルのハイパワーグラフィックマシンとして運用することができるため、今回検討しました。

GIGABYTE GAMING BOXを試すつもりだったのだが…

そういうわけで、調査の末に去年の年末に amzon.com で GIGABYTE GAMING BOX 580 を注文しました。これはRadeon RX 580 グラフィックカード組み込み済みで販売されているeGPUエンクロージャで、コンパクトさが際立つ一品です(eGPUはどれもけっこうなサイズなのです)。持ち運び用のバッグが付属しているなどモビリティが高いのもポイントです。一方、ファンが壊れてノイズが鳴るようになったとか、不快なコイル鳴きがあるとか、工作精度の点で不安なレビューも見られました。しかし、Amazon.comでの価格の安さ(本体$419に送料+Import Fee Depositで$56)とコンパクトさに惹かれ、注文することにしたのですが・・・

が、開封した製品がバッキバキに破損しており、そのまま返品となりました。残念・・・

GAMING BOXを返品し、もう一度同じ製品を注文する気にもならなかったので、改めてグラボとエンクロージャを選び直すことにしました。

グラボ

GAMING BOXのような一体型でない eGPU を使うためには、eGPUエンクロージャとグラフィックボードを用意する必要があります。エンクロージャごとに供給電力やカードサイズがあり、適合する組み合わせを選ぶ必要があります。 https://egpu.io というサイトは発売中のエンクロージャとグラボの詳細な情報やレビュー記事が掲載されており、製品選びの助けになりました。

egpu.io

Macで eGPU を使用するにあたって最も大きい制約は、GPUのメーカーです。ゲーム用のGPUとしてはnVidia製品の方がメジャーですが、Macは現在AMDGPUしかサポートしていないため、AMD製品から選択することになります。nVidiaは記事執筆時点でMojave用のドライバーをリリースしておらず、High Sierra以前の環境でしか動作しません。

僕は Windows PC に詳しくないので今回初めてグラフィックカードを調べたのですが、現在AMDグラフィックカードは、主に以下のラインナップのようです。ミドルレンジ〜ハイエンドのゲーミングPC相当の性能が欲しいのであれば、RX580, RX590, Vega56あたりから選ぶことになるみたいです。

  • Radeon RX 570 (GeForce GTX970 と同じくらいの性能)
  • Radeon RX 580 (GeForce GTX1060 と同じくらいの性能)
  • Radeon RX 590 (GeForde GTX1060 より速いくらいの性能)
  • Radeon Vega 56 (GeForce GTX1070Ti と同じくらいの性能)
  • Radeon Vega 64 (GeForce GTX1080 と同じくらいの性能)

現在グラボは暗号通貨のマイニング需要や代理店のマージンにより国内での価格が高騰しているため、私は今回は Vega56 を実質35,000円程度( サンデーくじで入手したクーポン 3000円を使用)でネットオークションで入手しました。グラフィックカードは経年劣化しにくいパーツですし、中古市場で手に入れても良いと思います。amazon.com をのぞいてもいいかもしれませんね。

MSI Radeon RX Vega 56 Air Boost 8G OC グラフィックスボード VD6516

MSI Radeon RX Vega 56 Air Boost 8G OC グラフィックスボード VD6516

androgamer.net

Razer Core X

次にeGPUエンクロージャですが、これは Razer Core Xを選びました。これは安価で、Vega56への供給電力も十分、USB-PDでMac本体への電力供給も可能、動作実績も豊富と堅実な機種ですが、とにかくデカいという製品です。

左端の一番でかいやつです。

www2.razer.com

現物を見てあまりのデカさに笑う私。

狭小住宅の住人としては設置性は重視したかったのですが、年末に大容量の本棚を導入したことでデスク周りの収納スペースに余裕ができたために導入を決意し、TSUKUMO eX.の地下のRAZERストアで税込35,424円から2,000円のTSUKUMOポイント割引を適用して購入しました。空間は正義。

なんとかデスク下のメタルラックに押し込めました。デカいeGPUも、どこか隠せる場所に設置できればなんとかなります。ただし Thunderbolt 3 アクティブケーブルは高価なので気をつけてください。

eGPUゲーミングに最適な Intel NUC Bean Canyon

さて、ここまででeGPUはセットアップできたのですが、ぶっちゃけGPUを酷使する作業は年に数回あるかどうかという感じなので、そのためだけにeGPUを導入するのは正直もったいないです。せっかくつよつよグラボを入手したので、これを使ってどうにかWindowsゲームでもできないか考えました。久しぶりにマウスでFPSがやりたい。PUBGとか俺もやりたい!

最初に考えたのは、eGPU を Mac の BootCamp から利用するアプローチですが、

という問題があるため BootCamp 案は却下し、ゲーム用にWindows PCを生やすことにしました。1台くらい WIndows マシンを持っておくと何かと潰しが効くだろうし。

PCの選定ですが、

  • 内臓グラフィックカードは必要なく
  • 代わりにeGPU接続のためのThunderbolt3コネクタを搭載し、
  • CPUはミドルレンジ以上。
  • 部屋が狭いのでコンパクトPCと言える筐体サイズ、
  • コンソールゲーム機が4,5万のこのご時世、ゲームのためだけにそんなにお金をかけたくない…

というわがまま条件で探したところ、ひとつだけベストマッチな製品が見つかりました。それが Intel NUC Bean Canyon NUC8i5BEHです。

INTEL インテル i5-8259U M.2 SSDに対応 2.5" (9.5mm厚) HDD/SSDも搭載可能 ハイパフォーマンス小型ベアボーンキット BOXNUC8I5BEH

akiba-pc.watch.impress.co.jp

NUC とは Intel が出している小型・安価なPCベアボーンキットで、本体の他に電源ケーブル、メモリ、ストレージを別途購入する必要がありますが、現行世代(Beran Canyon)の NUC8i5BEH は MacBook Pro 2018 13インチと同じ第8世代i5 CPU i5-8259U と Thunderbolt 3コネクタを搭載しています。しかもサイズは超コンパクト。これ、まさに eGPU ゲーミングマシンに最適なPCじゃないですか。

この製品を狙っていたところ、ちょうどPayPayの20%ポイント還元祭りの期間中に池袋のTSUKUMOで4.8万円で販売しているのを見つけたため、その場で購入しました。250GBのm.2 SSD(6千円)、16GBメモリ(1.6万円)、Windows10 Home Edition(1.7万円)、電源ケーブル(1千円) で合計9万6千円のところ、PayPayの20%ポイント還元で実質7.7万円となりました。

Tweetのツリーに写っていますが、ついでに隣のビックカメラでLG 27UK850-Wも20%ポイント還元で買いました。これも大変良い4Kモニタです。ありがとうPayPay)

かわいい!こんなに小さいのにCPUもメモリもSSDもほとんどMBP 2018 13と同じスペック。みんなが欲しかった Mac mini ってこういうやつでは?

セットアップには少し手こずって、Windowsをインストールしたところ有線LANも無線LANもドライバがないためWebからドライバがダウンロードできないという事態になりました。MacIntelのサイトからドライバ一式をダウンロードしてUSBでコピーしましたが、他にPCがない人は注意が必要です。

完成!+ベンチマーク

f:id:ono_matope:20190120211948j:plain

f:id:ono_matope:20190120215914j:plain

というわけで、MacBook Pro, Intel NUC, Razer Core X, Vega 56 による軽量ラップトップ+ハイパワークリエイティブPC+WindowsゲーミングデスクトップPCシステムが完成しました。コンパクトさがすごくないですか(Razer Core Xをうまく机の下に隠せた場合に限る)。

ケーブルの差し替えはどうしても多くなるので、ケーブルマネジメントはだいじ。

f:id:ono_matope:20190120220252j:plain

さっそくベンチマークをとりましょう。まずは Mac で GeekBench 4 の Compute スコアを計測します。

MBP内臓GPU (Iris Plus 655) eGPU (Razer Core X + Vega 56)
Metal 35,000 146,234
OpenCL 35,238 140,855

4倍以上のグラフィックパフォーマンスが得られました。

次は NUC の Windows3DMark の Time Spy と Fire Strike を実行します。

f:id:ono_matope:20190119205711p:plain
Time Spy

f:id:ono_matope:20190119210459p:plain
Fire Strike

スコア 結果URL
3DMark Time Spy 5351 https://www.3dmark.com/3dm/32565573?
3DMark Fire Strike 13214 https://www.3dmark.com/3dm/32570334?

結果ページによると 4KゲーミングPC と ゲーミングラップトップの中間程度のパフォーマンスと評価されました。安価な小型PCをベースに構成したシステムの性能としては上出来ではないでしょうか。実際に CoD:BO4 をプレイしていますが、HD解像度の最高画質で滑らかにプレイができています。BO4 たのしい!

静音性については、ゲームプレイ中はRazer Core Xのファンが強めに回転しますが、筐体ファン半径が大きいため音が低く、耳障りな音ではないです。

そういうわけで、

  • Intel NUC + Windows10Home 7.7万円
  • Razer Core X 33,424円
  • Vega56 Air Boost(中古) 35,000円

PayPayやクーポンを併用しましたが、eGPUセット(7万弱円)とNUCセット(7.7万円)をあわせて14万円で、eGPUをMac+Winで共有可能なゲーミングPC環境が構築できました。超コンパクトな設置サイズを考慮すると、なかなかコスパよく組めたのではないでしょうか。私のように、eGPU構成を検討しているMacユーザーの方は、Intel NUCを使ったサブマシン構成を検討してみるのもおすすめです。

部屋の本棚をIKEAのBillyにした話

今の部屋には、引っ越す前から育てている無印のパルプボードボックスを利用した本棚があるのだけど、じわじわと紙の本が増えて、そろそろ本が溢れるようになってきた。このままパルプボードを増設していってもよかったが、この製品は仕様上は5段までしか重ねられず、壁の上部が有効利用できない問題があったため、ここはひとつ背の高い本棚を導入し、本棚の設置面積を圧縮して部屋をより広く使うことにした。ゆくゆくはソファを置くなどもしたい。

f:id:ono_matope:20181219193137j:plain
before: 無印のパルプボードボックス

本棚選び

我が家の本棚としての要件は、大判の美術書から文庫版の漫画まで、効率よく収納するために可動棚をもつことと、スッキリしたデザインであることだったが、デザインについて納得できるものがなかなか見つからず、本棚選びは難航していた。

無印のパルプボードボックスがそうであったように、ぼんやりした曲線を排したソリッドなかたちの本棚が欲しかったし、色に関しては、部屋がドアや木枠を含めて白で統一されていたので、本棚も白というのは心に決めていた。その点で、入手しやすい定番商品である 大洋のエースラック も、白井産業のタナリオも選択できなかった。エースラックはなんかちょっとカーブが入っていたのがどうしても許せなかったのと、タナリオのホワイトはうっすら木目が入っており、展示店舗が近くにないために実際の見栄えを確認することができなかったためだ。

このあたりでKEAの定番書棚Billyに行き着いた。

www.ikea.com

Billyはまさに上に挙げた要件を満たす理想の本棚だったが、購入に踏み切れない問題として、「たわみ」問題があった。どうも調べてみると、買おうと思っていた幅80cmのBillyは本を目一杯に収納していると、かなりの高確率で棚がたわんでくるらしかった。

たわむのはかなり見栄えが悪いので躊躇したが、結局80cm幅のBillyを諦め、40cm幅のBillyを連結することで回避することにした。40cmのBillyでは、さすがにたわみの報告は見られなかったためである。結果的に、40cm*3のマス目デザインは見た目にも良いものであった。

もう一つの問題として、転倒対策があった。Billyはかなりの重量があるので、倒れてくるとマジで危ない。IKEAの本棚は日本の家具と違い、重心が手前側に来るので、L字金具による壁固定など、転倒防止をしないと危険だとIKEAの店員に教えてもらった(どの家具でも何らかの対策は必要なのだが)。

しかし部屋は賃貸なので壁への釘打ちは躊躇われる。どうしたものかとネットを検索していたら、家具転倒防止おじさんこと篠原進さんが考案した対向くさびという転倒対策が目に入った。書棚と天井の間に楔状に組み合わせたスタイロフォームを詰め、震災時に転倒する方向への力を抑えるというものだった。これと、一般的な転倒防止シートを組み合わせて転倒防止策とすることに決めた。

家具転倒防止―経験交流サイト

auient.hatenablog.com

設置

というわけでIKEAオンラインストアで40cm幅のBillyを3台と追加棚を注文。工作精度への期待と横連結のため、組み立てサービスを依頼した。連結は別料金3000円でやってくれる。配送と組み立ては異なる業者により行われるので、配送から組み立てまでの間、届いた部材を部屋に置いておく必要がある。

設置されたBilly。連結されているため、この時点で一人では持ち上げるのが難しいほど重い。しかしなんとか頑張って足元にふんばる君120をはさんである。Billyは両側面に出っ張りがあるため、適宜カットして挟んだ。

f:id:ono_matope:20181222111715j:plain
組み立てが完了したBilly

ニトムズ 家具転倒防止安定板 ふんばる君 120 M6090

ニトムズ 家具転倒防止安定板 ふんばる君 120 M6090

連結は、使わない棚穴にビスを通して行われる。IKEAの家具は一度分解すると再組み立てが難しいため引っ越し業者に敬遠されるといわれるが、ビスは簡単に外して再連結できるそうなので、引っ越しの際は横連結を解除すれば問題なさそうである。

f:id:ono_matope:20181222171308j:plain
連結ビス

対向くさびを作ろう

さて、篠原式のスタイロフォーム対向くさびを作成する。東急ハンズで21cmx10cmx6cmの白のスタイロフォームレンガ、ノンスリップシート、発泡スチロールカッターを購入。レンガ一つで2つのくさびを作れる。(6つ作ろうと思ってレンガを3つ購入したが、強度的に2つで充分ですよだったので、レンガは余った。)

f:id:ono_matope:20181223222849j:plain
素材

棚から天井の梁まで高さ7.5cm。奥行き10cmのスタイロフォームに勾配1/10の斜面を作るため、1cmの高低差で線を引く。

f:id:ono_matope:20181223154440j:plain
罫書き

スタイロフォームの上下にノンスリップシートをあわせ、(霧吹きがなかったため)クッキングペーパーで水を含ませ…

f:id:ono_matope:20181223162940j:plain
ノンスリップシートに挟まれたスタイロフォーム

ハンマーで押し込む。なんかちょっと角の壁紙が裂けてしまった気がするが、気にしない。これで完成である。ふんばる君を敷いて本を収納した時点で、重みからそこそこ安定はしていたが、対向くさびを追加することで本当に押しても引いてもビクともしない、まるでL字金具で壁に固定されているかのような頑丈さになった。これはすごい。

f:id:ono_matope:20181223223845j:plain
対向くさび完成

パルプボードボックスから本を移して完成(写真は対向くさび設置とパルプボードボックスの撤去の前)。初期よりだいぶ設置面積がおさえられたし、部屋にもよく馴染んでおり満足。ただ、新しい本棚の容量をすでにほぼ使い切っており、写真にもあるように本などが少し入りきっていない。これとは別に映画のパンフレットの束も本当は本棚にしまいたかったし、会社に置いてあるダンボール一箱分の技術書もいつかは持ち帰ってこないといけないので、Billyがもう一台必要かもしれない。しかし既に述べたように3台連結済みのBillyは絶望的に重く、場所の微調整などできればしたくないので迷うところだ。しばらくは完全移行せず、パルプボードボックスを一部残して運用するかもしれない。本棚沼はこわい。

f:id:ono_matope:20181222142319j:plain
After: IKEA Billy 40cm x 3

ざっくり理解するPaxos

ゴールデンウィーク中、Appleオープンソースとしてリリースした分散データベース FoundationDB のドキュメントを読んでいました。なかなか面白いデータベースだと思うのでこれについては別途書きたいですが、それはそれとしてFoundationDBでは、分散環境下でACIDトランザクションを実現するために、分散合意プロトコルとして有名なPaxosを採用しているようでした。

PaxosはGoogleのChubbyやCassandraのLight Weight Transactionなどで使われていますが、僕はいまだにPaxosがどのように動作するのかあまりよく分かっていませんでした。良い機会だと思い、FoundationDBの技術を理解するためにも連休の後半でLeslie LamportによるPaxosの論文の一つ Paxos Made Simple を読み、何となくわかった気持ちになったので備忘録としてここに書き残しておきます。(間違いがあったらマサカリ頂きたいです)

Paxosが達成したいこと

Paxos を使って達成できるのは、簡単にいうと以下のようなセマンティックです。

  • ネットワーク上の複数ノードが、「提案」された値の中からひとつの値だけを 「選択」したい。
  • 一度値が「選択」されたら、その値はあとから未選択状態に戻ったり、値が変わったりしない。
  • 多少の参加ノードが途中で故障したり復帰したりしても、残ったメンバーで破綻なくプロトコルが続行され、選択に至ることができる。

一度選択された決定が、後からなかったことにされたり、ひっくり返されたりしない、というのは日常生活からも有用性さが納得できる話です。ここでは、例として「今日のお昼ご飯」を決定する例に挙げます。ここでは複数のメンバーが「ラーメン」「カレー」「ピザ」などの値を「提案」し、そのなかからひとつのメニューを「選択」し、そのメニューのお店にメンバー全員で一緒にご飯を食べに行きたいとします。(例題設定は @nobu_k さんのスライドからお借りしました)

メンバーからの提案を受けてメニューを決定するリーダーが一人だけいれば、「ラーメンにしよう」などとすぐに「選択」できて話は簡単です。しかし、このワンマンリーダーはプロジェクトの単一障害点なので、プロトコル中に突然の交通事故で病院に搬送されてしまうと、残されたメンバーは昼飯のメニューを選択できなくなります。そのような場合、他のメンバーをリーダーに昇格させ、新リーダーに「カレーにしよう」などと選択してもらうことができます。しかし、旧リーダーの傷が浅く、すぐに病院から戻ってきて、「ピザにしよう」などと選択を続行する可能性があります(Crash-Recovery故障モデル)。これは一度決まった決定がひっくり返り、値が一つより多く選択されてしまった状態と言えます。こうなると、メンバーのうち一部は新リーダーとカレーを食べに、一部は旧リーダーとピザを食べに行ってしまうなどの分裂状態に陥る可能性があり、全員で一緒にご飯を食べるという目的は達成されません(Split-Brain障害)。

Crash-RecoveryによるSplit-Brainを避ける手法として一般的なのは、リーダーに心拍計を取り付け、確実にリーダーが死亡してから新リーダーを昇格させるというものです。しかし、この死亡確認までの所要時間は、長すぎるとメンバーはいつまでも昼飯に出発できない(可用性の低下)し、かといって短すぎるとやはりSplit-Brainを起こします。(SRE本では他にもSTONITH(Shoot The Other Node in the Head = リーダーの頭を撃ち抜く)コマンドというより積極的な手法も紹介されていますが、こちらもネットワーク障害などでリーダーの機能を確実に停止できない可能性あり完全ではありません)

このように、従来一般的な単一の「マスター」ノードに値の「選択」をさせるアーキテクチャは、Crash-Recovery故障モデルでの耐障害性がありません。そこで、単一のリーダーノードによる独裁的な決定ではなく、分散された複数のノードによる「合意」に基づいた選択をすることによって、ノードが多少故障したりNWが一時的に分断しても値を選択し続けられるような耐障害性を獲得したいという動機があり、そのための分散合意アルゴリズムのひとつがPaxosです。

…というのが前提についての自分のおおざっぱな理解です。

Paxos

ここからPaxosの解説を始めます。

Paxos では、参加ノードを、値を提案する Proposer, 提案を受け入れる Acceptor, 提案の合意状態を観測して「選択」を判定する Learner の3種類のロールに分けます(Fig.1)。それぞれ何台いても良いし、同じサーバーが複数のロールを兼務してもよいです。 Proposerにより提案された値が過半数のAcceptorに受理(accept)されたとき、Learnerはその値を「選択」することができます(そのため、Acceptorノードは3つや5つなど、奇数が好ましい)。そのため、Paxosは、Acceptorの過半数が生き残っている限り、合意に至るプロトコルのどの段階でProposerが全滅したり復活したりしても破綻しません。

Fig.1: f:id:ono_matope:20180513201645p:plain

通常、Acceptorが値をAcceptするとLearnerに通知されるのでLearnerは素早く合意を検知できますが、パケットロスなどのため、Learnerが常に形成された合意を知りえるとは限らないので、そういう場合はLearnerからAcceptorにaccept状況を確認しにいく必要があります。

Paxosプロトコル

さて、この各々のAcceptorノードに、どのようなプロトコルで値をAcceptさせるかが肝心な点です。ナイーヴなプロトコルではPaxosが求める正しさや耐障害性は得られません。たとえば、単純な「各Acceptorは最初に自分に提案された値をAcceptする」プロトコルでは、同時に3つ以上の提案がなされたり(Fig.2)、合意形成前にAcceptorがFailしたりすると(Fig.3)、いずれの提案も過半数を獲得できずにプロトコルが終了しません。反対に、「最後に提案された値をAcceptする」プロトコルの場合は、いちど値が選択されたあとも、選択値が変化してしまい、目的を達成できません。

Fig.2 単純なFirst-Write-Winでは3つ以上のproposeで誰も過半数を握れなくなる f:id:ono_matope:20180513201841p:plain

Fig.3 単純なFirst-Write-Winではacceptorのfailで誰も過半数を握れなくなる f:id:ono_matope:20180513201902p:plain

Paxos では、Proposerが提案するそれぞれの提案に各提案にユニークな番号(便宜上、ここでは提案番号と呼ぶ)を付与し、フェーズ1 (prepare) / フェーズ2 (accept) のふたつのフェーズからなるプロトコルで提案をやりとりします。提案番号は、提案どうしで重複してはいけないので、時刻とノードIDの組み合わせなどで生成します。

フェーズ1: Prepare / Promise

Paxosのフェーズ1は、Proposerが値を提案するところから始まります。フェーズ1のルールは以下の通りです。

  • Phase 1. (a): Proposerは過半数のAcceptorに、生成した提案番号nとともにprepare(n)要求を送信する。
  • Phase 1. (b): Acceptorが既に応答したどのprepare要求の提案番号よりも大きな提案番号nとともにprepare要求を受信したら、nより小さな番号の提案は今後acceptしないという約束(promise)と、もしあれば現在までにacceptした最大の番号の提案とともに要求に応答する。そうでなければ、Acceptorはこのprepare要求を無視する。
    • 無視とあるが、これはタイムアウトと無視エラーレスポンスを同一視してよく、レスポンスを返さなくてもプロトコルは破綻しないという意味で、パフォーマンス最適化のために即時に無視レスポンスを返してもよい。

Proposerが過半数のAcceptorからpromiseレスポンスを受け取れたらフェーズ2に進みます。できなければ、もっと大きな提案番号を用いて最初からやり直します。少し複雑なので、以下に図解します。(Fig.4, Fig.5, Fig.6)

Prepare要求した提案番号がAcceptorの最大のpromise済み提案番号よりも大きかった場合

この場合、AcceptorはProposerにpromise応答を返します(Fig.4)。Acceptorがすでに何らかの提案をacceptしていた場合、その最大の提案をpromise応答に付与します(Fig.5)。

Fig.4 f:id:ono_matope:20180513202326p:plain Fig.5 f:id:ono_matope:20180513202334p:plain

Prepare要求した提案番号がAcceptorの最大のpromise済み提案番号よりも小さかった場合

AcceptorはPrepare要求を無視します。(Fig.6)

Fig.6 f:id:ono_matope:20180513183720p:plain

フェーズ2: Accept

  • Phase 2(a): proposerがprepare(n)要求に過半数のacceptorからレスポンスを受信したら、それらのAcceptorに、提案番号n、値Vのaccept要求を送信する。Vはレスポンスされた中で最大番号の提案の値か、もし何の提案も受け取っていなければ任意の値。
  • Phase 2 (b): accept(n,V)要求を受け取ったAcceptorは、もしnが今までpromiseした最大の提案番号よりも大きければ、その提案をacceptする。そうでなければ無視する。
    • Phase 1 (b) と同様、無視ではなくIgnoreレスポンスを返すことで素早くリトライできる。

過半数のAcceptorが同じ値をacceptすると、合意が成立したとみなされ、Learnerはその値を「選択」することができます。Phase 2(a) での値Vの選択がやや複雑ですが、ここがPaxosのキモです。

accept要求の提案番号が、Acceptorがpromiseした最大の提案番号以上だった場合

問題なくacceptされる。

Fig.7 f:id:ono_matope:20180513202431p:plain

accept要求の提案番号が、Acceptorがpromiseした最大の提案番号より小さかった場合

accept要求は無視される。

Fig.8 f:id:ono_matope:20180513202517p:plain

Paxosプロトコル

Paxos Made Smple で定義されているプロトコルはこれだけです。本当にこれだけのプロトコルで、「値がただ一つだけ選択される」が実現できるのか、実際に試してみよう。ここではProposer2ノード、Acceptor5ノード、Learner1ノードの集合を想定します。本当はラーメン・カレー・ピザの例を続けたかったですが、作図上の都合により、値V1,V2,V3と記して説明します。

一番かんたんなパターン

まずはすんなり選択に至るパターンを試してみましょう。Proposer P1が提案番号n1, 値V1 の提案(n1,V1)を提案したいとします。

フェーズ1

Fig.9: P1が過半数のAcceptor(ここではA1,A2,A3の3ノード)に提案番号n1をprepare要求する。全てのAcceptorがn1をpromiseしてくれたのでフェーズ2に進む。 f:id:ono_matope:20180513203218p:plain

フェーズ2

Fig.10: P1がpromise応答したAcceptorにaccept(n1,V1)要求をする。いずれのAcceptorもこれをacceptする。 f:id:ono_matope:20180513203234p:plain

過半数のAcceptorが同一の値をacceptしたので、Learner L1は値V1を「選択」できるようになりました。よかったですね。

選択後に他の値が提案されるパターン

ところで、Paxosの合意においては、いちど値が選択されたら、それ以外の値が選択されてはいけません。たとえば、この状態からProposer P2が、大きな提案番号n2で値V2を提案した場合、はたして値V2が選択されずにいられるでしょうか。ためしてみましょう。(n1より小さな提案番号を使った場合については、フェーズ1を満たせず棄却されることが自明なので省略する)

フェーズ1

Fig.11: P2が過半数のAcceptor A3,A4,A5に提案番号n2をprepare要求する。全てのAcceptorはpromise応答するが、(n1,V1)提案をaccept済みのA3はこの提案(n1,V1)をpromise応答に付与する。 f:id:ono_matope:20180513203253p:plain

提案(n1,V1)はすでに過半数のAcceptorによりacceptされているので、このとき、P2がどの過半数のノードの組み合わせにprepare要求しようとも、少なくともひとつのノードからは提案(n1,V1)つきのpromiseが応答されることになります。

フェーズ2

Fig.12: ルールPhase2(a)により、P2は、元々の提案(n2, V2)を、acceptorから受け取った値V1で書き換えた提案(n2,V1)をノードA3,A4,A5にaccept要求する。このaccept要求は、すべてacceptされるが、結局すでに選択済みの値と同じ値がacceptされるに過ぎず、Learnerが選択する値はV1のまま変わらない。 f:id:ono_matope:20180513203302p:plain

このように、各ProposerがPaxosプロトコルを守る限りにおいて、いちど過半数にAcceptされた値(V1)はくつがえされることはありません。

これで、Paxosがどういう挙動をするものなのかなんとなくイメージできたと思います。

衝突するパターン

もう少し複雑な、同時に提案が走ったり、途中でProposerが落ちたりするパターンを試してみましょう。

P1がA1,A2,A3に(n1,V1)をprepare要求します。 f:id:ono_matope:20180513162536p:plain

P2がA3,A4,A5に(n2,V2)をprepare要求します。 f:id:ono_matope:20180513162626p:plain

P1がA1,A2,A3に(n1,V1)をaccept要求しますが、A3が受理しなかったのでやり直しになります。 f:id:ono_matope:20180513162739p:plain

P2がA4,A5に(n2,V2)をaccept要求しますが、A3に要求する前にプロセスが停止し、離脱してしまったとします。 f:id:ono_matope:20180513162855p:plain

そこに新たなP3が登場し、(n3,V3)を提案しようと、A2,A3,A4にprepare要求します。すでにP1,P2による提案をacceptしているノードからは、それらの提案がpromise応答されます。 f:id:ono_matope:20180513163040p:plain

P3は、提案(n3,V3)の値をpromise応答中最大の値に書き換えた提案(n3,V2)をA2,A3,A4にaccept要求(n3,V2)します。これで過半数のノードが同じ値V2をacceptしたので、選択値はV2に確定します。よかったですね。 f:id:ono_matope:20180513163212p:plain

ざっくりと雰囲気でいうと、Paxosは

  • Acceptorは、promiseした番号以上の提案番号だけをacceptする
  • Proposerは、Acceptorがacceptしている最大番号の提案で自分の提案を上書きする

のルールの組み合わせによって、後から提案するProposerほど、すでに場に出ている提案を追認せざるを得ない仕組みで選択値の変動を防いでいるようです。

Multi-Paxosと複製ステートマシン

ここまでで、Paxosがいかにお昼ご飯を選択するのに便利なプロトコルか理解してもらえたと思いますが、実際のところ、Paxosそのものでは大したものは作れません。Paxosのうまみは、単一の値についての合意というよりも、これを順序つきのコマンド列についての合意に拡張し、複製ステートマシン(RSM)の保持に応用できる点にあります。(Paxos Made Simpleの最後の章はこの複製ステートマシンの実装についての記述になっています。この論文にはMulti-Paxosという名前は一度も出てきませんが、他の論文や資料ではこの手法をMulti-Paxosと呼んでいるので、たぶんこれがMulti Paxosなんだと思います)

ステートマシンとは、ある状態にコマンドを入力して出力と新しい状態を生成する概念上の機械です。例えば、銀行システムは口座の全ての集合を一つのステートマシンと考えることできて、預金の引き出しは、口座に引き出し金額以上の預金がある場合に引き出し金額を出力し、金額を差し引いた新しい状態に遷移するコマンドと表現できます。複製ステートマシンでは、初期状態から全く同じ操作を、同じ順番で適用することで、複数のレプリカの状態を同一に保つことができます。RDBのbin-logみたいですね。

こういったシステムでは、一般的に単一のマスターサーバー(マスターDB)が全ての更新順序を決定する役割を担いますが、冒頭で議論したように、単一マスターのシステムは可用性とSplit-Brainの問題に悩まされることになるので、やはり分散合意による高い耐障害性を獲得したいわけです。そのためにMulti-Paxosを使います。

Multi-Paxos の仕組み

Multi-Paxosは、「連続したコマンド入力を受け取り、コマンドが同じ順序で適用されるように合意していく」ことで複製ステートマシンを構築します。複製ステートマシンは、順番つけられた、個別のPaxosインスタンスの列を持ちます。i番目のPaxosインスタンスで選択された値は、i番目のステートマシンコマンドになります。クライアントがProposerにコマンドを要求すると、そのProposerはそのコマンドが何番目に実行されるべきコマンドかを決定し、その番号の値についてAcceptorに提案を行います。

f:id:ono_matope:20180513145042p:plain

ところで、Paxosである値を選択するには、Prepare/Acceptの2フェーズが必要で、アクセプターが5台の場合は最低6ラウンドトリップ、さらに他の提案と衝突した場合はリトライのため追加のラウンドトリップが必要です。これではステートマシンとして使うにはあまりにスループットが低いです。

そこで、Multi-Paxosでは特定のProposerをLeader Proposerに選出し、そのノードにコマンド順序の決定を行わせる最適化を行います。さらにPaxosプロトコルを拡張し、Proposerは「n番目以降の全てのPaxosインスタンスに対して、同じ提案番号でAcceptorにPrepare要求」ができるようになります。これらの拡張により、リーダープロポーザは、リーダー選出直後などのレアなイベント時をのぞいてフェーズ1を省略でき、さらに提案同士の衝突による性能低下も回避できます。 

f:id:ono_matope:20180513203859p:plain

リーダーを選出と聞いて、Sprit-Brainが起きないか心配してしまうかもしれませんが、あくまで性能最適化のためのリーダーに過ぎず、もしリーダー以外のノードがコマンドを提案したとしても(性能は低下しますが)、個々のPaxosにより正しさは守られているので、選択値が壊れたりはしません。

Multi-Paxos リーダー交代時の挙動

リーダープロポーザの故障などで他のプロポーザにリーダーシップを切り替える場合を考えてましょう。まず、新任リーダーは、コマンドを受け入れる仕事を始める前に、決定済みの全てのコマンドの決定を知る必要があります。Multi-Paxosでは基本的にProposerはLearnerも兼ねるので、リーダーを任された時点でほとんどの選択はLearnしているはずですが、単一Paxosの項で少し触れたように、Acceptor側の全ての合意が即座に全てのLearnerに通知されるわけではないので、認識に欠けがある可能性があります。

では、この新Leader Proposerがどうやって完全な決定を知るかというと、旧リーダーのものより大きな提案番号で「n番目以降の全てのPaxosインスタンスに同じ番号でPromise要求」コマンドを使います。このコマンドを受けたAcceptorは、promiseとともに「n番目以降の全てのAccept値」をレスポンスするので、これで新Leader Proposerは全ての未認知コマンドの選択値を入手できます。ちなみに、この方法でも選択値が確定できなかった虫食いコマンドのインデックスに対しては、なんと「No-opコマンドを提案」してスキップします。歴史改変じゃないかと心配になりますが、上記プロトコルで選択値が得られなかったインデックスのコマンドは、過去においても一度も選択されていなかったコマンドという保証があるので大丈夫ということのようです(つまり、プロトコルが正しく動いている限り虫食いは現れないはず)。

Multi-Paxos その他

そのほか、Multi-Paxosについてはメンバーシップ変更時は、メンバーシップの変更そのものをひとつのコマンドとして記録すればいいよとか、Leader Proposerは、提案が成功すれば、Learnを待たずに次の提案に移って良いけど、未Learnのコマンドの個数はα個までに制限すると良いとか、細かい議論がいくつかあります。

PaxosとRaft

ここまでPaxosとMulti-Paxosの動作を見てきました。しかし、Paxosは現在は以前ほど流行っていないようです。機能と性能がほぼ同じだがシンプルで理解しやすいとする新しい分散合意アルゴリズムのRaftが2013年に発表されました。また、Paxos Made Liveでは、Paxosは現実の分散システムに実装するためには論文に書かれていないことが多く、最終的なプロダクトは「未証明のプロトコル」になってしまうと指摘されています。実際、定評のあるオープンソース製品でPaxosを実装したものはあまり聞きません。それこそCassandraのLWTやFoundationDBくらいでしょうか。

一方、Raftは(まだ読んでいないのですが)、リーダーエレクションなど、実装に必要な詳細が論文化されているため多様なオープンソース実装が存在し、特にGo言語向けの github.com/coreos/etcd/raft は、etcdだけではなくCockroachDB、TiDBにも採用されるなど、新時代の分散DBの興隆を支えている感があります。そういうわけで、次はRaftを勉強したいと思います。

Raft Consensus Algorithm

参考文献

SRE サイトリライアビリティエンジニアリング ―Googleの信頼性を支えるエンジニアリングチーム

SRE サイトリライアビリティエンジニアリング ―Googleの信頼性を支えるエンジニアリングチーム

Googleを支える技術 ?巨大システムの内側の世界 (WEB+DB PRESSプラスシリーズ)

Googleを支える技術 ?巨大システムの内側の世界 (WEB+DB PRESSプラスシリーズ)