プログラム

TOKYOAMEDAYO – 東京が雨予報ならYo

YoのAPIキーを取ってみたので試しに何か作ってみようということで、
東京地方に雨の予報が出ていたらYoしてくれるアカウントを作ってみました。

TOKYOAMEDAYO というアカウントに利用中の端末からYoしてもらえれば以降、毎朝7時30分に東京地方に雨の予報が出ていれば、このアカウントからYoされます。

スクリプトが行っているのは、livedoor天気の東京地方の本日の予報から「雨」が含まれていたらYoするだけです。今回試してみた様な「雨が降る/降らない」といった2値情報であれば、Yoで気軽にpush出来て良いなと思いました。

とりあえずnpm化したので、Yo API Keyと先のlivedoor天気の予報地域先のIDを渡せば、日本国内の他の地域の雨予報もYoできる。と思います。

NPM

Yo APIを利用するためにAPI Keyを取得する

2周り遅れぐらいでYo APIを利用してみたので、API Keyを取得する際に行った作業のメモ。

Yoをインストールする

tokenの取得にはYoのアカウントが必要なので、iPhoneにYoをインストール。
ここで取得したアカウント名を利用してAPI経由でYoはできないので、その辺りのアカウント名は要注意。*1

トークンの取得

http://yoapi.justyo.co/ へアクセスすると、API Key取得するために必要な情報の入力を求められるので、入力します。

メールを確認する

先のフォームで入力したメールアドレス宛にAPI Keyがメールで送られてくるので確認します。*2
メールは以下の様な文面。

Yo,
Thanks for taking an interest in our API!
We created an API key for your username, (作成されたユーザ名).
Your unique API key is ******************************.
Please keep your key secret.
To learn more about how to use the API visit https://bit.ly/yoapi.
We're excited to see what you create! You can submit what you create
at the Yo API Services Directory - http://submit.justyo.co
Create a Yo button: http://button.justyo.co
Let us know if you have any issues.
Thanks,
Yo API Team
http://justyo.co

http://dev.justyo.co/login.html を開くことで、ダッシュボードからも確認できますね。

APIの呼出

フォローしている全員にYoをするのは、

$ curl --data "api_token=(取得したAPI Key)" http://api.justyo.co/yoall/

特定のユーザにYoするのは、

curl --data "api_token=(取得したAPI Key)&username=(Yoする先のアカウント)" http://api.justyo.co/yo/

の様にします。要はPOSTするだけですね。

Rubyで呼び出すのであれば、

require 'faraday'
conn = Faraday.new(:url => 'http://api.justyo.co') do |builder|
builder.request  :url_encoded
builder.adapter  :net_http
end
conn.post '/yoall/', {:api_token => YO_API_KEY}

といった具合にすればよさそう。

注意としては、APIの利用制限が1分間に1回だけなことくらいでしょうか。いろいろなトリガでYoさせて遊べそうです。

APIの使い方は以下のページでも紹介されています。
https://medium.com/@YoAppStatus/yo-developers-api-e7f2f0ec5c3c

*1:例えば、 APIを利用してhogeというアカウントからYoさせたいのにiPhoneアプリhogeというアカウントを取ってしまうと通知できません。

*2:API Keyがメールで飛んでくるのがアレな気もしますが...

Coffee scriptでstep.jsを使う (Hubotで3秒毎に発言させる)

Hubotに3秒おきに発言させるため setTimeout 使う時、あれこれ試してみて以下の様に書いてみた。step.jsを利用してコールバックのネストが深くならなくて多少見やすいかなと思ったのですが、もっと良い書き方があったら是非知りたい。

cronJob = require('cron').CronJob
step = require('step')
new cronJob('0 15 * * *', () ->
step(
() ->
robot.send {room:'sample'}, "3時です"
setTimeout @, 3000
return
() ->
robot.send {room:'sample'}, "お茶の時間です"
setTimeout @, 3000
return
() ->
robot.send {room:'sample'}, "休みましょう"
return
)
).start()

lxc-cpu-usageというツールを作った

VPSの上で複数のLXCを作ってRubyやnode.jsのアプリケーションサーバを動かしたり、DBサーバを動かしたりしているのですが、
稀に「このコンテナでどのくらいCPUが利用されているのだろう?」*1と調べることがあって、その時には

  1. 該当のLXCのinitプロセスを見つける
  2. そこにぶら下がっているプロセスを確認
  3. それらのプロセスがどのくらいCPUを使っているか知る

といったことをやっていたので、その辺りをさっくりできるツールを作ってみました。nodeで作ってます。初npm登録。

lxc-cpu-usage lxc-cpu-usage lxc-cpu-usage このエントリーをはてなブックマークに追加

$ npm install -g lxc-cpu-usage でLXCが稼働しているホストにインストール。

その上でコンテナ名を引数で渡してやるとそのコンテナで動いているプロセスを列挙してCPU利用率を取得してくれます。

例えば、manage001というコンテナが稼働しているのであれば

$ lxc-cpu-usage -n manage001 -v
lxc-cpu-usage - Target LXC:manage001, Target PID:9844
------------------
PID     %CPU
10002    0.01
21669    0.03
(snip)
23340    0.06
23349    0.00
------------------
Processes: 16, CPU usage: 0.16

といった具合で指定したコンテナ内で稼働しているプロセスの一覧とその合計を得ることができます。

誰得感ありますが、コンテナの稼働率見ると、動いているアプリケーションの働いている感を感じられて幸せになれます。

参照

*1:深い意味は無くて、本当に興味でみることが大半...

Hubotを定期的に動かして発言させる

Hubotでcronでスクリプトを定期的に実行させる様に定期的に動かして例えばサービスのパラメータ等を通知させるのはどうするとよいのか調べたのでその際のメモ。

node-cronというnpmパッケージがあるので、これを利用するとcronと同じ書式でスケジューリングすることができます。

以下、REMPチームで利用しているHubotを毎時0分に利用状況を通知させる様にした際の例(を少し変えたもの)です。

cronJob = require('cron').CronJob
module.exports = (robot) ->
new cronJob('0 * * * *', () ->
http = robot.http('http://remp001/****').get()
http (err, res, body) ->
if(!err)
json = JSON.parse body
robot.send {room:'remp'}, "再生回数: #{json.today_play_count}"
robot.send {room:'remp'}, "検索回数: #{json.today_search_count}"
).start()

流れとしてはソースコードそのままですが

  1. node-cronで毎時0分に稼働させるジョブを作成
  2. REMP に用意しているサービスステータス取得のAPIへGETリクエスト
  3. JSONが返ってくるのでparseする
  4. Hubotに指定したチャットルームにそのパラメータを喋らせる

これだけ。

こうするとで定期的にHubotを動かしてチャットルームにREMPの利用状況を1時間毎に通知できる様になりました。いままで、こういった通知をちょっとしたスクリプトを書いてcronに登録して通知させたりしていたのですが徐々にHubotに寄せて行こうと思います。

併せて読みたい

hideack/remp_hubot · GitHub hideack/remp_hubot · GitHub hideack/remp_hubot · GitHub このエントリーをはてなブックマークに追加

npm_lazyを使ってみた – A lazy local cache for npm

CastoSTORYBOARDSでnode.js周りのアプリケーションのデプロイをする際にnpm installされる際、稀にnpm自体が重くなっていたりすることもあるので、ローカルにnpmのキャッシュサーバ的なものを立てられないかと調べていたらお手軽にできるものを見つけたので試してみた。

これを立ちあげた上で、

$ npm install --registry http://(npm_lazyを立ちあげたアドレス指定)

とnpmコマンドを実行する際にregistryを指定することで、npm_lazy を経由してnpmコマンドを実行することができます。

npm_lazy自体は、npmを丸ごとキャッシュする訳ではなく、必要とされたライブラリ毎にキャッシュを行うのでキャッシュ期限内のものが手元にあればそれを返し、無ければnpm本体を参照して新たに特定ディレクトリ下にファイルキャッシュを生成するといったプロキシ的な動き方をしてくれるので、特別にキャッシュ用にDBを用意したりする必要もなく気軽に利用できます。

今回、CastoSTORYBOARDS の場合、VPS配下のLinuxコンテナでそれぞれアプリケーションが動いているので別コンテナにいるmanageサーバと呼んでいるコンテナで npm_lazy を動かしています。

この様な構成で動かすときは npm_lazy の設定を少し変更する必要があるので、設定を修正してそこを参照して稼働させます。

まずは設定用のファイルを出力。

$ npm_lazy --init > ~/npm_lazy.config.js

出力したconfigファイル中の

  // external url to npm_lazy, no trailing /
externalUrl: 'http://10.0.***.***:10080',
  // bind port and host
port: 10080,
host: '10.0.***.***',

辺りを適宜環境に併せて修正した上で npm_lazy を立ちあげます。立ち上げる際に --config を指定して上で修正した設定ファイルを読み込み。

$ npm_lazy --config ~/npm_lazy.config.js

立ちあげた上で、*1

$ npm install --registry http://10.0.***.***:10080/

とすることで、CastoSTORYBOARDSのアプリケーションコンテナからも手元で立ちあげた npm_lazy を参照できる様になります。

*1:実際はこれだけだとアレなので、supervisorで立ちあげて永続化しています

minaで過去にデプロイしたreleaseディレクトリを整理する

2回調べたので記録。

mina でデプロイを行った場合、以下の様にデプロイ先のホストでは管理されます。

lrwxrwxrwx  current -> releases/86/
-rw-rw-r--  last_version
drwxrwxr-x  releases/
drwxrwxr-x  scm/
drwxrwxr-x  shared/
drwxrwxr-x  tmp/

で、この releases ディレクトリには1から始まる数字のディレクトリが掘られて、デプロイする度に順次増えていきます。

単純にデプロイを繰り返しているとこれが増える一方なので、

$ bundle exec mina deploy:cleanup
-----> Cleaning up old releases (keeping 5)
Connection to 10.0.***.*** closed.
Elapsed time: 14.00 seconds

deploy:cleanup というタスクを使うことで直近の5世代分のデプロイ結果を残してそれ以外のものをデプロイ先から削除することができます。

ライブコーディングアプリ「Casto」ができるまで ep.2

もう既に@まとめエントリを書いていてくれるのですが、別の視点でまとめてみようかなと思いまして書いてみます。ということで、ep.2。

"Casto"(キャスト)は一言でまとめると「リアルタイムで更新できるGist」といった形のWebアプリケーションです。

  • ブラウザへ配信したいファイルをドラッグ&ドロップでライブコーディング開始
  • 手元のファイルを編集するだけで編集内容を同じURLを見ている全ユーザに配信

を実現しています。作り始めた経緯は@のエントリを見ていただくことにして、Casto自体はnode.jsのRendrと呼ばれるライブラリ*1とSTORYBOARDSに用意したAPIで構成されています。

サーバー, APIの構成を含め、Castoは下の様な構成になっています。

Rendr

概要と印象

今回はRendrを使ったわけですが、特徴としては、

  • フロント側、サーバ側でbackbone.jsのモデル、ビューが共用される
  • 最初のアクセス時のサーバサイドでのHTML生成がbackbone.jsのmodel, viewを使って行われる
  • ブラウザ側にサーバサイド側で作られたHTMLが描画されて以降は一般的なbackbone.jsアプリと同様の動き方をする
  • DataAdapterという仕組みがあって、modelを介してRendrサーバを経由して任意のAPIへリクエストをフォワードできる
  • ドメインが異なるアプリケーションであってもRendrが一枚入ることでアプリケーションに併せて柔軟に外部APIが制御できる

といったところが挙げられます。まだ、今回利用した範囲もRendrの一部の機能ですが、以下の様な印象を持ちました。

  • サーバーサイドのテンプレートエンジンとフロントサイドのテンプレートエンジンが共用できるのはメリット大きそう
  • 当たり前ですが、node.jsなのでJavaScriptで完結する。加えてbackbone.jsのmodel, viewがサーバサイドでも利用できるのでフロントサイド, サーバサイドの作業を統一できる
  • SPAアプリでよくある「サーバーサイドの言語でDOMの枠を作る」→「backbone.jsのViewでDOMの中を埋める」が無い

ただ、ドキュメントがWeb上に少なかったり、サンプルも少ないので最初動かすまでは少し苦労しましたし、今もブラッシュアップに四苦八苦はしています...。

サーバーの準備

構成管理と構築

REMPSTORYBOARDSを現在はVPSに集約していて、VPSの上にLXCを構築して各アプリケーションを稼働させています。それぞれのLXCはChefを利用して構成管理されています。
既にSTORYBOARDSではアプリケーション自体はRuby(Padrino)で構築されていますが、一部node.jsを導入していたため、その際に準備したcookbookを利用してさっくりとコンテナを作りました。
コンテナさえ稼働させてしまえば、フロント側に用意したnginxでプロキシさせて、LXC上で稼働させたRendrサーバにつなげることで今回稼働させるサーバの準備をそれほど手間なく準備させることができました。

デプロイ

コンテナが準備できれば、あとはRendrアプリをデプロイとなって、こちらの手段もnpmのパッケージで閉じる形にしたかったのですが、リリース世代を管理したりデプロイ手順の整備にREMPやSTORYBOARDSで利用して慣れているRubyのデプロイツールであるminaを利用しました。このあたりの話は以前のブログのエントリを参照ください。CastoでRubyを利用したのは唯一この部分のみとなります。

他のnode.jsアプリのデプロイ方法もいくつか参考にしたのですが、Capistranoを利用しているケースを散見したので、方法としては妥当だったかなと思っています。

Rendrアプリの永続化

Rendrサーバ(node.jsアプリ)を単純に立ちあげたのみだとそのプロセスが死んでしまった際にそのままとなってしまうため、foreverを利用して永続化させる様にしました。

加えて、grunt-foreverというgruntプラグインを利用すると、

grunt.loadNpmTasks('grunt-forever');
grunt.initConfig(
forever: {
app: {
options: {
index: 'index.js',
logDir: 'log'
}
}
}
});

といった設定を足すだけで、

grunt.registerTask('startProduction', ['forever:app:start']);

といったタスクを定義することができる様になって、foreverを利用したサーバ起動・停止が簡単に行える様になります。

いま持っている課題

エラーページでのテンプレートの利用

404ページを用意して、404ステータスエラーが生じた時はこの様なページを表示させようとしたのですが、エラーハンドリングを行った際に任意のビューを利用したページ表示をさせたく、index.jsで、

var server = rendr.createServer({
dataAdapterConfig: config.api,
errorHandler: function (err, req, res, next){
if (err.status == 404) {
// [TODO] ここで任意のビューを利用して404ページを描画させたい!!
}

とすればよさそうなのですが、今もRendr自体のソースを読みながら手探り中です。


と、あれこれ書きましたが、チームでRendrという新しいライブラリを利用して手早くアプリケーションが構築できて、且つメリット・デメリットも多く発見できたこと。個人的には今回初のnode.jsアプリだったのですが、そのサーバサイド側の準備からRendrを利用した際のAPI接続がまずは一通り把握できたことはとても良かったと思います。

更にブラッシュアップはチームで継続していくつもりですので、是非使ってみて感想あれば是非、twitterアカウントIssues · hikarock/casto · GitHubでお知らせください。

*1:あえて作者がフレームワークと呼んでいないのでこの呼び方で。

Rendrで作られたnode.jsアプリをminaでデプロイする

Rendrで新しくアプリを作ろうとしていて、rendr-cli
プロジェクトの雛形を作ったので、では実際にデプロイどうしようと考えた時にminaを使ってやってみたので
その際のメモ。

手順としては、

  • rendrアプリをデーモン化して稼働させるためにforeverで動く様にする
  • minaでデプロイするタスクを書く

minaのデプロイはここあたりに書いているので詳細は省略。

foreverでデーモン化する

rendr-cliで作られたアプリのベースには既にgruntを使って開発環境用のサーバを
稼働させたり、stylusをコンパイルするためのgruntタスクが既に用意されていて、開発環境であれば

$ grunt server

とすれば、一通りのコンパイルと手元でサーバ起動、そしてstyles等の更新を監視して再コンパイルをしてくれるのですが、
これをこのままproduction環境で動かす訳にはいかないので、foreverでデーモン起動、停止をする
タスクを書き足します。

まず、foreverが使える様にするためにアプリケーションプロジェクトのルートで、

$ npm install forever --save

としてforeverをインストール。 --save を付けて package.json も更新します。

次にrendr-cliが作った Gruntfile.js にforeverを使ったアプリケーション起動、停止をするタスクを足します。

// Gruntfile.js
grunt.registerTask('startProductionNode', function () {
grunt.util.spawn({
cmd: 'node',
args: ['./node_modules/forever/bin/forever', 'start', 'index.js'],
opts: {
stdio: 'inherit'
}
}, function () {
grunt.fail.fatal(new Error("forever quit"));
});
});
grunt.registerTask('stopProductionNode', function () {
grunt.util.spawn({
cmd: 'node',
args: ['./node_modules/forever/bin/forever', 'stop', 'index.js'],
opts: {
stdio: 'inherit'
}
}, function () {
grunt.fail.fatal(new Error("forever quit"));
});
});

ここまで設定すると、アプリケーションをデーモン化して起動、停止ができます。

起動は、*1

$ grunt startProductionNode
Running "startProductionNode" task
warn:    --minUptime not set. Defaulting to: 1000ms
warn:    --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
info:    Forever processing file: index.js
Done, without errors.

一方停止は、

$ grunt stopProductionNode
Running "stopProductionNode" task
info:    Forever stopped process:
data:        uid  command                                            script   forever pid   logfile                             uptime
[0] sKj9 /Users/hideack/.nodebrew/node/v0.11.11/bin/node index.js 34008   34012 /Users/hideack/.forever/sKj9.log 0:0:1:15.836
Done, without errors.

これで、Rendrアプリをデーモン化して動かせる様になりました。

minaでのデプロイ

続けて実際にデプロイする際には、リポジトリから最新のコードを取ってきてstylus等をコンパイルして上で作ったgruntタスクを利用してアプリケーションを再起動させます。

gitリポジトリから引っ張ってくるところまではRubyのアプリケーションの場合と同様ですが、アプリケーションサーバの起動のタスクは独自で書く必要があるので以下の様なタスクを書き足します。

# rendr
# =============
namespace :rendr do
task :compile do
queue 'echo "-----> Start compile tasks."'
queue! %{
      cd #{deploy_to}/current
      grunt compile
    }
end
task :start do
queue 'echo "-----> Start server."'
queue! %{
      cd #{deploy_to}/current
      grunt startProductionNode
    }
end
task :stop do
queue 'echo "-----> Stop server."'
queue! %{
      cd #{deploy_to}/current
      grunt stopProductionNode
    }
end
end
desc "Deploys the current version to the server."
task :deploy => :environment do
deploy do
# Put things that will set up an empty directory into a fully set-up
# instance of your project.
invoke :'git:clone'
invoke :'deploy:link_shared_paths'
to :launch do
invoke :'rendr:compile'
invoke :'rendr:stop'
invoke :'rendr:start'
queue "touch #{deploy_to}/tmp/restart.txt"
end
end
end

ここまで準備できれば手元から、

$ bundle exec mina deploy

でRendrアプリのデプロイ&アプリの再起動*2が行えます。

*1:warning出ているのはアプリのプロセスの監視条件を設定していないためなので設定するべきですね...

*2:ただこのままではgracefulな再起動になってないので今後直していく予定

MacOSにパッケージから入れたnode.jsをnodebrewに移行した

node.jsは公式サイトでMac用のインストーラパッケージが配布されていて非常にインストールが楽なのだけど、これをやめてnode.jsのバージョンマネージャ*1であるnodebrewを導入したのでその際のメモ。

パッケージでインストールされたnode.jsを削除する

各所で紹介されている以下の様なコマンドで削除。

lsbom -f -l -s -pf /var/db/receipts/org.nodejs.pkg.bom \
| while read i; do
sudo rm /usr/local/${i}
done
sudo rm -rf /usr/local/lib/node \
/usr/local/lib/node_modules \
/var/db/receipts/org.nodejs.*

nodebrewのインストール

これはgithubリポジトリのreadmeに従って完了するのですが一応メモとして残しておきます。

まずはインストール

$ curl -L git.io/nodebrew | perl - setup

完了したら環境変数(~/.zshrcや~/.bashrc)にパスを通す

export PATH=$HOME/.nodebrew/current/bin:$PATH

そして一度環境変数を更新

$ source ~/.zshrc

そしてここから実際にインストール。インストールできるnode.jsのバージョンを得るには

$ nodebrew ls-remote
v0.0.1    v0.0.2    v0.0.3    v0.0.4    v0.0.5    v0.0.6
(略)
v0.11.0   v0.11.1   v0.11.2   v0.11.3   v0.11.4   v0.11.5   v0.11.6   v0.11.7
v0.11.8   v0.11.9   v0.11.10  v0.11.11

こんな感じで一覧が把握できる。そして実際にインストールするには、

$ nodebrew install-binary v0.11.11
$ nodebrew use v0.11.11
use v0.11.11
$ node -v
v0.11.11

といった具合でインストールできます。*2

参照

Mac OS X から Node.js をアンインストールする方法 - SONICJAM Developerz Blog

*1:Rubyでのrbenv的な位置づけ

*2:install-binaryでバイナリからインストールするため数秒でインストール完了します。オプションをinstallとした場合はソースからコンパイルするので時間を要します。