mallowlabsの備忘録 このページをアンテナに追加 RSSフィード

2013-03-24

Ruby を使って Fitbit API で遊んでみた

ハイテク万歩計Fitbit One を購入して遊んでいる。

ゲーミフィケーション的な要素があり、日頃の運動不足解消に一役かっている。

f:id:mallowlabs:20130324230638p:image

Fitbit 自体は、体重や体脂肪、睡眠時間や摂取カロリーなど健康に関する数値のデータベースを目指しているように見える。

f:id:mallowlabs:20130324230637p:image

最近の Web サービスらしく RESTful API を提供しており、データを流し込むことができるため、別サービスに貯めていた体重と体脂肪を Fitbit にインポートしてみた。

インポータ

実行には、Ruby と Bundler が必要。

OAuth の登録

dev.fitbit.comアクセスして、consumer キーと consumer secret を取得しておく。

準備
$ git clone git://github.com/mallowlabs/fitbit-weight-importer.git
$ cd fitbit-weight-importer
$ bundle install --path .bundle
$ cp config/fitbit.yml.example config/fitbit.yml
$ vim config/fitbit.yml
consumer / consumer key を記述する

CSV のフォーマット

以下のフォーマットで体重と体脂肪を準備する。

"Date","Weight","Fat"
"2012-09-09","54.80","16.90"
"2012-12-09","56.30","17.00"

実行

以下のコマンドで実行をする。
途中で URL が表示されるので、ブラウザアクセスし、表示される PIN コードを入力する。

$  bundle exec ruby bin/main.rb data.csv

データが登録されて、グラフが生成されているのを確認できる。

f:id:mallowlabs:20130324231319p:image

技術的な話

Fitbit API は素直な OAuth + RESTful API なので難しいことは何もない。
Fitbit API - API - Confluence

とはいえ、クライアントライブラリがあると便利である。
公式で紹介されているクライアントライブラリは、Java / PHP / .NET しかないが、 Ruby の 3rd パーティのライブラリとして Fitgem がある。

ドキュメントも充実しているため、Fitgem Reference Client を読めば困ることはないだろう。

この記事を読んで興味を持った方は Fitgem を使って何かサービスを作ってみてください!

2013-03-15

Tomcat7 でゼロダウンタイムデプロイ

「Web アプリバージョンアップ時に Tomcat再起動してもいいのは小学生までだよねー」
ということで、Tomcat でダウンタイム無しで Web アプリバージョンアップをする方法についてまとめてみる。

Parallel Deployment

Tomcat 7 から Parallel Deployment という機能が追加され、同一 Web アプリの複数バージョンを同時にデプロイができるようになった。

war のファイル名を somewebapp##001.war 等にしておくことで、

- $CATALINA_BASE/
  - webapps/
    - somewebapp##001.war
    - somewebapp##002.war

のように配備をすると、 http://localhost:8080/somewebapp/アクセスした場合に、セッションが継続している場合には古い方(001)を、そうでなければ新しい方(002)を Tomcat が勝手に使ってくれる。
この機能により、バージョンアップ時には連番をインクリメントした war ファイルを置くだけで、ダウンタイム無しにバージョンアップができるというカラクリになっている。

Tomcat の Web アプリケーションマネージャを見ると、デプロイされているバージョンや、どちらのバージョンの Web アプリセッションが生きているかなどを確認することができる。
古い方の Web アプリセッション数が0であれば、アンデプロイしてしまっても問題ない。

f:id:mallowlabs:20130315153420p:image


連番は Jenkinsビルド番号をつけると便利である。
私のチームの場合は、M2 Extra Steps Plugin でハードリンクを作成している。

f:id:mallowlabs:20130315161511p:image

画像内のコマンドは以下

$ ln "${WORKSPACE}/target/somewebapp.war" "${WORKSPACE}/target/somewebapp##${BUILD_NUMBER}.war"

VirtualWebappLoader

ここまでで、ゼロダウンタイムデプロイは実現できそうなのだが、私のチームではそのままでは使えなかった。
というのも、webapps/somewebapp/WEB-INF/lib/ の下にプラグインと称して、追加の jarデプロイ後に置く運用になっていたためだ。
また、環境による細かな設定変更も webapps/somewebapp/WEB-INF/classes/ にプロパティを置くこともしている。

この運用だと、war を置くだけではデプロイが完了しない。

そこで目をつけたのが Tomcat 7 から正しく動くようになった VirtualWebappLoader というクラスローダである。

Web アプリsrc/main/webapps/META-INF/context.xml に以下のような内容を書いて置く。

<?xml version="1.0" encoding="UTF-8"?>
<Context>
  <Loader
    className="org.apache.catalina.loader.VirtualWebappLoader"
    searchExternalFirst="true"
    virtualClasspath="${catalina.base}/extensions/somewebapp/WEB-INF/classes;
                      ${catalina.base}/extensions/somewebapp/WEB-INF/lib/*.jar"
  />
</Context>

context.xml の設定により $CATALINA_BASE/extensions というディレクトリを作成し、その下に追加で読み込ませたいファイルや jar ファイルを適切に配置すれば、デプロイ時に読みこんでくれるようになる。
重要なのは searchExternalFirst 属性で、これを true にしないと extensions 以下のファイルが優先されず、意図通りに動作しない。

注意事項

ここまでで、ゼロダウンタイムが実現できるが、メモリ周りは注意が必要である。

具体的には、内部的には二個のアプリデプロイしているのと同じなので、Permanent 領域もやはり2倍使う。
例えば、2回のデプロイをした時に PermGen の使用量の遷移は以下のようになる。(Java VisualVM で確認)

f:id:mallowlabs:20130315155811p:image

そのため、Tomcat の起動オプションの -XX:MaxPermSize を十分に大きく取っておく必要がある。
(アンデプロイしても PermGen の使用量は減らない?詳しい人教えてプリーズ)

2013.03.21 追記

id:bati11 さんからコメントを頂きました。

Tomcat の起動オプションに以下のオプションを追加すると PermGen スペースも開放されるそうです。

  • -XX:+CMSPermGenSweepingEnabled
  • -XX:+CMSClassUnloadingEnabled

ただし、メモリリークの問題が無いというのが前提です。
(私のプロジェクトで試したら開放されなかった…)

まとめ

  • Tomcat 7 から導入された Parallel Deployment を利用すれば、ゼロダウンタイムデプロイが実現できる
  • VirtualWebappLoader を利用すれば追加のクラスパスも使うことができる
  • ただし、メモリは2倍使うので十分に確保しておく必要がある

2013-02-18

Jenkins Bitbucket OAuth Plugin 書いた

JenkinsBitbucketアカウントログインしたいなーと思ったので書いてみた。

mallowlabs/bitbucket-oauth-plugin ? GitHub

設定画面はこんな感じ。

f:id:mallowlabs:20130218221919p:image

Bitbucket の権限設定とは全くリンクしないので、誰か対応して Pull Request 下さい。
アップデートサイトに登録する気もあんまり無いので、万が一必要な人がいたらアピールしてください。
(2013/05/30 追記) まさかのアピールを受けたのでアップデートサイトに公開しました。

プラグインの機能については想像通りだと思うので、 以下に BitbucketOAuth を使う時に注意すべき点などをメモしておく。

参照すべきドキュメント
注意点1: Consumer ID/Secret の取得

ドキュメントによっては Consumer ID/Secret を「メールしてくれたら送るよ」と
書いてあるが、現在はアカウント設定から自由に取得できるの注意。
名前だけの入力で発行できるので Github よりお手軽なのは Good.

注意点2: OAuth エンドポイントの URL

ドキュメントによって OAuthAPIURL が異なっている。

https://bitbucket.org/!api/1.0/oauth/request_token

とか

https://bitbucket.org/api/1.0/oauth/request_token

などと書いてあるが
BitbucketのOAuth:access token取得まで #Python #bitbucket - Qiita [キータ]
を参考に!無しの URL でうまくいった。
他の RESTful API

https://api.bitbucket.org/1.0/

とかだったりするのでわかりにくい。

注意点3: callback の verifier

ドキュメントにははっきりと書いておらず、画像の中にこっそりと書いてあるが、
oauth_callback を指定した時に返ってくる時の verifier が入っているパラメータoauth_verifier である。
OAuth 1.0a の仕様通りと言えば仕様通りなのだけど、他のパラメータはちゃんと書いてあるのにここだけ抜けてるので不親切な感じはする。

2013-02-04

Treasure Data を使って開発者向けチャットアプリ AsakusaSatellite メッセージログの解析をしてみた

HerokuからTreasure Dataに標準出力でデータインポートできるようになって多幸感がすごい - ハードコイルド・ワンダーランド というエントリを見て、Treasure Data が導入の手間をこれだけ下げてくれているのに使わないのは失礼にあたる、ということで使ってみました。
また前提として、プロダクションコードには一切手を入れないということも意識しました。

構成

今回の構成は、すでに Heroku 上で動作している AsakusaSatellite に対して、Heroku 上に立てた bot (as-treasure-data-logger) が AsakusaSatellite API を定期的にキックして、データを JSON標準出力に吐き続ける、という形にしました。

f:id:mallowlabs:20130203213148p:image

AsakusaSatellite の裏にいる MongoDB (MongoLab) を直接触ってもよかったのですが、API の使い勝手のテストも兼ねて API 経由にしています。

as-treasure-data-logger

as-treasure-data-logger は Heroku 上で動く bot として実装されています。
Treasure Data HadoopRedis To Go の2つの Heroku アドオンを追加しています。
Redis は最後に取得したメッセージの ID を覚えておくためだけに使っています。
この bot が定期的に AsakusaSatellite にメッセージ一覧を取得しに行き、標準出力JSON を吐きます。
Treasure Data アドオンが有効になっていれば、標準出力が拾われてどんどんデータが溜まっていくという形です。

bot のコードが短いので貼っておきます。

#! /user/bin/env ruby
# -*- mode:ruby; coding:utf-8 -*-

require 'json'
require 'open-uri'
require 'redis'

$: << "."

ASAKUSA_SATELLITE_ENTRY_POINT = ENV["ASAKUSA_SATELLITE_ENTRY_POINT"]
ASAKUSA_SATELLITE_API_KEY = ENV["ASAKUSA_SATELLITE_API_KEY"]
ASAKUSA_SATELLITE_ROOM_ID = ENV["ASAKUSA_SATELLITE_ROOM_ID"]
REDIS_URL = ENV["REDISTOGO_URL"]
COUNT = 50

uri = URI.parse(REDIS_URL)
redis = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password)

loop do

  since_id = redis.get("since_id")

  url = "#{ASAKUSA_SATELLITE_ENTRY_POINT}/message/list.json"
  url += "?api_key=#{ASAKUSA_SATELLITE_API_KEY}"
  url += "&room_id=#{ASAKUSA_SATELLITE_ROOM_ID}"
  url += "&count=#{COUNT}"
  url += "&since_id=#{since_id}" if since_id

  messages = []
  begin
    open(url) do |f|
      messages = JSON.load(f).map { |m| m["view"] = nil; m }
    end
  rescue => e
    warn e
  end

  messages.shift

  messages.each do |m|
    puts "@[heroku.message] " + m.to_json.gsub("\n", "")
    since_id = m["id"]
  end

  redis.set("since_id", since_id)

  sleep 300

end

必要な下準備などは README.md を参照してください。

Treasure Data でクエリを実行してみる

heroku td コマンドを使ってもいいのですが、
Heroku のアドオンページから Web インタフェースが使えるのでそちらを使ってみます。

アドオンページの 「Treasure Data」をクリックします。(コストが $0.00 なのも注目です。)
Heroku アドオンから Treasure Data を使った場合は、本家の Pricing よりかなりお得に使えます。
Treasure Data 社に感謝です。

f:id:mallowlabs:20130203213344p:image

ログインした直後の画面です。

f:id:mallowlabs:20130203213425p:image

Query をクリックして、クエリを打ち込み、Submit ボタンをクリックします。

f:id:mallowlabs:20130203213527p:image

クエリの実行には時間がかかるので、しばらく待ちます。

結果は TXT をクリックします。

f:id:mallowlabs:20130203215711p:image

banjun,8
suer,101
shimomura1004,64
mzp,68
atsumin,1
mallowlabs,107

Heroku 好きが誰かが一目瞭然です。
簡単ですね。

アクティビティ解析

AsakusaSatellite は開発者向けチャットアプリなので、開発のアクティビティが保存されています。
例えば Jenkins AsakusaSatellite Plugin を使っていれば、Jenkinsビルド結果がメッセージログに残っているはずです。

f:id:mallowlabs:20130203214744p:image

時間ごとのビルド回数を解析してみましょう。

Jenkinsビルド結果を拾うクエリを実行します。

SELECT
    SUBSTRING( v[ 'created_at' ] ,12 ,2 )
    ,COUNT( 1 )
  FROM
    message
  WHERE
    v[ 'body' ] LIKE '%SUCCESS%'
    OR v[ 'body' ] LIKE '%FAILURE%'
    OR v[ 'body' ] LIKE '%ABORTED%'
    OR v[ 'body' ] LIKE '%UNSTABLE%'
  GROUP BY
    SUBSTRING( v[ 'created_at' ] ,12 ,2 )

結果はこうなりました。

00,33
15,12
01,44
16,10
02,16
17,13
20,9
03,10
18,16
21,41
04,3
19,7
22,53
23,40
06,2
07,15
10,12
08,10
11,15
09,16
12,7
13,8
14,13

ソートして整形したものが以下です。


ビルド回数
0033
0144
0216
0310
043
062
0715
0810
0916
1012
1115
127
138
1413
1512
1610
1713
1816
197
209
2141
2253
2340

深夜に偏った非常に不健康なチームであることがわかりましたねw
(言い訳をしておくと、メンバは全員本職が別にあるのです)

時間ごとだけでなく、曜日ごとに分析してみたり、
ビルドの結果ごとに分析してみても面白いかもしれません。

まとめ

Treasure Data の Heroku アドオンがとにかく手軽です。
今回は約 60,000 レコードと、Treasure Data 社さんには申し訳ないくらいの小さなデータで試しましたが、
データ件数が増えても実行時間がほとんど変わらないのを見ると、その威力に驚きます。
また、AsakusaSatellite に通知していたビルド結果も、使い方によっては面白いことが出来そうだということがわかって、夢が広がります。
AsakusaSatellite と Treasure Data はどちらも面白いことができるアプリですので、ぜひ試してみてください。

2012-11-27

New Relic を Tomcat 7 (jsvc) で使ってみた

環境

インストール

基本的には、New Relic の setup ページの通りに操作する。

newrelic_agent2.10.1.zip を展開し、

/home/tomcat/newrelic

となるように配置する。

# cd /home/tomcat/newrelic
# mkdir logs
# chmod +w logs
# java -jar newrelic.jar install
Nov 27, 2012 17:58:22 +0900 NewRelic 1 INFO: Agent is using Log4j
***** ( ( o))  New Relic Java Agent Installer
***** Installing version 2.10.1 ...
Backed up start script to /home/tomcat/bin/catalina.sh.20121127_175822
Added agent switch to start script /home/tomcat/bin/catalina.sh
No need to create New Relic configuration file because:
 .:. A config file already exists: /home/tomcat/newrelic/newrelic.yml
***** Install successful
***** Next steps:
You're almost done! To see performance data for your app:
 .:. Restart your app server
 .:. Exercise your app
 .:. Log into http://rpm.newrelic.com
Within two minutes, your app should show up, ready to monitor and troubleshoot.
If app data doesn't appear, check newrelic/logs/newrelic_agent.log for errors.

インストーラの動きは、catalina.sh に以下の内容を書き込むだけらしい。

# diff /home/tomcat/bin/catalina.sh.20121127_175822 /home/tomcat/bin/catalina.sh
99a100,104
>
> # ---- New Relic switch automatically added to start command on 2012 Nov 27, 17:58:22
> NR_JAR=/home/tomcat/newrelic/newrelic.jar; export NR_JAR
> JAVA_OPTS="$JAVA_OPTS -javaagent:$NR_JAR"; export JAVA_OPTS
>

通常はこれで OK なはずだが、Tomcatデーモン化するために jsvc を使っている場合には、catalina.sh を読まないらしいのでうまくいかないっぽい。

今回試した環境は jsvc に付属している Tomcat7.sh というスクリプトTomcat を起動しているので、中身を読むと「JAVA_OPTS を上書きしたい場合は setenv.sh に書け」と書いてある。ので、その通りにする。

# vim /home/tomcat/bin/setenv.sh
# cat /home/tomcat/bin/setenv.sh
#!/bin/sh

NR_JAR=/home/tomcat/newrelic/newrelic.jar; export NR_JAR
JAVA_OPTS="$JAVA_OPTS -javaagent:$NR_JAR"; export JAVA_OPTS

あとは、Tomcat再起動して、適当に Java アプリを動してしばらくすると New Relic 側でデータを見ることができるようになる。