Quantcast
Channel: 無印吉澤
Viewing all 107 articles
Browse latest View live

Ansible の --extra-vars 引数を安全に使うためのラッパーを書いてみた

$
0
0
f:id:muziyoshiz:20160313233740p:plain

最近は、仕事でも趣味でも、サーバ構築を自動化したいときは Ansible を使ってます。

アプリケーションのデプロイには Capistrano も使うんですが、つい最近 Ansistranoという便利な Ansible role の使い方を覚えてしまったので、そこも Ansible で済むようになりました。

recruit.gmo.jp

ansible-playbook の --extra-vars 引数

ところで、Ansible の playbook を書いていると、「普段の動作は決まっているけど、ごくまれに違う動作をさせたい」ことがたまにありませんか? そういうとき、僕は ansible-playbook の --extra-vars 引数(-e 引数)を良く使います。

例えば、自作のアプリケーションをデプロイするための playbook で、「普段は master ブランチをデプロイするけど、たまに違うブランチをデプロイしたい」ことがあったとします。そういう場合、playbook のなかで version 変数を、

- hosts: apservers
  vars:version: master

のように定義しておいて、master ブランチをデプロイしたい場合は --extra-vars 引数なしで実行します。

$ ansible-playbook -i inventory deploy-app.yml

そして master 以外のブランチをデプロイしたい場合だけ、以下のように --extra-vars 引数を使ってブランチ名を指定します。

$ ansible-playbook -i inventory deploy-app.yml --extra-vars="version=develop"

Ansible 公式の Variablesにある通り、あらゆる変数指定のなかで --extra-vars の指定は最優先されます。そのため、このような上書きが可能なわけです。

--extra-vars のデメリット

この方法はだいたいうまくいくのですが、「変数名を間違えても何のアラートも出ない」というデメリットがあります。

例えばつい最近、僕が書いた playbook を使ってアプリをデプロイした同僚から、「develop ブランチを指定したのに master ブランチがデプロイされてる。playbook がバグってないか?」と言われたことがありました。何か実装間違えたかな、と思って彼が実行したコマンドを見たら、こうなっていました。

$ ansible-playbook -i inventory deploy-app.yml --extra-vars="develop"

調べてみたところ、これは無効な変数指定として ansible-playbook に無視されるようです。その結果、version 変数はデフォルトの master のままで、master ブランチがデプロイされました。その同僚はどうも「引数でブランチ名を変えられる」程度の理解だったようです。

そのときは同僚に --extra-vars 引数の使い方を解説して終わったのですが、あとから考えてみると、--extra-vars で動作を変更させること自体が危険なんじゃないか?という気がしてきました。今回は引数の使い間違えでしたが、引数の使い方を理解していても version を versoin と書き間違えてしまうくらいは、普通にありそうです。

--extra-vars の代替案としての vars_prompt

--extra-vars の代替案としては、Promptsに説明のある vars_promptを使うという方法があります。

vars_prompt は、playbook の実行後に、変数入力のプロンプトを表示するためのオプションです。例えば、playbook に、

- hosts: apservers
  vars_prompt:- name:"version"prompt:"Branch name?"default: master

と書いておくと、playbook の実行時に以下のプロンプトが表示されます。そのまま Enter キーを押せば version 変数に "master"が代入され、ブランチ名を入れればそちらに変わります。

Branch name? [master]:

これでまあ確かに安全になるので、多くの場合は vars_prompt を使うのがよいと思います。

ただ、master ブランチをデプロイするのが大半、というケースでは Enter キーを無駄にぺちぺち押さないといけないのが面倒です。また、カスタマイズ可能な変数がもっと多い場合は、その変数の数だけ Enter キーをぺちぺち押さないといけなくなります。何も考えずに Enter キーをぺちぺち押す習慣ができるのは、またなにか別の障害の原因になりそうで嫌な感じです。

--extra-vars を安全に使うためのラッパー ansible-playbook-se

さっきの同僚の例について考え直してみると、要するにあれは "develop"という僕の想定しない変数名を、ansible-playbook コマンドが受け入れてしまったのがいけなかったわけです。

それなら、--extra-vars 引数に指定できる変数名を限定できれば、それで十分安全になるのでは? そう考えて、ansible-playbook コマンドのラッパー "ansible-playbook-se"を試作してみました。最近覚えた Python 3 で実装し、GitHub にソースコードを置いておきました。

github.com

使い方はまず、このリンク先にある ansible-playbook-se と extra-vars-cheker を、どこかパスが通ったところに置きます。

次に、playbook が置いてあるのと同じディレクトリに、以下のような内容で extra-vars.yml という名前のファイルを作ります。このファイルには、playbook のファイル名と、各ファイルが --extra-vars で指定するのを許す変数名を書きます。

---deploy-app.yml:- version

そして、いつも使っている ansible-playbook の代わりに、ansible-playbook-se を使うようにします。そうすると、想定外の変数が指定された場合、ansible-playbook-se は以下のようなエラーを出して、処理を中断します。

$ ansible-playbook-se -i inventory deploy-app.yml --extra-vars="develop"
ERROR: Invalid extra-var format: develop
ERROR: extra-vars-checker Failed.

安全になりましたね? なりましたよね? でも、これはこれで何だか面倒な気がしますね……。

試しに作ってはみたのですが、まだ実際の環境には導入していません。こういうニーズって、他の人にもあるもんでしょうか? もしニーズがあればもう少しちゃんと作ろうと思うので、ご意見お待ちしています。


艦これアーケードのプレイデータ管理ツール Admiral Stats を作って公開したら、プレイヤーが多いのか少ないのかわからなくなってきた話

$
0
0
f:id:muziyoshiz:20161011225236p:plain:w600

これまでのあらすじ

艦これアーケードにハマった勢いで、夏休みにプレイデータ管理ツール Admiral Stats を開発し、9/3 にリリースしました。

www.admiral-stats.com

艦これアーケードと、Admiral Stats の詳細については、以前書いた記事をご覧ください。

muziyoshiz.hatenablog.com

簡単に言うと、艦これアーケードとは、艦娘と呼ばれるキャラのカードを集めて、選りすぐりのデッキを作成し、ステージを攻略していくアクションゲームです。数ヶ月に1回、大きなゲーム内イベントがあるので、それに向けてキャラのカード集めやレベル上げをしていくのが主な楽しみ方になります。Admiral Stats はそういう楽しみ方を助ける(主に自分が楽しくプレイする)ことを目的に開発しているツールです。

9/3 のリリースから約1ヶ月が経過しましたが、正直言って、なかなかユーザが増えずに苦戦しています。そこで今回はこれまでの Admiral Stats のアクセスログを解析し、今後の対策を考えようとしたら、よくわからなくなってきた……という話をします。

最近追加した機能

リリース後に追加した機能で、大きな機能は以下の4点です。

  • 9/10:Lv. 99 到達予想日の追加
  • 9/17:本家のデータ形式変更に対応(★の数のインポート、および一覧表示)
  • 9/18:Ruby なしで使える PowerShell 版のエクスポータ(ユーザの @sophiarcp さんが開発してくれた機能の取り込み)
  • 10/7:全艦娘との比較(艦娘カード入手率)ページの追加

特に、9/18 の PowerShell 版のエクスポータは、利用者増に大きく貢献してくれました。それまでは Ruby 版のエクスポータ(しかも bundler のインストールとか要求する)しか無かったので、そこで脱落する人が多かったと思われます。

また、10/7 に追加した 艦これアーケードの艦娘カード入手率は、Admiral Stats にプレイデータをアップロードした提督全体に対する、各艦娘カードを入手済みの提督の割合を表示する機能です。例えば、最もレアと言われる「秋月」のカードを、提督全体の何%が持っているか、といったことがわかります。

f:id:muziyoshiz:20161011225334p:plain:w600

これらの機能でユーザ数は増えたのですが、それでもプレイデータを Admiral Stats にインポートしてくれたユーザはまだ 30 名程度となっています。まだまだ少ないですね……。

Admiral Stats の利用状況

Admiral Stats 自体のログと、Google Analytics のデータを元に、利用状況を調べてみました。3行で 4行でまとめると、結果はこんな感じです。

  • Admiral Stats の離脱率は高いが、1回使ってもらえればそこからの定着率はそれほど悪くない
  • Admiral Stats にアクセスしたユーザのデバイスは、おおよそ PC:Android:iOS = 3:1:1 の割合
  • そもそもアクセス数が少ない
  • ユーザ増に一番寄与したのは、1回だけ取り上げてもらったまとめサイト

以下は、その詳細です。

Admiral Stats のログ(9/3 〜 10/9 の 37 日間)

まず、Admiral Stats のログを使って、定着したユーザ数を見ていきます。

利用状況 ユーザ数 ログインユーザ数に対する比率 GA 計測のユーザ数に対する比率
1回以上ログインしたユーザ(以下、ログインユーザ) 107名 - 16.8 %
1回以上データをインポートしたユーザ 30名 28.0 % 4.7 %
2回以上データをインポートしたユーザ 22名 20.6 % 3.4 %
3回以上データをインポートしたユーザ 19名 17.8 % 3.0 %
先週(10/3〜9)に1回以上データをインポートしたユーザ 19名 17.8 % 3.0 %

Google Analytics(GA)で計測したユーザ数に対するログインユーザの比率は 16.8 % でした。1回ログインするだけでも心理的抵抗があると思うので、これは、この種のツールにしてはそこまで低くないと考えています。

また、1回ログインしてくれたユーザのうち、何らかの理由で脱落したユーザが 72 % でした。この脱落率は高いですね……。理由は以下のように予想しています。

  • エクスポータが PC 必須のため、PC を持っていない(あまり使わない)ユーザが使えなかった
  • 使う気にはなったが、エクスポートツールのインストールに失敗した
  • ログインしてみたが、機能が期待したものと違った

3回以上データをインポートしてくれたユーザはそれなりに定着してくれたアクティブユーザである、と仮定すると、アクティブユーザの比率はログインユーザの 17.8 %、1回以上インポートしたユーザの 63.3 % でした。1回インポートしてもらえれば、そのまま定着する割合はまあまあ高いと言えそうです。

Google Analytics のデータ(9/3 〜 10/9 の 37 日間)

PV 数などの基本的な数値は以下の通りです。少ないですね。検索エンジン経由のアクセスはほとんどなく、アクセス元はこのブログに書いた記事、Twitter での宣伝、おーぷん 2ch での宣伝と、そのまとめブログ記事でした。

項目 計測結果
PV 数 3,845
ユーザ 638
セッション 1,328
新規セッション率 47.97 %

じゃあ、定着せずに離脱してしまったユーザはどこから Admiral Stats に来て、どんな端末を使っていたのか? 以下は、集客のサマリーから、Oauth 認証のトラフィックと、(ブックマークなどを介した)直接アクセスを除外したものです。これは、ユーザ定着後のアクセスを除いた、初回アクセスの数に近いと思います。

参照元 デバイス セッション 新規セッション率 新規ユーザ
まとめサイト デスクトップ 167 83.83% 140
Twitter デスクトップ 125 16.80% 21
まとめサイト モバイル 122 90.16% 110
Twitter モバイル 97 92.78% 90
おーぷん 2ch デスクトップ 59 27.12% 16
おーぷん 2ch モバイル 37 70.27% 26
はてなブログ(このブログ) デスクトップ 27 40.74% 11

Admiral Stats は PC のみ対応(モバイルは表示のみ対応)なので、デスクトップの新規ユーザ獲得が大事なのですが、その点で一番強かったのは1回だけ取り上げてもらえたまとめサイト(【艦これアーケード】プレイデータ管理ツールを作ってみた : 艦これアーケードまとめ速報@艦アケ 艦これAC)でした。Twitter もアクセスは多いのですがモバイルが主体で、デスクトップの新規ユーザは少なく、なかなか厳しいです。

なお、iOS と Android の比率は、若干 Android のほうが多いもののほぼ同率で、どちらか一方だけ対応すればよいという状況ではなさそうでした。

艦これアーケードのプレイヤーってどれくらいいるの?

とりあえず定着率は改善すべきことがわかりましたが、そもそもアクセス数が少ないこともわかりました。

アクセス数が少ないってことは、プレイヤーにリーチできてないのか、そもそもプレイヤー自体が少ないのか? そもそも、艦これアーケードのプレイヤーって実際に何万人くらいいるんだろう?と疑問に思い、手に入る情報の範囲でプレイヤー数を見積もってみました。

Admiral Stats のデータから推測 → 7,000 人

せっかくなので、まずは Admiral Stats のデータを使って推測してみます。

艦これアーケードのプレイヤーズサイトには「暫定順位」という項目があり、ここに全国での順位が表示されます。ただし、順位が低すぎると「圏外」と表示されます。集まったデータを見る限りでは、暫定順位は 3,000 位くらいから下になると「圏外」と表示されるようです。ちなみに、僕は週1回ゲーセンに通う程度なので、「圏外」以外の表示を見たことないです。

f:id:muziyoshiz:20161011225746p:plain
(プレイヤーズサイトより抜粋)

Admiral Stats のデータを調べてみたところ、9月に1回以上データをインポートしている提督は21名で、9月末時点の暫定順位は以下の通りでした。

  • 3,000 位以内:9名
  • 圏外:12名

もし、艦これプレイヤー全体もこれと同じ比率に従うなら、9月のプレイヤー数は 7,000 人、ということになります。ただ、さすがにそんなに少ないわけないですよね……。

Admiral Stats を使ってくれているユーザには(こんな地味なツールに興味を持ってくれるくらいなので)ライトユーザはあまりいないので、コアユーザに比率が偏りすぎているのだろうと思います。この推測は置いておいて、別の方法を試してみます。

ゲーセンの混み具合と設置店舗数から推測 → 約 37,000 人

僕は土日しかゲーセンに行かないので、平日の状況はわからないのですが、土日に行くと無制限台はだいたい埋まっていて、制限台は半々くらいで空いている印象です。

公式サイトの設置店舗検索にある店舗がすべてなら、艦これアーケードの設置店舗数は10月11日現在で 652 店舗です。

各店舗への納品数はわかりませんが、僕がよく行く地域のゲームセンターを例に上げるとそれぞれ 10, 2, 4, 4, 4 台で、平均 4.8 台設置されています。まずはこの値で概算すると、全国に筐体が 3129.6 台あることになります。あとは、少し雑ですが、以下のような仮定を置きます。

  • 筐体の主な稼働時間は9時〜21時間の12時間で、各筐体とも、その半分の6時間はプレイされている(半々くらいで空いている印象から)
  • 1人あたりの、1日のプレー時間は1時間
  • 一般的なプレイヤーは土日どちらかの1回だけ来る
  • 平日来るようなランカーは土日も当然来るだろうから、平日の分は無視

この仮定に基づくと、現在も艦これアーケードをプレイしているプレイヤー数は 652 * 4.8 * 6 * 2 = 37,555 名となります。うーん、こっちのほうが実体に近いですかね。

公式サイトからの推測 → ??

意外なことに、艦これアーケード専用の Twitter アカウントは存在せず、元々あった艦これの Twitter アカウント 「艦これ」開発/運営(@KanColle_STAFF)が艦これアーケードの告知も兼ねています。ブラウザ版とアーケード版でアカウントが分かれていれば、フォロワー数からプレイヤー数を見積もれたんですけどね。

他の公式サイトとしては、Admiral Stats もお世話になっている SEGA の艦これアーケード プレイヤーズサイトがあります。

余談ですが、最近このプレイヤーズサイトで、SEGA IDを登録するとプレゼントが貰えるキャンペーンをやっていました(敵泊地への突入!準備キャンペーン)。こういうキャンペーンをするということは、プレイヤー数と比較して、プレイヤーズサイトを使っているユーザはあまり多くないのかもしれません。

Wiki からの推測 → ブラウザ版の 100 分の 1

艦これアーケードに関する Wiki はいくつかあり、アクセスが多いのは以下の2つのようです。

前者の PV はわからないのですが、後者はサイドバーの一番下にアクセスカウンタがあり、10/9(日) の PV は 1,251 でした。艦これブラウザ版の Wikiの同じ日の PV は 127,706 で、艦これアーケード版の約100倍でした。

この差はプレイヤーの規模を表してるのでしょうか? それとも、アーケード版はブラウザ版ほど難しくないから、Wiki のニーズが低い、というだけなのでしょうか。アーケード版でイベントが始まったら、また状況は変わるかもしれません。

まとめサイトからの推測 → ブラウザ版の 8 分の 1

ブラウザ版のまとめサイトは複数ありますが、艦これアーケード専用のまとめサイトで、きちんと編集されていそうなのは1個くらいしか見当たりませんでした。元々あるまとめサイトが(公式 Twitter と同様に)ブラウザ版とアーケード版の両方の話題を扱っているため、新規参入が難しいのかもしれません。

まとめサイトのPV数はわかりませんが、Twitter のフォロワー数を比較すると、10/10時点で 艦これアーケードまとめ速報@艦アケ (@kancolleAC)のフォロワー数が 15,700、艦これ速報(@kancollect)のフォロワー数が 123,250 で、こちらは約8倍の差のようです。

ちなみに、Admiral Stats は1回だけまとめサイトに取り上げられたのですが、艦これ界隈ではツール関係の話題は嫌がられるようで、それ以降はスルーされています。宣伝方法として、まとめサイトに頼るのはちょっと難しそうです。

ブラウザ版とアーケード版のユーザ比からの推測 → 約 50,000 人

ブラウザ版のプレイヤー数は非公開ですが、先人の動向分析では MAU(Monthly Active User、月間の総ログインユーザ数)が約40万人と推測されています。

このデータを使えば、ブラウザ版とアーケード版のユーザ比さえわかれば、アーケード版の MAU を推測できます。

人気の比較手段の定番として、Google Trends で人気度を調べてみました。上のグラフは「艦これアーケード」のみ、下のグラフは「艦これアーケード」と「艦これ」の、2016年4月28日(艦これアーケード稼働開始日)以降の比較です。

人気度の平均値は 艦これアーケード:艦これ = 5:47 でした。「艦これ」に「艦これアーケード」も含むと考えると アーケード版:ブラウザ版 = 5:42 で、つまりブラウザ版はアーケード版の約 8.4 倍となります。奇しくも、まとめサイトのフォロワーの比率に近くなりました。

以上から、ブラウザ版の MAU はアーケード版の約 8 倍と仮定すると、アーケード版の MAU は約 5 万人となります。

その他、攻略サイト、ブログなど

定量的でない比較としては、アーケード版はブラウザ版と比べて、ユーザ発のコンテンツがほとんど無いような気がします。

例えば、ブラウザ版の攻略サイトに当たるものは、アーケード版についてはあまり見当たりませんでした。"艦これアーケード""攻略"でググると 約 356,000 件 (0.46 秒)と表示されるのですが、その大半が Wiki のようで、ページをめくっていくと(似たページを除外した状態では)7 ページ目までしか出てきませんでした。

あとは、ニコ動や YouTube に、解説動画や、単艦プレイ動画などが挙がっていて、参考になるものもたくさんあるのですが、それほど数は多くありません。アクションゲームなのでプレイ中の記録が難しいのと、SEGA 公式が撮影 OK としてないので、ユーザ主体のコンテンツもなかなか出にくいのでしょうか。

まとめ:Admiral Stats がこの先生きのこるためには

長くなりましたが、艦これアーケードのプレイヤー数としては 3 〜 5 万人くらいが妥当な推測に思えます。Admiral Stats としてはその 1 % の 300 〜 500 人くらいを目標に頑張りたいところです。

今後の予定としては、PC ユーザの定着率を上げるためにエクスポータを使いやすくし、検索エンジン経由のアクセスを増やすために 艦これアーケードの艦娘カード入手率のようなコンテンツを増やそうと思います。あとはイベント対応で何か面白い機能が浮かんだら突っ込みたいですね。

根本的にはスマホ対応しないとユーザ増は難しそうですが、僕にそのスキルがないのと、非公式ツールなので審査を通らない気がするため、着手すべきか悩んでいるところです……。

ともあれ今後もいろいろ機能追加していきますので、艦これアーケードをプレイ中の提督は是非一度使ってみてください。ただ、プレイデータのエクスポートに非公式ツールを使わざるを得ない関係上、利用は自己責任でお願いします。

www.admiral-stats.com

github.com

艦これアーケードのプレイデータ管理ツール Admiral Stats のソースコード公開のお知らせ

$
0
0
f:id:muziyoshiz:20161011225236p:plain:w600

ソースコード公開しました

艦これアーケードのプレイデータ管理ツール "Admiral Stats"のソースコードを、GitHub で公開しました。https://www.admiral-stats.com/で動いているのと全く同じものです。

github.com

先日正式リリースされた Ruby on Rails 5 で開発しています。Admiral Stats の開発に興味がある方や、動作の詳細に興味がある方はぜひご覧ください。

gitter のチャットルームも作りました

僕は本当につい最近まで知らなかったんですが、艦これブラウザ版には MyFleetGirlsってツールがあるんですね(完全に Admiral Stats の先人だ……)。ここの開発スタイルを参考にさせてもらって、gitter のチャットルームを作りました。

gitter.im

GitHub アカウントがあれば誰でも入れますので、ご意見・ご要望のある方はこちらにお寄せください。もちろん、お知らせ用の Twitter アカウント @admiral_statsや、GitHub の issue ページにお寄せいただいても OK です。

Admiral Stats の関連記事

Rails 5 での実装の解説と、Admiral Stats の利用状況の分析を過去に書きました。

muziyoshiz.hatenablog.com

muziyoshiz.hatenablog.com

Exrm(Elixir Release Manager)を使った Phoenix アプリケーションのデプロイ

$
0
0
f:id:muziyoshiz:20161122004038p:plain

Elixir の勉強中

最近、職場の飲み会で同僚に「Erlang VM はいいぞ」と熱弁されたのをきっかけに、「プログラミング Elixir」を買って Elixir を触りはじめました。Elixir でサンプルコードを動かすだけだと身に付かなそうなので、「Phoenix で API サーバを書く」というのを当面の目標にしています。

Phoenix というのは Web フレームワークの名前で、Elixir 版の Rails みたいなものです。ディレクトリ構造なども Rails に近く、Rails 経験者にはとっつきやすい代物です。実際、MySQL から取得したデータをそのまま JSON で返すだけの単純な API サーバはすぐ書けました。

そして、この API サーバをレンタルサーバにデプロイしようとしたんですが、Phoenix には Capistrano に相当するものがないんですね。でもまあ、同じような処理を Ansible で書けば済むだろう……と最初は思ってたんですが、Erlang VM の機能をフルに使おうと思ったら Capistrano のような流儀ではうまくいかないことがわかってきました。今回はそんな話をします。

Phoenix アプリケーションのデプロイ方法

Phoenix のサイトにあるガイドでは、Phoenix アプリケーションを(Heroku とかではない)普通のサーバにデプロイする方法が2通り紹介されています。

方法1. ソースコードをサーバに置いて、mix phoenix.server で起動

1つ目の方法は、Deployment / Introductionにある方法です。

Phoenix アプリケーションをローカルで開発するときは mix phoenix serverというコマンドで起動するのですが、本番環境でも同じように起動する、という方法です。以下のようにすると、デーモンとしても起動できます。

MIX_ENV=prod PORT=4001 elixir --detached -S mix phoenix.server

この方法は単純でわかりやすいのですが、アプリケーションの起動・停止や、決まったディレクトリへのログ出力、といった Web アプリで普通必要になるものを、自前で用意する必要がでてきます。

例えば、Phoenix アプリをデーモンとして起動した場合、PID を記録しておかないと停止できませんが、そういう処理を自分で書く必要があります。私の探した範囲では、How to reload the server in production. · Issue #1288 · phoenixframework/phoenixにある方法で、起動と同時に PID を記録できるようです。

elixir --detached -e "File.write! 'pid', :os.getpid" -S mix phoenix.server

また、ログ出力についても、onkel-dirtus/logger_file_backendなどを使って、出力先ファイルを指定しておく必要があります。

方法2. Exrm でビルドした結果をサーバに置いて、Exrm が自動生成したスクリプトで起動

そしてもう1つは、Deployment / Exrm Releasesにある方法です。

Elixir Release Manager (Exrm) というのは、Elixir で書かれたアプリケーションを、配布可能な tarball にまとめるためのビルドツールです。開発マシンやビルドサーバ上で mix releaseコマンドを実行すると、以下のようなディレクトリ構成でファイルが生成されます。ここでは、ビルドしたアプリケーションの名前を、仮に admiral_stats_api とします。

rel
└── admiral_stats_api
    ├── bin  (アプリケーション管理用のスクリプト群)
    │   ├── admiral_stats_api
    │   ├── admiral_stats_api.bat
    │   ├── install_upgrade.escript
    │   ├── nodetool
    │   └── start_clean.boot
    ├── erts-8.1  (Erlang ランタイム)
    │   ├── bin
    │   ├── doc
    │   ├── include
    │   ├── lib
    │   └── man
    ├── lib  (アプリケーションが依存するすべてのモジュール)
    └── releases
        ├── 0.0.1
        │   ├── admiral_stats_api.bat
        │   ├── admiral_stats_api.boot
        │   ├── admiral_stats_api.rel
        │   ├── admiral_stats_api.script
        │   ├── admiral_stats_api.sh
        │   ├── admiral_stats_api.tar.gz (※)
        │   ├── start.boot
        │   ├── start_clean.boot
        │   ├── sys.config
        │   └── vm.args
        ├── RELEASES
        └── start_erl.data

上記のツリーで (※) を付けた admiral_stats_api.tar.gz には、このファイル自身を除く配布物すべてが入っています。この tarball をデプロイ先(仮に /var/www/admiral_stats_api とする)で解凍して、

$ bin/admiral_stats_api start

を実行するとサーバが起動し、

$ bin/admiral_stats_api stop

を実行するとサーバが停止します。ログファイルは、自動生成される log ディレクトリ以下に出力されます。

また、Exrm の凄い点として、アプリケーションの無停止アップグレードが可能です。これは、Capistrano がやるような「一瞬止めて、シンボリックリンクの向き先を変えて、再起動」という無停止っぽいアップグレードではなくて、本当に無停止で、内部状態も含めてアップグレードする、という機能です。

例えば、新しいバージョン 0.0.2 をリリースしたいときは、さっきと同じように mix releaseで tarball を作り、その tarball をデプロイ先の /var/www/admiral_stats_api/releases/0.0.2/admiral_stats_api.tar.gz に置いてから、

$ bin/admiral_stats_api upgrade 0.0.2

を実行すると、以下のようなメッセージが表示されて、バージョン 0.0.2 が動作し始めます。

$ bin/admiral_stats_api upgrade 0.0.2
Release 0.0.2 not found, attempting to unpack releases/0.0.2/admiral_stats_api.tar.gz
Unpacked successfully: "0.0.2"
Generating vm.args/sys.config for upgrade...
sys.config ready!
vm.args ready!
Release 0.0.2 is already unpacked, now installing.
Installed Release: 0.0.2
Made release permanent: "0.0.2"

単純な API サーバで使うにはオーバースペックな機能な気もしますが、せっかく Erlang VM を使うんだし、今回はこちらの方法でデプロイすることにしました。

しかし、自動化しようとするとうまくいかない(なんで??)

上記の手順をコマンドで手で打ちながら確認して、「なるほど完全に理解した。この手順を単に Ansible で自動化すればデプロイ自動化できるよな!」と思って、こういう Ansible playbook を書きました。

  • git cloneで最新版をダウンロード
  • Git リポジトリ上に置いてない、パスワードなどを含むファイル(prod.secret.exs)を自動生成
  • コンパイル(mix do deps.get, deps.compile, compile
  • リリース用の tarball を作成(mix release
  • tarball をデプロイ先にコピー
  • tarball を /var/www/admiral_stats_api/releases/バージョン番号 に配置(解凍はしない)
  • upgrade コマンドを実行(bin/admiral_stats_api upgrade バージョン番号

で、この playbook を実行したところ、最後の upgrade コマンド実行のところでエラーメッセージが出て失敗しました。playbook と同じコマンドを手で打ったところ、こんなエラーメッセージでした。

$ bin/admiral_stats_api upgrade 0.0.2
Release 0.0.2 not found, attempting to unpack releases/0.0.2/admiral_stats_api.tar.gz
Unpacked successfully: "0.0.2"
Generating vm.args/sys.config for upgrade...
sys.config ready!
vm.args ready!
Release 0.0.2 is already unpacked, now installing.
escript: exception error: no case clause matching
                 {error,{enoent,"/var/www/admiral_stats_api/releases/0.0.1/relup"}}

一体何なのこれ……。

Phoenix のサイトや Exrm のサイトを読んでも理由が分からず、「プログラミング Elixir」にもそれらしい説明はなく、エラーメッセージで検索した結果をひたすら探し回ってわかったのですが、どうやら

「0.0.1 から 0.0.2 にアップグレードするための tarball を作るためには、 rel/アプリケーション名/releases/0.0.1ディレクトリ以下が残っている状態で mix releaseコマンドを実行しなければならない」

らしいです。

改めて確認したところ、0.0.1 ディレクトリがない状態で 0.0.2 の mix release を実行したところ、コンソール出力は以下のようになっていました。

$ MIX_ENV=prod mix release
Building release with MIX_ENV=prod.
==> The release for admiral_stats_api-0.0.2 is ready!
==> You can boot a console running your release with `$ rel/admiral_stats_api/bin/admiral_stats_api console`

その一方、0.0.1 ディレクトリがある状態で mix release を実行したところ、以下のように 0.0.1 から 0.0.2 へのアップグレードのためのファイルが生成されたことを示すメッセージが増えました。

$ MIX_ENV=prod mix release
Building release with MIX_ENV=prod.
This is an upgrade, verifying appups exist for updated dependencies..
==> All dependencies have appups ready for release!
==> Generated .appup for admiral_stats_api 0.0.1 -> 0.0.2
==> The release for admiral_stats_api-0.0.2 is ready!
==> You can boot a console running your release with `$ rel/admiral_stats_api/bin/admiral_stats_api console`

ここまでのまとめ:結局どうしたらいいの?

要するに、Exrm を使って Phoenix をリリースするためには、ビルド時に、少なくとも1つ前のバージョンのビルド結果をローカルに置いておく必要があります。upgrade コマンドを使ってアップグレードする限り、これは必須のようです。

世間の人はどうやって Exrm を使っているのか調べてみたところ、例えば andrewvy/ansible-elixirという role では、ビルド結果をすべて git push していました。ただ、Phoenix アプリのビルド結果には秘密情報(prod.secret.exs)も含まれるので、Phoenix アプリを OSS にするならこの手は使えません。秘密情報をすべて環境変数から読み込むように書き換えるという手はありますが、それでも、本来 Git に登録する必要がないファイルを Git に登録しなければならない、というデメリットは残ります。

Phoenix アプリケーションを開発してる人って、普通はどうやってデプロイしてるんでしょうか? Exrm なんて使わずに、mix phoenix.serverで起動する方法を採用して、起動スクリプトは自分で書いてるんでしょうか。経験者の方にぜひ教えてほしいです……。

この記事の続き

続きを書きました。

muziyoshiz.hatenablog.com

参考ページ

参考書籍

プログラミングElixir

プログラミングElixir

第18章「OTP:アプリケーション」の18.6節「EXRM − Elixir のリリースマネージャ」に、Exrm の解説が8ページほど書かれています。内部状態をマイグレートするために必要なコードについても若干説明あり。

Programming Phoenix: Productive |> Reliable |> Fast

Programming Phoenix: Productive |> Reliable |> Fast

まだざっと読んだだけですが、デプロイに関する話題は(少なくとも独立した節は)なさそうです。

Exrm を使った Phoenix アプリケーションのデプロイ方法を ansible-elixir-stack から学ぶ

$
0
0
f:id:muziyoshiz:20161122004038p:plain

これまでのあらすじ

Elixir の世界には、Ruby での Ruby on Rails に相当する "Phoenix"という Web アプリケーションフレームワークがあります。しかし、Capistrano に相当するものは無くて、デプロイの考え方は Rails とはだいぶ違いそうです。

前回は Elixir の世界のビルドツール Elixir Release Manager (Exrm) で作った tarball をデプロイする方法について紹介しました。ただし、upgrade コマンドで無停止アップグレードしたいなら、ビルド環境には最新のソースコードだけでなく、アップグレードする前のバージョンのビルド結果も置いておかなければいけない(!)という話をしました。

muziyoshiz.hatenablog.com

今回は、この Exrm を使ったビルド方法の話の続きです。

Phoenix アプリケーションの情報に関するネット上の情報

Phoenix アプリケーションのデプロイについてネット上の情報を探したところ、(検索ヒット数はかなり少なかったのですが)ansible-elixir-stack という Ansible role と、その紹介記事が見つかりました。この Ansible role のコードを読んでみたところ、デプロイ方法がやっと理解できたので今回ご紹介します。

ansible-elixir-stack の使い方

github.com

ansible-elixir-stack は ansible-galaxy からインストールできます。

$ ansible-galaxy install HashNuke.elixir-stack

Phoenix アプリケーションの mix.exs に exrm を追加してから、

$ curl -L http://git.io/ansible-elixir-stack.sh | bash

を実行すると、その Phoenix アプリケーションのディレクトリ内に、Ansible の実行に必要なファイル(playbook や inventory など)が自動生成されます。自動生成された playbook は ansible-elixir-stack を呼び出して、inventory ファイルに記載されたサーバに Phoenix アプリケーションをデプロイします。

この ansible-elixir-stack という role は基本的にオールインワンなので、Nginx サーバなども自動的にインストールしてしまいます。開発環境の構築に使うならこのままでいいかもしれませんが、本番環境を構築する場合、これを参考に独自の playbook を書く必要がありそうです。

ansible-elixir-stack を実行するための手順については、以下のブログ記事で詳しく紹介されています。そのため、この記事では特に触れません。

blog.johanwarlander.com

ansible-elixir-stack は自動アップグレードをどうやって実現しているのか?

deploy_type 変数での動作の切り替え

ansible-elixir-stack を普通に使うと、前回のブログ記事に書いた「方法1. ソースコードをサーバに置いて、mix phoenix.server で起動」と同じように、サーバを1回停止して、再起動します。

ただし、deploy_type という変数に "upgrade"という値をセットしておくことで、無停止アップグレードの動作に切り替わります。この動作の切り替えについては Hot code-reloading のページに記載されていました。今回はこちらの動作を解説します。

role 内部で実行されるコマンド

ansible-elixir-stack では、初回デプロイ時の playbook(setup.yml)と、2回目以降のデプロイ時の playbook(deploy.yml)が分かれています。ただ、いずれの場合も project.yml の以下の部分で git clone を実行し、サーバ上に Phoenix アプリケーションのソースコード一式をダウンロードします。

- name: "clone project"
  git:
    repo: "{{ repo_url }}"
    version: "{{ git_ref }}"
    dest: "{{ project_path }}"
    accept_hostkey: True
    force: True
    remote_user: "{{ deployer }}"

デフォルトでは project_path は /home/deployer/projects/{{ app_name }}です。

そして、release.yml の以下の部分で、Phoenix アプリケーションをビルドします。ちなみに、mix は ~/.mix 以下にインストールされたファイルを使うため、bash -lcの指定は必須です。

- name: "compile and release"
  command: bash -lc 'SERVER=1 mix do compile, release' chdir="{{ project_path }}"
  remote_user: "{{ deployer }}"
  environment:
    MIX_ENV: "{{ mix_env }}"
    PORT: "{{ app_port }}"

そして、最後の部分で、「git clone した最新バージョンのバージョン番号取得」、および「upgrade コマンドの実行」を行います。

- when: deploy_type == "upgrade"
  name: get app version
  command: bash -lc "mix run -e 'IO.puts Mix.Project.config[:version]'" chdir="{{ project_path }}"
  remote_user: "{{ deployer }}"
  register: app_version

- when: deploy_type == "upgrade"
  name: set upgrade command
  set_fact: upgrade_command='rel/{{ app_name }}/bin/{{ app_name }} upgrade "{{ app_version.stdout }}"'

- when: deploy_type == "upgrade"
  name: upgrade app
  command: bash -lc "{{ upgrade_command }}" chdir="{{ project_path }}"
  remote_user: "{{ deployer }}"
  environment:
    MIX_ENV: "{{ mix_env }}"
    PORT: "{{ app_port }}"

上記の upgrade コマンドの実行は、仮にアプリケーション名を sample_app、バージョン番号を 0.0.2 とすると、以下と同じ意味になります*1

$ cd /home/deployer/projects/sample_app
$ SERVER=1 mix do compile, release
$ rel/sample_app/bin/sample_app upgrade 0.0.2

デプロイ作業を常にこの playbook で実行しているなら、デプロイ先サーバの以下のディレクトリには、前回のバージョンのビルド結果が残っているはずです。

/home/deployer/projects/sample_app/rel/sample_app/release/0.0.1

そのため、前回のブログ記事で問題に挙げた「ビルド環境に、アップグレードする前のバージョンのビルド結果も置いておかなければいけない」という条件はクリアされます。

しかし、そう考えると新しく追加したサーバにいきなりバージョン 0.0.2 をデプロイする、という場合にはどうなるのか?が気になります。そういうケースでは、git clone しても上記の 0.0.1 ディレクトリは作られません。ただ、その場合はアプリはまだ動いていないため、単にバージョン 0.0.2 のアプリが新たに起動されるだけで、特に問題は起こらないようです。

この方法の懸念点

サーバごとのコードの状態の違い

Capistrano の場合は、通常はデプロイするたびに新しいディレクトリで git clone が実行されます。そのため、すべてのサーバで、余計なファイルがない環境で最新のコードが実行されるという安心感があります。

一方、上記の方法では、force=Yes で git clone が実行されるとはいえ、すべてのサーバ上のファイルが同じにはならない可能性があるのが気になります。まあ、そんなことを気にするなら、無停止アップグレードは諦めて Docker でも使え、という話かもしれません。

バージョン番号を上げるのを忘れそう

この方法に限らず、Exrm の upgrade コマンドを使う場合に共通した問題ですが、デプロイのたびに毎回必ず mix.exs 内のバージョン番号を上げて、git push する必要があります。ちょっとした更新のたびにこれを実行するのはかなり面倒です。

def project do
  [app: :hello_phoenix,
   version: "1.4.1",
   elixir: "~> 1.0",
   ...

この問題への対応として、ansible-elixir-stack の作者は Hot code-reloading のページにて「バージョン番号の末尾に Git のコミットハッシュ値を自動的につける」という方法を提案しています。

def project do
  {result, _exit_code} = System.cmd("git", ["rev-parse", "HEAD"])

  # We'll truncate the commit SHA to 7 chars. Feel free to change
  git_sha = String.slice(result, 0, 7)

  [app: :hello_phoenix,
   version: "1.4.1-#{git_sha}",
   elixir: "~> 1.0",
   ...

もしこの方法を採用するなら、この対策は絶対に入れておいた方がよさそうです。

*1:SERVER=1 というのは ansible-elixir-stack が勝手に作ったフラグなので気にしないで OK です。

Phoenix Framework に関する有名なベンチマーク同士の関係

$
0
0
f:id:muziyoshiz:20161224204900p:plain

Phoenix Framework(以下、Phoenix)は、Elixir のための Web アプリケーションフレームワークです。

Phoenix の開発者 José Valim 氏は Ruby on Rails のコミッタだったため(Rails Contributors - #5 José Valimを見る限り、2014年まで?)、使い勝手はとても Rails に似ています。そのため、Phoenix は Rails とよく比較され、「Rails よりも10倍近く速い」という評判を時々目にします。

ただ、その評判の出処になったベンチマークについて、僕は具体的な内容を知りませんでした。また、記事によってベンチマークのリンク先がまちまちで、それぞれの関係がよくわかりませんでした。自分で Phoenix を使ってみるにあたり、このあたりを少し調べてみたので、その結果をメモしておきます。

最初のベンチマーク

Phoenix と Rails に関する最初のベンチマークは、Chris McCord(Programming Phoenix: Productive, Reliable, Fastの著者)による2014年7月のブログ記事のようです。

littlelines.com

この記事は「Phoenix は Rails より10.63倍速い」というベンチマーク結果を示しています。ちなみに、これは Elixir vs Ruby Showdown というシリーズの2番目のポストで、最初のポスト(Elixir vs Ruby Showdown - Part One)では「Ruby の i18n gem と比較して Elixir 版は73倍速い」という結果を出してます。

このベンチマークの内容は以下のようなものです。この特徴は、後述する他のベンチマークにも引き継がれています。

  • URL '/:title'の :title 部分をコントローラに渡す
  • コントローラは、モデルの返り値を想定したマップ(固定値)の配列をビューに渡す
  • ビューは、受け取った配列を回して HTML を生成し、クライアントに返す
  • データベースへのアクセスは行わない
  • ログ出力は行わない
  • ENV = production で動作させる
  • ベンチマークツールには wrk を使って、30秒間テストする

具体的なコードの説明も載っているので、後述するベンチマークを読むにしても、まずこのブログ記事は読んでおいた方が良いです。

ベンチマーク対象の拡大

その後、このベンチマークに触発されて、ベンチマーク対象を他のフレームワークに広げたものが Matthew Rosenberg 氏により公開されました。

github.com

Phoenix は、Rails よりむしろ Sinatra に似た軽量フレームワークではないか、との理由から、比較対象は主に Sinatra にインスパイヤされたフレームワークから選ばれています。また、Phoenix の実装の基盤である Plug というライブラリを素で使った場合も追加されています。

  • Phoenix (Elixir)
  • Plug (Elixir)
  • Rails (Ruby)
  • Sinatra (Ruby)
  • Express, Express Cluster (JavaScript)
  • Martini (Go)
  • Gin (Go)
  • Play Framework (Java)

このテストは、Phoenix のバージョンアップに合わせて、何度か実施されています。

Round 1 と 2-3 はスペックが違うので単純に比較できません。

  • Round 1: 3.4GHZ Core i7 (quad core), 12GB RAM
  • Round 2-3: 4.0GHZ Core i7 (quad core), 32GB RAM

Round 1 はスペックが低いので、単純比較できるのは Round 2 と 3 のみです。Phoenix の結果だけ比較すると、Round 3 は 2 よりも若干遅くなっています。Plug の結果はほとんど変わっていないので、Phoenix に機能が増えたからでしょうか?

Round 3 の結果を見ると、Phoenix は Play より Consistency は優れているものの、Throughput は Play の約 0.47 倍となっています。Elixir は stop-the-world GC がないので Java より高速、と思っていたので、最初にこの結果を見たときはかなりがっかりしました。

高スペックなベアメタルサーバ上での結果

2015年7月に、上記の phoenix-showdown を Rackspace のベアメタルサーバ(CPU Dual 2.8 Ghz, 10 core, RAM 128 GB)で実行した結果が公開されました。

Comparative Benchmark Numbers @ Rackspace

スペックが高いほど Elixir の真価が発揮されるのか、このベンチマークでは、Phoenix の throughput は Play とほぼ同じか若干上で、Consistency では引き続き上回っています。Rails との差は更に開いて、Phoenix の throughput は Rails の15倍になっています。

感想(と職場ブログの宣伝)

素の状態では、Phoenix は Rails よりも10倍以上高速なのは確かなようです。ただ、他のコンパイル言語(Java や Go)と比較すると、throughput については決定的な差は無さそうです。

また、これらのベンチマークはデータベースアクセスやログ出力といった、普通のアプリには絶対にある機能を省いているので、周辺ライブラリの充実度によっては、開発者の多い Java & Play Framework で実装した方が、総合的には速くなるのかもしれません。Rails 開発者が多い環境なら Phoenix に飛びついてもよさそうですが、Java 開発者が多い環境では Play のほうが速い、ということもあるのかもしれません。

そのあたりが気になって、Elixir (その2)とPhoenix Advent Calendar 2016の24日目として、Phoenix で書いた簡単なアプリケーションサーバに、HBase アクセスや、ファイルへのログ出力を足したら、性能はどう変わるか、という話を書きました。時間の都合で Play との比較まではできていませんが、今後気が向いたらそこまで試してみたいと思っています。

recruit.gmo.jp

記事の公開後に教えてもらったページ

togetter.com

信用できる社内 Wiki をつくるために守ってほしい、たったひとつのルール

$
0
0
f:id:muziyoshiz:20170109184841j:plain

このページについて

この記事は、以前書いた「社内Wikiに情報を書くときに守ってほしい、たったひとつのルール」の続編です。前回は、個々のページをどう書くべきかという話をしましたが、今回は社内 Wiki 全体を信用できるものにする方法について考えます。

muziyoshiz.hatenablog.com

想定する環境

この記事は、ソフトウェア開発プロジェクトに関する Wiki が社内にあって、そこに各人がドキュメント(仕様書や手順書など)を書けるようになっている環境を想定しています。

私自身、ソフトウェア開発のときしか Wiki を使わないので、具体例もそのような環境に寄っています。ただ、ある程度は社内 Wiki 全般に通じる話かと思います。

ルール:「更新され続ける」ページと「更新されない」ページをはっきり分ける

ここ1年ほど社内プロジェクトをいくつか渡り歩いていたのですが、個人的には、このルールが徹底されている Wiki ほど「信用できる」と感じました。

具体的な実現方法はいくつかありますが、例えば、Wiki のホーム画面の上半分を「更新され続ける」ページへのリンク、下半分を「更新されない」ページへのリンクにします。以下は、ホーム画面の例です。

# 更新され続けるページ

- [[用語一覧]]
- [[サービス仕様]]
- [[インフラ構成]]
- [[アプリケーション構成]]
- [[開発ルール]]
- [[手順書]]
- [[主要機能の解説]]

# 更新されないページ

- [[ミーティング議事録・資料]]
- [[ユーザーストーリーごと(または機能ごと)の設計]]
- [[リリース履歴]]
- [[運用作業履歴]]
- [[障害履歴]]
- [[技術検討メモ]]
- [[現在はメンテナンスされていないページ一覧]]

誤解を避けるために強調しておくと、これはWiki を「信用できる」ページと「信用できない」ページに分けよう、という話ではありません。信用できないことがはっきりしているなら、そのページは消したほうが良いでしょう。

そうではなくて、これは「完全に信用できる」少数のページと、「条件付きで参考にできる」多数のページに分けよう、という話です。以下では、説明のために、もう少し具体的な話をしてみます。

「信用できる」とは?

この記事で言う「信用できる」という形容詞は、「信用できる技術者」を指すときの「信用できる」と同じような意味だと思ってください。

私が知っている信用できる技術者を思い出すと、大抵以下のような長所を備えています。

  • 専門分野について正確で詳しい知識を持つ
  • わからないことは素直にわからないといい、必要な調査やテストを行って、あとからそれを補う

同様の状態を Wiki で実現したものが、この記事でいう「信用できる Wiki」になります。

「更新され続けるページ」については、そこを読むだけで正確な情報を知ることができて、読み手はソースコードを読んだりする時間を節約できます。

一方、「更新されないページ」については、過去のある時点では事実だった(あるいはその書き手が事実だと信じていた)情報ですが、現在では正しいかわからない情報です。その場合も、読み手は、条件付きでその情報を参考にすることで、調査にかかる時間を節約できます。

逆に「信用できない」とは?

上で挙げた信用できる技術者の長所をひっくり返すと、以下のように、信用できない技術者になります。

  • 専門分野について、曖昧な知識しか持たない
  • わからないことについても「○○に違いない」と断言する

大抵の Wiki は放っておくと、信用できない技術者に似た、信用できない Wiki になってしまいます。例えば、こんな感じです。

  • 仕様ページに書いてある仕様が、その後の機能追加の際に更新されなかったために、現在の仕様と違う
  • ローカル開発環境の構築手順書が、ゼロから動作確認されなかったために、他の人が試すと動かない

こうなると、普通の人は「ソースコードを読むしかない」、「実際に試してみるしかない」と判断して、その Wiki を避けてしまいます。信用できない技術者に、相談する人はいないですよね。

その社内 Wiki には、実際には役に立つ情報もあったかもしれないのに、全体の信用を失ってしまったために役に立つ情報まで読まれなくなるとしたら、それはもったいないことです。

ルールを運用するためのヒント

このルールをチーム全体できちんと運用するためのヒントを、いくつか挙げておきます。

ヒント(1):「更新され続けるページ」は極力少なくする

このルールを採用する場合、何か機能をリリースするたびに、その機能に関連する「更新され続けるページ」はすぐ更新する必要があります。忙しい時期でもこのルールを守れるように、更新され続けるページは極力少なくしておいたほうがよいでしょう。

例えば、以下のような情報は、更新し続けるメリットが、更新作業のコストを上回ると思います。

  • 用語一覧
    • そのプロジェクト固有の用語一覧。主に、新メンバへの説明用。
  • サービス仕様
    • 顧客に提供するユーザーマニュアルや、サポート対象 OS・ブラウザの情報など。
  • インフラ構成
    • ネットワーク構成図や、システム構成図、IP アドレス一覧など。
  • アプリケーション構成
    • コンポーネント一覧、バッチ一覧、データベース定義書など。
  • 開発ルール
    • そのプロジェクトで採用する開発プロセス、開発用ツール、コーディング規約、テスト基準など。
  • 手順書
    • システム構築のための手順や、日常的な運用作業の手順など。
  • 主要機能の解説
    • そのプロジェクトで特に難しい機能や、わかりにくい機能の理解を助けるための解説記事。

ヒント(2):更新し続けられないと判断したら、「更新されないページ」に移す

理想的には、「更新され続けるページ」はできるだけ多いほうが良いです。ただ、開発が多忙になったり、逆に開発が一段落して開発者の人数が減ったりすると、更新にかけられる時間は減ります。

そういう場合は、どのページを残すかチーム内で議論して、更新しきれないページは「更新されないページ」に移しましょう。Wiki のページ数よりも、「その Wiki は信用できるかどうか」のほうを重視すべきです。

例えば、以下の階層にある「機能Aの解説」ページを最近更新できてない、と気付いたとします。

  • 更新され続けるページ
    • 主要機能の解説
      • 機能Aの解説

この場合、そのページを「更新されないページ」以下の階層に移す、という具合です。「機能Aの解説」ページの先頭(「このページについて」欄)に、更新できなくなった時期とその事情を書いておくと、読み手の助けになるでしょう。

  • 更新されないページ
    • 現在はメンテナンスされていないページ一覧
      • 機能Aの解説

ヒント(3):「更新されないページ」を「更新され続けるページ」に格上げしたいときは、必ず書き換える

更新されないページを書いているうちに、分量が多くなり、内容も充実したように思えるので、「更新され続けるページ」に格上げしたくなることがあります。例えば、以下のような場合です。

  • ある機能の設計に関するページが、その機能に関するよいまとめに思えるので、主要機能の解説ページに格上げしたい。
  • ある障害の原因を特定するためにソースコードを調査したときのメモが、ソースコードのよい解説に思えるので、主要機能の解説ページに格上げしたい。

しかし、元々これらのページを書き始めた目的は「主要機能の解説」ではないため、余分な情報が多く含まれている可能性が高いです。分量が多いほど更新が大変になるので、元のページとは別のページを作り、更新し続ける必要がある部分だけ残して、リライトしたほうがよいでしょう。

また、そもそも、有用な情報をすべて「更新され続けるページ」にする必要はありません。「更新されないページ」のなかに「技術メモ」のようなカテゴリを作り、そのなかに「2017年1月時点の技術メモ」のような形で残しておくという方法もあります。

ヒント(4):ルールを守るための責任者を立てる

上記のルールを徹底するためには、ある程度の強権が必要になります。

例えば、チームメンバが以下のようなことを始めてしまったときに、誰かがあるべき状態に戻す必要があります。このような行動は、各人の自主性に任せていてもなかなか防げません。意見の衝突が起こるからです。

  • 「自分がよく使うページだから」という理由で、ホーム画面にリンクを書き足す。
  • 「誰かが更新すべき情報だ(自分は更新しないけど)」という理由で、「更新され続けるページ」にページを足す。
  • 自分が必要だから更新し続けているページがあるが、責任を押し付けられたくないので、「更新されないページ」の中に残し続ける。

このような衝突を解決するために、ルールを守るための責任者をチーム内に立てて、その責任者は「このルールを守るためであればどのようにページを編集してもよい」と明言しておくのが良いと思います。メンバから異論がある場合はその責任者を中心に議論し、ときどき細かい運用を見直します。

この責任者は強権が必要で、かつ Wiki のほとんどすべての編集にざっと目を通す必要があるため、チームリーダが兼務するのが一番良いと思います。それが難しい場合は、簡潔な文章を書くのが得意な人を任命するのがよいでしょう。

まとめ

今回は、信用できる社内 Wiki をつくるために、私が個人的に実践している基本的なルールをご紹介しました。

このルールの目的は、「読み手が Wiki に期待するレベルを適切にコントロールすることで、読み手が Wiki に幻滅し、無視してしまうのを防ぐこと」です。Wiki のページを「完全に信用できる」少数のページと、「条件付きで参考にできる」多数のページに分けることで、読み手にとって信用できる Wiki をつくることができる、ということを具体例を挙げてお話ししました。

もしも、自分が後から参加したプロジェクトに「信用できない社内 Wiki」があったらどうするか? 元から使われている Wiki を大きく書き換えることもできないので、その場合は特定のページ以下を「信用できるサブ Wiki」として育てるのがよいと思います。このルールを色々アレンジしてみてください。

今回は社内 Wiki 全体の話をしましたが、個々のページを「信用できる」ように書く方法については、以前書いた以下の記事をご参考ください。

muziyoshiz.hatenablog.com

LT で艦これアーケードと Admiral Stats の色々を話してきました

$
0
0
f:id:muziyoshiz:20170128164643p:plain

秋葉原IT戦略研究所という、アニメ x IT ネタで同人誌を出したり勉強会を開催しているサークルがあります。そのサークルの勉強会「ShangriLa Meetup」の LT で、Admiral Stats の話をしてきました。

akibalab.connpass.com

スライド

Admiral Statsは、私が趣味で作っている、「艦これアーケード」というアーケードゲームのプレイデータを可視化するツールです。公式サイトにはない「時系列でのデータ表示機能」、「他提督(プレイヤー)との比較機能」などを備えています。

スライドの章立てはこんな感じです。

  • 艦これアーケードってどういうゲーム?
  • Admiral Stats の紹介
  • Admiral Stats の中身の解説
  • Admiral Stats を公開してみた結果

艦これアーケードをプレイしている提督の方は、Admiral Stats を試してもらえると嬉しいです。未プレイの方は、公式サイトのプレイムービーあたりを見て、興味が湧いたらプレイしてみてもらえると(ゲームのファンとして)嬉しいです。

www.admiral-stats.com


自作ツールのアイコンをクラウドワークスのコンペ機能で発注してみた

$
0
0
f:id:muziyoshiz:20170212132356p:plain

きっかけ

自作ツール(Admiral Stats)のリリース直後に、サポート用アカウント @admiral_statsを Twitter に用意していたのですが、やり取りが増えてきたので、デフォルトのたまごアイコンをそろそろ卒業したくなってきました。

以前から、アイコンのアイディアは考えていたのですが、良い案が全然浮かばなかったので、この機会にクラウドソーシングサービスを使ってみました。今回の記事は、クラウドワークスでの発注〜デザイン完了までと、使ってみての感想についてのお話です。

クラウドソーシングサービスを探す

まず、どこに発注するかですが、国内だとランサーズとクラウドワークスが有名だと思います。既存の案件をざっと見た感じ、アプリアイコンは1〜2万円くらいが相場なんでしょうか。5千円という案件もありました。

アイコンデザインだけなら、英語圏で頼むことも可能だろうと思い、海外のクラウドソーシングサイトも見てみました。ただ、数があまりにも多いのでよくわからず……。

最近読んだ SOFT SKILLSのなかで「自分のロゴをクラウドソーシングサイトに発注しよう」という話題のなかで取り上げられていた、以下の2社に目を通してみました。

例えば、Fiverr で “icon” で検索すると、「アプリアイコン5ドルで1個、訂正1回まで」みたいな仕事を受けてくれるデザイナーがすぐ見つかりました。これは極端な例としても、さすがに英語圏だと価格が大きく違うようです。

クラウドソーシングサービスを選択する

海外もいいな、と思いつつ、とりあえず希望するデザインを日本語で文章にしてみることに。

  • 「艦これアーケード」というアーケードゲームのプレイデータを管理する Web アプリのアイコンデザイン
  • デザインのモチーフは、操舵輪、歯車、カード、桜吹雪など、艦これアーケード自体のモチーフに近いものから選んでほしい
  • データの蓄積をイメージさせる、他のモチーフと組み合わせても OK
  • favicon としても使いたいので、小さいサイズで表示しても問題ないデザイン
  • 色は、艦これアーケードのロゴと同様の青系、またはアイコンに使われている黄色〜茶系
  • フラットデザインのUIに含めても、違和感のないデザイン

うん……。具体的なアイディアがなにも浮かんでなかったので、曖昧すぎですね。

ここまで書いて「艦これアーケードというゲーム自体や、そのモチーフについて英語で説明するのは面倒すぎるな」と気付いて、国内のサービスへ発注することに決定。

ランサーズとクラウドワークスのどちらにするか、で悩んだんですが、評判で検索してもよくわからず。なんとなく安価な印象があったので、今回はクラウドワークスのほうを選びました*1

仕事を依頼する

アプリのアイコンをどうやって発注するのかわからずに悩んだ末に、「デザイン >キャラクター・アイコン・アニメ >アイコン作成」カテゴリを選択。

f:id:muziyoshiz:20170212132948p:plain

依頼の形式は「プロジェクト形式」と「コンペ形式」から選べるんですが、デザインの具体的なアイディアがなかったので、複数の案が集まることを期待して「コンペ形式」を選択。まあ、結論から言うとあまり多くの案は集まらなかったんですが……。

応募期限は、当日の24:00から、14日後の24:00まで選択可能。今回は14日後を選択。

そして、仕事の内容を入力して、最後に予算の選択です。

f:id:muziyoshiz:20170212133019p:plain

32,400円のスタンダードプランがおすすめ、とありますが、趣味の、広告収入もないアプリでそこまで払う気にもなれず。かといって、カスタムで安くしすぎるのも良い案が集まらなそう(僕が逆の立場なら手を出さなそう)なので、標準のプランで一番安いエコノミーを選択しました。案件を目立つところに表示するためのオプションが色々ありましたが、低予算なのでオプションは無しで。

すべての登録が完了すると、メンバー(受注者)向けに以下のようなページが公開されます。

crowdworks.jp

ちなみにこのページ、募集中は受注者にしか見られないのですが、募集終了後は受注者以外(ログインしていない人)にも全公開されます。それが嫌ならプラス8,000円払うと非公開にできるみたいです。

発注〜デザイン決定までの流れ

1月16日に発注して、デザインが決定するまでの流れは、以下のような感じでした。

日付 できごと 提案人数 提案件数
1/16 1人目のデザイナー(過去の受注実績なし)から、最初のデザイン提案 1 1
1/20 1人目のデザイナーから、別デザインの提案 1 3
1/21 2人目のデザイナー(過去の受注実績20件くらい)から、最初のデザイン提案 2 4
1/22 提案済みのデザインに対するコメントを元に、1〜2人目のデザイナーから、別案の提案あり 2 10
1/24 2人目のデザイナーの案をベースにしようと決めて、更にやりとり 2 13
1/25 2人目のデザイナーに、最終案の微調整を依頼 2 17
1/25 採用する案を決めたので、締切を 1/26 に早めた 2 17
1/26 1人目のデザイナーから、別デザインの提案 2 18
1/26 採用デザイン決定 2 18

最初の4日間は提案してくれたデザイナーが、過去の受注実績のない1名のみだったので、1万円が無駄になるか?と心配だったのですが、あとから受注実績のあるデザイナーが提案してくれて、その方とデザインを微調整して最終決定、という流れでした。最終的には以下のデザインを採用しました。

f:id:muziyoshiz:20170128164643p:plain:w300

後から気づいたのですが、コンペ機能だと「この案にしよう」と思っても即決はできないんですね。終了を翌日の24:00に早めることしかできませんでした。まあ、これはデザイナー側への配慮なんだと思います。

感想

クラウドワークスのコンペ機能で発注してみた感想を三行でまとめると、こんな感じでした。

  • デザインを気軽に頼むには便利
  • 依頼金額を最低ラインにすると、品質の低いデザインにお金だけ取られる可能性が高そう
  • 信頼できるデザイナーがいるなら、同じ額を払ってそっちに頼むほうがいい

もう少し細かい感想は、以下の通りです。

提案人数と提案件数はだいぶ違う

コンペ方式だとデザインが採用されない限りは報酬0なので、デザイナーの視点で考えると、提案件数がすでに多い案件には参加したがらないでしょう。

そう考えると、発注者としては、気に入ったデザインが出てくるまでは提案件数を少なくしておきたいです。しかし、以下の理由でそうもいかない、というのがわかりました。

  • 気に入らなかったデザイナーの追加提案を拒否することはできない
    • この人のデザインは自分には合わないな、と思っても、追加提案は拒否できません
  • デザインの微調整を依頼すると、提案件数が跳ね上がる
    • コンペの途中でも、相手とメッセージをやり取りして、デザインを微調整してもらうのが一般的なようです
    • しかし、微調整してもらったが結局希望に合わなかった、という場合、提案件数が増えただけで新規提案が来にくくなる、というデメリットがあります
  • 提案保証人数を当てにするなら、スタンダードまで上げないと意味がない
    • 提案保証人数1名だと、実績の少ないデザイナーで埋まってしまう
    • 提案保証人数を2名以上にしたいなら、3万の「スタンダード」まで上げる必要がある
    • しかし、そこまでの金額を払うなら、個別に頼んだほうがよくないか?

今回の場合、デザインが大きく異なるのは5件のみで、残り13件はそれの微調整でした。

特定のデザイナーを指定して依頼できるなら、そのほうが双方にとって良い

自分でデザインすることを考えれば、1万で今回のデザインが得られたのには満足しています。

しかし、ある程度実績のあるデザイナーを探して、個別に(クラウドワークスの機能で言うなら「プロジェクト形式」で)提案したほうが、双方にとって良いだろうな、と思いました。まあ、当たり前の話かもしれませんけど……。

先日の 秋葉原IT戦略研究所の勉強会で、このサークルの同人誌のデザインをしているデザイナーの方にこの話をしてみたところ、

受注側としてはポートフォリオがないと仕事が来ないので、最初のポートフォリオを作る手段としては、デザイナーにとって良い手段なのではないか。

との感想をもらいました。まあ、確かにそうかも。

発注側としては、コンペ形式での実績を見て、良さそうな人に個別で依頼するのがよいのかもしれませんね。次の機会があったら、プロジェクト形式の方を試してみたいと思います。

*1:あとから知ったのですが、後述するエコノミープラン、クラウドワークスは 10,800 円ですが、ランサーズは 21,600 円でした。

Elixir のテスティングフレームワーク ExUnit, ESpec の比較

$
0
0
f:id:muziyoshiz:20161122004038p:plain

Elixir でのコードを書くにあたり、テスティングフレームワーク ExUnit および ESpec を調べてみました。

ExUnit は Ruby で言うところの Test::Unit で、ESpec は RSpec のようです。調べてみた結果、自分は ExUnit を使うことにしたのですが、せっかくなので調べたことをまとめておきます。

比較表

ExUnit ESpec
インストール方法 Elixir, Phoenix Framework 標準 mix で追加する(espec, espec_phoenix)
テストの書き方 主に assert で真偽チェック expectshouldで文章のように書ける
テストのグループ化 describe が使える(Elixir 1.3 以降) context, describe, example_group が使える
実行方法 mix testmix espec
テスト結果 失敗した行番号が含まれる 失敗した行番号が含まれない
テスト名の制約 日本語を使えない 日本語を使える

以下は、この比較表の詳細です。

ExUnit

インストール方法

Elixir の標準に含まれており、mix.exs に何も足さなくても使えます。

また、Phoenix Framework では mix phoenix.newを実行する際に、データベース接続のためのヘルパーや CaseTemplateが自動生成されます。これにより、ExUnit のテストケースを使ったモデルのテストが可能になっています。

テストの書き方

主に assert または refute での真偽チェックとして書く必要があります。

defmodule ElixirSampleTest do
  use ExUnit.Case
  doctest ElixirSample

  test "the truth" do
    assert 1 + 1 == 2
  end
end

標準出力をテストするための ExUnit.CaptureIOやログ出力をテストするための ExUnit.CaptureLogといった便利機能もあります。これらを使う場合も、最終的には assert を呼ぶ必要があります。以下は CaptureLog の使用例です。

  test "example" do
    assert capture_log(fn ->
      Logger.error "log msg"
    end) =~ "log msg"
  end

テストのグループ化

Elixir 1.3 から describe マクロが追加されて、テストのグループ化が簡単になりました。RSpec の describe と同じようにグループ化できます。

以下は ExUnit.Case のドキュメントに記載された例です。

defmodule StringTest do
  use ExUnit.Case, async: true

  describe "String.capitalize/1" do
    test "first grapheme is in uppercase" do
      assert String.capitalize("hello") == "Hello"
    end

    test "converts remaining graphemes to lowercase" do
      assert String.capitalize("HELLO") == "Hello"
    end
  end
end

実行方法

mix testで実行します。

$ mix test
..

Finished in 0.05 seconds
2 tests, 0 failures

Randomized with seed 37255

テスト結果

テストに失敗した場合、失敗したテストの開始行と、assertion に失敗した行の番号を表示します。

例えば、以下のように絶対失敗するテストを書いたとします。

defmodule ElixirSampleTest do
  use ExUnit.Case
  doctest ElixirSample

  test "the truth" do
    assert 1 + 1 == 2
    assert 1 + 1 != 2
  end
end

実行結果はこうなります。

$ mix test
.

  1) test the truth (ElixirSampleTest)
     test/elixir_sample_test.exs:5
     Assertion with != failed, both sides are exactly equal
     code: 1 + 1 != 2
     left: 2
     stacktrace:
       test/elixir_sample_test.exs:7: (test)



Finished in 0.04 seconds
2 tests, 1 failure

Randomized with seed 648187

テスト名の制約

test にも describe にも、日本語名(正確には、アトムに変換できない文字列)を使えないようです。例えば、以下のようにテストを書いたとします。

  test "1足す1が2に一致する" do
    assert 1 + 1 == 2
  end

これを実行すると、以下のようなエラーが返されます。

$ mix test
** (ArgumentError) argument error
    :erlang.binary_to_atom("test 1足す1が2に一致する", :utf8)
    (ex_unit) lib/ex_unit/case.ex:411: ExUnit.Case.register_test/4
    test/elixir_sample_test.exs:5: (module)
    (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
    (elixir) lib/code.ex:370: Code.require_file/2
    (elixir) lib/kernel/parallel_require.ex:57: anonymous fn/2 in Kernel.ParallelRequire.spawn_requires/5

BDD ではテストメソッド名に日本語を使おう、という意見があったりしますが、ExUnit では難しいようです。

ExUnit の参考ページ

Programming Phoenix: Productive, Reliable, Fast

Programming Phoenix: Productive, Reliable, Fast

ESpec

インストール方法

Elixir 標準ではないので、antonmi/especのページにあるように mix.exs に追加する必要があります。

def deps do
  ...
  {:espec, "~> 1.3.0", only: :test},
  ...
end

あとはいつもの mix deps.getでインストールしたあとで、以下のコマンドを実行して spec/spec_helper.exs を生成すれば準備 OK です。

$ MIX_ENV=test mix espec.init

Phoenix Framework で ESpec を使う場合は、mix.exs に espec_phoenixを追加します。これにより、モデルによるデータベース接続を伴うテストなどが可能になります。

テストの書き方

expect … to の組み合わせか、should を使って書きます。RSpec だと今は should は推奨されていないようですが、ESpec についてはそういう記載は見当たりませんでした。

defmodule ContextSpec do
  use ESpec

  example_group do
    context "Some context" do
      it do: expect "abc" |> to(match ~r/b/)
    end

    describe "Some another context with opts", focus: true do
      it do: 5 |> should(be_between 4, 6)
    end
  end
end

テストのグループ化

context、describe、example_group が使えます。RSpec に似ていますね。

antonmi/especに載っている例(下記)では example_group に説明文が付いていないですが、付けることも可能なようです。

defmodule ContextSpec do
  use ESpec

  example_group do
    context "Some context" do
      it do: expect "abc" |> to(match ~r/b/)
    end

    describe "Some another context with opts", focus: true do
      it do: 5 |> should(be_between 4, 6)
    end
  end
end

実行方法

MIX_ENV=test mix especで実行します。ただ、antonmi/especに従って mix.exs に設定を追加すれば、MIX_ENV=testは省略できるようになります。

$ mix espec
.

    1 examples, 0 failures

    Finished in 0.07 seconds (0.06s on load, 0.01s on specs)

    Randomized with seed 654062

テスト結果

テストに失敗した場合、失敗したテストの開始行を表示します。 assertion に失敗した行の番号は表示してくれません。

ExUnit の場合と同じように、絶対失敗するテストを書いてみます。

defmodule ElixirSampleSpec do
  use ESpec

  it "the truth" do
    expect(1 + 1) |> to(be 2)
    expect(1 + 1) |> to_not(be 2)
  end
end

すると、実行結果はこうなります。test の開始行の番号(4行目)しか出てきません。

$ mix espec
F

    1) ElixirSampleSpec the truth
    /Users/myoshiz/devel/elixir_sample/spec/elixir_sample_spec.exs:4
    Expected `2` not to equals (==) `2`, but it does.

    1 examples, 1 failures

    Finished in 0.11 seconds (0.1s on load, 0.01s on specs)

    Randomized with seed 728628

これくらいの内容なら、テスト結果からどの行かわかりますが、テストあたりの行数が長くなると辛くなってきます。長いテストコードは書くべきでない、という設計思想なんでしょうか。

コマンドライン引数などで、この動作を変更できるのではないかと思ったのですが、方法を見つけられませんでした……。これ、個人的には致命的に不便だと思うんですけど、他の人はどうなんですかね。

テスト名の制約

ESpec の it や describe は、テスト名に日本語を含めても、問題なく動作しました。

ESpec の参考ページ

まとめ

ESpec で書いたテストのほうが読みやすくなるのですが、総合的に考えて、個人的には今後は ExUnit を使うことにしました。

ExUnit は標準で採用されているために導入の手間が少なく、グループ化などの基本的な機能は備えているので、十分かなと。テスト失敗したときに行番号が表示されるなら、ESpec でも良かったんですけどね……。Elixir の練習のために admiral_stats_parseradmiral_stats_parser_exに移植していて、作業が終わりかけたところでこれに気付いたときは愕然としました。

ちなみに、Elixir には、Ruby における Cucumber 相当の “WhiteBread” や、FactoryGirl 相当の “ExMachina” もあるようです。そのうち、これらも試してみたいと思います。

Ruby on Rails 5 アプリにあとから API 機能(JWT, CORS 対応)を追加する

$
0
0
f:id:muziyoshiz:20170320163237p:plain

はじめに

https://www.admiral-stats.com/という URL で、Ruby on Rails 5 で作った Admiral Stats というサービスを動かしています。このサービス自体については、過去の記事を参照ください。

このサービスに最近 API 機能を追加したので、その方法を紹介します。一通り読んでもらえると、API サーバって割と簡単に作れるんだなーというのがわかると思います。

今回追加した API

API を追加した目的

Admiral Stats は、艦これアーケードというゲームのプレイデータを、ユーザ(提督)から時々アップロードしてもらって、それを時系列に可視化するサービスです。

f:id:muziyoshiz:20160828003101p:plain

プレイデータは、SEGA の公式プレイヤーズサイトから、非公式のエクスポータを使ってエクスポートしてもらいます。そのためのエクスポータも、ブックマークレット版、PowerShell 版、Ruby 版と色々配布しています。

今までは Web ブラウザで Admiral Stats にログインして、「インポート」ページからプレイデータをアップロードしてもらう必要がありました。

f:id:muziyoshiz:20170320163724j:plain:w600

自分で使っていてもこれが面倒だったので、この部分を自動化するために API を作ることにしたわけです。

最終的に作った API の仕様

  • GET /api/v1/import/file_types
    • Admiral Stats がインポート可能なファイル種別のリストを返す。
    • エクスポータによっては、現在の Admiral Stats がまだサポートしていないプレイデータもエクスポートする。この API を使うことで、未サポートのデータをアップロードしなくて済むようにする。
  • POST /api/v1/import/:file_type/:timestamp
    • ボディ部に含まれる JSON(プレイデータ)をパースし、データベースに登録する。
    • :file_type/api/v1/import/file_typesが返すファイル種別のいずれか。
    • :timestamp%Y%m%d_%H%M%S形式。日本にしか無いゲームのためのツールなので、問答無用で JST で解釈する。

この API の呼び出しは、後述するトークンで認証します。例えば、curl で呼び出す場合、Authorization ヘッダにトークンを入れて、以下のように呼び出します(このトークンは架空のものなので、実際に叩くと失敗します)。

happyturn% curl -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiaWF0IjoxNDg5MDY1Nzg1fQ.q-SlF1tPKyCiNBOzdbQ2JLSqSr7d380WbKxZp818xeo" \
> -i -X GET https://www.admiral-stats.com/api/v1/import/file_types
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
ETag: W/"289acc0adafeb819ebf2fca179abb2a7"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 553b2151-562e-47f7-b46f-c0a41384ed5a
X-Runtime: 0.044076
Vary: Origin
Transfer-Encoding: chunked

["Personal_basicInfo","TcBook_info","CharacterList_info","Event_info"]

使い方

API トークンの発行

ユーザは、まずは API トークンを発行します。Admiral Stats にログインすると、「API トークンの設定」というメニューが表示されるので、ここで新しいトークンを発行できます。

もしトークンが漏れた場合は、「トークンの再発行」ボタンを押すと新しいトークンが発行されて、古いトークンは使えなくなります。このような失効処理は、JWT の機能では実現できないので、後述するテーブルを使って実現しました。

f:id:muziyoshiz:20170320164221p:plain:w600

エクスポータの設定

次に、このトークンをエクスポータに設定します。この設定はブックマークレット版が一番簡単で、Admiral Stats が生成するブックマークレットに、そのユーザのトークンが自動的に埋め込まれるようになります。

f:id:muziyoshiz:20170320164428p:plain:w600

これをブックマークバーにドラッグアンドドロップすると、以下のようなブックマークレットが登録されます。

javascript:(function(u,t,b){var%20s=document.createElement('script');s.charset='UTF-8';s.id='admiral-stats-exporter';s.setAttribute('data-token',t);s.setAttribute('data-skip-backup',b);s.src=u;document.body.appendChild(s)})('https://www.admiral-stats.com/bookmarklets/exporter.js','eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiaWF0IjoxNDg5MDY1Nzg1fQ.q-SlF1tPKyCiNBOzdbQ2JLSqSr7d380WbKxZp818xeo','true');

ちなみに、PowerShell 版の場合は起動時に表示されるプロンプトでトークンを書く、Ruby 版の場合は設定ファイル config.yaml にトークンを書く、という方法で設定できます。

エクスポータの実行

あとは、従来通りに、ブックマークレットを SEGA 公式サイトで実行すると、エクスポートから自動アップロードまで行われます。

ただ、ブックマークレット版は、非同期の JavaScript しか書けない関係上、「エクスポートから自動アップロードのどこかが失敗したらエラーを出す」ということがうまくいきません(無理矢理書くことはできるのかもしれませんが……)。

その対策として、Admiral Stats に「API ログの確認」というメニューを作り、ここを見てもらえれば自動アップロードが成功したかどうかを確認できるようにしました。ちなみに、PowerShell 版と Ruby 版では、実行に失敗するとエラーメッセージが表示されます。

f:id:muziyoshiz:20170320164603p:plain:w600

以上が、API の簡単な使い方の解説でした。詳細は Admiral Stats の使い方をご参照ください。

JSON Web Token (JWT) への対応

まずは、API のための認証を簡略化するための、トークン発行・検証機能を実装します。このトークンとして、今回は JWT を採用しました。

JWT とは?

JWT とは、認証トークンとして使うために、JSON 形式のデータを Base64 エンコードして署名を付けた文字列のことです。

JWT に関する情報は https://jwt.io/にまとまっています。また、IETF の RFC 7519 - JSON Web Token (JWT)として仕様策定されています。ちなみに、この RFC によると、JWT の発音は「ジョット」らしいです。

The suggested pronunciation of JWT is the same as the English word “jot”.

JWT は、3個の Base64 文字列を、ドット(.)で繋いだ文字列です。https://jwt.io/に表示されているサンプルを、試しに irb でデコードしてみます。

irb(main):001:0> require 'base64'
=> true
irb(main):002:0> Base64.decode64('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9')
=> "{\"alg\":\"HS256\",\"typ\":\"JWT\"}"
irb(main):003:0> Base64.decode64('eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9')
=> "{\"sub\":\"1234567890\",\"name\":\"John Doe\",\"admin\":true}"
irb(main):004:0> Base64.decode64('TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ')
=> "L\x95@\xF7\x93\xAB3\xB16p\x16\x9B\xDFDL\x1E\xB1\xC3pG\xF1\x8E\x86\x19\x81\xE1N4X{\x1E\x04"

JWT では、特に暗号化されているわけではないのがわかると思います。また、3個目の Base64 文字列は署名なので、JSON にはなりません。署名の検証は、後述する gem で行えます。

また、トークンを検証する際には「有効期限が切れていないか調べる」とか「必要な権限を持っているか調べる」とか、よく行う作業があると思います。

JWT では、よく使う情報を格納するためのキー名が予約されていて、それらは Claim Names と呼ばれます。例えば、トークンの期限切れ時刻を表す “exp” (Expiration Time) という claim があります。この名前に従う必要はないんですが、従っておくと、JWT のライブラリにビルトインされている検証処理を使えて便利です。

JWT を操作するための gem

https://jwt.io/には、以下の3種類の gem が載っています。このサイトの比較表では、機能の違いは特にありません。

このなかで、jwt は最も star が多く、README がこれを読むだけでも十分 JWT を理解できそうなくらい充実していました。そこで今回は、無難に jwt を選びました。

Rails アプリに組み込む - トークンの発行

Admiral Stats が発行する JWT のペイロードは、以下の形式にしました。(設定ファイルへのコピペが面倒にならないように)トークンをなるべく短くしたかったため、"iat"の claim だけ採用しています。

{ "id": ユーザID, "iat": トークン発行日 }

トークンの鍵は、secret_key_base の値をそのまま使うことにしました。この値は Rails.application.secrets.secret_key_baseで参照できるようです。

また、一度発行したトークンをいつでも無効にできるように、トークンの内容を admiral_tokens というテーブルに保存するようにしました。JWT の検証に成功しても、このテーブルに同じトークンが登録されていない場合は「有効期限切れ」と扱うことにしました。

以下は、token_controller.rb 内に実装した、トークンの発行処理です。

beginAdmiralToken.transaction doAdmiralToken.where('admiral_id = ?', current_admiral.id).delete_all

        issued_at = Time.now
        token = JWT.encode({ id: current_admiral.id, iat: issued_at.to_i }, Rails.application.secrets.secret_key_base, 'HS256')

        AdmiralToken.create!(
            admiral_id: current_admiral.id,
            token: token,
            issued_at: issued_at
        )
      endrescue => e
      logger.error(e)
      @error = "トークンの発行に失敗しました。(原因:#{e.message}"end

Rails アプリに組み込む - トークンの検証

検証処理は、application_controller.rb 内の jwt_authenticate メソッドとして実装しました。この実装は An Introduction to Using JWT Authentication in Railsを参考にしました。

# JWT で認証したユーザ(提督)の情報を返すメソッドdefjwt_current_admiral@jwt_current_admiral ||= Admiral.find(@jwt_admiral_id)
  end# Authorization ヘッダに含まれる JWT で認証状態をチェックするためのメソッドdefjwt_authenticateunless jwt_bearer_token
      response.header['WWW-Authenticate'] = 'Bearer realm="Admiral Stats"'
      render json: { errors: [ { message: 'Unauthorized' }]}, status: :unauthorizedreturnendunless jwt_decoded_token
      response.header['WWW-Authenticate'] = 'Bearer realm="Admiral Stats", error="invalid_token"'
      render json: { errors: [ { message: 'Invalid token' } ] }, status: :unauthorizedreturnendunless@jwt_admiral_id# 有効期限の検査
      jwt_admiral_id = jwt_decoded_token[0]['id']
      ifAdmiralToken.where(admiral_id: jwt_admiral_id, token: jwt_bearer_token).exists?
        @jwt_admiral_id = jwt_admiral_id
      else
        response.header['WWW-Authenticate'] = 'Bearer realm="Admiral Stats", error="invalid_token"'
        render json: { errors: [ { message: 'Expired token' } ] }, status: :unauthorizedendendend# Authorization ヘッダの Bearer スキームのトークンを返します。defjwt_bearer_token@jwt_bearer_token ||= if request.headers['Authorization'].present?
                            scheme, token = request.headers['Authorization'].split('')
                            (scheme == 'Bearer' ? token : nil)
                          endend# JWT をデコードした結果を返します。defjwt_decoded_tokenbegin# verify_iat を指定しても、実際には何も起こらない# iat を含めない場合も、iat が未来の日付の場合も、エラーは発生しなかった@jwt_decoded_token ||= JWT.decode(
          jwt_bearer_token,
          Rails.application.secrets.secret_key_base,
          { :verify_iat => true, :algorithm => 'HS256' }
      )
    rescueJWT::DecodeError, JWT::VerificationError, JWT::InvalidIatError# エラーの詳細をクライアントには伝えないため、常に nil を返すnilendend

ちなみに、エラーメッセージは、以下のように出し分けています。

  • Unauthorized: Authorization ヘッダに Bearer トークンが含まれていない
  • Invalid token: JWT の署名検証に失敗した
  • Expired token: JWT の署名検証には成功したが、テーブル上に無かった

このメソッドをAPI のコントローラ(api_import_controller.rb)の before_action に追加すると、トークンでの認証が可能になります。

また、今回は、普通の Rails アプリに、あとから API サーバの機能を追加しているので、API 用のコントローラのみ CSRF 対策を無効化する必要があります。以下は、コントローラのコードの冒頭です。

class ApiImportController < ApplicationController
  include Import

  # API 用のコントローラでは CSRF 対策を無効化する
  skip_before_action :verify_authenticity_token

  before_action :jwt_authenticate

あとは API の機能そのものを実装すれば、API サーバの実装完了です。

Cross-Origin Resource Sharing (CORS) への対応

ただし、この API をブックマークレットから叩こうとすると、Web ブラウザが以下のようなエラーを出して、クロスドメイン接続をブロックしてしまいます。

クロスオリジン要求をブロックしました: 同一生成元ポリシーにより、https://www.admiral-stats.com/api/v1/import/Area_captureInfo/20170308_235926にあるリモートリソースの読み込みは拒否されます (理由: CORS ヘッダ ‘Access-Control-Allow-Origin’ が足りない)。

この同一生成元ポリシー(Same-Origin Policy)は、セキュリティのための機能ですが、今回のような場合には困ります。この接続を許可するには、API サーバ側が CORS に対応する必要があります。

CORS とは?

CORS とは、別ドメインの Web サーバが明示的に許可した場合に限り、あるドメインから別ドメインへのクロスドメイン通信を許可するための、HTTP の仕様です。

別ドメインの Web サーバが明示的に許可した場合だけ使える、という意味では、できることは JSONP(JavaScript の読み込みとしてリクエストを送り、コールバック関数でラップした JSON を返す)と大差ありません。しかし、CORS のほうが、サーバ・クライアント双方の実装を大幅に簡略化できます。

CORS のプロトコルを大まかに説明すると、次のようになります。

  1. ページ内で読み込んだ JavaScript が、別ドメインへの XMLHttpRequest を send する
  2. Web ブラウザが、1 の URL に対して、OPTIONS リクエストを送信する
  3. Web サーバが、Access-Control-Allow-Origin ヘッダなどを含み、ボディが空のレスポンスを返す
  4. 3 に含まれるヘッダが、1 のリクエストを許可している(例:Access-Control-Allow-Origin ヘッダにそのサイトのドメインが含まれている)かどうかを検査する
  5. 4 の検査をパスしたら、Web ブラウザが、1 の URL に対して、実際のリクエスト(GET や POST)を送信する
  6. Web サーバが、Access-Control-Allow-Origin ヘッダなどを含む、本来のレスポンスを返す

CORS に対応するための rack-cors gem

Ruby on Rails で CORS に対応するためには、rack-cors という gem を使います。

実は、Rails 5 から導入された rails new NAME --apiでアプリケーションを作ると、この rack-cors を使うための以下の設定が、コメントアウトされた状態で書き込まれます(参考)。

  • Gemfile に gem 'rack-cors'
  • config/initializers/cors.rb に以下の設定

しかし、Admiral Stats は、すでに普通の Rails アプリとして作ってしまっているので、これと同じことを手作業で行います。

今回は以下の内容で cors.rb を作成しました。設定と、実際に返される HTTP ヘッダの対応関係については、コード中に書いたコメントの通りです。

Rails.application.config.middleware.insert_before 0, Rack::Corsdo# Insert CORS request headers for API responses
  allow do# Access-Control-Allow-Origin: (Origin に書かれたものをそのまま返す)
    origins '*'# /api 以下の URL に対してのみ、CORS 対応# Access-Control-Allow-Methods: GET, POST, OPTIONS# Access-Control-Allow-Headers: (OPTIONS に対してのみ Access-Control-Request-Headers に書かれたものをそのまま返す)# Access-Control-Max-Age: 3600
    resource '/api/*',
             :methods => [:get, :post, :options],
             :headers => :any,
             :max_age => 3600endend

rack-cors の注意点

rack-cors を導入すると、OPTIONS メソッドに対してレスポンスを返すようになります。そのために routes.rb を変更する必要はありません

ただ、テストコードのなかで、この OPTIONS へのレスポンスをテストしようとしたところ、以下のように RoutingError が出て失敗しました。IntegrationTest から、rack-cors が生成するルートは見えないようです。

ActionController::RoutingError: No route matches [OPTIONS] "/api/v1/import/Personal_basicInfo/20170309_000000"
    test/controllers/api_import_controller_test.rb:233:in `block (2 levels) in <class:ApiImportControllerTest>'
    test/controllers/api_import_controller_test.rb:232:in `block in <class:ApiImportControllerTest>'

一方、routes.rb に書かれている GET や POST については、以下のように Origin ヘッダをつければ、正しく Access-Control-Allow-Origin ヘッダなどが返されました。

  test 'data_types Origin ヘッダがある場合'do
    get '/api/v1/import/file_types', headers: { 'Authorization' => "Bearer #{TOKEN}", 'Origin' => 'https://kancolle-arcade.net'}

    assert_response 200
    assert_equal JSON.generate(
        [
            'Personal_basicInfo',
            'TcBook_info',
            'CharacterList_info',
            'Event_info'
        ]), @response.body

    assert_equal 'https://kancolle-arcade.net', @response.headers['Access-Control-Allow-Origin']
    assert_equal 'GET, POST, OPTIONS', @response.headers['Access-Control-Allow-Methods']
    assert_nil @response.headers['Access-Control-Allow-Headers']
    assert_equal '3600', @response.headers['Access-Control-Max-Age']
    assert_equal 'true', @response.headers['Access-Control-Allow-Credentials']
  end

そのため「OPTIONS についても同様のヘッダが返されるはずだ」と考えて、テストを書くのは諦めました。まあ、すべての API に OPTIONS のテストを書くのは、現実的ではないですしね……。

以上で、ブックマークレット版のエクスポータからも、Admiral Stats の API を叩いて、JSON ファイルをアップロードできるようになりました。

まとめ

今回は、普通の Ruby on Rails 5 アプリに、あとから API 機能を追加するために必要だったことをまとめました。API 機能のために、jwt gem を使ってトークンの発行・検証を行い、rack-cors gem を使って CORS 対応(ブックマークレット対応)を行いました。

実際にやってみて、やり方さえわかれば、割と短時間で実装できるもんだな……と思いました。Admiral Stats のソースコードは GitHub で公開しているので、興味のある方は読んでみてください。

github.com

GNU social (OStatus) 自体の仕様に関する情報源まとめ

$
0
0
f:id:muziyoshiz:20170430143245p:plain

(上のロゴは、何故か スペイン語版の Wikipediaにだけあった。本当に公式のロゴ?)

はじめに

Mastodon大人気ですね。

僕もとりあえず mstdn.jppawoo.netにアカウントを取ってお互いにフォローし、どれくらいの時間差でトゥートが伝達されるのか観察したり、GitHub に公開されているソースコードを少し読んだりしました。

「Mastodon は分散型だ」とか「GNU social と互換性がある」という話を聞いて、一体どんなプロトコルなんだろう……と気になって少し調べたのですが、GNU Social 自体がかなり古いものらしく、ドキュメントを探すのにも苦労しました。

そこで、自分で探した範囲で、原典に近いと思われるドキュメントのリンク集を作っておきます。新しい情報が見つかったら、随時更新します。

プロトコル同士の関係

以下のドキュメントに書かれている、OStatus に関連するプロトコルをまとめると、次のようになります。

  • Atom と RSS フィードを、サーバ間の共通言語として使う
  • Webfinger を使って、サブスクライブしたい相手の Link-based Resource Descriptor Discovery (LRDD) ドキュメントを探す
    • Webfinger を使う前に、meta-data で Webfinger 用の URI を取得する。
  • サーバ間でフィードのアップデートを購読し、プッシュ配信を受けるために PubSubHubbub を使う
  • PubSubHubbub の機能不足を補うために、Atom の拡張(Activity Streams, Portable Contacts, Salmon)を使う
    • フィードが表す social activity を表現するために、Activity Streams を使う(例えば、フォローのときは “follow” verb を使う)
    • プロフィール情報を提供するために Portable Contacts を使う
    • リプライを送るために Salmon を使う

GNU social

OStatus は、GNU social にマージされた StatusNet に端を発するプロトコルです。そこで、まずは GNU social のドキュメントを当たりました。

  • GNU social - Wikipedia

    • 結論から言うと、GNU social そのものについては情報が失われていて、Wikipedia に載っている以上の情報はコードを読むしかないのかもしれない。
  • GNU social の公式サイト

    • 情報量はあまりない。
    • What is GNU social?に、GNU social が2010年に PHP スクリプトの集合として始まったこと、その後 StatusNet とコードベースが共有されたこと、2013年に Free Social project とマージされたことなどが書かれている。
    • FAQにある “Why are you using PHP? Ruby/Python/Perl/A GUI in Visual Basic would be better!” の答えにちょっと笑った。
  • GNU social の GitLab

    • 最新版は 1.2.x 系。リリースノートもタグもないため、詳細はよくわからず。
    • 開発は継続されているが、Contributors のグラフを見る限り、当初の開発者はほとんど手を引いている。
    • doc-src ディレクトリにドキュメントがあるが、ざっと見た限り、クライアント-サーバ間通信の情報しかない。サーバ間通信の情報は見当たらなかった。
  • GNU social の GitHub

    • 上記の GitLab のミラー?
  • The Unofficial GNU Social documentation!

    • 基本的に GNU social のインストール方法についてのマニュアル。
    • Protocol Overviewというページがあったので、これは!と思ってクリックしたら “GNU social runs primarily on voodoo magic. If anybody knows better please advise.”としか書かれてなかった。
    • ですよね。

OStatus

  • OStatus - Wikipedia

    • OStatus の歴史と概要、OStatus を採用するソフトウェア一覧あり。
    • このページでは関連するプロトコルとして Atom, Activity Streams, PubSubHubbub, Salmon, Webfinger の名前が挙げられている。
    • “Standards Work” の節に、「pump.io で使われているプロトコルを元にした “ActivityPub” と呼ばれる新しい標準があり、これが OStatus の後継になりうるとされてきた」という記述がある。
  • OStatus Community Group

    • W3C のサイトにある Wiki。情報量は決して多くないが、OStatus に関連するプロトコルについての説明がある。
    • (see spec) と書かれた部分がリンクになっており、そこに OStatus 1.0 Draft 2 の仕様がある。
  • OStatus Community Group - Workflow

    • HTTPリクエスト/レスポンス例を伴った具体例。もしかしたら、これが OStatus のシーケンスに関する、最も詳しいドキュメントかもしれない。
    • ただし、これは OStatus 1.0 Draft 1(リンク切れ)ベースらしいので、Draft 2 とは多少違う可能性がある。
  • OStatus 1.0 Draft 2 の仕様

    • 2010年8月公開。
    • 全体で7ページと、仕様書としては短い。詳細を他の仕様書に譲っているからだが、参照先のドキュメントが古いため、一部はすでに見つからなくなっている。
    • 用語の定義があり、OStatus を調べる人には、それだけでも有用かもしれない。
    • OStatus では、プライベートメッセージングやソーシャルグラフは対象外、と書かれている。
    • “12. Usage scenario” に利用シナリオが書かれている。

meta-data (Web Host Metadata)

これは mstdn.jp で簡単に試すことができます。例えば、https://mstdn.jp/.well-known/host-metaにアクセスすると、以下が返されます。

> curl "https://mstdn.jp/.well-known/host-meta"
<?xml version="1.0"?>
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
  <Link rel="lrdd" type="application/xrd+xml" template="https://mstdn.jp/.well-known/webfinger?resource={uri}"/>
</XRD>

Link-based Resource Descriptor Discovery (LRDD)

  • draft-hammer-discovery-06 - LRDD: Link-based Resource Descriptor Discovery
    • LRDD に関する最後の Internet Draft。
    • このプロトコルは、URI が示すリソースにアクセスするための「プロセス」を定義している。host-meta を使う方法は、そのプロセスの一つである。
    • host-meta を使う方法は “5.1. host-meta Document” に記載されている。

Webfinger

これも mstdn.jp で簡単に試すことができます。

LRDD が示すように “application/xrd+xml” を指定して https://mstdn.jp/.well-known/webfinger?resource=acct%3Amuziyoshiz%40mstdn.jpにアクセスすると、XML 形式で返されました。ちなみに、Web ブラウザでアクセスすると、JSON(application/jrd+json)が返されます。

> curl -H "Accept: application/xrd+xml" "https://mstdn.jp/.well-known/webfinger?resource=acct%3Amuziyoshiz%40mstdn.jp"
<?xml version="1.0"?>
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
  <Subject>acct:muziyoshiz@mstdn.jp</Subject>
  <Alias>https://mstdn.jp/@muziyoshiz</Alias>
  <Alias>https://mstdn.jp/users/muziyoshiz</Alias>
  <Link rel="http://webfinger.net/rel/profile-page" type="text/html" href="https://mstdn.jp/@muziyoshiz"/>
  <Link rel="http://schemas.google.com/g/2010#updates-from" type="application/atom+xml" href="https://mstdn.jp/users/muziyoshiz.atom"/>
  <Link rel="salmon" href="https://mstdn.jp/api/salmon/14903"/>
  <Link rel="magic-public-key" href="data:application/magic-public-key,RSA.wOHgmclLfwfGDWfxN1pWfGIwr5GTXbFhJG49yuqrdI6T2WULDvlXUJx3vSIMCiwtkZn-DE9Rhpyse9_69xshlYerke0RvI6OfnvTv20RqFEz0Z65k9W4GTcYKAKu441OzMnY9C3144SiecDpW2noULukzFOMOEY22ON21yQk94QAzJXFt2Hh35ia31uK_JI5NDWGrcl-Rdl8mTHDjhkA4sZC504IInxEpMSxOMMhs75DS_HYYdYuWX-hkGtGEZy5qEfz7HSrSMU8x6e-hwq_ULZ-a5TmIWslJkqWoX_T94gR0hiLPEjpNQpf7R50jB57dltmeo_wKyeETkjxoWBRDw==.AQAB"/>
  <Link rel="http://ostatus.org/schema/1.0/subscribe" template="https://mstdn.jp/authorize_follow?acct={uri}"/>
</XRD>

PubSubHubbub (PuSH)

twitter.com

Activity Streams

Portable Contacts

Portable Contacts は、Google Contacts などでも使われている仕様らしいが、正式な仕様書らしきものが見つからなかった。

Salmon (Salmon Protocol)

The Good Stuffによると、「鮭が上流に泳いでいくように」元のブログサーバにコメントを送り返すためのプロトコルなので、Salmon という名前にしたそうです。OStatus では、これをリプライを返すための仕組みとして使っています。

OStatus が使っている、その他の Atom 拡張

番外:ActivityPub

OStatus の範囲外ですが、ActivityPub についても取り上げておきます。

まとめ

実際にドキュメントを追ってみて、噂に聞く通り、OStatus は非常に雑多な(一部はすでに廃れた)プロトコルの組み合わせで作られていることがわかりました。それぞれのプロトコルが、Mastodon 上でどう実装されているかは、他の人の調査に任せたいと思います。

実際、OStatus について先行して調べていた岡本さんによると、GNU social などの既存実装は、これらのドキュメント通りに実装しても動かないそうです。

twitter.com

OStatus について、5月12日に出る(早い!)マストドン本で岡本さんが解説してくれるらしいので、とりあえず僕はこの本を待とうと思います。

okapies.hateblo.jp

これがマストドンだ!  使い方からインスタンスの作り方まで (NextPublishing)

これがマストドンだ! 使い方からインスタンスの作り方まで (NextPublishing)

職場ブログに書いた記事まとめ

$
0
0

2015年1月に現職についてから、職場のブログに色々と記事を書いてきました。四半期に1回のペースで、それなりにまとまった内容を書くことを心がけてます。

グループ会社のブログに寄稿することもあって、自分でもどこに何を書いたか思い出せなくなってきたので、自分のブログにまとめておきます。今後、記事が増えたらここに追記します。

DevOps

DevOps 関係の話題は継続的に追っていて、職場でも隙を窺ってツールを導入したりしています。新しいツールの話題に加えて、職場ブログでは(許可を得た上で)個人ブログではなかなか書けない泥臭い話題も書いています。

Embulk

recruit.gmo.jp

recruit.gmo.jp

recruit.gmo.jp

HashiCorp

recruit.gmo.jp

Habitat

recruit.gmo.jp

Ansible

recruit.gmo.jp

recruit.gmo.jp

プログラミング

関数型言語の苦手意識を克服したくて、Elixir を触って記事を書いたりしました。ブログのネタにはしていませんが、最近は Scala も勉強中(何度目かの勉強中)です。

Elixir

recruit.gmo.jp

recruit.gmo.jp

自社サービスの API

recruit.gmo.jp

recruit.gmo.jp

その他(API 設計、Hadoop)

グループ会社の Tech Blog に書いた記事です。Hadoop は業務でもかなり触っているのですが、職場にもっと詳しい Hadoop おじさんがいるので、記事にすることがあんまりなくて……。

techblog.gmo-ap.jp

techblog.gmo-ap.jp

艦これアーケード第2回イベント「南方海域強襲偵察!」 プレイデータ解析 〜甲種勲章の十分条件は何だったのか?

$
0
0
f:id:muziyoshiz:20170128164643p:plain

はじめに

2017年4月26日から5月31日まで、艦これアーケードの第2回イベント「南方海域強襲偵察!」が開催されていました。普通の人にこういう話をすると「アーケードゲームにイベントがあるの?」と言われるんですが、最近のアーケードゲームはネット接続してるので、期間限定イベントとか普通らしいです。

艦これアーケードの場合は、イベント期間だけ新キャラを入手できたり、凶悪なボスキャラと戦えたりします。一番難しい難易度は甲乙丙の「甲」で、これをクリアすると「甲種勲章」というアイテムを入手できました。

僕は艦これアーケードのプレイヤー(提督)向けに、プレイデータ管理ツール Admiral Statsというサービスを提供していて、ここに各プレイヤーのイベント攻略状況も集まっています。今回は、このデータをもとに、「甲種勲章」を入手できた提督に共通する特徴を探ってみました。

艦これアーケードと Admiral Stats については、以前スライドにまとめたので、興味のある方はこちらをどうぞ。

第2回イベント「南方海域強襲偵察!」について

今回のイベントは、前段作戦と後段作戦に分かれていて、それぞれの作戦に甲乙丙の難易度がありました。簡単な難易度をクリアすると、より難しい難易度に挑戦できるようになっていて、具体的には下図のような制限がありました。

f:id:muziyoshiz:20170629225150p:plain

(※1周クリア=1周目の掃討戦に出撃していること)

一番の目玉だった「大和」は後段作戦でしか入手できず、そのためには前段作戦に挑戦する必要がある……ということで、約1ヶ月間かなりの提督がゲーセンに詰めかけてました。

ちなみに、後段作戦の甲は出現までが面倒なうえに、とてもクリアさせる気があるとは思えない極悪難易度でした(※個人の感想です)(※褒め言葉です)。

集計対象のプレイデータ

今回は、前段作戦95名、後段作戦80名分のプレイデータが集まりました。

集計対象の詳しい情報は、以下の通りです。

  • 集計対象とした提督
    • 第2回イベントに参加して、Admiral Stats に1回以上プレイデータをアップロードした提督
  • 集計対象としたプレイデータ(イベント進捗情報、艦娘一覧など)
    • イベント攻略率と艦娘カード入手率は、2017年6月8日3:00時点のプレイデータを集計
    • レベルや経験値については、イベント終了時刻になるべく近いプレイデータを集計
      • 2017年6月8日3:00までにアップロードされたプレイデータのうち、イベント終了時刻(2017年5月31日23:59)に一番近い時刻にアップロードされたプレイデータ

SEGA の本家サイトから、イベント進捗情報をエクスポートできなくなったのは6月8日でした。Admiral Stats は毎日3時にデータをバックアップしているので、今回は6月8日 3:00のバックアップを使って解析しました。

プレイデータの解析結果

イベント攻略率

前段作戦

前段作戦の甲難易度をクリアしたのは全提督の 57.9 % でした。前段作戦は、まだ救いのある難易度だったので、この高い割合もわからなくはないです。

f:id:muziyoshiz:20170630003300p:plain

また、甲難易度をクリアした提督のうち 56.4 % が、レアカード(甲種勲章がプリントされた艦娘カード)を求めて、2周以上クリアしていました。10周以上クリアした提督は 10.9 % です。

f:id:muziyoshiz:20170630003334p:plain

詳細:イベント攻略率(南方海域強襲偵察! 前段作戦) - Admiral Stats

後段作戦

後段作戦の甲難易度をクリアしたのは全提督の 57.5 % でした。前段作戦よりもかなり難しくなっているのに、割合は全然変わってないのが不思議です。前段作戦しかアップロードしていない提督がいることによる影響を考えても、この割合はかなり高いような……。

f:id:muziyoshiz:20170630003356p:plain

また、甲難易度をクリアした提督のうち 60.9 % が、2周以上クリアしていました。10周以上クリアした提督は 17.4 % です。後段作戦は大和入手のチャンスがあったので、こちらのほうが前段作戦よりも周回されていたようです。

f:id:muziyoshiz:20170630003434p:plain

詳細:イベント攻略率(南方海域強襲偵察! 後段作戦) - Admiral Stats

艦娘カード入手率

以下は、このイベントでの新艦娘のみの入手率です。目玉だけあって、大和の入手率が他より 10 % 以上低かったのがわかります。また、前段・後段の両方で入手できた阿賀野と能代は、5 〜 7 % 高くなっています。

図鑑 No. 艦種 艦名 N Nホロ N中破
131 戦艦 大和 43.3 % 6.7 % 2.2 %
137 軽巡洋艦 阿賀野 65.6 % 8.9 % 4.4 %
138 軽巡洋艦 能代 62.2 % 8.9 % 3.3 %
139 軽巡洋艦 矢矧 57.8 % 6.7 % 5.6 %
140 軽巡洋艦 酒匂 58.9 % 4.4 % 2.2 %

また、Admiral Stats の方ではまだ表示できていないのですが、その他の限定カードの入手率は以下のようになりました。実際にプレイした感覚からすると、意外と高いな、という印象です。

図鑑 No. 艦種 艦名 限定カード
102 航空戦艦 伊勢改 32.2 %
103 航空戦艦 日向改 40.0 %

詳細:艦これアーケードの艦娘カード入手率(南方海域強襲偵察!) - Admiral Stats

艦隊司令部レベル、および経験値

ここから先は、Admiral Stats 上で表示していない(自動集計機能がまだない)集計結果の紹介です。

Admiral Stats で上記のイベント攻略率を公開した際に、「こんなツールを欲しがるのはやりこんでるプレイヤーだけなんだから、実際のプレイヤー全体よりも攻略率が大幅に高いのでは?(意訳)」という指摘がありました。

その点を検証するために、まずは、ステージのクリア回数とステージの難易度によって増える「艦隊司令部レベル」の分布を出しました。この結果によると、レベル90以上の提督は 49.47 % です。

艦隊司令部レベル 提督数 割合
0 以上 10 未満 3 3.16 %
10 以上 20 未満 1 1.05 %
20 以上 30 未満 2 2.11 %
30 以上 40 未満 5 5.26 %
40 以上 50 未満 5 5.26 %
50 以上 60 未満 10 10.53 %
60 以上 70 未満 7 7.37 %
70 以上 80 未満 7 7.37 %
80 以上 90 未満 8 8.42 %
90 以上 100 未満 28 29.47 %
100 以上 110 未満 18 18.95 %
110 1 1.05 %
総計 95 100.00 %

また、艦隊司令部レベルを経験値に換算したものが以下の表です。レベル99になるのに経験値が 1,000,000 必要なので、イベント参加提督の 33.69 % がレベル99以上ということになります。

経験値 提督数 割合
0 以上 100000 未満 14 14.74 %
100000 以上 200000 未満 14 14.74 %
200000 以上 300000 未満 8 8.42 %
300000 以上 400000 未満 6 6.32 %
400000 以上 500000 未満 6 6.32 %
500000 以上 600000 未満 3 3.16 %
600000 以上 700000 未満 6 6.32 %
700000 以上 800000 未満 3 3.16 %
800000 以上 900000 未満 3 3.16 %
900000 以上 1000000 未満 0 0.00 %
1000000 以上 2000000 未満 23 24.21 %
2000000 以上 3000000 未満 5 5.26 %
3000000 以上 4000000 未満 2 2.11 %
4000000 以上 5000000 未満 0 0.00 %
5000000 以上 6000000 未満 2 2.11 %
総計 95 100.00 %

Admiral Stats のユーザにはやりこみ勢が多い、という主張は、まあ正しそうですね……。とはいえ、そうでないユーザも居るので、甲難易度をクリアした提督と、そうでない提督の差を調べることはできそうです。

ちなみに、艦これアーケードのレベルアップに必要な経験値は、艦これ本家と同じと言われています。Admiral Stats では 艦これ攻略 Wiki の「経験値」ページを参考に経験値を計算しています。

攻略度と艦隊司令部レベル/経験値の比較

では、艦隊司令部レベルがどれくらい高ければ後段作戦の甲をクリアできたのか? それを調べるために、攻略度と、艦隊司令部レベル/経験値を比較したのが以下の表です。

この結果によると、後段作戦の甲をクリアできた提督は、最低でもレベル63、平均レベル96.3でした。艦これの経験値テーブルはレベル90前後から急上昇するので、経験値の平均を取ると1121310.2で、これはレベル100相当です。平均が高すぎる……。

甲クリア提督を見てもやりこみ度が高すぎるので、乙クリア提督を見たほうが参考になるかもしれません。後段作戦の乙をクリアできた提督は、最低でもレベル41、平均レベル79.2。経験値の平均は427060.0で、レベル83相当でした。この間のどこかに境界がありそうですね。

前段作戦の攻略度と、艦隊司令部レベル

攻略度 提督数最大最小平均標準偏差
甲攻略済551106395.49.9
乙攻略済15973066.819.4
丙攻略済15782649.515.0
未攻略1065632.521.6
総計95110677.027.2

前段作戦の攻略度と、艦隊司令部経験値

攻略度提督数最大最小平均標準偏差平均(レベル換算)
甲攻略済5559000002034001264850.91070768.899
乙攻略済1576150043500296233.3200063.173
丙攻略済1535620032500138513.389758.153
未攻略10219500150075690.073349.539
総計9559000001500808893.7980487.197

後段作戦の攻略度と、艦隊司令部レベル

攻略度 提督数最大最小平均標準偏差
甲攻略済461106396.39.9
乙攻略済15974179.216.0
丙攻略済14752649.515.5
未攻略552629.419.1
総計80110680.725.4

後段作戦の攻略度と、艦隊司令部経験値

攻略度提督数最大最小平均標準偏差平均(レベル換算)
甲攻略済4659000002034001360919.61121310.2100
乙攻略済1576150082000427060.0191149.683
丙攻略済1431900032500138964.388616.053
未攻略5132700150060000.052672.335
総計8059000001500890671.31020211.498

攻略度と艦娘の練度の比較

艦隊司令部レベルが低くても、特定の艦娘だけレベルを上げていればクリアは可能だったはずです。そこで、攻略度と、一定レベル以上の艦娘の数も比較してみました。

艦隊司令部レベルよりも、こちらのほうが、甲提督と乙提督の差が激しいですね。イベント開始時の全艦娘110隻をレベル99にしているようなやりこみ勢(凄すぎる……)が平均を上げています。

レベル70以上の艦娘数0で甲攻略済みの提督もいたので、低レベルでも攻略は可能だったみたいです。ただ、「高レベル艦娘を揃えていて、乙攻略済み以下」という提督はほとんどいないので、「レベルを上げれば物理で殴れた」とも言えそうです。

前段作戦

Lv50以上の艦娘数

攻略度最大最小平均標準偏差
甲攻略済114632.424.8
乙攻略済2509.06.9
丙攻略済802.92.9
未攻略200.60.8
総計114020.723.6

Lv70以上の艦娘数

攻略度最大最小平均標準偏差
甲攻略済114019.120.8
乙攻略済802.32.5
丙攻略済300.30.8
未攻略000.00.0
総計114011.418.2

Lv90以上の艦娘数

攻略度最大最小平均標準偏差
甲攻略済110010.618.7
乙攻略済100.20.4
丙攻略済000.00.0
未攻略000.00.0
総計11006.115.2

Lv99の艦娘数

攻略度最大最小平均標準偏差
甲攻略済11005.616.0
乙攻略済000.00.0
丙攻略済000.00.0
未攻略000.00.0
総計11003.212.5

後段作戦

Lv50以上の艦娘数

攻略度最大最小平均標準偏差
甲攻略済114634.825.6
乙攻略済25011.85.9
丙攻略済803.42.7
未攻略200.60.8
総計114022.924.2

Lv70以上の艦娘数

攻略度最大最小平均標準偏差
甲攻略済114121.021.8
乙攻略済803.72.3
丙攻略済100.10.3
未攻略000.00.0
総計114012.819.1

Lv90以上の艦娘数

攻略度最大最小平均標準偏差
甲攻略済110012.020.1
乙攻略済200.50.6
丙攻略済000.00.0
未攻略000.00.0
総計11007.016.3

Lv99の艦娘数

攻略度最大最小平均標準偏差
甲攻略済11006.517.4
乙攻略済100.10.2
丙攻略済000.00.0
未攻略000.00.0
総計11003.813.6

攻略度と雷巡改二(大井改二・北上改二)の所有数の比較

今回、特に後段作戦E-6は、雷巡の改二を持っているかいないかで難易度が大きく変わる、と言われていました(参考:南方海域強襲偵察! E-6 - 艦らぼ)。

そこで攻略度と、雷巡改二の所有数も比較してみました。以下の表の読み方ですが、2は大井改二と北上改二を両方持っている、1は片方のみ持っている、0はどちらも持っていない、という意味です。

こうしてみると、雷巡改二が1隻の場合と2隻の場合では、甲難易度の攻略率が大きく違うのがわかります。

前段作戦

攻略度012総計
甲攻略済11.58%8.42%37.89%57.89%
乙攻略済6.32%4.21%5.26%15.79%
丙攻略済12.63%2.11%1.05%15.79%
未攻略10.53%0.00%0.00%10.53%
総計41.05%14.74%44.21%100.00%

後段作戦

攻略度012総計
甲攻略済10.00%7.50%40.00%57.50%
乙攻略済5.00%5.00%8.75%18.75%
丙攻略済13.75%2.50%1.25%17.50%
未攻略6.25%0.00%0.00%6.25%
総計35.00%15.00%50.00%100.00%

雷巡改二(大井改二・北上改二)の所有数と、艦隊司令部経験値の比較

しかし、レアカードを持っているのはやりこんでいるからで、攻略度に直接影響しているのはやりこみ度の方なのでは?とも考えられます。

そこで、雷巡改二の所有数と艦隊司令部経験値と比較した結果は以下の通りです。やりこみ勢は雷巡改二を両方とも持っていることが多いが、片方だけ持っている提督とどちらも持っていない提督の差はそれほど無いことがわかりました。

大北改二の所有数提督数最大最小平均標準偏差
24259000001747001378459.51194011.3
114130000043500481650.0362335.6
03916000001500312987.2354371.6
総計9559000001500808893.7980487.1

うーん、このデータからは甲難易度クリアの十分条件はよくわからないですね。

雷巡改二の所有数、艦隊司令部レベル、攻略度の対比

これだと消化不良なので、最後に雷巡改二の所有数、艦隊司令部レベル、攻略度の対比をすべて対比してみました。

表が大きくなったので、画像で貼り付けます。表中に「0+」とあるのは、0以上10未満と読んでください。レベル90台だけは詳細に表示しています。

こうやって眺めてみると、「艦隊司令部レベルが高ければ、雷巡改二がなくてもクリアできている」、従って「甲種勲章が欲しければとにかくやりこんでレベルを上げろ」というのが結論っぽいですね。

前段作戦

f:id:muziyoshiz:20170630004411p:plain

後段作戦

f:id:muziyoshiz:20170630004427p:plain

まとめ

今回は、Admiral Stats にアップロードされたプレイデータをもとに、後段作戦の甲難易度をクリアして甲種勲章をゲットするための十分条件を探ってみました。その結果、

  • 艦隊司令部レベルが一定以上になるくらい、やりこんでいれば甲クリアできた
  • 雷巡改二を2隻とも持っていると甲クリア率が上がるが、艦隊司令部レベルの方が寄与が大きかった
  • 逆に、雷巡改二を2隻持っているだけでは、甲クリアに十分ではなかった
  • 艦娘のレベルが低くても甲クリア可能だったが、艦娘のレベルが一定以上なら確実に甲クリアできた
  • Admiral Stats のユーザはやっぱりやりこみ勢が多かった

といったことがわかりました。

もう少し分析方法を工夫したり、プレイデータをアップロードしてくれる提督が多くなれば(やりこみ勢に偏っていなければ)、甲種勲章をゲットできる・できないの差がもっと正確にわかるかもしれません。

第3回イベント以降も同じようにプレイデータ解析してみるつもりなので、こういうデータを面白いと思う艦アケ提督はぜひ Admiral Stats を使ってみてください。

www.admiral-stats.com

趣味で作っているサービスを Rails 5.0 から Rails 5.1 へアップグレードする

$
0
0
f:id:muziyoshiz:20170724152414p:plain

最近、趣味で作っているサービス(Admiral Stats)を Rails 5.0.0.1 から 5.1.2 にアップグレードしました。サービスが小さいので、それほど苦戦するポイントは無かったのですが、調べたこと、やったことをメモしておきます。

調べたこと

とりあえず、関連しそうな情報に一通り目を通しました。

アップグレード方針

上記のページを見る限り、今回は移行対象のアプリが小さいこともあり、特に問題もなくアップグレードできそうでした。

まず、「application.secretsですべてのキーをシンボルとして読み込むようになった」はコード中の参照箇所を確認して、問題なし。JWT のエンコード、デコード時に Rails.application.secrets.secret_key_baseと参照していましたが、これは Rails 5.1 でも動作しました。

秘密情報の暗号化は、元々 MySQL のパスワードなどは Apache から環境変数(SetEnv)で渡していたので、この方針を踏襲することにして変更せず。config/environments/production.rb で config.read_encrypted_secrets = falseと書いておいて、今まで通りに secrets.yml を使います。

Yarn のサポート、Webpack のサポート、デフォルトでのjQuery依存の廃止、については、引き続き jQuery を使うことにして今回は気にしないことに。

あと、主キーのデフォルト型が BIGINT に変更に変更される件については、テーブル作成時期によって主キーの型が変わるのは混乱しそうなので、INT に統一することにしました。アップグレード時に必要な作業は特にないですが、テーブル作成時に id: integerを付ければ OK(以下はその例)。ただ、これはうっかり忘れることが多そうなので、generate model 時に必ず自動で付くようにしたい……。

classCreateBlueprintStatuses< ActiveRecord::Migration[5.1]
  defchange
    create_table :blueprint_statuses, id: :integerdo |t|

やったこと

Rails 5.0 系の最新版へのアップグレード、および関連 gem のアップグレード

まずは Gemfileのバージョンを更新。

- gem 'rails', '~> 5.0.0', '>= 5.0.0.1'
+ gem 'rails', '~> 5.0.0', '>= 5.0.4'

そして bundle update を実行。参考にしたページには bundle update rails を実行して rails 関係だけをアップグレードし、関連 gem は後回しにすべき、というアドバイスがあったのですが、すっかり忘れてました……。今回はそのまま続行。

最後に、動作確認のためにテストがある部分は rails test で確認し、ない部分は手作業で軽くポチポチ確認。

Rails 関係では特に問題ありませんでしたが、rack-corsが 0.4.1 から 1.0.0 に上がった影響で動作が少し変わっていて rails test が失敗。その部分のテストコードを修正しました。

あとは、Capistrano が 3.6.1 から 3.8.2 に変わった影響で、動かない部分と、Deprecation Notice が出る部分があったのでそこを修正。

  • Capfile の修正
- lock '3.6.1'
+ lock '3.8.2'
  • Capfile で Git SCM plugin のロードを明示
# Include default deployment tasksrequire"capistrano/deploy"# Future versions of Capistrano will not load the Git SCM plugin by default.
+ require"capistrano/scm/git"
+ install_plugin Capistrano::SCM::Git

Rails 5.1.2 へのアップグレード

先ほどと同じく、まずは Gemfile のバージョンを更新。

- gem 'rails', '~> 5.0.0', '>= 5.0.4'
+ gem 'rails', '~> 5.1.0', '>= 5.1.2'

次は bundle update を実行。関連 gem はアップグレード済みなので、今回は rails 関連の gem のみがアップグレードされました。

そして rails app:updateを実行。bin 以下と config 以下のファイルが、新規作成されたり上書きされたりします。git で差分を見て、既存の設定を上書きしている部分以外は、基本的に採用しました。

  • bin/setup
    • bin/yarn の読み込みがコメントアウトされた状態で追加された。参考のために残した
# Install JavaScript dependencies if using Yarn# system('bin/yarn')
  • bin/yarn (新規作成)
    • 今回は使わないが、呼び出し自体は上記の通りコメントアウトされているので残した
#!/usr/bin/env rubyVENDOR_PATH = File.expand_path('..', __dir__)
Dir.chdir(VENDOR_PATH) dobegin
    exec "yarnpkg #{ARGV.join("")}"rescueErrno::ENOENT$stderr.puts "Yarn executable was not detected in the system."$stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"exit1endend
  • config/application.rb
    • 以下の行が追加された。確認のうえで残した
+    config.load_defaults 5.1
  • config/cable.yml
    • Redis PubSub で使う prefix が追加された。設定しても、この自作サービス(Admiral Stats)では使わないが、自動生成されたまま残した
+  channel_prefix: admiral_stats_production
  • config/puma.rb
    • ビルトインサーバの設定。コメントが変わっただけ
  • config/routes.rb
    • 単純に空にされるだけなので、上書きされたものをもとに戻した
  • config/secrets.yml
    • 環境変数から読み込む運用にしているので、そのままにした
  • config/environments/development.rb
    • Cache-Control の表記が分かりやすい表記に変わった(max-age の長さは同じ)ため、そのまま残した
-      'Cache-Control' => 'public, max-age=172800'
+      'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}"
  • config/environments/production.rb
    • config.read_encrypted_secrets = trueという行が追加されるが、これを false に変更した
  • config/initializers/assets.rb
    • 以下の行が追加された。Yarn は使っていないが、そのままにした
Rails.application.config.assets.paths << Rails.root.join('node_modules')
  • config/initializers/cors.rb
    • 基本的な設定が作成されたが、このファイルは元々作成済みだったため、元に戻した
  • config/initializers/new_framework_defaults_5_1.rb
    • 以下の内容で作成された。そのままにした。
# Be sure to restart your server when you modify this file.## This file contains migration options to ease your Rails 5.1 upgrade.## Once upgraded flip defaults one by one to migrate to the new default.## Read the Guide for Upgrading Ruby on Rails for more info on each option.# Make `form_with` generate non-remote forms.Rails.application.config.action_view.form_with_generates_remote_forms = false# Unknown asset fallback will return the path passed in when the given# asset is not present in the asset pipeline.# Rails.application.config.assets.unknown_asset_fallback = false

最後に、動作確認のためにテストがある部分は rails test で確認し、ない部分は手作業で軽くポチポチ確認して、リリースしました。

まとめ

5.0 から 5.1 へのアップグレードは、それほど詰まるところはありませんでした。アップグレードが溜まってると辛くなるので、早いうちにやっておいてよかったです。

ただ、当たり前の話ですが、アップグレード前にはちゃんとテストを整備しておかないとダメですね。今回のアップグレード対象は趣味のサービスで、テストより機能追加を優先してしまっていたので、今回のアップグレードはおっかなびっくりでした。なにか僕の気付いていない問題が見つかったら、報告頂けたら緊急で対応します……。


Wiki のページ、[[タイトル]] から作るか、いきなり作るか?

$
0
0
f:id:muziyoshiz:20170827200124p:plain:w480

最近、職場で Wiki について話していて、Wiki のページの作り方は2通りあるよね、って話になりました。

  1. ページ本文のなかに [[タイトル]]と書いて保存し、あとからそのリンクを踏んで「タイトル」ページの作成画面を表示する
  2. いきなりページの新規作成画面を開いて、タイトルを記入する

僕はだいたい1番で作る方なんですが、毎回2番で作る、という人もいました。

そんなことがあって「僕の Wiki の使い方は普通じゃなかったのかな?」と思って調べてみたら、最近の Wiki の一部(Qiita:Team や esa.io)は、1番のようなページの作り方ができないんですね。

そうなのか!これは面白い!と思って、Wiki の歴史を遡って色々調べてみました。今回はそんなネタです。

Wiki ソフトウェアそれぞれのリンク記法

まず、Wiki の機能を持つソフトウェア(以下、Wiki ソフトウェア)について、その Wiki 内のページにリンクするための文法を調べてみました。僕が有名だと思うソフトウェアと、最近登場した国産のソフトを主に取り上げています。

登場時期 ソフトウェア名 記法 WikiName [[pagetitle]]その他
1994 WikiWikiWeb Yes
1999 UseModWiki Yes Yes
2000 YukiWiki Yes Yes
2001 PukiWiki Yes Yes
2002 MediaWiki Yes
2002 Tiki ((pagetitle))
2003 Hiki Yes Yes
2004 Trac Yes Yes [wiki:pagetitle]
2004 Confluence [pagetitle]
2006 Redmine Yes
2006 Backlog Markdown Yes (※)
2008 GitHub Markdown Yes (※)
2011 GitLab Markdown [text](pagetitle)
2013 Qiita:Team Markdown [text](url)(作成前にリンク不可)
2014 Crowi Markdown <pagetitle>
2015 esa Markdown [text](url)(作成前にリンク不可)
2015 DocBase Markdown [text](url)(作成前にリンク不可)
2016 Scrapbox [pagetitle]
2017 Kibela Markdown [text](url)(作成前にリンク不可)

(※) 相対パスで書けばリンク可能だが、[[pagetitle]]記法と比べたメリットが無いので省略

以下、この表の読み方と補足事項です。

  • 「登場時期」列
    • ベータ版と正式版があるときは、正式版のリリース日を登場時期とした
    • 正確なリリース日がわからないものは、確認できた最も古いリリースの日付とした
  • 「記法」列
    • 最近は Markdown 記法(またはその独自拡張)が多いため、その場合は “Markdown” と記載した
    • Wiki ソフトウェアが複数の記法をサポートしている場合は、Markdown 記法での仕様のみ記載した
  • WikiName 列が “Yes”
    • ページ本文に CamelCase で単語を書くと、それが自動的に Wiki ページへのリンクになるもの
  • [[pagetitle]]列が “Yes”
    • ページ本文に [[タイトル]]形式で単語を書くと、それが自動的に Wiki ページへのリンクになるもの
  • 「その他」列
    • WikiName と [[タイトル]]以外の文法を採用しているもの
    • ページ作成前にリンクを張れる Wiki ソフトウェアのほうが多いため、作成前にリンク不可のものはそのように明記した

[[タイトル]]記法の歴史は古い

ウォード・カニンガムが最初に作成した Wiki(WikiWikiWeb)は、CamelCase で単語を書くと、自動的にその名前を持つページへのリンクになります。また、その名前を持つページがまだ作られていない場合は、CamelCase?のように末尾に ?が付いて、この ?がページの新規作成画面へのリンクになります。これは、他の読者に、新しいページの作成を促すための工夫だったようです。

WikiWikiWeb にはこの CamelCase の記法(WikiName とも呼ばれる)しか無かったのですが、これでは不便だということで、後発の UseModWiki で、初めて [[タイトル]]記法が発明されました。

UseModWiki は、MediaWiki が開発される前に、Wikipedia で使われていた Wiki ソフトウェアです。History of wikisによると、この記法が発明されたのは、Wikipedia のユーザビリティを改善するためだった、とのことです。

This CamelCase convention was used by most wiki software for the first few years of wikis' existence. In 2001, the software UseModWiki, which at the time was in use on Wikipedia, switched to allow internal links to be done using standard spelling and double square bracket instead, in order to improve Wikipedia’s usability. This square bracket syntax has since become more of a default convention for internal links within wiki software in general.

WikiName のルールは日本語では不便すぎるので、YukiWiki や PukiWiki といった国産のソフトウェアが早々に [[タイトル]]記法を採用したのは納得できます。

[[タイトル]]からページを作れない Wiki が増えている

Markdown が普及して以降、Markdown やその拡張の GitHub Flavored Markdown(GFM)を採用した Wiki が増えています。昔から存在する Wiki があとから Markdown を採用したパターンもあります。上記の表では Backlog がそのパターンです。

Markdown は Wiki を想定せずに作られたので、Wiki ページにリンクするための文法がありません。そのため、Wiki ソフトウェアごとに、Wiki で一般的な [[タイトル]]記法を導入したり、Markdown のリンク記法を Wiki のために拡張したりしています。

その一方で、Qiita:Team や esa などの新しいソフトウェアの一部は、「ページの作成前にリンクを張る機能は不要」と割り切っているのか、そのような文法を用意していません。

どういうことか? 例えば esa で新しいページを作ると、URL は https://muziyoshiz.esa.io/posts/5のようになります。従来の Wiki は URL にタイトルの一部が入っていましたが、この種の Wiki では連番が入るので、事前に予想はできません。そのため、リンクを張りたければ、ページを作ったあとで、[タイトル](/posts/数字)のように、Markdown の文法でリンクを書く必要があります。これはめんどい。

ただ、esa の場合は、この面倒さを減らすためにリンクのサジェスト機能を提供しています。# からテキストを書き始めると、同じチーム内の投稿へのリンクがサジェストされます。

f:id:muziyoshiz:20170827214108p:plain

そして投稿を選ぶと、以下のように Markdown 形式のリンクに置き換えられます。

f:id:muziyoshiz:20170827214121p:plain

esa の場合、テンプレートを元に階層化されたページを作れるので、ページ名はだいたい「日報/2017/08/28/masahiro_yoshizawa」のようになります。これを手入力するのは現実的じゃないですよね……。それも、[[タイトル]]に相当する記法がない理由の一つかもしれません。

まとめ

[[タイトル]]という記法は昔からあって、さらにその大本には WikiWikiWeb が持っていた「ページの新規作成を促す機能」があった、という話をしました。だから、[[タイトル]]のリンクを踏んで新しいページを作る、というのは昔からの Wiki の使い方、と言えるでしょう。

しかし、これはインターネット上で誰もが参加できるような Wiki では必要でも、企業内などに閉じた Wiki では不要な機能なのかもしれません。企業内ならテンプレートに従ってページを作る方が一般的で、それなら

  • テンプレートに従って、いきなりページを作る
  • 必要なリンクはあとから書き足す

という使い方を助けるほうがいいのでしょうか? こういう使い方が今後一般的になっていくのか、気にしてみたいと思います。

アンケートにご協力ください(9月3日まで)

あわせて読みたい

この調査中に見つけた History of wikisはかなり読み応えがあって面白いので、Wiki に興味があったら是非一読をおすすめします。個人的に面白かったのはこのあたりです。

  • フローからスタックへ、という話題は、1996年に WikiWikiWeb へ ThreadMode が実装されたころにすでに存在していた
  • WikiWikiWeb にカテゴリとトピック(今で言うタグのようなもの)の機能が提案されたことがあったが、カテゴリのみが採用された
  • 「Wiki の機能をメール、IM、SNS と結合しようとした試み」として Google Wave の話が載っている

https://en.wikipedia.org/wiki/History_of_wikisen.wikipedia.org

このブログで過去に書いた Wiki 関連記事もあわせてどうぞ。

muziyoshiz.hatenablog.com

muziyoshiz.hatenablog.com

艦これアーケードのプレイデータ管理ツール Admiral Stats 1年分のユーザデータ解析

$
0
0
f:id:muziyoshiz:20170128164643p:plain

今月の9月3日に、艦これアーケードのプレイデータ管理ツール Admiral Statsの公開から1周年を迎えました。少しずつユーザが増え、開発に協力してくれる方も現れて、おかげで今日まで楽しく開発を続けてこられました。本当にありがたいことです。

1周年というのはよい節目なので、今回は Admiral Stats のユーザデータを解析して、利用傾向を調べてみました。この手のプレイデータ管理ツールを公開すると、どれくらい人が集まるのか(あるいは集まらないのか)の参考としてどうぞ。

解析対象は MySQL 上のデータです。解析方法の詳細は、このブログ記事の最後に貼っておきます。

アクティブユーザ数

Admiral Stats は、Twitter アカウントがあれば誰でもログインできるようになっています。しかし、この時点では、プレイデータは何も表示されません。

ログイン後に、SEGA 公式サイトからエクスポートしたプレイデータを Admiral Stats にインポートすると、プレイデータを時系列に表示できるようになります。

そのため、ログインしただけではアクティブユーザとは言えず、インポート機能を使っていればアクティブユーザと言えます。ただ、1回インポートして辞めてしまう人も割といるので、いくつかの基準で集計してみました。

グラフ上の項目名 集計基準 9月3日時点
ログイン その日の23:59までに1回以上ログインしたユーザ数 417
インポート その日の23:59までに1回以上インポートしたユーザ数 248
インポート(過去30日) その日から過去30日以内に1回以上インポートしたユーザ数 113
インポート(過去60日) その日から過去60日以内に1回以上インポートしたユーザ数 134
f:id:muziyoshiz:20170928002847p:plain

このデータを見ると、だいたい、以下のような傾向がありそうです。

  • ログインしても、インポートする前に離脱してしまうユーザが多い。しかし Admiral Stats 自体の改善が進んだためか、離脱率は徐々に下がっている
  • 2016年9月の まとめサイト取り上げ時は、離脱率がかなり高かった
    • その後の1週間で増加したログインユーザのうち、78%(=61/78)が離脱してしまった
    • 当時は Ruby 版エクスポータしか無く、使うのが難しかったためと思われる
    • 流入の効果は1週間程度しか続かなかった
  • 2017年7月に 艦らぼで紹介してもらった際は、離脱率が低かった
    • 公開後1週間で増加したログインユーザのうち、32%(=9/28)しか離脱していない(!)
    • これは、艦らぼに 丁寧な解説記事を書いてもらえた影響もかなり大きいと思ってます
  • 艦これアーケードの期間限定海域(イベント)の期間中はアクティブユーザが増えて、終わると一時的に落ち込む

余談ですが、Admiral Stats は、おーぷん2ちゃんねると Twitter(@admiral_stats)で宣伝していました。おーぷんで宣伝したのはまとめサイトでの取り上げを期待してのことだったのですが、

  • 機能追加しても、まとめサイトでは最初の1回しか取り上げられなかった
  • おーぷんで宣伝しても、ある時期から PV が増えるだけでインポートユーザ数は増えなくなった

ことから、いまは Twitter でしか宣伝していません。

MAU (Monthly Active User) と継続率

インポート機能を使っているユーザを「アクティブユーザ」と定義して、これをもう少し詳しく調べてみました。

グラフ上の項目名 集計基準 8月末日時点
MAU(全体) その月に1回以上インポートしたユーザ数 118
MAU(その月の新ユーザ) MAU のうち、その月に初回ログインしたユーザ数 29
継続率 前月に1回以上インポートしたユーザのうち、その月もインポートしたユーザの割合 83.3 %
f:id:muziyoshiz:20170928002920p:plain

この指標で見ても、Admiral Stats の離脱率は徐々に下がっている(=継続率が徐々に上がっている)と言えそうです。

また、意外なことに、毎月増えるユーザ数(その月の新ユーザ)はイベントにそれほど左右されず、一定の値を保っていたようです。しかし、それが今年の7月で2倍近く増加しています。とうとう普及が一山越えたんでしょうか?

インポート回数

ユーザ1人あたり、1ヶ月に何回くらいインポートしているか調べてみました。インポート回数が多いほど、Admiral Stats を日常的に使っていると言えます。

Admiral Stats は複数のプレイデータ(提督情報、艦娘情報、など)をサポートしていますが、「提督情報」のインポート回数のみを数えました。ほとんどのユーザがこのデータをインポートしており、これを Admiral Stats の利用回数と同一視して差し支えないと判断しました。

以下がその結果です。僕自身のインポート回数(動作確認含む)と、それぞれの月で最もインポート回数が多かったユーザは、外れ値として計測対象外としました。

平均 標準偏差 合計
2016-09 5.84 4.51 111
2016-10 5.33 4.80 192
2016-11 5.52 5.35 276
2016-12 5.71 6.57 217
2017-01 4.65 4.36 214
2017-02 5.02 5.10 246
2017-03 7.45 7.83 417
2017-04 8.02 7.73 497
2017-05 9.60 8.83 806
2017-06 8.36 9.64 619
2017-07 9.50 11.40 893
2017-08 9.77 11.05 1133
全期間 7.76 8.79 5621
f:id:muziyoshiz:20170928002958p:plain:w640
f:id:muziyoshiz:20170928003012p:plain:w640

インポート回数も、イベント期間中は増えて、終わると一時的に落ち込んでいます。それとは別に、インポート回数の平均と標準偏差が増えていますが、これは Admiral Stats のヘビーユーザが徐々に増えている影響のようです。

エクスポータの種類別のユーザ数

SEGA 公式サイトからプレイデータをエクスポートするために、以下のエクスポータを提供しています。エクスポータは Admiral Stats の「使い方」ページからダウンロードできます。

  • Ruby 版
    • 僕が最初に公開したエクスポータ
  • PowerShell 版
    • sophiarcp さんが Ruby 版を移植してくれたもの(2016-09-18 提供開始)
  • ブックマークレット版
    • sophiarcp さんが開発してくれたもの(2016-10-29 提供開始)
  • Python 版
    • mimikun さんが開発してくれたもの(2017-04-18 提供開始)

今年の3月12日に、エクスポートしたプレイデータを Admiral Stats に自動アップロードする機能を追加しました。この自動アップロード時の User Agent から、実際に使われているエクスポータの種類が(やっと)わかるようになりました。

以下がその結果です。全ユーザが自動アップロード機能を使っているわけではないので、MAU よりは少なくなっています。また、1人が複数のエクスポータを使っている場合もあります(例:家では Ruby、外ではブックマークレット)。

Ruby 版 PowerShell 版 ブックマークレット版 Python 版 その他
2017-03 5 10 14 0 0
2017-04 5 8 26 0 0
2017-05 7 9 45 0 2
2017-06 5 8 43 0 1
2017-07 6 7 69 1 1
2017-08 3 6 86 1 1
f:id:muziyoshiz:20170928003058p:plain:w640

ブックマークレット版のユーザ数が圧倒的ですね。正直、この集計をするまで、Ruby 版を使ってる人はもっと多いと思ってました……。PowerShell 版とブックマークレット版を作ってくれた sophiarcp さんにはホントに頭が上がらないです。

集計方法

今回の集計は、以下の手順で行いました。普通ですね。

  1. MySQL のバックアップデータをローカル開発環境に持ってくる
  2. rails console で CSV 出力する
  3. CSV を Excel に取り込んで、グラフを書いたり、ピボットテーブルを作る

rails console で実行したコマンドは、2周年のときの集計のために(自分が忘れないように)Gist に貼っておきました(Gist: Admiral Stats 1周年のユーザデータ解析)。Admiral Stats のソースコードは GitHub (admiral_stats)にあるので、興味のある方はそちらもどうぞ。Star を押すと開発者が喜ぶのでオススメです。

あわせて読みたい

muziyoshiz.hatenablog.com

Admiral Stats がどういうものかわかるプレゼン資料です。

muziyoshiz.hatenablog.com

今回は Admiral Stats の利用傾向を見ましたが、こちらはプレイデータの中身を掘り下げた記事です。

Nginx でリバースプロクシを立てるときに気にすべき proxy_next_upstream 設定

$
0
0
f:id:muziyoshiz:20171026000347p:plain:w400

個人的に、Nginx で「これは危険だ」と思っている設定があって、Nginx でなにかあるたびにその設定をつい疑ってしまいます。その設定について他の人に話すたびに、いちいち資料を集めるのが面倒になってきたので、今回はその設定項目についての情報をまとめておきます。

まだ理解に自信がない部分があるので、新しい情報が入ってきたら、この記事を適宜修正します。

リバースプロクシ設定の基本

Nginx をリバースプロクシとして使う時には、ngx_http_upstream_moduleでサーバのグループを定義します。そして、サーバ名やロケーション(パス)に対して、送信先のグループを指定します。

以下はマニュアルにある例です。その Nginx サーバへのすべてのアクセスを、backend グループに指定されたいずれかのサーバに送信します。

upstream backend {
    server backend1.example.com       weight=5;
    server backend2.example.com:8080;
    server unix:/tmp/backend3;

    server backup1.example.com:8080   backup;
    server backup2.example.com:8080   backup;
}

server {
    location / {
        proxy_pass http://backend;
    }
}

この送信に関わる設定は、proxy_pass を含む ngx_http_proxy_moduleの方にあります。このモジュールの設定のなかで、(僕が個人的に)よくつまづくのが proxy_next_upstream から始まる設定です。

proxy_next_upstream から始まる設定

これらは、upstream(リクエストの送信先)からエラーが返されたり、リクエストがタイムアウトした場合の動作に関する設定です。

  • proxy_next_upstream
    • 失敗したリクエストを他のサーバに再送する条件(複数指定可)
  • proxy_next_upstream_timeout
    • Nginx 側でリクエストがタイムアウトしたと判断するまでの時間
    • proxy_next_upstream で timeout が指定された場合のみ、この設定が使われる
    • 時間の単位は Configuration file measurement unitsを参照
  • proxy_next_upstream_tries
    • proxy_next_upstream の条件に合致したリクエストを、最大で何台のサーバに送信するか
    • マニュアルには明示されていないが、この送信回数は最初の1台を含む
      • 1が設定されたら、最初のサーバ1台にしかリクエストを送信しない
      • 3が設定されたら、最初のサーバ1台への送信と、それ以外の2台への再送を行う

これらの設定が明示的に指定されなかった場合のデフォルト値と、その意味は以下の通りです。

  • Default: proxy_next_upstream error timeout;
    • 何らかのエラーが発生した場合、または Nginx 側でリクエストがタイムアウトした場合に、リクエストを再送する
    • ここで言う「エラー」とは、(転送先)サーバへの接続時、リクエストの転送時、またはレスポンスヘッダの読み込み時に発生するエラーのこと
    • 4xx 応答、5xx 応答は、ここで言う「エラー」には含まれない
  • Default: proxy_next_upstream_timeout 0;
    • Nginx 側でのタイムアウトは起こらない(0 は無制限を表す)
  • Default: proxy_next_upstream_tries 0;
    • upstream ディレクトリで定義されたすべてのサーバに対して順番に、エラーが発生したリクエストを再送する(0 は無制限を表す)

不適切な設定が問題になるケース

proxy_next_upstream_tries を指定せずに使っていると、バックエンドのサーバへの接続で何らかのエラーが発生したら、最悪の場合、そのリクエストはすべてのサーバに対して送信されます。

例えば、以下のような状況になると、無駄なリクエストが Nginx で大量に増幅されて、システム全体の負荷が急増します。

proxy_next_upstream 設定を何も指定していない状態で、
→ アプリケーションサーバが何かのバグで不正なレスポンスを返すようになる
→ そのバグを踏むリクエストが来る
→ そのリクエストがアプリケーションサーバの台数だけ複製される(サーバ10台なら10倍になる)
→ システム全体の負荷が急増

また、proxy_next_upstream_timeout だけ設定していると、こういうこともあり得ます。

proxy_next_upstream_timeout が2秒に設定されていて、proxy_next_upstream_tries は未指定の状態で、
→ 処理時間が2秒を超える重いリクエストが来る
→ その重いリクエストがアプリケーションサーバの台数だけ複製される(サーバ10台なら10倍になる)
→ システム全体の負荷が上がって、普段は2秒未満のリクエストも2秒以上かかるようになる
→ それらのリクエストも10倍に複製される
→ システム全体の負荷が急増

あるべき設定

個人的に考える、あるべき設定は以下の通りです。

proxy_next_upstream_tries は必ず0以外に設定する

この値がデフォルト値の0(無制限)でさえなければ、上記のような問題は起こらないので、まずこれを設定します。

1にすれば再送は起こりませんが、アプリケーションサーバを再起動するような場合にいちいちエラーが出てしまいます。再起動の場合のみを考えるなら、この値が大きすぎても意味はありません。そのため、proxy_next_upstream_tries は2〜3でいいと思います。

proxy_next_upstream_timeout はアプリケーションサーバ側の応答時間より長くする

proxy_next_upstream_timeout がアプリケーションサーバ側の応答時間よりも短いと、せっかくアプリケーションサーバがレスポンスを返しても Nginx で破棄されてしまいます。これではサーバの計算資源の無駄遣いです。

そのため、アプリケーションサーバの応答時間を事前に見積もって、それより長い時間を proxy_next_upstream_timeout に指定しましょう。これは、タイムアウト設計をきちんとしましょう、そして時間がかかる処理(データベース接続)があるならアプリケーション内にきちんとタイムアウト処理を入れましょう、という話ですね。

応答時間の見積もりが難しいなら、proxy_next_upstream_timeout はデフォルト(タイムアウトなし)のままでもいいと思います。

サービスによっては再送処理をオフにする

API 提供時など、クライアント側で再送処理をしてくれるなら、proxy_next_upstream off;を設定し、再送処理をオフにするという手もあります。

関連情報

Amazon Linux, RHEL, CentOS での pip のインストール方法の違い

$
0
0
f:id:muziyoshiz:20171120220420p:plain

やらかした話

Amazon Linux は Red Hat Enterprise Linux (RHEL) をベースに開発された Linux ディストリビューションです*1。しかし、古い RHEL 5〜6 をベースにして、その後の開発は分岐しているため、パッケージ管理の方法などには違いがあります。

RHEL や CentOS では、EPEL を使って pip をインストールしても最新版にならず、pip install -U pip (pip install --upgrade pip) を実行する必要があります。

しかし Amazon Linux には独自の yum repository があるため、以下のように yum で最新版にアップデートできます。

$ sudo yum update python27-pip

しかし、それを忘れていて、Amazon Linux 上でつい

$ sudo pip install -U pip

としたところ、/usr/bin/pip が削除されて、新しい pip が /usr/local/bin/pip にインストールされてしまいました。ぐぐってみたら、同様の報告が Stack Overflow にもありました。

stackoverflow.com

「もしかして、これって最新の RHEL や CentOS でも同じことになるんだろうか?」と思って、EC2 上で検証してみました。

検証結果

結果を先に言うと、RHEL や CentOS ではそうなりませんでした。

  • Amazon Linux AMI 2017.09.1

    • 最新の pip が入っており、これ以上アップデートできない(Python 2.7.12, pip 9.0.1)
  • Amazon Linux AMI 2017.03.0

    • Python 2.7.12, pip 6.1.1 がインストールされている
    • pip は alternatives で管理されており、/usr/bin/pip は /etc/alternatives/pip へのシンボリックリンク
    • pip install -U pip を実行すると、/usr/bin/pip は削除され、/usr/local/bin/pip にインストールされる
    • yum update python27-pip でアップデートすれば /usr/bin/pip のままで pip 9.0.1 になる
  • Red Hat Enterprise Linux 7.4 (HVM), SSD Volume Type

    • Python のバージョンは 2.7.5 で、pip はインストールされていない
    • yum install epel-release で EPEL をインストールできず、rpm ファイルをダウンロードしてインストールする必要がある
    • yum install python-pip で pip 8.1.2 がインストールされる
    • インストール先は Amazon Linux と同じ /usr/bin/pip
    • pip install -U pip を実行すると、パスは /usr/bin/pip のままで pip 9.0.1 にアップデートされる
  • CentOS 7 (x86_64) - with Updates HVM

    • Python のバージョンは 2.7.5 で、pip はインストールされていない
    • yum install epel-release で EPEL をインストールできる
    • これ以降は RHEL と同じ

補足:現時点の最新バージョンは Python 2.7.14, pip 9.0.1

まとめ

結論としては、Amazon Linux では pip install -U pip しちゃ駄目ですが、他の Red Hat 系だと特に問題ない(むしろ yum では最新版が入らない)みたいです。Amazon Linux の pip は alternatives で管理されていることが影響しているんでしょうか?

Qiita で同様の症状がいくつか報告されていて、これらはどうも Amazon Linux ではなさそうですが、これ以上は深入りしないでおきます。

この件を Python に詳しい同僚に話したところ、yum が Python 2 で動作する関係で、RHEL や CentOS ではなかなか Python のバージョンを上げられないという事情もあるそうです。

pip は Python 3.4 以降に標準添付されていますが、RHEL や CentOS でその恩恵が受けられるのは当分先になりそうですね……。

おまけ(1):今回の検証に使った AMI

f:id:muziyoshiz:20171120220437p:plain
f:id:muziyoshiz:20171120220448p:plain
f:id:muziyoshiz:20171120221007p:plain

おまけ(2):そもそものきっかけ

今回そもそも何故 Amazon Linux で pip をアップデートしようとしたかというと、こんな経緯でした。

pyenv や virtualenv を使っていれば、今回みたいな目には遭わないと思います。Ansible で使いたいだけだから、と手抜きしたのがよくなかったですね。

Ansible が Python に依存してるせいで、ときどきどうでもいいところでつまづく気がします(今回のは完全に自業自得ですけど)。運用管理ツールを Go で作りたくなる人の気持ちが最近よくわかります……。

*1:AWS Developer Forums: Amazon Linux AMI - what distro is this based on?での Ben@AWS の回答によると、Amazon Linux は RHEL 5.x と、RHEL 6 の一部を元にしている。

AWS re:Invent 2017 Serverless re:Cap レポート 〜 Lambda, AppSync, Fargate, Cloud9, ML Service

$
0
0
f:id:muziyoshiz:20171120220420p:plain

最近、Serverless 関係の開発力を付けないとなあ……と思っていることもあって、AWS のイベントに参加してきました。

いずれちゃんとしたレポートが出てくると思いますが、個人的に気になった部分のメモを公開しておきます。

Serverless Updates (AWSJ SA 小梁川貴史)

  • AWS Lambda
    • メモリ容量を最大3GBまで設定可能に
    • Go 言語と .NET Core 2.0 のサポートを プリアナウンス
    • CloudTrailに起動イベント(invoke)も記録されるように
    • 関数ごとに同時実行可能数の上限を設定可能に(いままではアカウント全体での上限のみ)
    • コンソールから、その関数に設定したロールでアクセス可能なサービスを確認できるようになった
    • CodeDeploy での段階的デプロイのサポート
    • AWS Serverless Repository
  • Amazon API Gateway
    • いままでは Lambda のVPCアクセスを挟む必要があったが、その必要がなくなった
    • カナリアリリースのサポート(複数バージョンの混在)
    • API Integration(統合API)のタイムアウトのカスタマイズ設定が可能に
    • アクセスログの書式指定が可能に
  • Amazon Cognito
    • 認証機能の強化(ASF)
  • AWS Step Functions
    • re:Invent 前に発表された内容:state machine の update が可能となった

(※もし、スライドが公開されたら「本日ご紹介した update 一覧」のページだけ見れば概要がわかる)

AWS AppSync (AWSJ PS 塚越啓介)

  • DevOps Consultant の方
  • AppSync
    • フルマネージド GraphQL サービス
    • リアルタイム機能とオフライン機能にフィーチャ
    • 現在パブリックプレビュー中
  • AppSync のコンセプト
    • AWS AppSync Client: クライアントライブラリの提供
    • DataSource: Amazon DynamoDB, ElasticSearch, AWS Lambda をサポート
    • Identity: GraphQL Proxy での認証
    • GraphQL Proxy: リクエストのマッピングなど
    • Operation:
    • (※あと2つの特徴はメモできなかった)
  • GraphQL の解説
  • GraphQL Subscription
    • Mutation をトリガーにしたイベントベースモード
  • コンソールのデモ
    • Schema, Data Source の定義など
    • GraphiQL みたいなコンソールを提供してくれる
  • クライアントのデモ
    • ネットワークが繋がらないときは、ローカルストレージにキャッシュされる
    • ネットワークが復帰すると、バックグラウンドで同期される
    • 片方のクライアントからイベントを送ると、subscribeしている他のクライアントにpublishされて、画面が更新される
  • 質問
    • pagination の実装はどれくらい面倒見てくれるのか?
      • 支援の機能はある。詳しくはサンプルコードを参照
      • データベース側への制約はない。DynamoDB 側で next token とか設定すれば、自動的にやってくれる

AWS Fargate (AWSJ SA 大村幸敬)

  • Fargate 概要
    • インスタンス管理不要
    • タスクネイティブAPI
      • タスク → 複数のコンテナを1つにまとめた単位
      • タスクごとに Elastic Network Interface (ENI) が振られる → タスクに直接 IP アドレスを設定できる
      • ECS の場合は、コンテナはホストと通信する構成だった
    • リソースベースの価格
      • CPU と メモリは、50 種類のパターンから選択
      • 秒単位の課金
    • SLA は 99.99%(EC2 と同等レベル)
    • 現在、全世界の Kubernetes 上で動くワークロードの 63% は、AWS 上で動いている
    • Amazon EKS
      • EKS であっても、インフラ部分の管理は必要
      • Fargate の EKS サポートは 2018 年の予定
  • Lambda と Fargate の使い分け
  • デモ
    • マルチAZ、パブリックサブネット/プライベートサブネットの構成
    • タスクごとに CPU、メモリなどを指定
  • Fargate Under the Hood
    • Task Definition を登録。これを使ってタスクを実行
    • Per task ENI で動くので、今まで使っていた VPC でのアクセス制御が可能
    • ALB/NLB との組み合わせが可能
    • visibility がなくなった部分は、CloudWatch でメトリクスを確認するなど
    • Fagate 関連セッション(CON333, CON401 あたりが面白い)

AWS Cloud9 (AWSJ Specialist SA 福井厚)

  • Cloud9
    • クラウドネイティブなIDE
    • EC2 か、SSH 接続可能な Linux サーバにインストールできる
    • Cloud9 からコンソールを実行して、AWS CLI を利用したりできる
    • 複数のダッシュボードを利用して、(アカウントを?)切り替えられる
    • AWS CodeStar と連携
    • 一般的な IDE と同様の操作(例:ショートカットキー、ステップ実行、ブレークポイントなど)
  • Serverless Application Integration
    • リモートの Lambda ファンクションを参照できる
      • Cloud9 をインストールできるリージョンは限られるが、Lambda は任意のリージョンのものを参照できる
    • SAM のテンプレートを自動作成
    • Cloud9 上で Lambda のパラメータのペイロードを変更して、実行できる
  • Collaboration
    • IAM ユーザ間で Cloud9 の環境を共有できる
    • 共有したユーザ間でのチャット、ペアプログラミング(共同編集)ができる
  • AWS CodeStar Integration
    • CodeStar から Cloud9 の環境を構築できる
  • Cloud9 の利用自体は無料
    • EC2 の実行時間と、ストレージ利用量だけが課金対象

ML Services (AWSJ Specialist SA 西谷圭介)

  • 西谷さん、2月末に「サーバーレスアプリケーション開発ガイド」を発売予定
  • AWS の ML サービススタックの Services 層に追加された4つの製品の紹介
  • Amazon Compprehend
    • 自然言語理解サービス
    • 英語とスペイン語に対応
    • キーフレーズの抽出、関連する用語の分類、言語の認識、感情分析(文章のニュアンス)
    • Twitter 等のリアルタイム分析だけでなく、S3 上のファイルに対するバッチ処理も可能
  • Amazon Translate
  • Amazon Rekognition Video
    • 画像認識サービス Amazon Rekognition の動画版
    • 分析結果は1つの JSON で返される
    • JSON にさまざまな分析結果が格納されており、必要に応じて使う
  • Amazon Transcribe
    • 音声をテキストに変換するサービス(文字起こし)
    • プレビューでの対応言語は英語とスペイン語
    • 通常音声と電話音声の両方をサポート
    • タイムスタンプと、その時刻の文字起こしの信頼度
    • ユースケース:コールセンターの音声データの可視化
      • S3 -> Lambda -> Amazon Transcribe -> Amazon Comprehend -> Athena -> QuickSight

感想

Serverless といいつつ、真っ先に連想されそうな Lambda の話はあまりありませんでした。最初のサマリと、あとは Cloud9 の話題が一番 Lumbda に近かったですかね。それでも結構面白かったです。

Fargate は、運用の楽さと AWS サービスとの連携機能が魅力的で、Docker を使うならやっぱり便利そう。Fargate の EKS 対応がリリースされたら、実業務で使ってみたいところです。

GraphQL は以前に少しだけ調べたことがあるんですが、GraphQL Subscription というのが出ているのは知りませんでした。動きが早い分野なので時々見ないと駄目ですね。この辺を読むのがいいでしょうか?

Viewing all 107 articles
Browse latest View live