MyCSS

2010/12/27

日経SYSTEMS 2011年1月号で紹介されました! はてなブックマークに追加

日経SYSTEMS 2011年1月号の「クラウド利用のシステム構築」という特集にて、ブログ製本サービス「MyBooks.jp」での事例を取り上げていただきました。
取材当日はいろいろとお話しさせていただきましたが、一番大きく扱ってもらったのが「画像のキャッシュ」に関する事です。AWSの米国東海岸にあるデータセンターと日本国内との距離の問題について解説されています。また、自前運用のデータベースからAWSのRDSに乗り換えた事も触れていただきました。
この特集記事ではたくさんの現場の人たちの声が聞こえてきます。「クラウド」の本格運用はまだ始まったばかりかもしれませんが、徐々に普通な事として浸透してきているなぁ、という印象を持ちました。それと同時に、みなさんそれなりに苦労されているな、とも(苦笑)

でも、クラウドに限らず何でもそうだと思いますけど、良いところを的確につかんで信頼性やコストでメリットが出るのであれば、悪いところは工夫すりゃ何とかなると。その為のエンジニアリングな訳ですし。色々難しいところはあるかと思いますが、情報インフラを「シェア」していくクラウドコンピューティングは今の潮流にも合っていると思います。

2010/11/09

Twitterアイコンのシールを作りました はてなブックマークに追加

前回のエントリで、@crown_sugarさんにTwitterアイコンを描いてもらった訳ですが、記念にPRINTAILさんにお願いしてTwitterアイコンをシールにしてもらいました。


こんな感じで、自分が40人いる訳ですが(笑)、勢い余って10枚発注した結果がこちら。


ちょっとやりすぎましたかね(汗)おそらく息子は大喜びして部屋中にシールを貼りまくる事でしょう(違)

ちなみにこれだけ作っても、送料込みで¥1,540でした。(実際は3シートからで¥980)
印刷品質も良いですし、これは大変お買い得です!

全然関係ないですけど、PRINTAILさんのお仕事はオンデマンド印刷を駆使して一般コンシューマー向けのサービスを展開されてらっしゃいます。印刷屋目線で見るとちょっと興味深いですね・・

2010/11/05

Twitterアイコンを@crown_sugarさんに描いていただきました はてなブックマークに追加

Twitterで有名なイラストレーター、@crown_sugarさんにイラストを描いていただきました!
ご存じない方は、Togetterに経緯がまとめられておりますので、ぜひご覧ください。


Twitterのアイコンですけど、初めて人と会って名刺交換する際に、TwitterのIDが併記されていると、実はフォローしていたり、フォロワーだったりと、意外に繋がっている事も多くびっくりします。(ちょっと前まではそれがブログでしたが・・)
その時に名刺にTwitterのアイコンがプリントされていたり、シールになって貼ってあったりすると一発で解ります。(ちなみに、早速PRINTAILさんにシールを発注しましたw)

@crown_sugarさん本当にありがとうございました。ソーシャルメディアを生かしてすばらしいお仕事されていると思います。今後のご活躍も期待しております!

2010/10/19

Software Design 2010年11月号に寄稿いたしました はてなブックマークに追加

Software Design (ソフトウェア デザイン) 2010年 11月号
2010年10月18日発売
B5判/216ページ
定価1,280円(本体1,219円)



「第1特集 Amazon事例 国内新サービスから学ぶクラウド時代のシステム管理[実践]基礎知識」という特集で、自分が手がけているブログ製本サービス「MyBooks.jp」とその編集ツールである「MyBooks Editor」において、Amazon Web Serviceをどう活用しているのか?という事例を6ページ程でご紹介させていただきました。

思い起こせば、個人的にクラウドの衝撃(笑)を受けてから、かれこれ一年半以上が経過しました。最近になって「クラウド」という言葉がよく聞こえる様になってきましたが、よく解らない使われ方をしているケースも見受けられますし、単なるマーケティング用語、バズワードと捉える向きもあるようです。

しかし、国内のいわゆるユーザー企業は、自分たちでクラウドを使い始めています。そして、その潜在的なパワーを身を以て体験しています。今年の始めに「中小企業こそクラウドコンピューティングの波に乗れ」という記事を書いた時には、中小企業やSOHO/ベンチャーにこそ、クラウドは効く、という思いがありました。それは今でも変わっていません。(とはいえ、まだ一年経っていない訳ですが・・思えば遠くへ来たもんだw)

なお、この特集前半では、今をときめく株式会社gumiさんの事例が載っております。1-2章はインフラご担当石川さんの執筆で、とてつもないトラフィックをAWSでさばくノウハウが掲載されておりますし、CTO堀内さんのAWSに取り組んだきっかけなど、貴重なお話が満載です。特に、今後AWSに取り組もうかな?と考えている/取り組む予定がある方は必見の内容です。

ちなみに、弊社の事例ですと、gumiさんに比べれば断然スケールが小さいですが、導入したきっかけが似ているので勝手に親近感を覚えております(笑)

なお、さりげなくAWSWatchに言及しているのは秘密です。

2010/09/03

第4回JAWS-UG勉強会でUStream配信にトライした件 はてなブックマークに追加

第4回JAWS-UG勉強会でUStream配信を担当させていただきました。
その時のメモです。



やりかた
会場は秋葉原にある富士ソフトさんのセミナールームで、席に電源と有線LANの口があって非常にVIPな会場となっております。

中継に使った機材は以下の通りです。


なんていうか、リストにする程でもないですねw

DVは既に廃盤のもので、ハードディスクに記録するタイプではなく、DVテープのタイプです。ほとんど使っていなくて勿体ないなぁ、と思っていたものを引っ張りだしました。ただ、これには2つの落とし穴がありました・・

  • 本体と電源アダプターを繋ぐDCコードが紛失。バッテリーはそれほどへたっていなかったし、テープを回す訳じゃないので二時間は持つだろうと判断。でも、かりにダメになった時の為に、現場でUSBカメラをお借りしました。(その節はどうもありがとうございます!)冗長化重要!!
  • FireWireケーブルのピンの数が合わなくて苦労しました。カメラ側は4pin、Mac側は9pin(FireWire800)で、持っているケーブルが9pin-6pin6pin-4pinという意味不明な状態でして、6pin-6pinのメス-メスアダプタなんか無いかな?とアキバの電気屋をうろつきましたが見つからず、結局6pin-4pinの変換アダプタをかませる事でなんとかなりました。



    我ながらひどいですね(汗


配信に使ったソフトウェア

事前にいくつかサイトを見て、結局UStream Producerが一番お手軽そう、という事でこれにしました。有償のPro版ですと、カメラが複数台使えるので、画質の良いDVでスクリーンを撮影しつつ、USBカメラで話者の表情をPicture in Pictureで捉える、みたいなカッコいい事が出来ますが、なにぶん初めての配信だし、円高とはいえ$199はちょっとなーw みたいな事で素直にフリー版を使いました。

一点工夫した事は、本番が始まる前は事前に作っておいたタイトル画像とアジェンダを静止画で映しておいて、始まったらDVに切り替えました。そんなもんです。私にできる事は(笑)

心配だったのは音声でしたが、会場のPAがちゃんとしているので音の伝わりがよく、DVのしょぼいマイクでも問題なかったようです。念のためモニター用にこれまた安物のヘッドフォンを持っていったのですが、予想通り現場の音とモニターの音がごっちゃに聞こえてしまってほとんど意味をなさなかったですね(笑)

Producerの使い方は本当に簡単です。DVを繋ぐと自動で認識されますので、入力をセレクトしてやるだけです。録画をしておく場合は、右下のRecordボタンを押すと、UStream側にアーカイブされます。



今後の課題
という訳で、ひとまず配信するだけならすごく簡単に出来る事が解りました。まぁ、会場は有線LANがあって、かつ音も良かったという点で非常に助かったのですが、逆にネットワークが弱くて音が拾いにくい環境だと結構辛いかもしれません。

反省点としては、やっぱり三脚は安物じゃダメだと言う事(苦笑)。カメラをパンさせるときにぎこちない動きになってしまいますし、カメラの角度が微妙に斜めになった時に直すのが大変です。(というか直らなかった)

その他、カメラの設置場所は大問題です。DVはズームが効くので会場の一番後ろの席を陣取った事は問題なかったのですが、位置が低くて前に座ってらっしゃる方の頭が映り込んでしまいました。

例えば、プレゼンが終わり質問タイムに入った時、話している人をズームしたい訳ですが、どうにも位置が低く、三脚ごと持ち上げて撮影していました(滝汗)次回の課題ですね・・

本当は固定カメラと移動カメラで切り替えたいのですが、そうすると有償版にアップグレードして、カメラも買って、マイクも・・なんて事になりかねないので非常に危険です(笑)

ちなみにJAWS-UGの勉強会参加は3回目で、うち2回は発表する側でしたから今回はゆっくり話を聞けるかな?と思ったけど大間違いでした(笑)

ともあれ、世に言うダダ漏れは非常に楽しい事が解りました。ハマりそうな予感・・



2010/08/06

b-mobile SIMを光ポータブルに突っ込んでみた件(Mac) はてなブックマークに追加


SIMフリー版の光ポータブルを申し込んで、待つ事一月半・・。ようやく手元にやってまいりました。さっそくb-mobileのSIMを差し込んでみた所、普通に繋がりましたんで報告しておきます。※NTT東日本ではb-mobileのサポートを公式にうたっておりません。ご注意ください。

Macでの設定方法
付属してきたマニュアルにはWindowsの方法しか書いておらず、Macの場合は光ポータブルをUSBで繋いで、その中にあるPDFを見よ、とあっさり書いてあります。が、マカーの皆さんはここで怯んでは行けません(笑)。ブラウザだけで設定が出来ますので簡単です。
  1. まず、b-mobile SIMの開通手続きが済んでいなければ先にやっておきます。単にパッケージに書いてある電話番号に携帯電話から電話をかけて、音声ガイダンスに従って操作するだけです。数分で終わります。
  2. つぎに、光ポータブルにb-mobileのSIMとバッテリーを装着し、電源を入れます。電源ボタンは長押ししないとダメです。この時、SIMの横に張ってあるシールにSSIDとKEYが書かれていますので、控えておきます。
  3. しばらくすると、AirMacでSSIDと同じネットワーク名が見えるので、そいつに接続します。パスワードはKEYになります。
  4. 繋がったら、ブラウザにhttp://192.168.11.1と打ち込みます。そうすると、設定画面にアクセスする事が出来ます。
  5. ログインというリンクがありますので、そこをクリックすると認証のダイアログが出ます。ユーザー名root、パスワードは空でOKです。(パスワードは後で設定する事が可能です。仮に光ポータブルを落としてしまった時などの為にも、パスワードは設定しておいた方が良いでしょう。)
  6. インターネット接続を行う(3G/HSPA)というリンクから、3Gの設定に入ります。公式サポートされているイーモバイルであれば自動で設定が入るようですが、b-mobileはパッケージに書いてある設定情報(もしくはこちらに書いてある情報)を手動で打ち込む必要があります。ちなみにPINは空白のままで行けました。
手順としてはこれだけです。簡単ですね。一応 MacBook ProiPod Touch、HT-03A(Android)で接続を確認しました。(無線LANだから当たり前だけど)

気になるb-mobileの速度
b-mobileはdocomoの回線が使えて安いけれども、回線速度が300kbpsに制限されています。これが体感的にどんなもんか?という疑問があると思いますが、巷で言われている通り、テキストベースのメールやTwitterをやってみた感じでは、通常のdocomoの3Gとの違いがまったく解りませんでした。Webページの表示は、画像が徐々に現れてくる昔懐かしい感じ(笑)で、ヘビーユースには向いていません。もちろんこれで動画を見るのは無謀です。Google Mapはもっさりしてます。
ちなみにb-mobile側でWebアクセラレーターというプロキシーが用意されていますので、こいつを通せば少しは改善します。が、サイトによってはうまく表示出来なかったりしますので、注意が必要です。


2010/07/23

フジロックのアーティスト情報を検索出来るAndroidアプリをリリースしました はてなブックマークに追加


恥ずかしながら、はじめてAndroidアプリをマーケットに出しました。
Fuji Rock Festival 2010 に出演するアーティスト情報をさくっと検索出来るアプリです。



利用シーン

  1. 会場を移動中、たまたま通りかかったステージで良い感じのアーティストが演奏していた。
  2. パンフレット持ってないし、首からぶら下げているタイムテーブルにはアーティスト名しか書いていない・・
  3. そこでこのアプリで検索!フジロックフェスティバルオフィシャルホームページのアーティスト情報を瞬時に表示!

概要
アプリを起動すると、今年出演するアーティストの一覧がズラッと表示されます。



気になるアーティストを選択すると、このようにアーティスト情報を見る事が出来ます。


はい。お分かりの通り、単にオフィシャルWebサイトのアーティスト情報ページを表示しているだけです(汗)
もう少し工夫しろよ!という感じもありますが、なにぶん思いついたのが二日くらい前でして、半年ぶりにやってみたAndroidプログラミングはきれいさっぱり忘れてしまい、半日かけてようやくここまで出来たという代物でございます。

はじめは自分用に動けば良いや、と思っていたのですが、せっかくAndroidだし、マーケットに公開しちゃえ!という事で出してみました。


ダウンロード(Android端末からアクセスしてください)


ちなみに・・
一昨年にマイブラが来る!という事で、2歳児をつれて一日だけ強行して以来の参戦です。
フジロックは99年の豊洲開催以来、相当長い事続けて行ってましたが、ここ数年はあまりの人の多さに辟易して行くのをやめておりました。

今年は運良く、Moon Caravanに当選しましたので、ゆるゆる〜とキャンプしながらまったりと過ごす予定です。

なお、フェスが開催してからのバグ修正等は不可能ですので、ご了承ください(笑)

2010/06/21

Amazon EC2は、まれに「計画停止」されます はてなブックマークに追加

Amazon Web Serviceを暫く運用していると、突然Amazon EC2 Teamより、下の様なメールが送られてきてびっくりする事があります。

Hello,

We have noticed that one or more of your instances are running on a host degraded due to hardware failure.

i-12345678

The host needs to undergo maintenance and will be taken down at 12:00 GMT on 2010-06-XX. Your instances will be terminated at this point.

The risk of your instances failing is increased at this point. We cannot determine the health of any applications running on the instances. We recommend that you launch replacement instances and start migrating to them.

Feel free to terminate the instances with the ec2-terminate-instance API when you are done with them.

Sincerely,

The Amazon EC2 Team

これは、「あなたの使っているi-12345678というEC2インスタンスは、ハードウェアの障害により2010-06-XX 12:00 GMTにターミネイトされます」というものです。
つまり、予告された時刻にAmazon側で該当するEC2インスタンスを落としてしまうという事です。

これはEC2で運用しているなかでどうしても避けられません。JAWS-UGへの投稿にもありましたが、自分は最初にこの手のメールを受け取って、かなり驚きました。

EC2の計画停止
Google App EngineやForce.comのようないわゆるPaaS環境ですと、計画停止があったとしてもこちらは何もする事がありません。(と言うか、何も出来ません。)ですが、IaaS環境であるAWSの場合、EC2インスタンスがどのように構築されているか、使う人によって様々なので、計画停止の場合の対処方法はケースバイケースになります。

ごく一般的には、

  • 警告されたEC2インスタンスは落として、事前に用意しておいたAMI(Amazon Machine Image)から新しくインスタンスを立てる

というのが一般的だと思います。・・と言う事は、AMIは必ず取っておく必要がありますね。

AMIがないと、最初から環境を構築して行くはめになります。また、取ってあったとしても、最新のAMIがなくて焦る・・なんていうケースも考えられます。(それ自分?(笑))

AMIを取っておくのは結構面倒な作業ですが、このように強制的にターミネイトされる場合もありますので、めんどくさがらず、要所要所でAMIを取っておきましょう!

この辺は以外と知られていない事実だと思います。EC2を使い始めた方々が「びっくりされないよう」に、エントリに起こしておきました(笑)

追記:
  • このメールが来てからAMIを取得し、別途起動させても接続出来なかった、という例があったようです。http://twitter.com/hashiva/statuses/16687702743 @hashivaさんありがとうございます!
  • 昔のブログにも同じ事書いてました。記憶力の減退が露呈しております。http://d.hatena.ne.jp/toolkit/20090821/p1
  • 「計画停止」と言っても、あるリージョンの該当するインスタンスだけです。当たったらラッキーだった、とでも思いましょう(笑)
関連:

2010/06/15

第2回 AWS User Group Japan 勉強会でライトニングトークしてきました はてなブックマークに追加

第2回 AWS User Group Japan 勉強会でライトニングトークしてきました。
スライドはこちらです。問題あるコマは問答無用で外してあります(笑)

今回LTをして思った事
本編の方のLTで、Amazon SNSを使った事がある人は?という事でアンケートをとりましたが、見事に0名でした。まぁ、そうは言っても、実はちょっと試したという人が数名いるとは思いますが(笑)、いずれにしても知名度が低い事はよーく分かりました。まぁ、SNSのヒトバシラーになって、自ら好んで恥をさらしたプレゼンで、皆さんに何らかの印象が残れば、それで御の字かなぁ?と思っています。

SQSに関しても150名いた会場の中で、わずか数名いらっしゃる程度で、まだまだ認知度が低いです。そもそも、この手のメッセージングAPIは用途が限られるのかもしれません。でも、このようなメッセージングシステムを間において、各システム間を祖結合に作ると、相互依存性が無くなる為にシステム全体がシンプルになるだけではなく、運用がとても楽になるというのが一番のメリットなんじゃないかな?と考えています。

さらにいうと、AWSはEC2とS3だけじゃないという事を、もっと知ってもらいたいです。1 AWSユーザーとしての経験からすると、IaaSの領域を超えた周辺のサービスは、非常にシンプルだけれどもポイントを押さえて作ってあるので、実戦投入させやすいです。

例えばSQSは、非同期のシステムを作りたいというニーズに即座にマッチします。RDSというMySQLサーバーを即座に使えるサービスは、今まで運用管理で苦しめられていた人達を解放するサービスです。(私はまだ解放されていませんが、これから解放される予定ですw)

なぜ、Amazonの中の人でもないのに、宣伝みたいなことを言うのか?というと、単純に利用者が増えると、規模の経済が働き単価が下がる可能性が大きいからなんです。実際、AWSは日に日に機能が充実していってますが、過去一度も値上がりした事がありません。これからも無いとは言い切れないですけれど(笑)。この点は、利用者にとって最も嬉しいポイントじゃないでしょうか?

そうはいっても、SNSを使って10万通メール送って、2ドルですから、これ以上安くなっても・・ってな話もありますが(笑)

Amazon SNSの事に関しては、別のエントリを起こします・・

しかし、前回もそうでしたけど、富士ソフトさんのこの会場は素晴らしいです。各テーブルに電源とLANがあるなんて素晴らしすぎます。今回はさすがにiPad使ってらっしゃる方が大勢いましたね。次回はさらに増える予感。
※ちなみに、会場の無線LANは混雑するので、LANケーブルで繋いだ方が快適ですよ。

今回は日経BP社 中田さんのプレゼンがすごく良かったです。2010年7月7日号の日経コンピュータは楽しみです。
また、米国よりエバンジェリストのJeff Barrさんも講演をしてくれました。トイレですれ違った時も、あのスマイルでした!


当日のUstream録画はこちらです。

2010/06/01

Amazon SNS自腹実験人柱サイト、AWSWatch をリリースしました。 はてなブックマークに追加

Amazon Web Serviceを監視して、異常があったらAmazon Simple Notification Serviceを通してメールで通知するサイト、AWSWatchを作ってみました。 ロゴがいかにもやっつけでダッサイですけど。

作った動機
あまり大きな声で言いたくないのですが(笑)、2010年5月、立て続けにUS-EASTで障害が発生しました。


一発目はゴールデンウィーク中の5月4日で、こちらのブログでも言及されている通り、一部のEC2インスタンスが結構長い間ストップしてしまいました。個人的にも仕事で使っているインスタンスのうち1台が、外部からの強制リブートがかかった形跡を確認しています。

通常サービスを立てる際は、サーバーの監視というのをやると思いますけど、仮にトラブルが発生した際、原因がアプリケーションなのか、OSなのか、それとももっと低レイヤーのハードウェアなのか?という切り分けをするかと思います。そう言った事をクラウドでやろうとすると、ハード的な部分はそもそも仮想化されているので、直接見る事が出来ません。見えたところで、どうしようもないのですけど、基本的にはクラウドベンダーが公開する障害情報を頼りにするしかありません。

AWSであれば、Service Health Dashboardで随時確認する事が出来ます。ですがこのサイト、ぱっと見キレイにまとまって見やすい感じがしますが、いざ問題があった時、時系列を追うのが結構厳しいです。基本的にはRSSフィードを見てね、という作りでちょっと不親切です。また、時間表記が太平洋時間だったりしますので、日本人的には結構つらいものがあります。だいたい、障害があった時は、基本的に「心の余裕」がありませんので、この作りはあまりいただけません・・。

というわけで、
  • 障害があったらメールを受け取りたい
  • 自分の好きなタイムゾーンで時刻を表示したい
という事がやりたくて、AWSWatchを作りました。

Heroku
開発は帰宅してから空いた時間でボチボチと作っておりました。Amazon SNSのRubyクライアントを作る所から始めたので、なんだかんだで二週間くらいかかりました。完全に個人で運用するサイトですので、最初はお金のかからないGoogle App Engineを使おうかと思いましたが、一番手になじんでいるのがRailsですし、以前から興味があったHerokuを使ってみようと思い、あまり深く考えずにチョイスしました。

このHerokuですが、RubyでWebアプリが書ける人であれば、即利用可能です。herokuのGemをインストールして、gitが使えれば、こんな感じでサービスを公開出来ます。
gem install heroku
rails awswatch
cd awswatch
git init
git add .
git commit -m "First Release!"
heroku create
git push heroku master
gitでpushした時点でdeployが完了した事になります。

マイグレーションは
heroku rake db:migrate
consoleは
heroku console
とやれば、
script/console production
をやっている事になります。スバラシイ!!

Herokuに関しては、この辺を参考にさせていただきました。

個人的にここが素晴らしい!と思った点は、
  • 基本的に無料
  • Gemを入れるだけという気軽さ
  • Deployが簡単
  • SSLが無料で使える
  • 独自ドメイン使用可能
  • Add-onが充実している

特にAdd-onに関しては、
  • New Relicというアプリケーションモニターが利用可能
  • Exceptionalという例外を通知してくれるサービスが利用可能
  • Amazon RDSも利用可能!(RDSの利用料はかかります)
あたりが個人的にポイント高いです。

ただし、無料の範疇だとスレッドは1つですので、トラフィックが多いサイトになると厳しいです。その辺りが気になる方は、今のところGoogle App Engineが最強か?と思います。(Rubyだと厳しそうですが・・)

Amazon Simple Notification Service(Amazon SNS)
2010年4月5日に、Amazon Simple Notification Service(Amazon SNS)というサービスがリリースされました。(参考:アマゾン、クラウドサービスにメッセージ送信機能「Amazon SNS」を追加 - CNET Japan

Amazon SNS・・・実に誤解をはらみそうなネーミング(笑)ですが、SQS同様メッセージングサービスという事で、かゆいところに手が届く的なサービスになっています。

SNSとSQSの違いは、プッシュとプルの違いでしょうか。SNSはメッセージをEMail、HTTPなどに配信します(プッシュ)。SQSは、メッセージをQueueに取りに行く必要があります(プル)。SNSで真っ先に思いつくところとしては、メールの一斉配信です。これを自前でやろうとしたら大変な事は容易に想像出来ますが、その大変な部分をAmazonが肩代わりしてくれるという、非常にAmazonらしいサービスになっています。しかも、例によって激安です。メールは10万通送信しても、たった2ドルです。

この部分だけを見ると、スパマーにもってこいのサービスじゃね?と一瞬思ってしまいますが、そこはちゃんとしています。ユーザーがメールの配信を希望する(Subscribe)と、SNSは、ユーザーに確認メールを自動送信します。そのメールの中に書いてある確認用のリンクをクリック(Confirm)すると、はじめてメールが送信されるようになります。



まだ出たばかりのサービスですので、API的にもちょっとこれは・・という点がなきにしもあらずですが、それは今後の充実に期待!という事で。SNSに関しては、また追って記事を書きたいと思います。

突っ込み所が満載な件
という訳で、サービスはHerokuに置いています。識者の方はここで「?」となるはずです。
公開前日に発覚した、驚愕の事実がございます(笑)


Herokuが完璧なまでにEC2に乗っかっておりました!知りませんでした !(^^)! 事前に調べておけよ、って感じですが、HerokuにAmazon RDSのAdd-onがある時点で「?」と思うべきでしたね。監視のサービスが落っこちたら監視になりませんので、別のインフラに載せるのがエンジニアの嗜みですけど、最初から躓いてますね・・まぁ、そのうち気が向いたらGoogle App Engineにでも移行して、反省の意を表明したいと思います(笑)。でも、そもそもメール通知にSNSを使っている時点で企画倒れな様な気もしますね・・。

2010/05/19

Amazon SNSのRubyライブラリを(適当に)つくりました はてなブックマークに追加

Amazon Web ServiceのSimple Notification Serviceですが、Rubyのライブラリが見つからなかったので、適当に作りました。元ネタはRightScale提供のRightAwsで、これをSNS対応させた形です。

ソースを張っておきますので、ご自由にお使いください。動作無保証です。未実装のメソッドもあります。テストも不完全です。無い無い尽くしでアレですが、ご自身の責任範囲でお使いください。

本来であればGitHubからForkさせるべきなんでしょうけど、使い方を把握していないので・・すみません・・

※注:すでに同じ様な事やってらっしゃる方がいました。http://github.com/bemurphy/right_aws/blob/add_sns/lib/sns/right_sns_interface.rb

require "right_aws"

module RightAws

  class SnsInterface < RightAwsBase
    include RightAwsBaseInterface

    DEFAULT_HOST      = 'sns.us-east-1.amazonaws.com'
    DEFAULT_PORT      = 443
    DEFAULT_PROTOCOL  = 'https'
    DEFAULT_PATH      = '/'
    API_VERSION       = '2010-03-31'

    @@bench = AwsBenchmarkingBlock.new
    def self.bench_xml; @@bench.xml;     end
    def self.bench_sns; @@bench.service; end

    # Creates new RightSns instance.
    # 
    # Example:
    #
    #  sns = RightAws::SnsInterface.new(aws_access_key_id, aws_secret_key)
    #
    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
      params.delete(:nil_representation)
      init({
          :name                => 'SNS',
          :default_host        => ENV['SNS_URL'] ? URI.parse(ENV['SNS_URL']).host    : DEFAULT_HOST,
          :default_port        => ENV['SNS_URL'] ? URI.parse(ENV['SNS_URL']).port    : DEFAULT_PORT,
          :default_service     => DEFAULT_PATH,
          :default_protocol    => ENV['SNS_URL'] ? URI.parse(ENV['SNS_URL']).scheme  : DEFAULT_PROTOCOL,
          :default_api_version => API_VERSION
        },
        aws_access_key_id     || ENV['AWS_ACCESS_KEY_ID'],
        aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'],
        params)
    end


    #--------------------
    #      APIs
    #--------------------

    def add_permission()
      raise 'Sorry! Not Implemented!'
    end
    
    def confirm_subscription()
      raise 'Sorry! Not Implemented!'
    end
    
    def set_topic_attributes(topic_arn, attribute_name, attribute_value)
      request_params = {'TopicArn' => topic_arn, 'AttributeName' => attribute_name, 'AttributeValue' => attribute_value}
      link   = generate_request("SetTopicAttributes", request_params)
      result = request_info(link, QSnsSetTopicAttributesParser.new(:logger => @logger))
      return result
    rescue Exception
      on_exception
    end
    
    def get_topic_attributes(topic_arn)
      request_params = {'TopicArn' => topic_arn}
      link   = generate_request("GetTopicAttributes", request_params)
      result = request_info(link, QSnsGetTopicAttributesParser.new(:logger => @logger))
      return result
    rescue Exception
      on_exception
    end

    def create_topic(topic_name)
      request_params = {'Name' => topic_name}
      link   = generate_request("CreateTopic", request_params)
      result = request_info(link, QSnsCreateTopicParser.new(:logger => @logger))
      return result
    rescue Exception
      on_exception
    end

    def delete_topic(topic_arn)
      request_params = {'TopicArn' => topic_arn}
      link   = generate_request("DeleteTopic", request_params)
      result = request_info(link, QSnsDeleteTopicParser.new(:logger => @logger))
      return result
    rescue Exception
      on_exception
    end

    def list_topics(next_token = nil )
      request_params = {'NextToken' => next_token}
      link   = generate_request("ListTopics", request_params)
      result = request_info(link, QSnsListTopicsParser.new(:logger => @logger))
      # return result if no block given
      return result unless block_given?
      # loop if block if given
      begin
        # the block must return true if it wanna continue
        break unless yield(result) && result[:next_token]
        # make new request
        request_params['NextToken'] = result[:next_token]
        link   = generate_request("ListTopics", request_params)
        result = request_info(link, QSnsListTopicsParser.new(:logger => @logger))
      end while true
    rescue Exception
      on_exception
    end

    def subscribe(topic_arn, protocol, endpoint)
      request_params = {'TopicArn' => topic_arn, 'Protocol' => protocol, 'Endpoint' => endpoint}
      link   = generate_request("Subscribe", request_params)
      result = request_info(link, QSnsSubscribeParser.new(:logger => @logger))
      return result
    rescue Exception
      on_exception
    end

    def unsubscribe(subscription_arn)
      request_params = {'SubscriptionArn' => subscription_arn}
      link   = generate_request("Unsubscribe", request_params)
      result = request_info(link, QSnsUnubscribeParser.new(:logger => @logger))
      return result
    rescue Exception
      on_exception
    end

    # return {:subscritions => []}
    def list_subscriptions(next_token = nil )
      request_params = {'NextToken' => next_token }
      link   = generate_request("ListSubscriptions", request_params)
      result = request_info(link, QSnsListSubscriptionsParser.new(:logger => @logger))
      # return result if no block given
      return result unless block_given?
      # loop if block if given
      begin
        # the block must return true if it wanna continue
        break unless yield(result) && result[:next_token]
        # make new request
        request_params['NextToken'] = result[:next_token]
        link   = generate_request("ListSubscriptions", request_params)
        result = request_info(link, QSnsListSubscriptionsParser.new(:logger => @logger))
      end while true
    rescue Exception
      on_exception
    end

    # return {:subscritions => []}
    def list_subscriptions_by_topic(topic_arn, next_token = nil )
      request_params = {'TopicArn' => topic_arn, 'NextToken' => next_token}
      link   = generate_request("ListSubscriptionsByTopic", request_params)
      result = request_info(link, QSnsListSubscriptionsParser.new(:logger => @logger))
      # return result if no block given
      return result unless block_given?
      # loop if block if given
      begin
        # the block must return true if it wanna continue
        break unless yield(result) && result[:next_token]
        # make new request
        request_params['NextToken'] = result[:next_token]
        link   = generate_request("ListSubscriptionsByTopic", request_params)
        result = request_info(link, QSnsListSubscriptionsParser.new(:logger => @logger))
      end while true
    rescue Exception
      on_exception
    end

    def publish(topic_arn, subject, message)
      request_params = {'TopicArn' => topic_arn, 'Subject' => subject, 'Message' => message}
      link   = generate_request("Publish", request_params)
      result = request_info(link, QSnsPublishParser.new(:logger => @logger))
      return result
    rescue Exception
      on_exception
    end


    #--------------------
    #      Requests
    #--------------------

    def generate_request(action, params={}) #:nodoc:
      generate_request_impl(:get, action, params )
    end

    # Sends request to Amazon and parses the response
    # Raises AwsError if any banana happened
    def request_info(request, parser)  #:nodoc:
      request_info_impl(:sdb_connection, @@bench, request, parser)
    end

    
    #--------------------
    #      PARSERS:
    #--------------------

    class QSnsCreateTopicParser < RightAWSParser #:nodoc:
      def reset
        @result = {}
      end
      def tagend(name)
        case name
        when 'TopicArn'  then @result[:topic_arn]  =  @text
        when 'RequestId' then @result[:request_id] =  @text
        end
      end
    end

    class QSnsDeleteTopicParser < RightAWSParser #:nodoc:
      def reset
        @result = {}
      end
      def tagend(name)
        case name
        when 'RequestId' then @result[:request_id] =  @text
        end
      end
    end

    class QSnsListTopicsParser < RightAWSParser #:nodoc:
      def reset
        @result = { :topics => [] }
      end
      def tagend(name)
        case name
        when 'NextToken'  then @result[:next_token] =  @text
        when 'TopicArn'   then @result[:topics]     << @text
        end
      end
    end

    class QSnsSubscribeParser < RightAWSParser #:nodoc:
      def reset
        @result = {}
      end
      def tagend(name)
        case name
        when 'SubscriptionArn' then @result[:subscription_arn] =  @text
        when 'RequestId' then @result[:request_id] =  @text
        end
      end
    end

    # same with QSnsDeleteParser...
    class QSnsUnubscribeParser < RightAWSParser #:nodoc:
      def reset
        @result = {}
      end
      def tagend(name)
        case name
        when 'RequestId' then @result[:request_id] =  @text
        end
      end
    end

    class QSnsListSubscriptionsParser < RightAWSParser #:nodoc:
      def reset
        new_member()
        @result = {:subscriptions => [] }
      end
      def tagend(name)
        case name
        when 'NextToken'  then @result[:next_token] =  @text
        when 'TopicArn'   then @member[:topic_arn]  =  @text
        when 'Protocol'   then @member[:protocol]   =  @text
        when 'SubscriptionArn'   then @member[:subscription_arn] =  @text
        when 'Owner'   then @member[:owner]         = @text
        when 'Endpoint'   then @member[:endpoint]   =  @text; @result[:subscriptions] << @member; new_member();
        end
      end
      private
      def new_member
        @member = {:topic_arn => '', :protocol => '', :subscription_arn => '', :owner => '', :endpoint => ''}
      end
    end

    class QSnsPublishParser < RightAWSParser #:nodoc:
      def reset
        @result = {}
      end
      def tagend(name)
        case name
        when 'MessageId' then @result[:message_id] =  @text
        when 'RequestId' then @result[:request_id] =  @text
        end
      end
    end

    # same with QSnsDeleteTopicParser...
    class QSnsSetTopicAttributesParser < RightAWSParser #:nodoc:
      def reset
        @result = {}
      end
      def tagend(name)
        case name
        when 'RequestId' then @result[:request_id] =  @text
        end
      end
    end

    class QSnsGetTopicAttributesParser < RightAWSParser #:nodoc:
      def reset
        @result = {:attributes => []}
      end
      def tagend(name)
        case name
        when 'key' then @key = @text
        when 'value' then @result[:attributes] << {:key => @key, :value => @text}
        when 'RequestId' then @result[:request_id] =  @text
        end
      end
    end
  end

end

2010/05/01

シューゲイザー・ディスク・ガイド はてなブックマークに追加


この本の存在は、ナタリーの記事で知りました。仕事中に「たまたま」見つけまして、おもわず「うそ!」と声を上げてしまった事は内緒です。

なんで2010年にもなって「シューゲイザー」??っていうか、今の若いもん(苦笑)に言っても通じないんじゃ?と、すっかり音楽の世界に疎くなってしまったアラフォーとしては、驚くとともに、嬉しいような、恥ずかしい様な、近年あまり経験した事の無かった感情(?)が渦巻いてしまいました。

Lovelessを彷彿とさせる表紙、帯には明らかにSwervedriver、Adorable、PaleSaints・・おぉ、我が青春の90'sのジャケットが映っているではありませんか。

当時のシューゲイザーは「マッドチェスター」や「ブリッドポップ」「グランジ」の陰に隠れていた様な気がしたのですが、どうもそうではなかったようです。

2008年フジロックでのマイブラ、当然行きました。まだ小さかった息子をおんぶして・・もちろん、耳を保護するイヤーマフ装着で。はじめのうちはおとなしく、キッズゾーンに敷物を敷いて遠巻きに見ておりましたが、あまりにもパフォーマンスがまばゆくて、「すまん、父ちゃん、行ってくる」と母子を残し、前方に駆け寄った記憶が(笑)

フジロックそのものは豊洲で行われた第二回から、初の苗場開催、第三回の伝説の風呂なし斜度20度ゲレンデキャンプ三日間(笑)を経て、もう何回行ったか忘れてしまいましたが、すっかり夏フェスも定着するにつれて人も多くなり、もうハナから行くのはヤメにしておりました。ですが、マイブラは別格ですね。

本の中身ですが、これを編集するのは多大な苦労をされたのではないかと推察します・・。(@otoan69様がすばらしい仕事をされております)本当にこれでもか!という位のディスクレビューの応酬で、編集者の気迫すら感じます。資料としても、非常に貴重なものとなっています。ある意味、万人には勧められないけれども、これから「ワウを半開きにしよう」とか、「トレモロをdepth100%にしよう」とか、「ワーミーペダルを踏もう」としている若人がいたら、「この本に紹介されているアルバムを最低10枚聴いてからにしなさい」と言うでしょう・・って、ほとんど何を言っているのかよくわかりませんが、その位充実している内容です。これが1,900円って安すぎる・・。

貴重なビリンダお姉様のインタビューもありますし、ケヴィンのエフェクターボードの写真も載っています。(楽器屋のエフェクター売り場か!状態です)

さらに、現役シューゲイザー必見のエフェクターガイド。我が家にはなぜかPROCO RAT2
が2つもあり、さらには全然使えないで有名なBOSSのDF-2もあるんですが、やはり両方とも載っていましたね(笑)
ちなみに若かりし頃は、YAMAHAのSPX-900のリバースゲートの代わりに、おなじくYAHAMAのFX-500B(ベース用)のリバースアーリーリフレクションをWet100%で使用しておりました。あぁ、懐かしい。

しかし、来年はLoveless発売から20年ですね。もう、これだけで驚きというか、10代の音楽やっている子たちからすると、それってすでにクラシック?という領域ですもんね。本当にびっくりします(笑)

Chapterhouse、Ulrich Schnaussの来日 + 我らがLuminous Orangeの公演は予想通り仕事で行けませんでしたが、アラフォーの分際で今からシューゲイズするのも悪くないかな?いや、迷惑かな?とか、悶々と考えたりするのでした・・


2010/04/13

QCon Tokyo 2010にてライトニングトークをします はてなブックマークに追加

先日のAWS勉強会でAmazon SQSの活用事例を発表しましたが、QCon 東京 2010 カンファレンスでのJAWSのセッションでもお話しさせていただく事になりました。※JAWSとは、Amazon Web Serviceのユーザーグループの事です。

QCon Tokyo 2010でJAWSセッション開催。

前回は時間が5分という中で、SQSを活用する上でのポイントのみを凝縮してお話しした感じでしたが、今回は多少お時間を頂きましたので、もう少し詳しく突っ込みたいと思います・・と思いましたけど、SQS自体、非常にシンプルなものなので、話をどう拡張させるかは当日のお楽しみという事で・・おそらくテクニカルな内容よりも、もっと別な話になるかもしれません・・。

JAWSのセッションでは、あのWakameのお話もじっくり聞けます!気になる方は多いのではないでしょうか?(かくいう私もその一人)Wakameは今後AWSにとって重要なツールになると思いますので、Don't miss it!です。

さらに、19日(月)のカンファレンスパーティーにもJAWSのブースがあるという事ですので、自分はその辺をウロウロしていると思います。見かけたら声をかけていただけると喜びますので、よろしくお願いします(笑)。

・・・しかし、そうそうたるメンバーの中にスピーカーとして混ぜてもらうだけでかなりガクブルものです。GoFの一人であるErich Gamma氏なんかほとんど神だし(笑)、我らがMatz氏もいたりという中で、あんた誰?って感じですが、面も割れてしまった事ですし、ここは一つ楽しんできたいと思います。

なお、イベント自体は有料のカンファレンスになっています。今からチケットを買えば3万円という結構セレブなお値段になっちゃいますが、なんと、JAWS登録メンバーだけの特別ディスカウントというものがあります!詳しくはリンク先を見ていただくとして、これを機にJAWSメンバーになってQConに行って、AWSユーザーをスケールアウトさせましょう!

2010/04/08

第1回 AWS User Group Japan 勉強会でライトニングトークしてきました はてなブックマークに追加


第1回 AWS User Group Japan 勉強会でライトニングトークしてきました。
スライドはこちらです。微妙に問題ありそうなコマは外してあります(笑)

DropBoxの正しい使い方(笑)
第0回にも参加したかったのですが、気がついたときには既に人数オーバーで参加出来ませんでした。今回もすごい勢いで参加者登録があったようで、注目度の高さがうかがえます。会場は150名という事でしたが、満席でした。すごい・・。

自分は発表者の立場だった訳ですが、実は当日ちょっとしたトラブルがありまして、自分の出番前の方々のプレゼンをよく見る事が出来ませんでした。ってのは、実にしょーもない事なんですが、肝心のスライド資料をDropBoxに置いたのがまずかった!会場の無線LANにトラブルがあり、最新版のスライドデータを取ってくる事が出来なかったという(笑)

会場の富士ソフトさんの会議室には有線のLANポートがあり、ネットワークケーブルさえあればOKという事だったんですけど、あいにく持ち合わせておらず、かといってヨドバシまでひとっ走りするか!?ってのも時間的に難しい訳で・・
ただ、幸運にも一つ前のバージョンがローカルにあったので、そこから最新版に作り替える作業を出番前まで黙々とやっていた訳です。
http://twitter.com/dateofrock/status/11691139715

でも、幸運な事に、出番直前になって無線LANが回復して事なきを得ました。
http://twitter.com/dateofrock/status/11691422756

DropBoxがローカルにファイルをコピーしているという仕様が、実に素晴らしいという事に気がつきました(笑)。AWS使ってますし、完璧ですね!(何が!?)

発表について
5分間でSQSの説明と実例紹介を詰め込んだので、相当早口だったと思います。スミマセン!また、皆さんインフラよりの話が多い中、自分だけ思いっきりアプリの話で、しかもかなりベタなSQSの使い方の紹介でしたので、思わず「K」と「Y」の文字が浮かびましたが、大体笑いが取れたので良しとしました(?)

話の中身ですが、このブログで書いてある事そのものです。よろしければご覧ください。

自分はアプリ側の人間なので、インフラ系のコアな話について行けない部分も多々あり、まだまだ勉強不足な事を痛感・・。その点、takashi.hashibaさんが発表されていたCloudworksは、自分の様な立場の人間に取って嬉しい限りです。しかも無料サービスとは・・。感動したので、早速試しています。

その後の懇親会でもいろいろとお話しさせていただき、勉強になりました。そして、何よりもAWS・・というか、クラウドの勢いみたいなものを感じました。まさに旬ですね。

コアスタッフの方大変おつかれさまでした。貴重な場を作っていただき感謝申し上げます。次回はなんと会場がスケールアウトするそうなので、さらに盛況になる事が予想されます。いろいろスゴいよ!!たのしみ!!

今回の教訓
  • オフラインでもどうにでもなるように準備しておくべし!
  • ネットワークケーブルを持ち歩くべし!

2010/03/23

Amazon EBSとRightAwsを使って自動バックアップ環境を構築する はてなブックマークに追加

Amazon EBSとは、EC2にアタッチする事の出来る外部ボリュームです。以下に素晴らしい解説記事があります。
要は外付けハードディスクなのですが、魅力的なのがボリューム毎に丸ごとバックアップが取れるという点です。これを定期的に実行しておけば、いざという時のバックアップになりますので、夜も良く眠れますw
※ちなみに、上記記事ではコマンドラインでの操作となっていますが、AmazonのGUIコンソールからもボタン一発でスナップショットが取れます。

バックアップは定期的に取らないと意味が無いので、自動化したい所です。自動バックアップの記事はいくつか見つけましたが、Ruby版が見つからなかったので自分で作りました。そのメモを残しておきます。

参考にさせていただいた記事:

やりたい事
定期的にEBSのバックアップを取る。その際、履歴を残しておく。古い履歴は自動削除されるようにする。

やった事
上記の参考にさせていただいたスクリプトをもとに、Ruby版を考えてみました。
RubyでAWSのAPIにアクセスするためには、RightAwsが主流だと思いますので、そちらを使います。

gemで配布されていますので、

sudo gem install right_aws

でインストール完了です・・と言いたい所ですが、これだと最新版が落ちてこないので、手動でgemパッケージを作って、ローカルからインストールしました。

git clone http://github.com/rightscale/right_aws.git
cd right_aws
rake gem
sudo gem install pkg/right_aws-1.11.0.gem

gemコマンドでインストール出来るバージョンは、サポートするAWS APIのバージョンがちょっと古めです。別にそれでも問題はありませんが、新しいAPIですとスナップショットにdescriptionを残す事が可能になります。
「このスナップショットはどのボリュームのスナップショットなのか?」
という事をdescriptionにメモっておけば、管理が大変楽になります。

スクリプト本体
require 'rubygems'
require 'right_aws'

AWS_ACCESS_KEY = 'XXXXXXXX'
AWS_SECRET_KEY = 'YYYYYYYY'

unless ARGV.size == 3 then
  puts 'Usage: ebs_snapshot.rb <num> <volume-id> <volume-label>'
  exit 0
end

history   = ARGV[0].to_i # 履歴はいくつ残しておくか?
vol_id    = ARGV[1]      # スナップショットを取るEBSのボリュームID
vol_label = ARGV[2]      # スナップショットのメモ(description)

# descriptionを生成
# この場合だと、vol_labelに時刻をくっつけたものになります
t = Time.now
desc = "#{vol_label} #{t.strftime('%Y/%m/%d %H:%M:%S')}"

# スナップショットを取る
ec2 = RightAws::Ec2.new(AWS_ACCESS_KEY, AWS_SECRET_KEY)
ec2.create_snapshot(vol_id, desc)

# 保存してあるスナップショットで、目的のボリュームIDを持つものだけのリストを作る
snapshots = []
ec2.describe_snapshots.each do |snapshot|
  if vol_id == snapshot[:aws_volume_id] then
    snapshots << snapshot
  end
end

# 履歴分を残して古いスナップショットを削除
snapshots = snapshots.sort_by { |snapshot| snapshot[:aws_started_at] }.reverse
snapshots.each_with_index do |snapshot, i|
  unless i < history then
    ec2.delete_snapshot(snapshot[:aws_id])
  end
end

もし、自前でgemを作るのが難しい場合は、descriptionをサポートしないAPIになってしまいますが、上記のスクリプト23行目の
ec2.create_snapshot(vol_id, desc)

ec2.create_snapshot(vol_id)
にしてあげれば動作します。 これをcronに仕込んでやればOKです。
sudo crontab -e

*/30 * * * * /path/to/ebs_snapshot.rb 3 vol-12345678 "hogehoge data volume"
この設定ですと、ボリュームIDが「vol-12345678」のものを、30分おきにバックアプする事になります。

2010/03/18

nanorcを一発インストール はてなブックマークに追加

Linux環境でのテキストエディタはviかEmacsか?という感じですが、自分はヘタレプログラマーなので、GNU nanoを愛用しています。
コードを書くのはもっぱらIDEやGUIのエディタなので、nanoを利用するのは、サーバー上の設定ファイルを書き換えたりするくらいです。
でも、そうはいっても、何も設定していないデフォルトの状態で作業するのも結構な苦行ですし、複雑な設定ファイルだとテキストの色づけが欲しくなってきます。

nanorcについて
nanoの初期設定は/etc/nanorcあたりに入っていると思いますが、自分自身の設定を作りたい場合、ホームディレクトリに.nanorcを置けば良い事になっています。(設定の内容は試験運用中なLinux備忘録 nanoの設定に解りやすく書かれています。)

色づけに関しては、デフォルトでいくつかサンプルが入っている場合も多いのですが、正直言って足りません。だからといって、自分で書くのはしんどいので避けたい所ですが、みんな大好きGoogle Codeに大量のnanorcを発見しました。今回ありがたく使わせてもらったので、その作業をメモっておきます。(置いてくれた人どうもありがとう、ってここで叫んでも気がつかないか。)

nanosyntax - Project Hosting on Google Code

nanorcを取り込む
単純に欲しいnanorcをダウンロードして、それを~/.nanorcにincludeしてやれば良いだけです。たとえば、Ruby用のnanorcを~/.nanorc.d/に保存した時は

include "~/.nanorc.d/ruby.nanorc"

と~/.nanorcに書いてあげれば、Rubyのファイルを編集するときこのように幸せになれます。

nanosyntax - Project Hosting on Google Codeには大量のnanorcがあるので、基本的には必要なものだけを取り込むべきでしょうけど、いちいち見極めるのも面倒なので、こんなスクリプトを書きました。これで一発でダウンロードと設定が完了します。

cd ~
mkdir .nanorc.d
wget -A nanorc -l 1 -r -nd -np -P .nanorc.d http://nanosyntax.googlecode.com/svn/trunk/syntax-nanorc/

# エラーになるファイルを除外
rm ~/.nanorc.d/aptsrclist.nanorc
rm ~/.nanorc.d/sources_list.nanorc
rm ~/.nanorc.d/etcportage.nanorc

ls -1 .nanorc.d | sed -e 's|.*|include "~\/.nanorc.d/&"|' > .nanorc

エラーになるファイルを除外とありますが、これは自分の環境(Ubuntu Server 9.10)で問題になったものです。

2010/03/16

Railsのログをsyslog-ngで集約する はてなブックマークに追加

Railsのログをsyslog-ngを使って一カ所にまとめる手順をメモしておきます。
使用前

ここにRailsのサーバーが3台あります。前段にはロードバランサーが立っています。ログはデフォルトの場所にバラバラに記録されるので、記録としては相当使いにくいものになります。
使用後

ログ集計用のログサーバーを別途立てます。Railsアプリはsyslog経由でログを記録するようにします。syslogにはsyslog-ngを使い、Railsアプリが出したログはudp経由でログサーバーに随時転送するようにします。
今回は例として、ポート番号を5140番とします。
ログサーバーはudp:5140を見張っていて、ログが流れてきたらファイルに書き出すという算段です。

導入
そもそもなんでsyslog-ngなのか?という点に関してですが、個人的には導入のしやすさでした。もちろんその他も多くの利点があると思います。

作業としては、syslog-ngのインストールと設定、RailsアプリをSyslogLogger経由でのロギングに変更するだけです。既に運用が始まったものでも、結構簡単に移行出来ると思います。

今時syslog-ngを導入するには、パッケージで一発インストールでしょうから、お決まりの呪文を唱えましょう。※Ubuntu Linux Server 9.10で試しています。
sudo apt-get install syslog-ng
当たり前だけど、Railsサーバー、ログサーバーとも両方行います。

次に、RailsアプリのロギングをRubyのLoggerから、SyslogLoggerへ切り替えます。といってもやる事は簡単で、gemでインストールして、ちょこっと設定を変えるだけです。
sudo gem install SyslogLogger

config/environment.rbに以下の記述を追記します。
Rails::Initializer.run do |config|
  # (中略)
  config.gem 'SyslogLogger', :lib => 'syslog_logger'
  # (中略)
end
今回の想定では本番環境のみsyslogでロギングしたいので、config/environments/production.rbに下記を追記します。最初の行に書いておいた方が良いでしょう。
require 'syslog_logger'
RAILS_DEFAULT_LOGGER = SyslogLogger.new "rails_app"
rails_appはRailsアプリのログである事を識別するためのラベル(プログラム名)です。これはsyslog-ngで使用します。(rails_appというのは例です。解りやすいものにする方が良いでしょう。)


syslog-ngの設定(Railsアプリ側)
syslog-ngの設定そのものは簡単です。Railsアプリのサーバーではログをudpでログサーバーに向けて流す設定を行います。
syslog-ngの設定ファイルは、Ubuntuの場合だと
/etc/syslog-ng/syslog-ng.conf
にありますので、コイツに追記してやる事にします。
destination dr_rails_app {
    udp("192.168.1.100" port(5140));
};

filter f_rails_app {
    program(rails_app);
};

log {
    source(s_all);
    filter(f_rails_app);
    destination(dr_rails_app);
};
ここでは便宜的に、ログサーバーのIPアドレスを192.168.1.100としています。
destinationとfilterを定義して、それをlogでまとめるという感じです。
destinationは、見ての通り192.168.1.100へudpの5140ポートで送ると記述します。
filterはRailsアプリ側で指定したラベル(プログラム名)を記述します。
logの中の、source(s_all)というのは、Ubuntuだと全てのメッセージソースという事でデフォルトで定義されているものです。(これは環境によって書き方が変わってくる部分だと思います。)syslogがうけたログで、rails_appというラベルの付いたものだけを抽出し、それをログサーバーに送るという記述になっています。


syslog-ngの設定(ログサーバー側)
次にログサーバー側の設定です。
source s_rails_app {
    udp(ip("0.0.0.0") port(5140));
};

destination df_rails_app {
    file("/var/log/rails/production.log");
};

filter f_rails_app {
    program(rails_app);
};

log {
    source(s_rails_app);
    filter(f_rails_app);
    destination(df_rails_app);
};
sourceは自分自身のudp:5140です。
destinationはログの書き出し場所をファイルパスで示しています。
filterはRailsアプリ側で指定したラベル(プログラム名)を記述します。
これらをまとめて、logの中に記述します。

セキュリティの絡みでもっとやるべき事があるとは思いますが、基本的な事はこれだけです。
ログの取りこぼしが絶対あってはならないものであれば、udp経由での通信は厳しいので避けるべきです。

参考にさせていただいたサイト:

2010/02/21

デブサミ史上最強最高のセッションを目撃した! はてなブックマークに追加

目黒雅叙園で開催されるDeveloper's Summit(デブサミ)に今年も参加してきました。
今年で何回目の参加になるだろう?フル参加は難しいとしても、あのポジティブな雰囲気が楽しくて、何かと都合を付けて毎年行っています。

今年はなんと言っても個人的にデブサミ史上最強最高のセッション、「建築から開発プロセスを学ぶ~パタンランゲージ」から得られるものがすごく大きかった。得られたというより、なにかヒントとなるような言葉をバシバシ浴びせられて、でも消化不良なんだけど、思ったり考えた事を少しずつ自分のものにしていって、外にも出して行こうみたいな、そんな感じです・・って、すでに文章がおかしいですが、とにかく何かを猛烈に浴びせられました(笑)

当日メモが取れないくらい心に沁み入ったので、
を参考に思い出しつつ、このエントリーを書きました。感謝です!!

「IT」とひとまとめにしちゃうと、いろんな要素が関わってきて複雑になるので、「ソフトウェア」に絞って思った事を書きます。デブサミもソフトウェア開発者の集まりだし。

建築とソフトウェア
建築って、人類が家を造るようになってからの壮大な歴史があるのに対し、ソフトウェア開発はたかだか何十年だから、そもそも比べるのもおこがましいような気もします。また一方で、実際の手で触れられるものを扱う建築と、触れられない抽象的なソフトウェアとでは問題の本質が違うと思います。
でも、最終的に人の暮らしの為に必要不可欠なものを作っていくという点では建築もITも同じだし、建築からアーキテクチャという言葉を貰ってきた以上、本質的に通じるものはなんなんだろう?と、以前からぼんやり考えていました。

セッション中の中埜先生の言葉で
全部の要素を満たす住宅を作ったが、機能的でいいんだけど心がない
という事は、身の回りの小さなレベルでよく経験する事だと思います。でも、そういう部分ってプロダクトデザインやソフトウェアユーザビリティーの分野で盛んに問題として取り上げられている部分だと思いますし、それなりに成果が出ているのではないか?とも思います。

でも、建築ってプロダクトそのものが人間の感性を刺激する役割があります。その場にいるだけでなんか気持ち良いと感じたり、雰囲気が開放的だから人のコミュニケーションが円滑になったり、その逆もあったりなど、建築の機能的な役割を果たす事は当然として、その先の人間に対する影響を考慮して「設計」するという部分にまで到達しています。もちろん専門的な事は全く知らないし、いまだに「パターン、Wiki、XP 」ですら未読な自分ですから、ド素人の意見です。でも、素直にそう感じてしまいます。

そういった、言葉にしづらいし、100人いれば100通りの意見が出てくる感性的な部分に対してどうやって折り合いを付けていくのか?それって、今まさにソフトウェア開発が直面している問題だし、その部分が上手く行っていると歴史的にコンセンサスがとれているソフトウェアってあるのかな?と率直に考えました。

ソーシャルアーキテクト?
前日の「アーキテクチャに憧れろ - 『ソフトウェアアーキテクトが知るべき97のこと』著者パネルディスカッション」というセッションでも出てきましたが、「ソーシャルアーキテクト」的な人材はまだ存在しないという事。中埜先生も様々な意見を取りまとめるコーディネーターが足りないとおっしゃっておりました。そういった流れを作るためにも、道具としてのパターンランゲージがもっと必要なのか?それ以前の問題なのか?アジャイル開発が現実的な解なのか?とか、考えれば考えるほど訳が分からなくなってきますね(苦笑)。

小津安二郎の映画で家族でおだやかに朝食を食べているシーンは、日本以外でも素直に心地よいと感じる事が出来る。そういった人類共通の感覚みたいなものはまさにソフトウェアに必要です。また、子供も年寄りも男も女も、年齢性別分け隔てなく使われていくソフトウェアは、今後の社会的責任の度合いが増していくでしょう。

「0」と「1」しか知らない馬鹿正直な「箱」に「ソースコード」を通して日々命を吹き込むソフトウェア開発者の人たちは、そういう重要な仕事をしているのだと自覚して、同時に胸を張るべきだと思います。

ソフトウェアアーキテクチャは建築アーキテクチャに比べてどうなのかとか、そういう比較論ではなくて、良い所でヒントになりそうな部分は取り入れるとか、手法を応用するなど、もっと相互のやり取りが出来るようになれば良いのかな?なんて思ってみたり。

自分の中でさっぱりまとまらないうちにエントリーを起こしてしまいました。でも、何かを書かずに入られませんでした。
私の父が建築業界で働いていた影響があるかもしれません。
祖父は大工でした。なにか血が騒いだのか!?という気がします。

いずれにしても、このようなセッションがデブサミで行われた事はちょっとした事件なんじゃないかと思います。セッション後の拍手の大きさが何よりもそれを物語っていたのではないでしょうか・・。

2010/02/10

OSXでRAMディスク はてなブックマークに追加

ふとRAMディスクを使いたい時ってありますよね(笑)?OSX以前は標準でその機能がついていましたが、当時はメモリも高かったので、あまり使い勝手が無かったように思います。
OSXになってからはGUIでこそ無理ですが、コマンド打てば実現可能です。(GUIがよければ、そういったツールもあるようです。 )

普段自分が使っている簡単なRubyのスクリプトを晒しておきます。よろしければどうぞ。(ご利用は自己責任でお願いいたします。)


#!/usr/bin/ruby

disk_size = 10 # Megabytes
block_size = disk_size * 1024 * 1024 / 512

device = `/usr/bin/hdid -nomount ram://#{block_size}`
`diskutil eraseDisk HFS+ ramdisk #{device}`

このスクリプトを実行すると、デスクトップに「ramdisk」という名前でマウントされます。マウント解除はゴミ箱に入れるなどして行ってください。

2010/02/01

1915 はてなブックマークに追加

Bloggerにお世話になる前、エキサイトシーサーはてなと、転々としてきました。技術系と日常系とブログを分けて書いたりもして、同時に3つくらい書いていた事もありました。でも、不惑の年も近づいている事だし(笑)ごちゃごちゃさせず、シンプルに、今後はこれ一本で行こうと思います。

今日はなんだか不思議な事にあったので、メモ。

相方と、もうすぐ4歳になる息子と三人でバスに乗っていた。
途中のバス停で、手を挙げて小走りに走ってくるおじいさんがいた。
ちょっと息を弾ませながら、自分と息子が座る席の前に、どっかりと座ってこっちを振り返った。息子の顔を見ながら、こう言った。
爺:「ほぉ、賢そうな顔してるの。」
俺:「はい。よく言われます。」(と心の声(笑))

しばらくすると、突然、自分の事を大きな声で語りだした。まぁ、子連れだったらよくある事。でも、こういうおじいさんの身勝手な話は、大抵の場合面白いので、素直に聞き役に回るのが吉。でも、今日はちょっと様子が違った。

爺:「僕はねぇ、今年95なのよ。息子が二人。35歳。60の時の子どもよ。」
俺:「きゅ、きゅうじゅうごですか!?」(どうみても60位にしか見えませんが。)
爺:「体罰はいかんよ、絶対に。体罰は。あんた、やってる?」
俺:「いえ、やってませんけど・・」(突然何!?(笑))
爺:「うちの息子が小さかった頃ねぇ、悪さをしたからお尻をパンパン叩いたのよ。それがねぇ、この95になってもね、35の息子に未だに言われる。尾を引いてるんだねぇ。だからね、子どもに手を上げる事だけは、絶対にやっちゃいかんよ。」
俺:「はい・・」
爺:「僕はねぇ、若い頃中国にいてねぇ、その後韓国に14年、上海にもいたなぁ。韓国では漁師をやっていた。その後は日本に帰ってきて、ヤンキー相手に商売さ。」
爺:「今からこの辞書を図書館に寄付してくるんだ。オックスフォードの英英辞典と・・」
おじいさんのもつビニールの袋には、広辞苑クラスの分厚い辞書が3冊ほど入っていた。すげぇ。時折出てくる「英語」は、どう聞いても「英語」的発音だ。絶対ペラペラだ。このじいちゃん。この辺から、ちょっと背筋が伸びる、俺(笑)。
爺:「これからはなんと言っても英語ね、英語。世界に出て行くためには、英語がしゃべれないと話にならん。」
俺:「はい。そう思います。」(汗)
爺:「お絵描き、とかいうでしょ?なんだろうね、あれ。」
俺:「!?」
爺:「なんでも"お"をつけりゃいいと思ってる。"絵を描く"で良いんだよ。」
そこから、世界で一番紳士的な国は、「英国」だ、と力説・・。でも、この話は「あー、そうですか。」と受け流す事はできなかった。
要するに、「子ども扱い」ってのは、子どもに大して失礼だと。もちろん力の無さや経験不足など、子どもに対して大人がカバーしてあげるのは当然として、日々の暮らしの接し方で「お子様」扱いするのはおかしいと。もっと、人間として尊厳を持って接してあげないと、あとで子ども自身が苦労する・・。そもそも、「お子様」なんていう日本語、おかしいよね。まぁ、裏を返せば、今の大人が「大人」になりきれていないという意味にも取れるんだけど。
爺:「今の日本はねぇ、ほんとに駄目だよ。」
俺:「・・・」
爺:「でもね、この子の時代になったら、きっと日本は良くなる。」
俺:「!?」
爺:「あんたの時代は駄目だろうけど、この子の時代にはきっと良くなる。」
おじいさんはそう言い残すと、そそくさとバスを降りてしまった。話した時間、5分位かな。


95歳、計算してみたらなんと1915年、大正4年生まれ。第一次世界大戦のまっただ中。関東大震災はまだ。終戦の時はすでに30歳・・。という事は、子どもの頃から青年期まで、ずっと戦争臭漂う世界の中で生きてこられた方。本家本元のロスジェネ世代。そんな人が、2010年に、重たい辞書3冊抱えてバスを追っかけて走ってるなんて・・・。「子どもを抱きかかえて腰が痛い」とか言ってる場合じゃないな、まったく。

今、本当に先行き不安な事ばかり言われるし、実際そうなんだけれども、ちょっと我々には想像も及ばないタフな人生を送ってきた大先輩からすると、

「あんたの時代は駄目だろうけど、この子の時代にはきっと良くなる。」
という風景が見えるのかなぁ。

「あんたがもっとしっかりせにゃ、この子らのためにも。」
と背中をたたかれたような気がした。
なんていうか、もっと説教してもらいたかった。
こういう事をズバズバ言う爺さん、少なくとも自分の周りはいない。

こういう人知れず町中にいる爺さん婆さんの話を映像にして、渋谷の映画館で単館上映でもすればヒットするよ、間違いなく(笑)。若い世代こそ、こういう話に飢えてんじゃないのかなぁ、と思ったりもして。




あ、ブログのタイトル変えました。すんません。

2010/01/28

Amazon SQSの使いどころ 実際編 はてなブックマークに追加

Amazon SQSに関しての前回までの記事
AmazonSQSが使えるのは解ったとして、実際の所はどうなんでしょう?という所を、サンプルソースを交えて書いてみたいと思います。内容は自分の経験に基づいて書いてますが、実物と比べるとかなり簡略化していますのでご注意ください。でも、考え方としては大きく外れる事はありません。

想定するストーリー
PDFを作る処理をWeb上でやります。このPDFを作るには、最低でも数分かかる事が解っています。したがって、PDFが出来たら、ユーザーにメールでお知らせする事にします。


ここでは、フロントエンドにRuby on Railsを、バックエンドにJavaを用いています。両者は同一のデータベースを参照出来るようになっています。言い換えると、両者で共有している部分はデータベースだけで、粗な結合になっています。

フロントエンドの動き
フロントエンドの役割は、ユーザーと直接対話する事です。ここでは、PDF作成受付画面と、受付完了画面の表示を行います。

動きを解説する前に、少しデータベースに関して書いておきます。データベースは、ごく一般的なRDBを使用し、PDF作成ジョブを表す「Jobs」というテーブルを用意してあります。

このJobsテーブルは、このように定義してあります。
CREATE TABLE jobs (
  id SERIAL PRIMARY KEY,
  sqs_message_id VARCHAR(128),
  sqs_message_handle VARCHAR(256),
  status VARCHAR(50)
);
statusとは、PDFの作成状態がどうなっているのか?という識別子です。PDFの作成が始まっていなければwaiting、PDF作成中であればmaking、作成完了であればfinished、となります。
sqs_message_idとは、SQSにメッセージを投げると取得出来るIDで、SQSのQueueに積んであるメッセージを識別するものです。
sqs_receipt_handleとは、SQSからメッセージを受領したときに取得出来る文字列で、メッセージを削除するのに使用します。また、このカラムに値が入っているという事は、SQSメッセージを受けた事という目印にもなります。

フロントエンドはPDF作成のリクエストを受けると、Jobsテーブルに、status = 'waiting' でレコードを挿入します。次に、SQSメッセージにJob情報(ここではプライマリキーであるID)を書き込み、SQSのサーバに対してメッセージを投げます。その後「受け付けました」画面を出して、PDFが出来上がったらメールでお知らせする旨を表示します。
以上でフロントエンドの役割はおしまいです。

下記は、Ruby on Railsのコードの例です。これは、PDF生成リクエストを受け付けるコントローラ(Jobs)内のメソッド(create)を想定しています。SQSにアクセスするために、RightAwsを使っています。
class JobsController < ApplicationController
  def create
    @job = Job.new
    @job.status = 'waiting'

    if @job.save then # jobインサートが完了したら、SQSにメッセージを投げる
      sqs = RightAws::SqsGen2.new('AWSのID', 'AWSのSecretKey')
      queue = sqs.queue('SQSキュー名')
      message = queue.send_message(@job.id) # メッセージの中身はJobのID
      @job.sqs_message_id = message.id      # JobにSQSのメッセージIDを記録しておく
      @job.save!

      flash[:notice] = "PDFが出来たら、メールするので待っててね!"
      redirect_to :action => :thanks, :id => @job
    else
      render :nothing => true, :status => 500
    end
  end
end

バックエンドの動き
バックエンドは、ユーザーに見えない所でひたすらSQSを監視しています。バックエンドはメッセージの存在に気づいたら、それを一つ取り出し、中身を読みます。メッセージにはJobのIDが書いてありますから、それをもとにデータベースを見に行って、該当するレコードを引っ張りだします。PDFの作成が終わったら、ユーザーに「できたよ!」メールを送信し、Jobのstatusを「finished」にしておしまいです。
一つのジョブをこなしたら、またSQSの監視に戻ります。陰でひたすら作業する、地味でつらい仕事です(笑)。

下記はJavaのコードの例です。PollingCommandというRunnableとして実装していますので、サーバ側ではこのオブジェクトをScheduledThreadPoolなどで定期的に実行する事になります。また、SQSへのアクセスは、typicaを使用しています。
ちなみに、Javaのデータベースの扱いをどう書こうか迷ったのですが、解りやすさを優先させるため、エラーを全く考慮していない素のJDBCとしました。仕事では決してこのようなコードを書かないでくださいね(笑)。実物はS2JDBCを利用してサクサク書きました。
package com.dateofrock.blog.sqssample;

import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.xerox.amazonws.sqs2.Message;
import com.xerox.amazonws.sqs2.MessageQueue;
import com.xerox.amazonws.sqs2.SQSUtils;

public class PollingCommand implements Runnable {

 public void run() {
  // SQSからメッセージを受信
  MessageQueue msgQueue = null;
  Message message = null;
  try {
   // キューに接続
   msgQueue = SQSUtils.connectToQueue("SQSキュー名", "AWSのID", "AWSのSecretKey");
   // メッセージを一つ受け取る
   message = msgQueue.receiveMessage();
   if (message == null) {
    // メッセージが受け取れなかったらなにもしない
    return;
   }

   // メッセージの中にJobのプライマリキーが書いてある
   long jobId = Long.parseLong(message.getMessageBody());

   // ジョブステータスを作成中にアップデートし、receiptHandleを記録しておく。
   Job job = findById(jobId);
   job.status = "making";
   job.sqsReceiptHandle = message.getReceiptHandle();
   updateJob(job);

   // SQSからメッセージをdelete
   msgQueue.deleteMessage(message);

   // PDF生成してるつもり
   Thread.sleep(3000);
   // メールを送信してるつもり
   Thread.sleep(1000);

   // ジョブステータスを完了にアップデートしておく。
   job.status = "finished";
   updateJob(job);

  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 public class Job {
  public long id;
  public String status;
  public String sqsMessageId;
  public String sqsReceiptHandle;
 }

 private void updateJob(Job job) {
  final String sql = "UPDATE jobs SET status = ? , sqs_message_id = ?, sqs_receipt_handle = ?, updated_at = ?";
  Connection con = null;
  try {
   con = DriverManager.getConnection("JDBCのURL", "DBユーザー名", "DBパスワード");
   PreparedStatement ps = con.prepareStatement(sql);
   ps.setString(1, job.status);
   ps.setString(2, job.sqsMessageId);
   ps.setString(3, job.sqsReceiptHandle);
   ps.setDate(4, new Date(System.currentTimeMillis()));
   ps.executeUpdate();
   ps.close();
  } catch (SQLException e) {
   e.printStackTrace();
  } finally {
   try {
    con.commit();
    con.close();
   } catch (SQLException e) {
    e.printStackTrace();
   }
  }
 }

 private Job findById(long jobId) {
  final String sql = "SELECT * FROM jobs WHERE id = ?";
  Job job = null;
  Connection con = null;
  try {
   con = DriverManager.getConnection("JDBCのURL", "DBユーザー名", "DBパスワード");
   PreparedStatement ps = con.prepareStatement(sql);
   ps.setLong(1, jobId);
   ResultSet rs = ps.executeQuery();
   rs.next();
   job = new Job();
   job.status = rs.getString("status");
   job.sqsMessageId = rs.getString("sqs_message_id");
   job.sqsReceiptHandle = rs.getString("sqs_receipt_handle");
   rs.close();
   ps.close();
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   try {
    con.commit();
    con.close();
   } catch (SQLException e) {
    e.printStackTrace();
   }
  }
  return job;
 }
}

まとめ
キューをまともに扱うとすると、結構大変です。ですが、その面倒な部分をSQSを使う事によって、比較的楽に非同期処理を実装する事が出来ました。今回の例では、わざわざ間に「Jobs」というテーブルを挟んだので、ちょっと面倒な事になっていますが、このような処理が不要なケースもあると思います。

SQSの活用例として記事にしておきました。どなたかのお役に立てれば幸いです。

2010/01/26

Dropboxで手軽にSubversionのリポジトリ はてなブックマークに追加

個人で開発をやる際、複数のマシンでソースコードの同期をはかりたい場合があると思います。その際、Web上の無料のホスティングサービスなどを利用する機会が多いかと思いますが、Dropboxを使って、お手軽にSubversionのリポジトリの同期に成功したので、メモしておきます。

unfuddle.com
・・・と言っておきながら、全く関係ないサイトの紹介です(汗)。個人的にはここ半年くらい、GitとSubversionが無料で使える、unfuddle.comというサイトを使っています。
このサービスはプロジェクト管理(Redmineがベース??)が出来るので、かなり使えます。GitHubGoogle CodeSourceForge.JPのような、オープンソース対象のホスティングサイトとは異なり、無料アカウントでリポジトリを非公開に出来るのがポイントです。

でも、このサイトは基本的に「本気モードプロジェクト(?)」が対象です。チケット切って開発するようなプロジェクトに使うべきで、ちょっとした実験コードやメモなどはコミットしにくいという問題があります。

リポジトリのレイアウトを工夫すれば何とかなりそうだけど、本気モードプロジェクトにゴミを紛れ込ませるのはちょっと(笑)・・もっと単純に、ソースの同期だけでいいから手軽にできる方法がないかな?と考えていました。

Dropboxがあるじゃないか
Dropboxは今や手放す事ができない、便利すぎるサービスです。こいつをソースコードの同期に使えないか、以前試してみた事がありました。やり方としては非常に安易で、Dropboxフォルダに直接プロジェクトを作るやり方です。ですが、これはお勧めできません。例えばEclipseでJavaをやってると頻繁にコンパイルが走るので、細かいファイルが沢山更新されます。すると、Dropboxも一生懸命同期しようとして、動作が重くなります。また、ログファイルなどの「同期が不要なファイル」も同期してくれるので、ファイルサイズが大きいと悲惨です。無駄に計算資源とネットワークリソースを使ってしまうという、エコな観点からも(?)NGですね。

なんかうまい事できないかなぁ、と思っていましたが、「gitとDropboxでお手軽・無料のSource Hostingを実現する」という記事を見つけ、それSubversionに応用しよう!という事で試してみました。(前置きが長い)

何をどうする?
単に、リポジトリをDropboxに同期させるだけです。以上。・・だと、ブログに書くまでもないので、自分のやった事をさらしておきます。

やった事
まず、最初にお断りですが、MacOSX環境の話になります。ですが、どのプラットフォームでも基本的には同じだと思います。※Windows環境で同じような事をやっている方がいらっしゃいました。

Dropboxのフォルダは、デフォルトで

/Users/ユーザー名/Dropbox

になりますが、自分の場合、同期したいマシン間でユーザー名が違う(笑)ので、パスが統一できません。仕方がないので、Dropboxの初期設定から「Dropbox Folder Location」を選んで、下記に変更しました。

/Users/Shared/Dropbox


場所は基本的にどこでも良いと思いますが、マシン環境によって変わらない場所の方が良いです。また、この例では物理的にフォルダを移動させていますが、シンボリックリンクでも行けます。

リポジトリは、このDropboxフォルダの下に作ります。

svnadmin create /Users/Shared/Dropbox/SVNRepository

リポジトリのURLは

file:///Users/Shared/Dropbox/SVNRepository

です。つまり、ローカルリポジトリを使いつつ、Dropboxにリポジトリ自体を同期させるというやり方です。※最初はsvnserve立てようとか思っていましたが、よく考えたらfile://でいいや、という事に気がつきました。

しばらく使ってみていますが、特に問題は起きていないです。複数台同時に作業して、コミット/アップデートしていくとタイミング的におかしな事になりかねませんが、出先のノートでコミット、自宅のデスクトップでチェックアウト・・なんていう使い方だと問題ないかと思います。

ちなみにコミットはローカルなので早いですよ(笑)。

2010/01/25

GAE/Jで実行環境を特定する はてなブックマークに追加

Google App Engine for Javaで、実行環境によって処理を切り替えたい場合のメモです。
Railsだったら、
if RAILS_ENV == 'production' then
# do something that's production-only
end
ですが、GAE/JのAPIを使うと、
if (SystemProperty.environment.value() == SystemProperty.Environment.Value.Production) {
// do something that's production-only
}
のようになります。

直接
System.getProperty("com.google.appengine.runtime.environment")
としても、"Production" とか "Development" という文字列を得る事が出来るようです。

Amazon SQSの使いどころ その2 はてなブックマークに追加

前回、キューを使って、時間がかかる処理を非同期で処理するフローを書きました。今回は、キューにAmazon SQSを使うと、なぜ嬉しいのか?を書いてみたいと思います。

メリット
その1:キューを作らなくていい
なんだかバカみたいですが、前回も書いた通り、信頼性のあるキューを作るのは大変な事です。既に実績のあるサービスを、格安(激安)で使えるのですから、利用しない手は無いです。信頼性という意味では、
メッセージを複数のデータセンターにコピーするので、メッセージが消える心配をしなくてよい。
とAmazon側は言っています。これぞまさにクラウドって感じですね。メッセージのやり取りはSSL経由で、さらにユーザー認証がかかっていますので、基本的なセキュリティーは保たれていると思います。
その2:導入が簡単
AmazonのAPIは大抵REST/SOAPの二種類が用意されていますが、SQSも例外ではありません。ただ、APIを素のまま利用するケースはまれだと思います。Java、Ruby、Pythonなど主要な言語のライブラリがありますので、そちらを使う事になるでしょう。自分の場合だと、メッセージを投げるフロントエンドはRubyでRightAwsを、メッセージを受けるバックエンドはJavaでtypicaを使っています。そもそもSQSで出来る事が単純なので、ライブラリ側のAPIも単純なものになっています。
その3:激安
一番大事な所ですが、安いというより、激安です。料金は、
$0.01 per 10,000 Amazon SQS Requests ($0.000001 per Request)
ですが、これだけ見ても??だと思います。実際に経験したケースだと、30秒おきにSQSにポーリングするスレッドを4本持ったサーバが2台、丸一ヶ月動いた時(単純計算で、30秒おきに8回、SQSにメッセージを取りに行く)で、
2,763,389 Requests $2.76
という請求が来ました。日本円にして250円くらいですので、為替レートの変動する事を考えるとほとんど誤差と言っても良いでしょう。仮に10倍だとしても2,500円ですから。

デメリット
とは言うものの、いい事ばかりではありません。かといって悪い事ではないんですが、独特のクセを知っておかないと想定外の動きをするので要注意です。
その1:メッセージの順番が保証されない
キューってFIFOだから、メッセージを入れた順番に受け取れると思いがちですが、実際は結構ばらついて返ってきます。これは、SQSがメッセージを複数データセンターにコピーする時間が影響してるのかな?と思います。


ですので、順番にシビアな事をやりたい場合は、別途その情報を管理する必要があります。
その2:メッセージ個数カウントが適当
これは「現在キューに何個のメッセージが溜まっているのか?」を正確に知る事が不可能という意味です。適当はちょっと言い過ぎですが、だいたい合っているというか、おおざっぱです。この辺もシビアにやりたい場合は問題になるかもしれません。
その3:メッセージの削除が保証されていない
自分は最初この制約を見たときに「えぇっ!」と思いましたが、実際はそれほど大変な問題ではありません。一度処理したメッセージには印をつけておいて、仮に再度受け取ったとしても実行済みで処理させない様にすれば良いだけです。自分はそうしています。ただし、経験上二重にメッセージを受け取った事は無いです。

総合するとメリットが上回る
デメリットに関しては、使う側で工夫すれば割と簡単に乗り越えられる問題じゃないかと思います。総合的に考えると、非常にコストパフォーマンスが高いのではないでしょうか?

ちなみに、今までの経験からすると、メッセージを投げてから受け取れるまで数秒、長い時だと30秒ほどかかる場合があります。肝心の信頼性ですが、今までメッセージが消えた事はありません。EC2やS3は結構な頻度で落ちてますが、SQSはそれほどでもないです。(気がついてないだけだったりして・・)

次回はサンプルコードを交えて、具体的にどのような実装になるのか解説してみたいと思います。

2010/01/22

Amazon SQSの使いどころ その1 はてなブックマークに追加

Amazon Web Serviceには「EC2」とか「S3」などの、いかにもクラウドなサービスの中に、地味だけど非常に便利なSimple Queue Service(以下、SQS)というサービスがあります。これはいったい何?という感じですけど、使いようによっては非常に有用なサービスです。日本語の解説は少ないのですが、この辺にありますね。
自分が手がけているWebのサービスでは、このSQSが結構重要な役割を果たしています。その経験を交えて解説してみようかな?という試みです。

キュー!
キューなんてものはあまりにも基本すぎて、特別な事じゃない感じがします。ですが、たとえプログラミング言語レベルで簡単に出来るとしても、実際にシステムで信頼性のあるものを作ろうとすると、なかなか大変です。また、そういったビジネスとは直接的に関係のない周辺層の部分を作るのは、限られた開発リソースだと苦しいものがあります。

そもそもWebのシステムでキューが必要なのか?という疑問がふつふつとわき上がってくるかと思いますが、最近ではWebで大量データを扱うなんて事は日常茶飯事ですから、結構使い道があるのじゃないかなぁ?と思います。

要は、時間のかかる処理を非同期で行いたいときに、キューがあれば便利だぜ!という事です。

非同期処理
一口に非同期と言ってもいろいろなレベルがあると思いますが、ここで言うのは、一つの処理に数分から数時間かかってしまう・・というレベルというものです。SQSの解説では、動画の変換なんかが例に挙げられていますが、動画がサーバにアップロードされると、サーバー側では変換処理のメッセージをキューになげて、すぐさま「受け付けました」画面を出す・・みたいな事を想像されると解りやすいかなと思います。

自分の場合は、PDFを扱っています。そのPDFは、テキストも多いですが、ネット上の画像を収集して随時張り込んで行く必要があり、点数が多いとかなりの時間がかかってしまいます。そのためPDFの作成要求があれば、いったんフロント側に立っているサーバーがメッセージをキューに放り込んで、別のサーバーがメッセージを取りに行く、と言った様なフローを作っています。



絵がしょぼくてすみませんorz

「受付のひと」は直接エンドユーザーに画面を提供するフロントエンドのサーバーです。
「PDFを作るひと」は、エンドユーザーからは見えない所にいるPDF生成専門のサーバーです。

「受付のひと」は、どういった内容のPDFを作るかをメッセージの中に書いて、キューに放り込み、「PDFが出来たらお知らせするから待っててね!」という画面をエンドユーザーに表示します。
「PDFを作るひと」は、定期的にキューをチェックして、メッセージがたまっていれば1つ取り出し、メッセージの内容に従ってPDFを作ります。出来上がったらエンドユーザーに「出来たよ!」メールを送信し、1つのジョブが完了です。その後は、またキューをチェックして・・と、延々と繰り返します。

おおざっぱに言うとこのような流れです。この話は、SQSに限った話じゃなくて、一般的なキューを扱う話になります。次回では、なぜSQSだと嬉しいのか?について書いて行きたいと思います。