大コンテナ時代を生きのこるためのJSON Schema

実行環境をコンテナ化するDockerが普及して久しく、CIやローカルの開発環境などどこかでコンテナ技術に触れているのではないでしょうか。

コンテナはその性質上、設定のプロビジョニングに古典的な設定ファイル (のパス) 受け渡しが難しいです。etcdconsulのようなKVS (= Key-value store) を用いることもあるでしょうが、素朴には環境変数で与えることが多いでしょう。

HerokuはThe Twelve-Factor Appというパターンを提唱し、その中でStore config in the environmentと述べています。

The twelve-factor app stores config in environment variables (often shortened to env vars or env). Env vars are easy to change between deploys without changing any code; unlike config files, there is little chance of them being checked into the code repo accidentally; and unlike custom config files, or other config mechanisms such as Java System Properties, they are a language- and OS-agnostic standard.

The Twelve-Factor App

参考: Building Twelve Factor Apps on Heroku | Heroku

しかし環境変数グローバル変数なので無闇に参照すれば古式ゆかしきスパゲティコードまっしぐらです。また環境変数の値は文字列として扱われるので表現力に乏しく利用者のために値の定義域を示す必要がしばしば生じますが、どのように行うべきでしょうか?

そんな悩みをJSON Schemaで解決しようという試みを紹介します。

環境変数の値の定義

ある環境変数がとりうる値の形式や範囲を考えてみます。

たとえば真偽値 (boolean) が挙げられます。 DEBUG=1デバッグフラグを立てるような変数です。
1か0か、あるいは値はなんでもよくて定義されていれば真とする場合もありますが、併せて真偽値とします。

あるいは数値 (number) も考えられます。 LOGLEVEL=1LOGLEVEL=7 など、ログ出力を数値で指定するケースがあります。

真偽値と数値を一般化すると列挙 (enum, enumeration) になります。たとえば PLACK_ENV=test PLACK_ENV=production など、文字列として扱われるがそのとりうる値があらかじめいくつかに決まっています。

また特殊な例として複数値 (multiple values) についても考える必要があります。
環境変数は辞書構造なので、ある名前に対応する値はただひとつに定められるため、複数の値を渡したい場合は値をデータ区切り文字 (delimiter) で分割して扱います。

たとえば PATH は値をコロンで区切ります *1

PATH はコロンで区切るというコンセンサスが得られているのであまり問題になりませんが、独自の変数を定義するときに区切り文字は何であるかはいよいよもって明示しなければ正しく使うことはできません。

JSON Schemaの利用

そこでJSON Schemaを利用します。

JSON Schema (JSON Schema Core)はその名の通りJSONの形式を表明する形式で、スキーマに基づいて入力の検証を行うためのボキャブラリを定義するJSON Schema Validationなどの仕様とあわせて利用されます。
以下、CoreとValidationをまとめてJSON Schemaと呼びます。

JSONと名がついていますがあくまでシリアライズ形式にJSONを選んでいるというだけで、利用する上でその他の制約はありません。

JSONとしてシリアライズ可能、すなわちJSONが定義する辞書構造 (object) とコレクション (array) のほかいくつかのスカラ値 (string, boolean, null) のいずれかで表現できる入力であればJSON Schemaで表明・検証できます。

先に述べた通り環境変数は名前と値がそれぞれ文字列である辞書構造なのでJSONとしてシリアライズ可能です。

以下はあるスクリプトが期待する環境変数JSON Schemaの例です:

{
  "title": "Schema of some script's environment variables",
  "$schema": "http://json-schema.org/draft-06/schema#",
  "type": "object",
  "properties": {
    "DEBUG": {
      "type": "string",
      "enum": [
        "1",
        "0"
      ]
    },
    "LOGLEVEL": {
      "type": "string",
      "enum": [
        "0", "1", "2", "3", "4", "5", "6", "7"
      ]
    },
    "PLACK_ENV": {
      "type": "string",
      "enum": [
        "development", "test", "production"
      ]
    },
    "PATH": {
      "type": "string",
      "pattern": "[^:]+(:[^:]+)*"
    }
  },
  "required": [
    "PLACK_ENV"
  ]
}

先に述べたnumberやbooleanを期待する例がJSON Schemaで記述されています。

実際にこのスキーマを用いて検証を行ってみます。

Rubyjson-schema.gemを利用します。以下のGistにコード例を示します:

Gemfile · GitHub

実行結果の例です:

✘╹◡╹✘ < ruby validates_env.rb
The property '#/' did not contain a required property of 'PLACK_ENV'
✘>﹏<✘ < PLACK_ENV=a ruby validates_env.rb
The property '#/PLACK_ENV' value "a" did not match one of the following values: development, test, production
✘>﹏<✘ < PLACK_ENV=development ruby validates_env.rb
✘╹◡╹✘ <

PLACK_ENV=a を渡したらきちんと怒られてかわいいですね。

独自実装を含む他の検証方法に比してJSON Schemaを選ぶ利点はいくつかあります。

  • 相互運用性が高い
    • ランタイムによらず利用できるため、複数の言語を採用している場合に障壁となりません
  • 検証のセマンティクスの一貫性が保たれる
  • スキーマの再配布・拡張が容易
    • たとえば組織内で設定に用いる環境変数を共通化したいとき、あるURLでスキーマを配布・参照することで普及させやすくなるかもしれません
  • JSON Schemaのエコシステムの恩恵に与れる
    • たとえばドキュメント生成が簡単になるかもしれません

一方、物足りないと感じる点としては、enumやpatternプロパティを駆使するほかなく、あまり可読性が高くない点でしょうか。

この点については、JSON Schemaの仕様ではformatプロパティに独自のキーワードを追加・利用することを許容しているため、booleanライクな文字列の定義を追加し、それをformatに指定することで冗長な定義を避けられるかもしれません。

Implementations MAY add custom format attributes. Save for agreement between parties, schema authors SHALL NOT expect a peer implementation to support this keyword and/or custom format attributes.

JSON Schema Validation: A Vocabulary for Structural Validation of JSON

むすび

以上、設定のスキーマJSON Schemaで記述・検証することでコンテナ時代に即したtwelve-factor appパターンを促進する試みでした。

私はtwelve-factor appの環境変数を推進する姿勢については環境変数の表現力の乏しさを懸念し懐疑的でしたが、JSON Schemaを利用することを思い付いてからは懸念も解消され強く共感します。

既に述べたように独自formatを定義・利用するバリデータがあるとより便利かもしれないという思いもあるので、引き続き検討・導入していきたいところです。


この記事ははてなエンジニア Advent Calendar 2017の8日目の記事でした。

明日 (12/9) はid:ikesyoです、おたのしみに!

*1:ただしUNIX/Linuxにおける場合、Windowsでは異なる

寝て起きる

昨日書いてたコード、テストが落ちるけどわけがわからないので途方に暮れたけど、一晩寝て起きたらもしかしてこうでは? という気付きが得られて、出社して試したらうまくいった。

集中力は単に集中力と言われるのであらゆる対象に対して1つの共有のプールがあって、そこからリソースを奪い合うかのようなイメージだけど、意外とそうではなくて対象ごとにそれぞれプールがあって残量が0になると知性がなくなる、みたいなモデルなのかもしれない。

自分の中に集中力リソースが100あって、好きなタスクに100まで自由に注げるわけではなくて、集中力プールが5つあって、それぞれ20ずつ、みたいなかんじなのかもしれない。

だとすると、ひとつのことをずっとやっても集中できるのは20までなので、効率が悪い。最も効率よく働くには、集中力プールの数だけタスクをかけもちすればよい。

しかし集中力プールの数がいくつなのかは知らないし、使える時間は相変わらず共有リソースなのでどういう順番でやるかを考える必要がある。

あとタスクの進みが悪いときに他のタスクに手をかけるとなんとなく気まずさがある。

この世はままならぬ。

インストールがとても高速化されたcapistrano-strategy-jenkins_artifact 0.4.0をリリースした

GitHub - aereal/capistrano-strategy-jenkins_artifact: Capistrano 2 strategy that uses Jenkins' artifact as a distribution provider.

Release v0.4.0 · aereal/capistrano-strategy-jenkins_artifact · GitHub

capistrano-strategy-jenkins_artifactというgemを作ってメンテしており、これの0.4.0をリリースしました。

Travisのビルドでは最大約30倍速くなりました:

`bundle install` before: 126.69sec
`bundle install` after: 4.22sec

Goodbye jenkins_api_client by aereal · Pull Request #21 · aereal/capistrano-strategy-jenkins_artifact · GitHub

JenkinsのAPIを利用するためにjenkins_api_clientというgemを使っていたのですが、これはXML APIを利用することもあるので依存にnokogiriが入っており、`bundle install` に時間がかかるのがネックでした。

実際にこのgemで利用するAPIJSON APIでまかなえたので、net/httpベースで書き直すことでjenkins_api_client経由のnokogiri依存をなくしました。ありがとうjenkins_api_client.

このgemはCapistrano 2 strategy that uses Jenkins' artifact as a distribution provider.という説明の通り、Jenkinsのビルド成果物を取得するstrategyを提供します。

もともとはてなブログのデプロイ設定のために書いていたコードをgemとして切り出したもので、いまではMackerelなど社内のいくつかのサービスで導入されています。gem化してよかった。

ついでにOSS化したことでテストをちゃんと書こうというモチベーションが高まりお得。

参考: はてなブログのデプロイを約6倍高速化したはなし - Sexually Knowing

ISUCON7予選に秒速5000兆クエリというチームで参加した

ISUCON7 開催&日程決定! #isucon : ISUCON公式Blog

ISUCON7のオンライン予選にid:astjid:tanishiking24の3人で秒速5000兆クエリというチームで出場した。最終スコアは9万ちょっとで、ベストスコアとほぼ同値だったので実力を出し切ったと思う。

ISUCONに競技者として参加するのは初めてで、エンジニアリングにスコアをつけて競い合うというのは聞いていた以上に楽しい。

ちなみに「競技者として初」というのは、昨年のISUCON6の参考実装の移植として関わっていたので、ISUCONと関わったという意味では初ではなかった。

準備

2日前くらいにcatatuy/private-isuで練習した。振り返るとISUCON7の予選問題も画像をいかに効率よく返すかというところが最初のボトルネックで、早速予習が活きてお得。

pt-query-digestを使うとか、nginxのアクセスログを解析してボトルネックを見つけるとか、見つけたボトルネックの解消の分担だとか、タイムキープだとか、一通り押さえておきたいところは練習できたので事前準備は万端と言ってよい。

当日

開始が遅れるとのことだったのでオフィスの近くにあるちょっといい肉料理のお店でランチをいただいた。

肉専科はふう 京都御所南の和牛ステーキを堪能できる洋食のお店

開始してからは、まず3人でレギュレーションの読み合わせをした。予選だけど複数台構成ということでおおっとなる。練習していた時のデプロイは雑にホストに入って `git pull` するだけだったけど、手間とかリスクを考えて早めにデプロイ整備をしようということになった。

ほか、ローカルとグローバルにそれぞれ帯域制限があることなどを確認しあった。この時点で参考実装は読んでいなかったけれどグローバル側が100Mbpsだったので、とにかくベンチマーカーから送られてくるリクエストを減らす = クライアントキャッシュを使うのが肝要というあたりをつけた。

読み合わせが終わってからはid:astjSSH鍵のセットアップなどをしつつ、自分が初期プロビジョニング (mackerel-agentを入れたり、redis, memcachedを入れるなど) やデプロイ準備を進め、id:tanishiking24がkataribeのセットアップをしつつコードリーディングを進める、という分担になった。

レギュレーションを読み合わせましょう、とか、3人がどういう分担になるといいか、とかはISUCON本戦出場経験のあるid:astjに音頭を取ってもらった。普段からオペレーションも得意だしあてにはしていたけど、実際かなり頼りになり助かった。
id:tanishiking24も「じゃあこれよろしく」と振って危なげなく進めてくれるので、チームの並列度はマックスで最高だった。

練習でやったとおりnginxのアクセスログMySQLのslow.logを見てボトルネックを1つずつ潰していく方針で進めた。

id:astjがやってくれたimage.nameにuniqueインデックスを張る変更でスコアが5万くらいに上がってテンションが上がってきたと思う。早いチームは早いうちからスコアを伸ばしていて内心やや焦っていたけど、同じ土俵に立てたことで安心しつつ、競い合えているなーと楽しくなってきた。

その後、N+1クエリを殺すなど分担してスコアをちまちま上げつつ、いよいよアイコンの配信をどうにかしないと帯域で当たってどうしようもない、という状態になって案を考えはじめた。

last-modifiedをつけてconditional GETさせよう、という方針になったのだけど、これはあとから考えると穴がある判断だったし、ハマる原因になってしまったな、と反省。

JSやCSSにはif-modified-sinceは来ているように見えたけどアイコンには来ていないので、last-modifiedヘッダの日付形式がおかしいのではないか、とか、304を返すロジックを間違えているのではないか、とか後から考えれば的外れな検討ばかりして時間が消えていった。たぶん3時間くらい費してしまったと思う。

最後のほうは本当にお通夜で、しかも大差ない変更をしたつもりだけどスコアが5万→3万→2万→3万とよくわからない上下をするので、一層お通夜が進んだ。

だんだん頭がおかしくなってきて、gzip圧縮かけているとstrong etagが毎回変わってしまいそれでキャッシュされていないのでは? ということになりgzipを切るなどしたが、効果はなし。

頭も朦朧としてきてヤケクソになって誰ともなくcache-control max-age=0とかつけてfailしなかったら儲けものじゃない? って話になり、ベンチマーカーを走らせるとなんと6万くらいになった。

その時のid:astjはこんなかんじだった:

                                 ,.へ
  ___                             ム  i
 「 ヒ_i〉                            ゝ 〈
 ト ノ                           iニ(()
 i  {              ____           |  ヽ
 i  i           /__,  , ‐-\           i   }
 |   i         /(●)   ( ● )\       {、  λ
 ト-┤.      /    (__人__)    \    ,ノ  ̄ ,!
 i   ゝ、_     |     ´ ̄`       | ,. '´ハ   ,!
. ヽ、    `` 、,__\              /" \  ヽ/
   \ノ ノ   ハ ̄r/:::r―--―/::7   ノ    /
       ヽ.      ヽ::〈; . '::. :' |::/   /   ,. "
        `ー 、    \ヽ::. ;:::|/     r'"
     / ̄二二二二二二二二二二二二二二二二ヽ
     | 答 |     コ ロ ン ビ ア       │|
     \_二二二二二二二二二二二二二二二二ノ

あとは初期データに含まれるアイコン画像を静的に書き出すのをやって9万 (ベスト値) が出る。……ものの、my.cnfを書き換えてベンチマークを走らせると5万くらいに落ちる→もう一度走らせる→スコア戻らず→変更を戻す……というようなよくわからないスコアの上下が相変わらず発生するので、最後の30分はベストスコアである9万を越えるまでひたすらエンキューする猿たちになっていた。結果、最後の10分くらいでベストスコア近いスコアが出たので、そこで打ち止めとなり終了。

実践できて、次回もやっていきたいこと

  • ボトルネックの解析・発見→修正という基本的なプロセスを回しつづけたこと
  • 30分ごとに現況確認をしあい、ハマるのを概ね防げたこと
  • (良くも悪くも) 普段の自分たちが切れる手札だけを切れたこと、本番一発勝負の必殺技みたいなものはなかった

反省点

  • ベンチマーカーのconditional GETの挙動が不可解だとなったときに、動かす手が止まってしまった
    • 「仕様的には我々は間違っていないはずなのに」となったときに、仕様に照らすのではなく、ベンチマーカーの動きを知るためとにかくあれこれやってみるべきだった
    • 他にボトルネックのあたりはついていたが、帯域で当たっているのでまずアイコンをどうにかしないといけない、けど手がない……ということで3人とも止まってしまった
  • 他の人の感想を見るとgzip_staticとか知らないディレクティブがいろいろあった

感想

普段やっていることに近いし社内の他のチームと感想戦をしあってみて、丁寧な配信最適化をやろうというかんじで、楽しめたし自分たちに足りていないところがあることを痛感させられ、たいへん楽しめました。

悔しくありつつも、初ISUCON参戦で楽しかったので、次は優勝したい。

余談

予選参加することを決めたあとで10月22日に『響け!ユーフォニアム』のコンサートと被っていることに気がついて、慌てて21日の参加で問題ないか確認した時が一番肝が冷えた。

週5日8時間労働は厳しい

しばらく体調が悪く、週末になるとへとへとになる状態が続いている。

土日に休むとリフレッシュできて月曜日には元気一杯になるので、老衰による疲れやすさがあるにせよ、体力の最大値がそんなに大きく目減りしていることでもなさそうなので、今週は午後からの勤務として労働時間をだいたい50%にしている。

そしていまのところ調子がいい。いきなり気温が下がって身体に負荷がかかっているにしては、わりといい調子が続いている。

あとフィジカルというよりメンタルの負荷が大きそうで、午後に出勤するまでのあいだ、家の掃除をしたり洗濯をしたり、あるいはピアノを弾いたり、そういう時間が増えたことも大きいように思う。

つまりそういった家事や趣味をするぶんのフィジカルリソースの消費は変わらずあるものの元気というわけなので、メンタルリソースの消費が激しいという推論。

生涯で得られる幸福の総量について考えると、収入を2倍にするより労働時間を半分にするほうが総量は大きくなりそうだし、健康も保てて寿命が延びそう。

スマートフォンからSlackを消した

休んでいる時にたまに見たりしてしまい無闇に疲れていたので消してしばらく経つ。

緊急の連絡はDMでやりましょうって合意できているし、DMはメールでも通知が来るようにしているので、ひとまず困らないだろうと思っているけど、実際どうかはわからない。

あとグループウェアにも気軽にアクセスできないようにしたいけど、それはちょっとめんどうそうで悩む。

休み中に疲れが溜まる要因を減らしていきたい。

『Scalaで自動作曲の練習』を社内勉強会で話した

speakerdeck.com

だいぶ大仰なタイトルでありますが掲題の内容を社内勉強会で話しました。

音楽を作る古典的な方法論 (機能和声) を取り上げ、その中でも和声 (harmony) をコアドメインと見据え、実際に和音進行の規則などを実装したという内容です。

和声と周辺の概念をScalaで表現したコードを交えながら、和音や調・音階をエンジニア向けにどういうパラメーターを受け取ってどのように導出できるか、といった観点で紹介しています。

端折ったりやや正確さに欠ける説明になっているところがあると認識していますが、プログラミングあるいは抽象的な構造とその生成規則を発見するという観点でおもしろさを感じてもらえるようなアレンジです。

紹介したコードは素朴なWebアプリケーションとして実装し、公開しています: GitHub - aereal/music-study

きっかけ

アカデミックな知識を師事したりきちんと学んだことはないのですが、ピアノを弾き始めたころからずっと学んできた分野で、プライベートワークもいくつか作っています。

一方、プログラミングを始めてからしばらくして、ものごとを抽象化して似た特徴や規則を発見するおもしろさを見出してから、たとえば和音の特徴付けは音程 (周波数比) で決まるよなとか、音楽や音楽を作り上げるルールたちをプログラミングや数学の語彙を使って述べ直すことができそうだなということはずっと頭の中にありました。

ちょうど社内勉強会の順番がやってきて、最近は業務に直結した話が多くマンネリを感じていたので、ハッタリを効かせる意味と自分に発破をかける意味で「自動作曲」というワードで取り組むことにしました。

実際にコードを書き始めたのは10日前くらいでいけるやろと思っていたけど後述する点にハマるなどし、動くものができたのはギリギリだったのでだいぶ焦りました。
なにせ発破をかけるために大仰なタイトルをつけたのに、蓋を開けたら動くものはなにもありませんでした、だと顔向けできないので……。

普段の業務範囲からだいぶ離れたドメインを扱うので退屈に感じないかなあという心配もしていたのですが、思ったより反響があったことには救われました。実際、こういうことが考えられるよねという実装を提示してワイワイ議論が起こったらいいなと目論んでいたので、そういった意味では成功したといえます。


難しかったところ

当初は12平均律を前提として、周波数を実装に使っていたのですが数値の精度が足りなくてテストが落ちはじめた時は、進まない進捗と共に絶望的な気持ちになりました。

Rationalに切り替えていくのも大変だったので、動くものを作ることを重視して、物理的な実装に寄せる作戦は諦めました。

また種々の概念は一見すると規則的で実装しやすく見えたのですが実際はそうでもなく、ある言葉が指す概念が非常に曖昧あるいは広すぎてユビキタス言語を発見するのに戸惑いました。

たとえば音程の完全1度、短2度、長3度、増1度みたいな名前は、一見すると「修飾子」+「N度」という構造をとれるのではないかと思うのですが「N度」という概念は五線上の距離を指す一方、修飾子は半音上げたり下げたりといった音高に作用するものなのでドメインが異なり表現が困難です。

音程は結局、半音の数で表すこととし、半音の数が6つの場合はDiminishedFifth, AugmentedFourth, あるいはTritoneという名前で参照できるということにしました。

12平均律が前提であればこれでよいのですが、音律に依存せずN度の表現ができると望ましいと感じているので今後改善していきたいところです。

今後の展望

Web Audioなどを用いて実際にこのソフトウェア実装から生成した和音進行を耳にできるようにして、そこから実装されていない規則を追加していきたいですね。

また、和声といいつつ和音進行しか実装できていないので、せめて声部配置くらいは実装したいし、できていて然るべきかなと思っています。


プレゼンテーション内で短三和音を誤って「長三和音」としていた点を修正しました。

リモート勤務メモ

社内グループに書いていたメモをせっかくなので放流します。

前提

  • 昼食はオフィスで食べられる
  • 基本はオフィス勤務だが、相談の上リモート勤務も可能
  • 自宅からオフィスには徒歩で20分、自転車で10分
  • 京都在住
  • 北海道の実家で2週間程度のリモート勤務を2回経験

リモート勤務の感想

  • お昼ごはんの用意をしていると昼休みがあっという間になくなる
    • 普段オフィスにいるとランチをいただくのに20〜30分くらい、残りは休憩スペースで同僚と話したり本を読んだしている
  • お昼休み・定時になるとチャイムが鳴るが、自宅だと鳴らないのでしょっちゅう時計を気にして疲れる
  • 途中までSlackで参加していた会話が口頭に変わると経過がわからなくなるので、あとはうまく話が進んでいることを祈るしかなくなる
    • いまのチームに慣れたからいいけど、入りたてとかだと不安だと思う
  • 人と関わりがなくて気が滅入ってくる
    • 普段、人と話すわけではないけど同じフロア・同じオフィスにたくさん人がいて、なんとなくワイワイしているのを感じるだけでけっこう満足していることがわかった
  • 椅子がしょぼいオフィスチェアで自宅勤務2日目で身体中が痛くなってきた
  • 外部ディスプレイなくて効率落ちる気がする
  • 顔を合わせるMTGの時、こっちはHangoutで1だけど、向こうは会議室で複数人いるという状況だと、1対多という状況に言い様のないストレスを感じる
  • 設計の相談とかするときにホワイトボードが使えないのけっこう不便
    • 手書きしたメモをSlackに上げたりとかしていた

結論

リモート勤務するなら本格的にディスプレイや椅子を揃えて環境を作らないと自分はやりづらいということがわかった。

あるいはMacBook1枚とインターネットさえあれば砂漠でも仕事できるという強靭な心身を手に入れるか。

あと通勤が苦ではなく、むしろデスクワークに終始しがちな毎日において貴重な身体を動かす機会と捉えていた節がある。

天候が悪かったり、どうしても自宅にいない用事が発生したら、柔軟に自宅で勤務できる会社なので、いまのところ自分はリモート勤務そのものに対するこだわりは無いかなあと思った。

むしろオフィス勤務だと福利厚生としてディスプレイとかいいオフィスチェアを用意してもらえるので、そういう点を考えると自宅勤務はけっこう非効率だなあと感じた。

追記

リモート勤務メモ - Sexually Knowing

リモートワークなのにオフィス時間割に合わせてる????

2017/08/29 17:28
b.hatena.ne.jp

労働形態の話と勤務地の話は別なので、コアタイムがある時は勤務地がどこであろうと合わせるべきと思います。

という形式的な話もありつつ、物理的に同じ空間にいないからこそ朝に挨拶するなどして不在かどうかを発信することも、同じ空間にいない時に他のメンバーに不安を与えないために重要だと思って時間は特に意識して守るようになりました。

#builderscon tokyo 2017で『Goで実装する軽量マークアップ言語パーサー』というトークをしてきた

Goで実装する軽量マークアップ言語パーサー - builderscon tokyo 2017

buildersconは初参加。ぼちぼちパーサーを書いていたのでいい機会だと思って応募し採択されてこの度初ビルコンで話してきた。

トークについて

60分枠でひとりで話すのは初めてで時間配分にとても悩んだ。なんとか20分×3みたいな構成にしようと思ったけど前半は抽象的な話が多かったので眠くなってしまったかもしれない。

仕事とは関係なく作り上げたものについて話をするのは、ずいぶん気が楽だし自信を持てることだったなあと思い出した。仕事は自分ひとりの成果といえるものはごく少ないし、自分は抽象的な話をするのは苦手だ。

聞いたトークについて

特におもしろかったトークについて。

まず、基本オフレコなので感想を書けないのがとても残念だけど、オフレコという縛りがあるのも納得の前夜祭のトークはどれも強烈だった。

DeepLearningによるアイドル顔識別を支える技術 - builderscon tokyo 2017

id:sugyanさんのアイドルの顔認識の話。

アイドルの顔の系統が似通っていて識別が大変という話がおもしろかった。たしかに年代が近くその時代でかわいいとされる顔立ちの人がなるものと考えると頷ける。

自撮りの角度が決まっているのでそれを学習してしまいたまに違う角度で写っていると判別できないことがあるという話を聞いて、いわゆるハナザーさん (花澤香菜) アングルがある話を思い出した。

小さく始めて育てるコンパイラ - builderscon tokyo 2017

id:rhysdさんのコンパイラを作っていく話。多相型の推論アルゴリズムがいくつかあるということも知らなかったのでまさに「知らなかった、を聞く」だった。

おもしろそうだったので自分もコンパイラを書いてみたい。

buildersconについて

コピー通りWeb系に囚われないトークがたくさんあって刺激的だった。あとスピーカー弁当はじめ供出いただいたフード類がおいしくて嬉しかったです。次もなにか話したい。

あとめっちゃいい杓文字いただけました。

https://www.instagram.com/p/BXU1ew_BrgP/
😹 #builderscon

Content-Security-Policyやらsubresource integrityなどに関するメモ

最近、セキュリティに特に気をかけなければいけないサービスの開発をしていて調べた知見のメモ。

subresource integrity

Subresource Integrity - Web security | MDN

いわゆるチェックサムの仕組み。 integrity 属性に ${hashalgorithm}-${hashdigest} 形式の値を書いておき、フェッチしたファイルのハッシュ値が一致していなければブラウザが読み込みをブロックする。

これは、たとえばCDNが攻撃されるなどしてスクリプトなどが改竄された場合に有効。

JSやCSSはプリ・ポストプロセッサで処理し出力することがほとんどだろうが、subresource integrityとどうやって統合するかというとgulp-hashsumを使ってコンパイルしたファイルのハッシュ値をdigest.jsonに保存し、HTMLのレンダリング時にそれを参照してscript/link要素に埋める、という方法が考えられる。
開発しているサービスは、使っているツールセットなどの兼ね合いからこの方法をとっている。

webpack-subresource-integrityのようなプラグインもある。

Content-Security-Policy

インタラクティブな要素が少ないので、Plackミドルウェアで一律 default-src 'none' style-src 'self'を指定した後にホワイトリスト形式で追加するという運用にしている。

これでいくつかハマった点があるので紹介。

Google Tag Managerとの兼ね合い

Google Tag ManagerはHTML断片やGoogle Analyticsなどのタグタイプを選べるが、これはつまるところ動的なスクリプトの実行・読み込みなので script-src などのポリシーが適用される。

上記のようなホワイトリスト形式のポリシーと相性が少し悪い。

動的なスクリプトの実行については、都度nonceを付与するということも考えられるけど、ホワイトリスト形式で運用したいページではそもそも使わないのが手間を考えると楽かと思う。

外部スクリプト読み込みは、諦めてタグタイプを追加する度にポリシーに追加することにした。といってもGoogle Analyticsくらいしか使わないだろうから、そんなに困っていない。

複数ポリシーを指定するとorではなくandで検証される

仕様に書いてある

Content-Security-Policy: default-src 'self' http://example.com http://example.net;
                         connect-src 'none';
Content-Security-Policy: connect-src http://example.com/;
                         script-src http://example.com/

Is a connection to example.com allowed or not? The short answer is that the connection is not allowed.

Content Security Policy Level 2

たとえばデフォルトは厳しいルールを指定しておくが、エンドポイントごとに緩めたい時はデフォルトのポリシーをレスポンスヘッダから取得しマージした値をレスポンスヘッダに再度書き出すということが必要。

広告に関する考え

※やまなし、いみなし、おちなし

Web広告は目にするとイラつく
街頭広告はそうでもない
テレビCMはイラつく

WEB広告は有限のリソースを浪費されているかんじがする
ページと関係ないと→ネットワークリソースや表示領域が浪費された
ページと関係あると→広告とそうでない情報を区別する労力が浪費された
なぜ広告を区別する必要があるか
広告は情報価値の期待値が低い
なぜ低いと感じるか
一般的な情報は外部の指標によりフィルタリングされる (Google, SNSの反応)
広告は入札の仕組み以外にフィルタリングの仕組みが存在しない
→ 一般的な情報を評価するのと広告が広告であるのか判断するのとで、労力が変わっているのか気になる

テレビCMがイラつくのも恐らく同根
時間が奪われる

街頭広告はなぜイラつかないのか
浪費されるリソースが無限に近い
壁面は無限 目を背けられる
歩いているときは情報を求めていない → 価値の低い情報を見ても区別したり評価する必要がない

最近Atomでコードを書く時間を増やしている

最近思うところあってAtomでコードを書く時間を増やしている。

といっても普段書くPerlはこれまで通りVimで、趣味でTypeScriptを書く時にAtomを使うようにした。

Vimの使い勝手に慣れ切ったつもりだけど、昨年一年間はIntelliJScalaを書く時間が多かった。それが思いの外に馴染んだので、もしかしたらVim以外でもなんとかなるのでは? と思い、まずは趣味の時間からちょくちょく使いはじめることにした。

Vim設定をGitで管理してけっこう設定に凝っている。道具を自分の手に馴染むように変えるスタイル。

対してAtomで書く時は、あまりカスタマイズせず、キーボードショートカットも覚えずにGUIでぽちぽちすることにしている。

いままで設定を練ったVimよりできることは減ったり手間がかかるようになったはずだけれども、耐え難い感じはしない。

むしろGUIがリッチなので便利に思う時が多い。

その後、ENSIMEを試してみたところVimより安定して使えたのでScalaを書く時もAtomにしようかな。

grpcを見たメモ

クライアント (ブラウザ) とサーバーがやりとりするためにgrpcを使えないかなと思ってサンプルコードを見たりちょっと触ったメモ。

結論からいうと今回の用途には合わないので見送ったけど、後のために。

ブラウザでは使えない 😭

Can I use it in the browser?

Not yet. This is an area that's being actively explored and we welcome feedback and contributions. We are collecting names of people interested in early access program here. There is node.js support for server side JavaScript.

grpc / FAQ

メモ

  • protoc(1) でサービス定義からクライアント実装と、サーバー実装の雛形を生成する
  • サーバーは定義されたRPCインターフェースを実装するのと、listenさせるところまでは最低限書く必要がある
  • G SuiteのAPIはgrpc (Protobuf?) で提供されている

Scalaで書いたWebアプリケーションのレスポンスにデプロイされているGitコミットのSHA1を含める

WebアプリケーションのレスポンスヘッダにGitのsha1を含めておくと、デプロイに失敗していて古いバージョンだった (そのせいでおかしかった) ということに気がつきやすくなって多少便利。

Perl (PSGI) だと、 `git rev-parse HEAD > VERSION` などしておいて Plack::Middleware でファイルの内容を読んで `Plack::Util::header_set` と `Plack::Util::response_cb` で用が済むけれど、Scalaコンパイル時にいろいろできそうな気がしたので調べた。

sbt-buildinfoでビルド情報をcase objectに出力する

sbt-buildinfoというsbtプラグインを使うとプロジェクト名やバージョンなどの情報を含むcase objectのコードを生成してくれる。

Usageより:

lazy val root = (project in file(".")).
  enablePlugins(BuildInfoPlugin).
  settings(
    buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion),
    buildInfoPackage := "hello"
  )
GitHub - sbt/sbt-buildinfo: I know this because build.sbt knows this.

`build.sbt` にこう書いておくと `hello.BuildInfo` に `name` や `version` が埋められたcase objectを定義するコードが出力される。

sbt-gitでGitのコミット情報をプロジェクトのバージョン文字列にする

sbt-gitというsbtプラグインを使うとプロジェクトのバージョン文字列をGitのコミット情報から生成することができる。

`git describe` を使うようにするとか、自分でsha1を加工する (e.g. 先頭8文字だけを使う) ことができるので使い勝手がよい。

タグは使っていないのでsha1の先頭8文字を埋めるようにしている。

lazy val root = (project in file(".")).
  enablePlugins(GitVersioning).
  settings(
    git.formattedShaVersion := git.gitHeadCommit.value map { sha =>
      sha.substring(0, 8)
    }
  )

Playのレスポンスを加工してバージョンを含める

Action.async { implicit request =>
  val result = ???
  result.withHeaders("x-version" -> app.BuildInfo.version)
}

こういうかんじ。

筋トレティータイム

僕はコーヒーが苦手で、飲むと必ずお腹を壊すので避けるようにしている。

その代わりではないけれど、気分転換したいときには紅茶を淹れて飲むことが多い。

コーヒーと違って蒸らし時間があるので待ち時間がどうしても発生する。ティーバッグを淹れてそれを処分する関係上、自席にも戻りづらい。

そこで腹筋ローラーの出番となる。

もともと学生時代と比べて筋肉の衰えが気になりはじめたので買ってオフィスに送ったのだけれども、持って帰るのが面倒で置きっぱなしにしていた。

あるとき、ふと思い立って紅茶を蒸らしているあいだに腹筋ローラーをしてみたらちょうどいい時間だったので、これ幸いとしばらく続けている。

紅茶の蒸らし時間はだいたい2分くらいで、慣れていないうちは10回腹筋ローラーするだけでもそれくらいの時間がかかる。

数回続けると慣れて素早くできるようになってくるので、成長と共に蒸らし時間あたりの回数が増えていって、ティータイムと共に成長の実感が得られて思わぬ喜びがあった。

腹筋ローラー、1オフィスに1台オススメです。