AWSでiOS向けに動画を自前配信する方法

iOSではHLS(HTTPS Live Streaming)という規格に則らなければならない。
AWSのElastic Transcoderで変換できるようだ。
[HTML5] Amazon Elastic Transcoder で変換した HLS(HTTPライブストリーミング)形式の動画を video タグで再生する | Developers.IO
CloudFrontで配信して、videoタグでシンプルに表示できる。

PC向けもやる場合はRTMPでの配信が必要となる。
これもCloudFrontはプロトコルに対応している模様。
AWS S3 + CloudFront で動画ストリーム – Qiita

videoタグの記述例:

  <video width="320" height="240" preload="none" controls
     src="http://d18672pjq89e1s.cloudfront.net/hlsdemo/index.m3u8">
  </video>

  <video width="320" height="240" preload="none" controls>
     <source src="rtmp://s2ev6wge2z2w04.cloudfront.net/cfx/st/&mp4:PyConAPACTW2014terada.mp4” type='rtmp/mp4' />
     <source src="http://dgk03b4kg3q7d.cloudfront.net/ios/hls_400_.m3u8” type="application/x-mpegURL" />
  </video>

かかる費用の参考:

npmを宇宙に見立ててパッケージと依存関係をビジュアライズする「npm universe」

npm-university-1

npm-university-2

npm名前空間を宇宙、パッケージを星として、依存関係を線で繋ぎ描写しています。
まるで宇宙にインクをこぼしたかのような印象。
有名なパッケージ名で検索すると面白いですよ!

操作方法

  • WASDキーで移動
  • Lキーで依存関係の表示切り替え
  • Spaceキーでステアリングモードの切り替え

参考

MongoDB v2.8.0-rc0 がリリースされた

昨日の深夜に、MongoDB v2.8.0-rc0(Release Candidate) がunstableでリリースされました。Stable releaseまでいよいよ秒読み開始です。
個人的にアツイのは「Improved Concurrency」なので少し説明します。

ドキュメントレベルのロック

待ちに待ったdocument-level lockingです。
MongoDBは書き込みが弱い事が自分の中で有名(笑)ですが、それが大幅に改善されます。

MongoDB 2.6(stable)では、データベースレベルの書き込みロックです。
つまり、あるデータベースのコレクションに対して書き込みを行っている間は、同じデータベースのどのコレクションに対する読み書きもできないということです。
これがドキュメントレベルになることによって、同一のドキュメント以外なら読み書きできるようになる、という事です。

下図は、カンファレンスでのデモンストレーションの様子のキャプチャです。
同時に複数のクライアントが書き込みオペレーションを実行しています。
赤いグラフで縦軸が書き込み数です。左側の小さい山がdb-level lockingで、右の大きな山がdoc-level lockingです。
書き込みパフォーマンスが大幅に改善している事がわかります。

mongodb2.8-document-level-locking

昔は書き込みロックはグローバルだったのを考えると、やっとまともに使えるようになったなぁという感じです。

そのほかのアップデート

プラッガブルなストレージエンジン

MongoDBのストレージエンジンはあんまり性能が良くない事で評判なんですが、プラッガブルになった事でMySQLみたいに付け替え可能になりました。
これで有志による性能がより高いストレージエンジンの開発が期待されます。

圧縮

On-dick compressionによってI/O効率が30〜80%よくなるそうです。

参考

さらに詳しい情報は以下をチェックしてください。

koaをCoffeeScriptで書く

tl;dr

  • koaは、JavaScript(ES6)のgeneratorを活用したnode.js向けのwebフレームワークです
  • CoffeeScriptの開発版で、最近generatorの構文がサポートされました
  • 早速、試しにCoffeeScriptでkoaを使ってみたらちゃんと動きました

Generatorとは?

JavaScriptの機能の一つです。
JavaScriptでは非同期的な処理の結果はコールバック関数で受け取るのが通例です。
しかし、例えばネットワーク通信系の処理などでは、コールバックが多くなりすぎてコードがとても複雑になってしまう問題がありました。
Generatorは、このコールバック地獄を解決できると期待されている機能です。

以下に、node.jsによるコールバック関数を使った非同期処理を記します:

var fs = require('fs');

// カレント ディレクトリーのファイル一覧を取得する
fs.readdir('.', function(err, files) {
    // 先頭のファイルの中身を読み取る
    fs.readFile(files[0], 'utf-8', function(err, data) {
        // 読み取った結果を出力する
        console.log(data);
    });
});

みづらいですね。
しかしGeneratorを使うと:

var co = require('co');
var fs = require('fs');

co(function *() {
  var files = yield co.wrap(fs.readdir)('.');
  var data = yield co.wrap(fs.readFile)(files[0], 'utf-8');
  console.log(data);
});

シンプル!
構文的には、function*yieldキーワードが新しい要素です。

詳しくは下記の記事が分かりやすいので読んでみて下さい。

CoffeeScriptでgeneratorがサポートされた

ECMAScript 6でサポートされるgeneratorは、V8で実装されました。
V8を使っているnode.jsでも、unstableバージョンで使用出来ます。
先述の通り、このgeneratorは新しい構文が必要なため、CoffeeScriptでは使えませんでした。
しかし長い議論の末、開発版でついにgeneratorが対応になりました!finally!

上記Pull Requestによると、function内にyieldキーワードがあると、自動的にgenerator(function*)に変換されるようです。

さっそく使ってみる

node.jsはバージョン 0.11.9 以上を用意して下さい。

開発版のCoffeeScriptをインストールします:

$ npm install --save git://github.com/jashkenas/coffeescript.git

以下に簡単な例を示します(a2z.coffee):

a2z = ->
  c = 97

  while (c < = 'z'.charCodeAt(0))
    yield String.fromCharCode(c++)

g = a2z()
i = 0
console.log ++i, g.next().value
console.log ++i, g.next().value
console.log ++i, g.next().value

以下のコマンドで実行します:

$ coffee --nodejs --harmony a2z.coffee
1 'a'
2 'b'
3 'c'

使えました!

koaをCoffeeScriptで書く

koa は、node.js用の次世代Webフレームワークです。
generatorの仕組みを活用しています。
CoffeeScriptのgeneratorサポートによって、koaもCoffeeScriptで書けるようになりました。
さっそく書いてみます。

app.coffee:

koa = require 'koa'
app = module.exports = koa()

app.use (next)->
  this.body = yield (cb)->
    cb null, 'hello, world'

app.listen 3000 if !module.parent

以下のコマンドで実行します:

$ coffee --nodejs --harmony app.coffee

http://localhost:3000/ を開いてみましょう。
hello, worldと表示されたら成功です!

今後の進化が楽しみですね。

fluentd + MongoDB + Elasticsearch + Kibanaでログを可視化する

elasticsearch+kibana

SaaSは利用料が高いのでOSSを使う

サーバのログを可視化するSaaSは沢山あり、手軽にBusiness Intelligenceを施行できます。
DataDogとかKeen IOとかlibratoLogglyなどなど。
とても便利そうですね。でも価格が高い!
なんでもかんでもSaaSに頼ってたら毎月数十万とかになりそうです。貧乏にはつらいです。
だから、OSSで無料でやりましょう。
もちろんインフラ代は最低限かかります。
でも、クラウドリソースはいずれ水みたいになりますから、そこはしばらくの辛抱です。

要件

独自フォーマットのログを扱いたい

今回はApacheのアクセスログみたいな一般的なものではなく、独自のアプリケーションログを集積して可視化します。
ログデータのフォーマットは以下のようなイメージです:

{
    "logid": "Log ID",
    "level": "Log level (DBG/MSG/WRN/ERR)",
    "message": "Message",
    "module": "Name of the application module",
    "hostname": "Hostname sent the log",
    "time": "timestamp of the log",
    <Further data..>
}

このフォーマットは昔に設計したもので、解析するのを意識していなかった事情があります。

アプリケーション特化の情報も一緒に格納したい

上記フォーマットに書いてある<further data..>の部分は、アプリケーション特化の情報です。
エラーログならコールスタックとか、ユーザ登録ならユーザIDとかです。
そういう特別な情報は、可視化までしなくとも個別に見られるようにしておきたい。
また、今回の構成が将来的に変更になっても対応しやすいように、フォーマットをシステム特化で縛りたくない。

グラフ設定を簡単に柔軟に変えられるようにしたい

Graphiteとかすごく良いんですが、細かい調整がやりにくい。
タイムスケールをもうちょっと細かくみたいな、とか、ヒストグラムの分割単位を変えたいとか、そこはパイグラフで見たいとか、トレンドで見たいとか、このシリーズだけ抜き出したいとか。
あと、上記でも挙げた特化データを個別にすぐ表示できると便利です。
GrowthForecastもいいんだけど、イマドキRRDtoolかよ・・JSでグリグリ動くのがいい。

システム構成

上記の要件を満たす構成は、以下のようなイメージです。
タイトルの通り、fluentd + MongoDB + Elasticsearch + Kibanaとなりました。

log_visualization_system

以下、それぞれ概要と用途を説明します。

fluentd

ログ集積のためのツール。
PHP/node.js/Ruby/Python製などのさまざまなプログラムと連携させられます。
syslogやApache/nginxログにも対応。
それらのログを集めて、フィルタリングしたり整形した後、集積先へと転送してくれます。

MongoDB

NOSQLのDBMSです。
構造体のデータを格納できます。
スキーマレスなので、アプリケーション特化の情報をどんどん追加格納できます。
Capped collectionで高速にログを蓄積できます。

今回は、ログの一次格納先として使用します。
のちのち可視化部分のシステム構成が変更になった場合に、元のデータをオリジナルで保持しておけば対応がいくらか容易になるからです。

Elasticsearch

Luceneベースの全文検索サーバ。
MongoDBと同じく、スキーマレスで構造体が扱えます。
MongoDBからデータを流し込んで、可視化用にインデックスを作成してくれます。

Kibana

タイムスタンプがついたデータならなんでも可視化できるというツール。
Elasticsearchと密結合になっています。
UIがよくできていて、柔軟に設定を変えられるので、今回の用途にもってこいです。

Chefを使ったセットアップ手順

基本的にサーバはChefで管理したいので、Cookbookを拾ってきて使います。
以下を前提に進めます

  • MongoDBサーバはすでに立ててある
  • サーバはDebian/Ubuntu
  • インフラはEC2

手順内にsspeと出てきますが、プロジェクトのコードネームです。
ご自身のプロジェクト名と読み替えてください。

fluentdの設定

アプリケーションは、td-agent(fluentd agent)に対してhttp経由でログを投げます。
td-agentは、受け取ったログを指定のmongodbに転送します。
mongodbはreplicasetなので、typeをmongo_replsetとしています。

<source>
  type http
  port 8888
</source>

<match sspe.log.*>
  type mongo_replset
  database fluent
  collection log

  # Following attibutes are optional
  nodes mongodb-server-1:27017,mongodb-server-2:27017

  # Set 'capped' if you want to use capped collection
  capped
  capped_size 1000m

  # Other buffer configurations here
  flush_interval 10s
</match>

ElasticsearchとKibanaのインストール

1つのサーバにElasticsearchとKibanaの両方を入れます。
Elasticsearchには、以下の4つのプラグインもインストールします。

  • elasticsearch-mapper-attachments
    • elasticsearch-river-mongodbから依存
    • GridFSを取り扱うためのもの
  • elasticsearch-river-mongodb
    • MongoDBからElasticsearchにデータを流し込むためのもの
  • elasticsearch-head
    • 簡易管理UI
  • elasticsearch-HQ
    • headよりリッチな管理UI

まずはEC2ノードを立てる

Knifeコマンドでサクっと立てます。

  • OS: Ubuntu Server 14.04 LTS (HVM), SSD Volume Type
$ knife ec2 server create 
    -I ami-a1124fa0 
    -f t2.small 
    --node-name server-name 
    --security-group-ids sg-hogehoge 
    --subnet subnet-111111111 
    --associate-public-ip 
    --ebs-size 50 
    --ssh-key ssh-key-name 
    --identity-file ~/.ssh/id_rsa 
    --ssh-user ubuntu 
    --ssh-gateway ubuntu@hogehuga 
    --verbose

CookbookをChef serverにアップロードする

Berksfileに以下の行を追記します。

cookbook 'elasticsearch'
cookbook 'java'
cookbook 'kibana', git: 'git@github.com:lusis/chef-kibana.git'

なぜかKibanaだけ同名の想定とは別のものが入ってしまったので、repos uriを指定します。
インストールしてアップロードします。

$ berks install
$ berks upload

Chefノードを設定する

ノードの設定を編集します。

$ knife node edit server-name
{
  "name": "server-name",
  "chef_environment": "_default",
  "normal": {
    "tags": [

    ],
    "java": {
      "install_flavor": "openjdk",
      "jdk_version": "7"
    },
    "elasticsearch": {
      "version": "1.2.2",
      "cluster": {
        "name": "sspe"
      },
      "plugins": {
        "elasticsearch/elasticsearch-mapper-attachments": {
          "version": "2.3.0"
        },
        "com.github.richardwilly98.elasticsearch/elasticsearch-river-mongodb": {
          "version": "2.0.1"
        },
        "mobz/elasticsearch-head": {
        },
        "royrusso/elasticsearch-HQ": {
        },
        "elasticsearch/elasticsearch-lang-javascript": {
          "version": "2.1.0"
        }
      }
    },
    "kibana": {
      "config": {
        "elasticsearch": "window.location.protocol+"//"+window.location.hostname+":"+9200",
        "default_route": "/dashboard/file/guided.json"
      },
      "kibana": {
        "web_dir": "/opt/kibana/current"
      }
    },
  },
  "run_list": [
    "recipe[java]",
    "recipe[elasticsearch]",
    "recipe[elasticsearch::plugins]",
    "recipe[kibana::install]"
  ]
}

設定出来たら、chef-clientを実行します。

動作確認

以下のように結果が返ってきたら成功です。

$ curl "http://localhost:9200/_cluster/health?pretty"
{
  "cluster_name" : "sspe",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 1,
  "number_of_data_nodes" : 1,
  "active_primary_shards" : 0,
  "active_shards" : 0,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0
}

Elasticsearchの設定

インデックスの作成

Elasticsearchのインデックスとは、RDBMSでいうデータベースみたいなもんです。
下記コマンドで作成します。

curl -XPUT 'http://localhost:9200/sspe/'

マッピングの作成

マッピングとは、RDBMSにおけるスキーマ定義みたいなもんです。
明示しなくても自動で作成させる事もできますが、今回は勉強も兼ねて指定します。

$ curl -XPUT 'http://localhost:9200/sspe/log/_mapping' -d '
{
    "log" : {
        "properties" : {
            "logid" : {"type" : "string" },
            "level" : {"type" : "string" },
            "message" : {"type" : "string", "store" : true },
            "module" : {"type" : "string" },
            "hostname" : {"type" : "string" },
            "time" : {"type" : "date" },
            "userinfo": { "type": "object", "enabled": false, "include_in_all": true }
        }
    }
}
'

MongoDB riverの設定

riverとは、Elasticsearchにデータを流し込む経路設定のことです。
elasticsearch-river-mongodbをインストールしてあるので、これを使う設定をします。

$ curl -XPUT "localhost:9200/_river/mongodb/_meta" -d '
{
  "type": "mongodb",
  "mongodb": { 
    "servers":
    [
      { "host": "mongodb-server-1", "port": 27017 },
      { "host": "mongodb-server-2", "port": 27017 }
    ],
    "options": { 
      "secondary_read_preference" : true
    },
    "db": "fluent", 
    "collection": "log"
  }, 
  "index": { 
    "name": "sspe", 
    "type": "log"
  }
}'

実行すると、データの抽出が開始されるはずです。

データの流入確認

下記URLから、headプラグインの管理画面を開きます。

http://<elasticsearch-server>:9200/_plugin/head/

Screen Shot 2014-08-05 at 3.43.45 AM

ドキュメント数が増えていれば成功です。

トラブルシューティング

Cannot start river mongodb. Current status is INITIAL_IMPORT_FAILED

もし設定がうまく動かなくて、Elasticsearchを再起動してもログに上記が吐かれて止まってしまう場合。
Riverのステータスをリセットしてやれば再稼働します。

$ curl -XDELETE 'http://localhost:9200/_river/mongodb/_riverstatus'

実行後、Elasticsearchを再起動します。

Kibanaの設定

ElasticSearchとの連動確認

ESとKibanaをインストールしたサーバにブラウザからアクセスします。

http://<elasticsearch-server>/#/dashboard/file/guided.json

すると、下記のような画面が表示されるはずです。

Screen Shot 2014-08-05 at 3.40.39 AM

自分用のダッシュボードを設定する

以下にアクセスすると、空っぽのダッシュボードにアクセスできます。

http://<elasticsearch-server>/#/dashboard/file/blank.json

ADD A ROWというボタンをクリックして、グラフを表示する領域の行を追加します。

Screen Shot 2014-08-05 at 3.46.27 AM

Rowの名前を入力後、Create Rowします。

Screen Shot 2014-08-05 at 3.47.23 AM

次に、パネルを追加します。

Screen Shot 2014-08-05 at 3.48.53 AM

Screen_Shot_2014-08-05_at_3.51.10_AM

  1. パネルタイプをhistogramにします
  2. 幅を12に設定
  3. タイムフィールドにtimeを指定
  4. Auto-intervalのチェックを外す
  5. Saveボタンをクリック

すると・・・

Screen Shot 2014-08-05 at 3.51.19 AM

やった!出ましたね!
エラーログのみを表示したい場合は、QUERYの欄に level:ERR と打ちます。
クエリ構文はLuceneと基本同じものが使えるようです。以下が参考になります。

参考リンク

[iOS] アカウント情報をアプリ間で安全に共有する方法

keychain500x500

はじめに

google-642x309

Google Drive, Docs, SheetsのiOS版では、アプリ間でGoogleアカウント情報を共有しています。
いずれかのアプリで一度認証すれば、他のアプリでは面倒な認証手順が不要になります。
これはどういう仕組みで行われているのでしょう?
アプリはどのようにして、ユーザの入力無しにユーザを識別しているのでしょう。

Keychain:安全に機密情報を取り扱う仕組み

実は、先に述べたGoogleのアプリ達は、KeychainというOSの仕組みを使っています。
Keychainは、もしあなたがMac OSXユーザであればなじみ深いと思います。実はiOSにも備わっています。
これは、パスワードや秘密鍵、証明書などの機密情報を安全に管理するための仕組みです。
Keychainで保管されるデータは暗号化されます。

アカウント情報の取り扱いに最適

Keychainと同じようにユーザ情報を保存するための仕組みとして、UserDefaultsというものがあります。
UserDefaultsは、主にプリファレンスなどを保存するための仕組みです。
UserDefaultsと比べて、Keychainが機密情報の取り扱いに優れている点は以下です:

  • アプリが削除されてもデータが残る
  • 同じKeychain Access Groupに属するアプリ同士でアクセスできる (Keychain Access Groupは後ほど詳しく説明します)
  • データが暗号化される

Keychain Servicesとは

Keychain Servicesは、Keychainを使うためのAPI群です。
以下からAppleのドキュメントを参照できます:

Mac OSXとiOSではAPIが異なるので注意です。
ここでは、iOSでの使用を前提として説明します。
日本語の資料では以下が参考になります。

しかしながら上記ページは古くて、XCode上の手順が有効でない部分があります。
本ページでは、XCode 5で設定する場合を説明します。

Keychain Servicesの基本的な使い方

Framework

利用するには、Security.frameworkをプロジェクトに追加してください。

便利なラッパモジュールを使おう

Appleが用意したAPIをそのまま使うのもいいですが、我々には時間がありません。
オススメは LUKeychainAccess です。
LUKeychainAccessを使えば、NSUserDefaultsと同じ感覚でKeychainのアイテムを管理できます。
以降は、このラッパを使う前提で説明します。

基本的な使い方

NSUserDefaultsと全く同じです。

[[LUKeychainAccess standardKeychainAccess] setBool:NO forKey:@"authorized"];
BOOL authorized = [[LUKeychainAccess standardKeychainAccess] boolForKey:@"authorized"];

Keychainでのアクセス制御

制御の軸はアイテムに「いつ」アクセスできるのかと、「どれが」アクセスできるのか、の2つがあります。

Accessible属性

アプリケーションが「いつ」アクセスできるのかを制御します。詳しくはこちら
LUKeychainAccessでは、それぞれ以下の定数で指定できます。

  • LUKeychainAccessAttrAccessibleAfterFirstUnlock
    • 再起動後最初のアンロック以降/次の再起動まで
  • LUKeychainAccessAttrAccessibleAfterFirstUnlockThisDeviceOnly
    • 再起動後最初のアンロック以降/次の再起動まで
  • LUKeychainAccessAttrAccessibleAlways
    • 常にアクセス可
  • LUKeychainAccessAttrAccessibleAlwaysThisDeviceOnly
    • 常にアクセス可
  • LUKeychainAccessAttrAccessibleWhenUnlocked
    • デバイスがアンロックされた状態
  • LUKeychainAccessAttrAccessibleWhenUnlockedThisDeviceOnly
    • デバイスがアンロックされた状態

以下のように指定します。

LUKeychainAccess* keychainAccess = [LUKeychainAccess standardKeychainAccess];
keychainAccess.accessibilityState = LUKeychainAccessAttrAccessibleWhenUnlocked;

Keychain Access Group

「どの」アプリケーションがアイテムにアクセスできるのかを制御します。
デフォルトでは、アクセスできるアプリケーションはそのアイテムを格納したアプリケーション自身だけです。
グループの指定によって、同じグループに属するアプリケーションからのアクセスを許可出来ます。
このグループは、アイテムごとに指定できます。

例えば、あなたのapplication-identifiercom.foobar.applicationだとします。
あなたのアプリケーションが読み書き可能なグループは、そのグループ識別子がapplication-identifier前方一致する場合です。
グループによる権限の違いは以下の通りです。

  • アクセス可なアイテムのグループ
    • com.foobar.application
    • com.foobar
  • アクセス出来ないアイテムのグループ
    • com.apple.calendar
    • com.foobar.mailer

つまり、com.foobarというグループに属するアイテムを作成すると、アクセス出来るアプリケーションはapplication-identifiercom.foobar.*に相当するものになります。

また、注意点ですが、同じグループに属していてもアクセスできない場合があります。
それは、Code Signing Identityがアプリケーション間で異なる場合です。
つまり、アイテムを共有するアプリケーションは同一ベンダーのものでなければなりません

Keychain Access Groupの変更

Keychain Access Groupをデフォルトのapplication-identifierから変更するには、Entitlementsを作成します。
XCode 5ではこの作成手順が簡単になっています。

まず、ビルドターゲットのCapabilitiesタブを開きます。

Screen Shot 2014-07-10 at 12.02.58 AM

Keychain Sharingという項目を見つけたら、スイッチをONにしましょう。
Keychain Groupsというリストが現れたはずです。
ここに、指定したいグループの識別子を入力します。
グループは複数指定できます。
一番上に記述したものが、デフォルトで使用されるグループになります。

シミュレータにおけるグループの取り扱い

シミュレータでも動作しますが、テストは実機でやる事を強くおすすめします。
というのは、シミュレータだと常にグループがtestとなるからです。
シミュレータ上では、グループ指定に関わらず全てのアプリケーションから全てのアイテムにアクセス出来ます。

TestFlight用ビルドとデバッグ用ビルド間ではアイテムを共有出来ない

ここで、TestFlight経由でインストールしたビルドと、USB経由でインストールしたビルドのapplication-identifierを別々にすればテストできるかもしれないと思いますが、これも無理です。
理由は、Code Signing Identityが異なるからです。
共通のCode Signing Identityで、異なるapplication-identifierのアプリを用意しましょう。

アイテムのキー

基本的な使い方のセクションで説明した通り、アイテムのキーは単純な文字列で指定できます。

[[LUKeychainAccess standardKeychainAccess] setBool:NO forKey:@"authorized"];

これは以下のような単純なkey/valueストアです。

key = value

Keychain Servicesでは、この単純なキーに加えて、オプションでサービス名も付けられます。
以下のようにserviceディレクトリにkey/valueを格納していくイメージです。

service.key = value

サービス名の指定は、TwitterやFacebookの認証情報をセットにして格納したい場合に便利です。

サービス名つきアイテムへのアクセス方法

残念ながらLUKeychainAccessは、サービス名の指定にまだ対応していません。。
そこで、このラッパをフォークして対応させてみました。

サービス名対応版なら、以下のようにサービス名を指定できます。

[[LUKeychainAccess standardKeychainAccess] setBool:NO forKey:@"authorized" service:@"Twitter"];
BOOL authorized = [[LUKeychainAccess standardKeychainAccess] boolForKey:@"authorized" service:@"Twitter"];

簡単ですね!
本家にはこの拡張をプルリクエストしておきました。

自前ホストのHubotとSlackを連携させる方法

images hubot-featured

最近、チャットツールをHipChatからSlackに乗り換えました!

Slackとは?

いわゆるチャットツールです。技術者向け。
SlackはHipChatと違ってUIがリッチで分かりやすいです。オシャレ!
HipChatと同じくAPIを備えており、GitHubやAsanaなど様々なサービスと連携させる事ができます。
そして、タイトルにもあるHubotとも連携させる事ができます。

Hubotとは?

チャット上で使えるボットです。Node.jsで書かれています。GitHub社が開発しました。
ボットというとTwitterのボットを想像するかもしれませんが、基本は同じです。
ボットに向けて、決まった書式でメッセージを送ると、そのメッセージに応じた処理を行います。
例えば、Jenkinsのビルドを実行したり、アプリケーションをデプロイしたり、ネコ画像を拾ってきたりします。

Hubotを導入するメリット

従来は個別のターミナル内で行っていたタスク指示が、チャットウインドウに移ります。
この事のメリットは主に二つあります。

  1. 作業ログが残る
  2. チームとのコミュニケーションと作業が統合される

ビルドやデプロイなどは、いつ誰がやったのか残っている事が望ましいです。
また、チームワークにおいてはそのタスクが属人的にならないようにする事が重要です。
タスク実行をチャットウインドウから行う事で、直接的な作業が会話にシームレスに混ざります。
それによって、デプロイなどの手順や気をつけるべき点が自然と共有される形になります。

このようにチャットを使ったサーバ運用をChatOpsと呼びます。

Herokuではなく自前サーバのHubotと連携させたい

Slack用Hubotプラグインがあるので、書かれている手順に従えば簡単に設定できます。
日本語の情報もnanapiなどに掲載されています。
しかし、どれもHubotをHerokuにデプロイする場合の手順です。
いやいや、俺は自前サーバにHubotを稼働させる場合の手順が知りたいんだ
という訳で、このページでは自前ホストのHubotとSlackを連携させる手順について説明します。

Hubotのインストール

既に自前サーバで動作している方は読み飛ばして下さい。

  • Hubotをインストール
    $ npm install -g hubot coffee-script
    
  • botの作成
    $ hubot --create [path_name]
    $ cd [path_name]
    
  • Slack用アダプタのインストール
    $ npm install hubot-slack --save
    
  • 試しにローカルで起動
    $ ./bin./hubot
    

Slackとの連携設定

Slackの以下のページから、環境変数に設定する値を取得します。

Screen Shot 2014-07-08 at 11.15.43 PM

それぞれ、環境変数に設定します。

$ export HUBOT_SLACK_TOKEN=<token>
$ export HUBOT_SLACK_TEAM=<team>
$ export HUBOT_SLACK_BOTNAME=<bot name>

Hubotは、HTTPインターフェースも備えています。これはデフォルトで8080ですが、PORTの環境変数を指定することで変更できます。

$ export PORT=9999

ファイヤーウォールの中にいる場合は、このポートで外からhubotにアクセスできるようにしておきましょう。
EC2の場合はセキュリティグループを確認しておきましょう。
そして、あなたのHubot HTTPインターフェースへのURLをSlackのHubot URLの欄に記入します。

Screen Shot 2014-07-08 at 11.23.14 PM

これで、SlackはHubotへのメッセージをこのHTTPインターフェースに送信するようになります。
さあ、Hubotを起動してみましょう。

$ ./bin/hubot --adapter slack

Screen Shot 2014-07-08 at 11.26.54 PM

成功です!
Enjoy ChatOps!