PebbleからNew RelicでWebサービスの状態が確認できるWatchfaceを作りました
インストールはこちらから。ただし、あなたのスマートフォンがiPhoneの場合は、まだインストールできません。自分でPebble用のアプリをビルドできる場合は、GitHubリポジトリからソースコードをアレして$ pebble build
ってやってください。
- https://apps.getpebble.com/applications/54703777762f64feae000039
- https://github.com/soutaro/magellan
先週、東銀座Pebbleハッカソンっていうイベントがあって、そこで開発の一部を行いました。
スマートウォッチについて
iPhone 6 plusとかを使っていると感じるんだけど、明らかにポケットに入れるには大きすぎるので鞄に入れたりすることになって、これまでよりもスマートフォンとの距離が遠くなってしまう。その距離を埋めるためのものとして、スマートウォッチという存在は自然な拡張だと思う。
Watchfaceについて
Pebbleでは常に画面に出てる時計文字盤に対応するWatchfaceという種類のアプリを開発することができる。
これはすばらしい機能だと思うんだけど、多分ちゃんと理解している人が少なかった。大体みんな時間や天気を表示したり、ミッキーマウスとかポケモンとかの画像を背景に入れるくらいしか使ってないくて、とてももったいない。サーバの情報はかなりエクストリームだと思うけど、多分だけど気温よりももっと表示するべき情報がある。*1
この常時表示されている時計の画面は、スマートウォッチを確認するときとか見せびらかすときとかに常に目に入る部分で、ユーザー体験の一等地として熾烈な争いが勃発するべき場所だと思う。ちまちまボタンや画面を操作するのではなく、手首を見るだけでなにか良いことが起きるというシステムは、腕時計くらい自然に確認できるデバイスだと、かなり本質的だと思うんだけど。
そう思っていたけど、まあ誰も活用しているように見えなかったので、実はそうでもないのかなーと思って、試しに作ってみたのだった。
感想
文字盤にいきなりサーバの状況が出てるのはかなり面白い状況で、オフィスのでかいディスプレイに表示するよりもはるかに気軽に確認ができる。僕の場合サーバの健康状態を確認する頻度が10倍くらいに上がったし、確認できなくて不安に思うことが減った。
最初は時間を表示してなかったんだけど、腕時計を見たときに時間がわからないというのが、なんか良くわからないんだけどなんだかストレスを感じる状況だったので、小さく表示することにした。生まれたときから携帯電話があって、腕時計をつけたことがない人だと、こういうストレスは感じないのかもしれない。
Apple Watchについて
Apple Watchは、その辺が残念で、文字盤はユーザーから手が出せないようになっている*2。Glanceと呼ばれる種類のアプリがそれを補完するものとして機能する様に見えるので、そこに期待するしかない。Appleの社内でこういう考察がされていないというのはさすがに考えづらいので、いろいろとバランスを考えた結果、こうなったのだろう。
とはいえ、ロック画面をカスタマイズさせることを頑なに拒んできたチームの製品なので、あまり期待していない。ロック画面よりもユーザー体験の向上にはるかに直結しているところだと僕は思っていて、なのでカスタマイズできるようになると良いなーと思う。
一方で画面がカラーなのはすばらしいと思う。Pebbleは白黒なので、黒で「Green」とか「Red」とか書くはめになってて、もうちょっとなんとかなって欲しい。
Pebbleについて
- 今なら$77なので、2個買って右手用と左手用にすると良いと思います https://getpebble.com
- 基本的に日本語が出ないのでそこは諦めましょう http://www.texpress.co.jp/pebble_hiragana
4時間かかるバッチ処理に失敗していた
#!/bin/bash set -e PWD=`pwd` cd /var/tmp/data/... ${PWD}/do_something.sh ...
${PWD}
って、シェルの組み込みの変数で常にカレントディレクトリを返す子。先頭で代入した変数名が悪い。
#!/bin/bash set -e basedir=`pwd` cd /var/tmp/data/... ${basedir}/do_something.sh ...
これなら大丈夫。
WordPressを使いこなせる上司になろう
WordPressは辛い。何が辛いか。
- ふとアップデートすると壊れる
- こまめにアップデートしていると割と大丈夫だけど、ちょっと期間が空くと壊れる
- WordPressでCSSの変更したら壊れた画面が全世界に配信された
- プラグインを入れたら画面が見れなくなった
基本的にはWordPress使いたくないんだけど、そうはいってもBlogとかをいちいち自分で作るのは明らかにばかげているので、しぶしぶWordPressを使います。でも辛い。辛いのはできるだけ避けたいので、できるだけ辛くなくWordPressを使う方法を考えました。
基本的な戦略
普通に記事を書くときは別ですが、CSSとかプラグインとかを変更するときは、いきなり本番環境に適用するのではなくて手元の環境でちゃんと確認するべきです。
つまり、普通のWebアプリケーションを開発するときの流れをそのまま適用したい。普通どうするかというと、手元のPCに開発用の環境を作って、そちらで開発をします。また、Gitなどのバージョン管理ツールを使って、変更を追跡します。手元でちゃんと動作することを確認できる状況になったら、その時点でGitに変更を記録して、Productionのサーバにデプロイする。それでもぶっ壊れたらGitから古いちゃんと動作するバージョンを取ってきて元に戻す。これが基本的な流れです。
WordPressで作るサイトでも、この流れに乗りたい。もちろん、WordPressで記事を書くときはWebのUIをそのまま使うべきです。しかし、プラグインを入れたり、PHPを書いたり、CSSを書いたりしているときには、そうではない。
開発環境と本番環境を使い分ける
まず、開発環境と本番環境で、いくつか違うことをやらないといけない場面がありますので、それを切り替えられるようにします。切り替えないといけないことというのは、
- トップページのURL
- データベースの設定
などがあります。Railsではdatabase.yml
に書いたり、config/environments/development.rb
とかに書いたりするやつのことですね。また、例えばThe Twelve-Factor Appなんかでは、設定は環境変数に入れろと書いてあります。が、ここでは面倒なのでソースコードにそのまま書きます。
wp-config.php
にこんな感じで書きます。
if ( !defined('ABSPATH') ) define('ABSPATH', dirname(__FILE__) . '/'); if ($_SERVER["WPEnv"] == "development") { if (file_exists(ABSPATH . "development-config.php")) { require_once(ABSPATH . "development-config.php"); } } else { define('DB_NAME', ...); define('DB_USER', ...); define('DB_PASSWORD', ...); define('DB_HOST', ...); define('WP_SITEURL', 'http://blog.ubiregi.com'); define('WP_DEBUG', false); }
WPEnv
という環境変数にdevelopment
とか入れておくことにして、それで設定を切り替えます。本番環境用の設定はソースコードにそのまま書いていますが、開発環境用の設定はファイルから読み込みます。これは、開発環境では、個々の開発者・計算機によって、適切な設定が変わるからです。具体的にはMySQLの設定とかがホスト名によって変わります。あと、WPEnv
は、Apacheの設定ファイルで設定します。
多分、設定しておいたほうが良いのは、
- WP_SITEURL
- DB_NAME
- DB_USER
- DB_PASSWORD
- DB_HOST
- WP_DEBUG
くらいだと思います。
ApacheとMySQLをセットアップする
Apacheは手元のMacで動かしたくないので、Vagrantを使います。MySQLは、弊社で開発しているRailsアプリでも使うので、全部のMacにインストールされているので特に問題になりません。MySQLが無い人はがんばってインストールしてください。
なぜMySQLをVagrantに含めないのかというと、データベースは開発の全行程を通して使い回したいことの方が多いからです。Vagrantに入れておくのも一つの方法ですが、その場合vagrant destroy
するたびに全部データが消えます。消えてもいい人はそっちの方が設定が簡単になるので良いかもしれませんね。
適当にVagrantの設定を作ってあるので、見てみてください。
それぞれ設定のポイントを列挙します。
ネットワーク関連
VagrantからMacにアクセスできるようにVagrantfile
で設定しています。多分これはVirtualBoxのみの設定なので、VMWareとか使う人は適宜書き換えてください。起動時に必ずネットワーク設定を確認されるので、それがうざい人もまあ各自でなんとかすること。
avahi-daemon
をインストールしてあるので、VMからMacに名前でアクセスできるようになっています。上述のdevelopment-config.php
を作るときに、MySQLのサーバ名を名前で設定できて便利。なはず。
また、localhost:8080
にアクセスしたときに、VMのポート80に繋がるように設定しています。開発中のWordPressサイトにアクセスするときは、http://localhost:8080
にアクセスすれば良い。
Apacheの設定
Apacheの設定はwordpress.conf
に書いてあります。ここでWPEnv
を設定するようになっています。
あとはログとかの設定は、VMの中と共有されたlog
ディレクトリに書き出すように設定しています。
MySQLの設定
MySQLは、次の2点を設定します。
- ローカルホスト以外の接続を受け付けるように設定すること
- ちゃんとユーザを作っておく
VMで動いているWordPressから実機のMacで動いているMySQLに接続することになるので、別の計算機からログインできるようになっている必要があります。試すには、Macで、
$ mysql -h `hostname` <その他のオプション>
などとして、ちゃんと繋がるかどうかを確認します。
開発用のデータベースなので、できるだけ簡単になにも考えずに接続できる設定にする、という場合は、
bind-address=*
GRANT ALL PRIVILEGES ON *.* TO root@'%';
などとします。どこからでも接続できて、ユーザー名
root
でパスワード無しでログインできる設定です。私は開発用マシンならばこれで良いと思いますが(本番用はもちろんちゃんとパスワード設定します)、いろいろポリシーがあると思います。
開発を始めるときの手順
こんな感じで設定するようにすると、開発を始めるときの手順はこんな感じになります。
local-wordpress
のリポジトリを$ git clone
してくる- リポジトリの
site/wordpress
以下に、WordPressのアーカイブを展開する $ vagrant up
するhttp://localhost:8080
にアクセスしたときにWordPressサイトに繋がるようになっているので、いろいろサイト作成の作業を行うwp-config.php
を編集して、ついでにdevelopment-config.php
を作るwp-config.php
には、上のPHPのコードをコピペして、本番環境の設定を書く(後回しでもok)development-config.php
には、開発環境用の設定を書いておく
- サイト用のリポジトリを作る
今後の課題と想定される質問
デプロイをどうするのかまだ考え中。一番素朴にやると、普通にSSHでサーバにログインして$ git pull
するとかになるけど、ちょっと悲しいのであんまりやりたくない。Capistranoとかでうまいことできるはず。
- Vagrantがちゃんと動かない
- ネットワークの設定がちゃんと動かない事件が発生したことがあります。VMWareを使うようにすると、ちょっとは安定するようになります。
- データベースの内容に依存するようなコードを書かざるを得ないけどどうしたものか
- どうしたもんか……カテゴリとか記事のIDとかが、HTMLに出てくる問題には困っている。開発開始時とか、デプロイ時にMySQLのデータをコピーする戦略くらいしか思いつきません。
- Vagrantpressって知ってる?
- 知ってるけど、あれはプラグインを作るための環境なので、ちょっと今回の用途には合わない。これはサイトを作りたいときの話。具体的には、VagrantpressだとVagrantを起動するたびにMySQLの内容が消えたりするところが、使いにくい。
- デプロイがめんどうならHerokuとか使えば?
- ちょっとやってみるとすげええ面倒くさいので、お勧めしません。面倒くさいというのは、実際にサイトを運用する手順の話なので致命的である。画像アップロードとかはWordPressはかなり良い感じの実装が(クライアントサイド的には)されているのですが、それを全て捨てて、変なプラグインを使うことになります。お勧めはしない。
ユビレジのサービス状況を監視するためのモニタを作りました
ユビレジではNew RelicとかLogglyとか使ってサービスに問題が生じていないかを監視しているのですが、そうはいってもあんまりちゃんと監視できていないのが問題になっていました。簡単に言うとWebブラウザでいちいちアクセスするのは、明らかにめんどくさいわけです。具体的にNew Relicを見ようと思ったとすると、
- New Relicを見ようと思う
- ブラウザの新しいタブにrpm.newrelic.comと入れる
- パスワードを入れる
- アプリケーション一覧からUbiregiを選ぶ
- やっとモニタにたどり着く!
という手順が必要になります。全体的に面倒くさいのですが、この中で一番問題になるのは1の「New Relicを見ようと思う」ということです。あんまり思いません。そして、問題が発生するのは、問題が発生すると思いもしなかったタイミングです。メール通知なんかもありますが、わりと簡単に他のものに埋もれます。
そういうわけでこうなりました。
- 上半分はNew Relicの画面(レスポンスタイム、スループット、エラー率、Apdexが表示されている)
- 左下はHerokuのDyno数を表示(ユビレジではHireFireを使って自動でWeb Dynoを増減しています)
- 右下はTwitterでエゴサーチした結果
これを開発チームのデスクからお菓子エリアの動線に接するように設置したところ、お菓子を食べるタイミングで必ず目に入るようになりました。割と良い感じです。
作り方(ハードウェア)
iPad miniと、Apple TVと、DELLで買った19インチのディスプレイと、ディスプレイアームを使っています。ディスプレイはもっと大きくても良かったかもしれない。iPadが余っていたので使いましたが、別にMac miniとかでも良かった気もします(次で補足します)。
CI用の計算機やテスト用のレシートプリンタなどを配置してあるメタルラックにディスプレイアームでくっつけました。
作り方(ソフトウェア)
まず、Panicが公開しているStatus BoardというiPadアプリを使います。いまいち使いどころが難しいアプリだなーと思っていましたが、こんなぴったりの使い道があったなんてびっくりです!有料オプションのHD出力を購入すると、良い感じのアスペクト比で出力してくれます。
New Relicの画面は、Webページを表示する機能を使います。New Relicのチャート埋め込み機能を使うとiframeのスニペットができるので、それを並べてサイズ調整したHTMLを作り、Dropboxに置いて、それを表示しています。サイズ調整は、現物あわせでがんばります。
- New Relicの埋め込みチャートのiframeの先を、直接Status Boardに表示することも考えましたが、サイズ調整が難しかったので諦めました
- New RelicのiPadアプリもありますが、ちょっと今回の用途には使えない感じの機能でした
HerokuのDyno数は、グラフを表示する機能を使います。CSVファイルを作れば、それを適当にグラフに表示してくれます。本当は24時間分あるのですが、12時間分しか表示されなくて残念。Dyno数は、Mac miniが1時間毎にherokuコマンド経由で数えます。Dropboxで共有されているフォルダにCSVを書き出せば、後は自動でStatus Boardまで繋がります。
Twitterのエゴサーチは、Status Board組み込みの機能。
その他に注意したことと言えば、iOSのアクセスガイドを使っています。アクセスガイドは特定のアプリしか実行できないようにする機能ですが、今回はStatus Boardがクラッシュしたときに自動で再起動するために使っています。
ずっと見ていたわけではないのでクラッシュしたことがあるかどうかはわかりません。アプリがクラッシュするというのは一般的な心配事であって、Status Boardの安定性に関するなんらかの言明ではありません。
privateって書きたくない
まず前提として
- publicにするべきものはpublicにするべき(公開されたAPIであるもの)
- privateにするべきものはprivateにするべき(公開すると都合が悪いもの)
である。ところが、残念なことにこの二つの間には、どちらとも言いがたいものがかなりの分量あったりする*1。それをどうするべきなのかというと、簡単に言うと(理論上)二つの戦略を考えることができて*2、
- 良くわからないやつは全部publicにする
- 良くわからないやつは全部privateにする
それで、僕は前者を選択しがちであるという話。もちろんケースバイケースで、ライブラリを作ってるときはできるだけprivateにするし、例えばattr_accessor
とattr_reader
だったら後者を選ぶし、まあ基準としては曖昧なんだけど。あるいはObjective Cだと、ヘッダファイルを書くのが面倒なのでPrivateにする(宣言しない)という現象が発生するし、NSMutableArray
とNSArray
であればNSArray
を選ぶ。
https://github.com/soutaro/unification_assertion/blob/master/lib/unification_assertion.rb
例えば、上のプログラムで言うと、UnificationAssertion#substitute
なんかが、僕の言うところの「どちらとも言いがたいもの」である。
これはUnificationAssertion
モジュールの外からは呼ばれないメソッドなのでprivateでも良い(公開されたAPIとは言えない)。コメントも書いてないので、実装した人(1年前の僕)もpublicなAPIであるとは認識していないことが推測できる。一方で、この操作はこのプログラムにおいてかなり本質的な操作なので、「実装の詳細なので隠蔽しなくてはいけない」と言うほどの物でもない。その辺を考えた結果「privateにするべきものでもないのでpublicのままにしておこう」となった。
ちなみに、例えば代入に対応するクラスを定義せずにHashをそのまま使っていたりメタ変数のためにシンボルを流用しているように、かなりプログラム全体のサイズを小さくすることにも注目して実装している。この「1ファイルで完結するくらいの小さなプログラム」であるという事実も、privateとpublicのどっちの戦略を採用するかにかなり強く影響している。
アプリケーションを書いているときに、こういう些末なことに頭を使いたくないという感覚も大きい。Webアプリケーションを書いてるときに、上のsubstitute
メソッドの可視性について考えることには時間は使いたくない。
こういう、publicなものを好む嗜好というのは、僕の場合にはかなり最近に後天的に獲得された物である。ソフトウェアのモジュールがどのような関係を持っているべきなのか、というのは実際にはけっこう考えるのが楽しいことなので、放っておくと時間を忘れて(しょうもないことを)考え続けてしまう。そういう状況は避けるべきなので、些末なこと、つまり超重要ではないことに悩み始めたら考えるのを止めて、ひとまず最小のタイプ数で制約を満たすものを選ぶような、そういう思考を行うように教育した。ような気がする。
それは話が逆になっている
種を明かせば簡単で、エンジニアはしょぼいアイディアでも自分で実装できちゃうというだけの話なのですよ、これ。実装を一人でできるので、アイディアの段階で枝刈りされることがなくて、結果として世の中に出てくるものの数で言えば「エンジニアが出したしょぼいアイディア」を実装したものが多くなる。一方で、エンジニアじゃない人はがんばってしょぼくないアイディアを考えて、アイディアがしょぼくないことをエンジニアに説得して頼み込んでやっと実装してもらえるので、明らかにしょぼいアイディアは世に出ないという。
アイディアのしょぼさと当たるかどうかには強い相関性はないというのが現在の常識だと思うので(適当に言ってるけど)、つまり実装できる人が、試行回数を最大化できる分、強いと言うことになる。がんばってしょぼくないアイディアを考えて枝刈りを避けないといけない人は大変ですねーという感じ。がんばってください。
技術的制約がどうのこうのとか、ユーザーベースでサービスを考えるとか、その辺はまあどっちでもいいや。僕も似たようなことを考えるし。
Cross Origin Resource Sharingのプリフライトの背景を理解する
Ajaxで他のホストのデータにアクセスできるようになるやつについてです。
この辺のドキュメントを読んでいても、どうも今ひとつ納得できなかったりすることがないかと思います。
- シンプルなリクエストとプリフライトリクエストはなにが違うの?
- まずシンプルなリクエストとプリフライトリクエストをなぜ区別しているの?
- プリフライトリクエストは「実際のリクエストを送信しても安全かを確かめるために」あると書いてあるが、「安全な」リクエストと「安全でない」リクエストはどう違うの?
シンプルなリクエストとプリフライトリクエストの二つがあることはわかるけど、なぜ二つに分けないといけないのか良くわからないし、その定義もなんか無駄に複雑に見えて良く理解できない。なぜ application/x-www-form-urlencoded
が特別扱いされているのか。そもそも、プリフライトリクエストとはなんのためのものなのかは、全然具体的に説明されていない。プリフライトリクエストではなにをすれば良いのだろう。何も考えずに200を返すだけで良いのだろうか。何らかの認証を行うのは、プリフライトリクエストのなかでやって良いのだろうか?キャッシュの時間はどの程度の長さにして良いのだろう?
まあ、こんな感じ。
さて、適当にぐぐってみると、Stack Overflowの記事が見つかります。
おお。これっぽい。
プリフライトリクエストの背景
簡単に言うと、
- シンプルなリクエストは、元々Webブラウザで発行可能だったクロスドメインなリクエスト
- これまでもできていたことなので、これからもできないといけない
- Webアプリケーションの責任として、このようなリクエストはきちんと処理できなくてはいけない
- プリフライトリクエストは、CORSによって発行可能になったリクエスト
- この種類のリクエストが発行されたときにきちんと動作しないのは、Webアプリケーションの責任ではない
- Webブラウザの側で、リクエストを発行して大丈夫であることを確認しないといけない
ということです。
シンプルなリクエストを良く見てみると、
- カスタムヘッダなしのGETはこれまでもWebブラウザから発行できていたリクエストなので、特別な対応は必要ない → シンプルなリクエスト
- 普通の
form
を通して他のドメインにPOSTできていたようなもの(カスタムヘッダがないとか、content-typeがapplication/x-www-form-urlencoded
とか)は、これまでも普通にWebブラウザから発行できていたので、普通のWebアプリケーションは処理できるはず → シンプルなリクエスト
ということです。
一方でプリフライトリクエストはと言うと、それ以外のもので、つまりXHRとかを使わないとブラウザ内からは発行できないものです。CORSで初めて発行可能になったリクエストなので、Webサーバが対応しているかどうかを、確実に検査する必要があるわけですね。
サーバがCORSに対応しているかどうかって……Access-Control-Allow-Originと何が違うの?
一瞬混乱するかもしれませんが、違います。
Access-Control-Allow-Origin
は、リクエストが処理されて、ブラウザに返ってきたときに見えるやつなので、POST
みたいな冪等でないリクエストの場合にはやくにたちません。ブラウザに返事が返ってきた時点では、すでにPOST
が処理されてしまっていて、サーバのデータが変更されてしまっているわけです。
まとめ
- プリフライトリクエストはWebアプリケーションがCORSに対応しているかどうかを確認するためのもの
- 対応してるなら、深く考えずに200返しちゃって問題ない(多分)
- キャッシュの時間も「やっぱりCORS止めます!」みたいなことがないなら、適当に決めれば良い(多分)