Amazon Web Services

2016年12月 7日 (水)

CodeBuildでgo言語のプロジェクトをビルドする際の注意事項

こんにちは。エンジニアの島袋です。

前回に引き続き、AWS CodeBuildの話になります。

CodeBuildでgo言語のプロジェクトをビルドする場合のハマりポイントに気づいたので、そちらを紹介します。

要点から先に言うと、以下です。

  • CodeBuildは/tmp配下にリポジトリをチェックアウトするため、ビルド対象がGOPATH配下にない
  • GOPATH配下にないとvendoringが有効にならないため、go build時に「パッケージが見つからない」と言われる

go buildがコケる??

前回のエントリではbuildspec.ymlのphase名をtypoしたせいでドハマリしてたんですが、その後typoを修正し、再度ビルドを実行すると、今度はgo buildでコケました。

エラーメッセージには「cannot find package〜」と表示されています。

依存しているパッケージが見つからないということですが、そんなはずはありません。

installフェイズでglideを使い、依存パッケージは全てvendorディレクトリの中にダウンロード済みです。

では何が駄目だったのでしょうか?

ビルド対象のプロジェクトがGOPATH配下になかったため、vendoringが有効になっていなかった

GOPATHとvendoringについて簡単に説明しておくと、GOPATHはgo言語を利用する際に設定が必要になる環境変数で、ユーザが任意のパスを指定する必要があります。

go言語でbuildコマンドを叩くと、buildに必要な依存パッケージをGOPATH/src配下から走査します。

(go get "パッケージ名" とコマンドを叩くと、GOPATH/src配下にパッケージをダウンロードできます)

ところが、この方法ではプロジェクト毎に利用するパッケージのバージョンを管理することができません。

(go getコマンドは常に最新版を取得します)

そこで追加された機能がvendoringです。

vendoringは有効な状態では、GOPATH/src配下を走査する前に、プロジェクトのルートディレクトリ直下のvendorディレクトリからパッケージを走査します。

上述のglideはvendorディレクトリ内に依存パッケージをダウンロードするパッケージマネージャです。

(rubyにおけるbundlerのようなものですね)

そんな便利なvendoringなんですが、ビルド対象のプロジェクトディレクトリをGOPATH/src配下に配置していないと有効になりません。

ところが、CodeBuildはビルド対象として指定したリポジトリを"/tmp/src257031055/src"といった感じで/tmp配下にチェックアウトしてしまうんですね。

当該ディレクトリ内のvendorディレクトリ内に依存パッケージをダウンロードし、go buildコマンドを叩いても、vendoringが有効になっていないため、GOPATH/src配下した走査してくれず、「cannot find package〜」と表示されてしまうわけです。

筆者含め、go言語利用者はGOPATH/src配下にディレクトリを作ってコードを書き始めるのが半ば常識化しているため、vendoringが無効になっていることに気づくのに時間を要しました。

回避策

とりあえずのワークアラウンドなんですが、以下のようにしました。

...省略...
  pre_build:
    commands:
      - glide install
      - mkdir -p $GOPATH/src/github.com/stk132/tsg
      - mv * $GOPATH/src/github.com/stk132/tsg/.
  build:
    commands:
      - cd $GOPATH/src/github.com/stk132/tsg && go build

はい。pre_buildフェイズでGOPATH/src配下にディレクトリを切って、CodeBuildがチェックアウトしてきたリポジトリの中身を全てコピーしています。

(これで当該ディレクトリの中ではvendoringが有効になります。)

そしてbuildフェイズでは当該ディレクトリに移動して、go buildを実行します。

(cdコマンドとgo buildをチェインしているのは、別の行にわけて実行すると、元のワーキングディレクトリに戻っているためです。多分Dockerfileのビルド時と同じ挙動??)

これ、もうちょっとスマートな解決方法ないですかね。。。。?

GOPATH=$GOPATH:pwd

とかして、ワーキングディレクトリをGOPATHに追加する方法も考えたんですけど、それだとビルド対象プロジェクト内のサブパッケージが走査できなくなるんですよね。

終わりに

よくよく考えると、jenkins等も同様の問題をはらんでいる気がするのですが、もしかしてgo言語のプロジェクトのCIを回している人にとっては今回のようなのは常識だったりするのでしょうか?

筆者は専らローカルで利用するCLIコマンドばかり作っているため、ビルドもローカルマシン上で行なっていたため、まんまとハマってしまいました。

2016年12月 5日 (月)

AWS CodeBuildに入門失敗した話

こんにちは。エンジニアの島袋です。

今回のエントリ内容については表題の通りです。

typoで日曜の夜をふいにしました。

要約すると以下です。

  • CodeBuildはbuildspec.ymlのphase名をtypoしてもエラーにはならない
  • phase名をtypoすると、phasesが全て無視される?

AWS CodeBuildとは

re:invent 2016で発表されたAWSの新しいサービスの1つで、コンテナベースのフルマネージドなCI環境を提供する的なやつです。

(CodeBuild単体だとビルドに特化してるから、CIとは言わないかな?)

ユーザが自分で管理するjenkinsなどと比較して、勝手にスケールしてくれるので、ビルドキューが詰まって長時間ビルド完了待ちをすることがないというのがウリのようですね。

(あと根本的にサーバー管理の必要がない)

詳細については

あたりを参考にしてください。

早速使ってみようとしたところ。。。

CodeBuildではビルド手順をyaml形式でbuildspec.ymlというファイル名で保存し、ビルド対象のプロジェクトのルートディレクトリ直下に配置する必要があります。

今回は筆者が作ったslackにコメント投稿するためのgo言語製のCLIコマンドをビルドしました。

buildspec.ymlは以下のようになります。

version: 0.1

environment_variables:
  plaintext:
    GOPATH: "/go"

phases:
  install:
    commands:
      - mkdir -p $GOPATH/{bin,src}
      - curl https://glide.sh/get | sh
  pre-build:
    commands:
      - echo prebuildstart
      - PATH=$PATH:$GOPATH/bin
      - echo prebuildend
  build:
    commands:
      - echo buildstrat
      - glide install
      - go build
  post_build:
    commands:
      - echo Build completed on `date`


artifacts:
  type: zip
  files:
    - oshirase

一応buildspec.ymlの記法について説明すると、以下のようになります。

  • environment_variables : 環境変数の設定
  • phases : 実際のビルド手順
    • install : ビルドに必要なコマンドなどのインストール
    • pre_build : ビルド前の作業
    • build : ビルド作業
    • post_build : ビルド後の作業
  • artifacts : zipにパッケージングするビルド成果物の指定

はい。pre_buildとするところをpre-buildとしてしまったんですね。

これに気づくのに二時間近く費やしました。

なぜそれほど時間がかかったかというと、phasesの中に誤ったphase名を記述しても、エラーにならず、ビルド完了となってしまったためです。

ビルドは完了しているにも関わらず、artifactsに指定したビルド成果物が見つからないとうログが出力されていたためおかしいと思い、artifactsの内容について試行錯誤してたんですね。

(そもそもビルド成果物見つからないのであればエラーにしてほしいところなんですが)

今回の場合、typoしたpre_buildだけじゃなく、phasesを全て素通りしてartifactsの処理だけ走っていたようです。

(それが規定動作なのかは現状不明ですが)

終わりに

typoには気をつけましょう。

あと、codebuildはビルド対象のブランチを指定できないようですね。codebuild単体だとjenkins氏やCircleCI等のCIサービスにすぐにとってかわるということは無さそうです。(他のcode〜シリーズと組み合わせること前提ですかね?)

2016年7月 3日 (日)

lambdaからlambdaを起動するリレー方式SQSコンシューマ

こんにちは。エンジニアの島袋です。 最近AWS LambdaによるSQSコンシューマを実装していて、困ったことがあったのでそれについて書きます。

TL;DR

  • SQSのコンシューマをLambdaで実装している
  • scheduled eventでlambdaを起動して、キューに存在するメッセージを全て処理したい
  • キューが空になるまでlambdaからlambdaを起動するリレー方式を実装した

作ってるもの

以下のようなシステムを作っています。

  • S3にファイルがputされる
  • S3 Event NotificationでSQSにメッセージをキューイング
  • Lambdaで実装したコンシューマがメッセージを取り出し、S3にputされたファイルについて何らかの処理を行う

割とあるあるな感じのユースケースだと思います。

S3 Event Notificationで直接Lambdaを起動すれば良いような気もしますが、先日こんなことがありましたね。

サーバレスアーキテクチャの「AWS Lambda」、米国東リージョンの一部のユーザーで障害。約3時間エラーや遅延など

Lamdaの障害が記憶に新しい〜新しすぎる〜(;´Д`)

S3 Event Notificationで直接Lambdaを起動した際にエラーが発生すると、リトライ処理が面倒そうなので、間にSQSを挟むことにしました。

(SQSの場合、メッセージコンシューマが明示的にメッセージを削除しないと一定時間経過後(visibilityTimeout)にメッセージは再びキューに入るので、自動リトライ可能)

Lambda自体はscheduled eventで一定時間毎に起動する想定です。

ファイルの処理に即時性が要求されないのであれば、特に問題の無い構成に見えます。

しかし、LambdaでSQSコンシューマを実装する場合、次の問題があります。

Lambdaは1回の起動時間に制限があるため、1度の起動で全メッセージを処理することが難しい

即時性が要求されないとはいえ、まだキューにメッセージが残ってるのに一定時間経過後にしか処理されないというのは少し微妙です。

できれば1回の起動で現在キューに存在しているメッセージについては全て処理して欲しいところです。

しかし、上記の通りLambdaには起動時間の制限があります。

Lambdaの起動時間上限目一杯までループで処理を繰り返す方法もありますが、起動時間管理の処理を書くのは結構面倒そうです。

Lambdaを複数並列に起動すればいけそうですが、仮にファイルの処理結果をなんらかのデータストアに書き込むと想定すると、データストアにかかる負荷が気になります。

データストア等にに過負荷をかけることなく、キューに残存するメッセージを全て処理する方法はないものでしょうか??

後続処理をまかせるLambdaを起動すればよいのでは?

一度メッセージを処理し終わって、sqsにキューにメッセージが残っているか確認し、残っていれば自身と同じLambdaファンクションを指定して起動するようにすれば良さそうです。

リレーでバトンタッチするイメージですね。

Undoukai_baton_relay


この方式の場合、厳密には「一回の起動」ではありませんが、次回のスケジューリングを待つことなくキュー内のメッセージが全て処理されます。

以下のような感じ

import λ from 'apex.js';
import AWS from 'aws-sdk';
import 'babel-polyfill';

const main = async () => {
  const lambda = new AWS.Lambda();
  const sqs = new AWS.SQS();
  // メッセージを取得
  const data = await sqs.receiveMessage({
    QueueUrl: 'xxx',
    MaxNumberOfMessages: 0,
    WaitTimeSeconds: 1,
  }).promise();

 if (data.Messages.length === 0) return;

  // メッセージに対してなんらかの処理をすると同時にdeleteMessageBatch用のEntryを作成
  const entries = data.Messages.map((message, idx) => {
    /* do somethng */
    return {
      id: idx.toString(),
      ReceiptHandle: message.ReceiptHandle,
    };
  });

  // 処理済みのメッセージを削除
  await sqs.deleteMessageBatch({
    QueueUrl: 'xxx',
    Entries: entries,
  }).promise();

  // キューに残っているメッセージ数を問い合わせ
  const attr = await sqs.getQueueAttribute({
    QueueUrl: 'xxx',
    AttributeNames: ['ApproximateNumberOfMessages'],
  }).promise();

  // String型なので、数値変換
  const remains = parseInt(attr.Attributes.ApproximateNumberOfMessages, 10);

  // メッセージが残っていればlambda起動
  if (remains > 0) {
    await lambda.invoke({
      FunctionName: 'xxx',
      InvocationType: 'event',
      LogType: 'none',
    }).promise();
  }
};

export default λ((e, ctx) => {
  main();
});

全体的にエラー処理ははしょってるので注意してください

(async/awaitつかってるところはtry-catchで囲む必要があります)

export defaultのところが見慣れない感じになっていますが、これはapexでlambdaを管理するときに利用できるラッパーです。

(弊社、Salesforceメインの会社なので紛らわしいプロダクト名ですね。。。)

重要なところはlambda.invokeのパラメータInvocationTypeにeventを指定しているところです。

ここでeventではなくRequestResponseを指定すると、起動したLambdaの処理が完了するまで処理をブロックしてしまうため、キューが空になるか、タイムアウトが発生するまで処理が返ってこないことになるため、無駄にコンピューティング時間を消費します。

eventを指定した場合、Lambda起動後に即座に処理が返ってくるので、あとは後続のLambdaにおまかせということになります。

まだ本番投入はしていませんが、動かしてみたところまずまず問題なさそうです。

Lambdaにgo言語サポートを加えて欲しい

実は今回のSQSコンシューマはいったんgo言語で実装していたんですが、serverlessの風にあてられて、もしくは社内でserverless風吹かせたくてサーバの運用コスト削減のためにLambdaで実装し直したという経緯があります。

前述のapexを利用すると裏ワザ的な方法でgo言語をLambdaで利用することが可能なんですが、残念ながらSQSをサポートしていないため諦めました。

Javascriptで書くと非同期処理の管理が煩雑になるんですよね。

今回はasync/awaitを使っているので大分マシですが、Promiseでやるとすごい縦長になって、あとからソースを見て読解するときになかなかつらいことになりそうです。

(愚直にcallback使うのは無論論外)

じゃあPython使えよって話になるんですが(実際ググったらPython利用しているケースが多い)、

  • サーバサイド : go言語
  • フロントエンド : javascript
  • Lambda : Python

と3言語同時に扱うのはなかなか厳しい(´・ω・`)

というわけでAWSさんは一刻も早くgo言語のサポートをお願いします(人∀・)タノム

おまけ : ショートポーリングに対する誤解

lambdaを5分毎に起動して、receiveMessageでmaxの10件づつメッセージを取り出して処理すればいいと考えていたんですが、実際に書いて動かしてみたところ、キュー内には10件以上メッセージが存在するにも関わらず、メッセージが1,2件しか返ってきません。

APIドキュメントを確認すると、以下の記述が見つかりました。

Short poll is the default behavior where a weighted random set of machines is sampled on a ReceiveMessage call. This means only the messages on the sampled machines are returned. If the number of messages in the queue is small (less than 1000), it is likely you will get fewer messages than you requested per ReceiveMessage call. If the number of messages in the queue is extremely small, you might not receive any messages in a particular ReceiveMessage response; in which case you should repeat the request.

aws-sdk APIリファレンスより引用

ショートポーリングの場合、キュー内のメッセージ数が少ない(1000以下など)場合、キュー内のメッセージ数で重み付けされたサンプル数のサーバにしか問い合わせにいかないため、MaxNumberOfMessagesに指定した数より少ないメッセージしか返ってこず、キュー内のメッセージ数が極めて少ない場合、メッセージが返却されないことがあるため、繰り返しreceiveMessageを発行する必要がある。

という理解であってるんですかね?

ロングポーリングの場合はどうなんでしょうか? 次の記述が見つかりました。

もう 1 つのメリットは、キューに使用可能なメッセージがあるがレスポンスに含まれていない場合に、偽の空のレスポンスを減らすことができる点です。この状況は、Amazon SQS がショート(標準)ポーリングを使用する場合(デフォルトの動作)に発生します。このとき、レスポンスに含めることができるメッセージがあるかどうかを調べるために、(重み付けされたランダム分散に基づいて)サーバーのサブセットだけに対してクエリが実行されます。一方、ロングポーリングを有効にすると、Amazon SQS はすべてのサーバーに対してクエリを実行します。

Amazon SQS ロングポーリング - Amazon Simple Queue Service より引用

どうやらロングポーリングの場合、上記の問題は発生しないようです。

(receiveMessageのWaitTimeSecondsを1以上にすると、ロングポーリングになる)

Lambdaはコンピューティング時間で課金されるため、なるべく「待ち」の時間は減らしたいところですが、ショートポーリングで1,2件づつメッセージを処理するよりは効率が良さそうです。

2014年10月28日 (火)

AmazonWebServicesアカウントの強制停止から復活

こころです。 
ある日AWSマネジメントコンソールにログインすると


■アカウントが強制停止に

20141027_150537_2

”ご使用のアカウントは停止されているため、認証は失敗しました。AWSカスタマーサポートにお問い合わせください。”

の文字が。

実は支払い先のクレジットカード情報を更新していなかったためbilling statusが更新されずに強制停止されたのでした。
あわててクレジットカード情報を更新し、未払い請求を無しにしてもらったのですが、

20141027_150537_3

マネジメントコンソールは相変わらず停止されています。
ので、サポートにケースをあげてみました。
https://aws.amazon.com/support/

返信がきました。

20141028_110122

一度アカウントがロックされてしまいますと、未払い分をお支払いいただいても アカウントのロックは自動的には解除されません。 お手数ですが、お支払完了後に本窓口にアカウント再開ご希望の旨をご連絡いただく形となっております。

のお返事。
その後すぐにマネジメントコンソールにアクセスできるようになりました!


■アカウント復活方法

AWSアカウントが強制停止された場合、強制停止理由(この場合不払い)を解決しても自動的にはアカウント再開はせず、サポートに連絡して復活して欲しい旨明示すると復活してもらえます。


■まとめ

  • AWSマネジメントコンソールにアクセスできなくなる(当然)
  • サービスにも一切アクセスできなくなる(サイトやメールを運用してる場合は当然停止。)
  • アカウントを再開した場合、停止直前の状態で起動される(停止していたインスタンスは停止状態、起動していたEC2インスタンスはStart状態でした。)

こうならないように請求情報には目を通し、複数アカウントを管理している場合は一括請求(consolidated billing)を設定し請求を集約すると良いと思います。

気づいたのが停止してから2,3日だったのですが、長期放置してしまうとTerminateされていても文句は言えなかっただけによかったです。まあその場合もsnapshotとか何らかバックアップは取っておいてくれるのでしょう、AWSなら、きっと。

2012年1月22日 (日)

Panda Streamを使ってWEBサイトに動画アップロード&エンコード&再生機能を作る

こんにちは、大橋です。
ひさしぶりの記事投稿になります。

今日はPanda Streamという動画アップロード、エンコード(変換)および再生機能を自分のサイトに組み込めるサービスを使ってみたので記事にしてみます。Panda Streamの公式サイトは以下です、あわせて見てみてください。

http://www.pandastream.com/


<Panda Stream概要&特徴>

Panda Streamを使うと、Youtubeみたいに動画ファイルをブラウザからアップロードして、アップロードしたファイルをブラウザ上で再生できる機能を簡単に既存Webサイトに組み込むことができます。特徴は以下です。

  1. すべての機能がREST API形式で提供されていて、Ruby, Python, PHPなどAPIを簡単にコールするライブラリも用意されている
  2. ブラウザからのアップロードはpandaのサーバ側に直接送信されるので、自前のサーバで動画アップ用にインフラ/ミドルなど構築をしなくてよい
  3. データのストア先はAmazon S3を使うので、ストレージを自前で用意する必要なし
  4. アップロード、再生するときのJavaScript, HTML,などサンプルが一通り用意されている。
  5. 多様なInputフォーマットからFLV, MP4(H.264), WebMなど主要な再生環境向けへの変化に対応。

名前はなんだかかわいいですが、できることはけっこうすごいですね。なにかと難しい、動画アップ、変換、が簡単にできちゃいます。HerokuのAdd-onにもなっているので、Herokuを使うときにも便利(←一部の人だけの恩恵かもですが・・・)。

<動画機能を利用するための流れ>

早速使っている前に、大まかな手順を確認しましょう。Panda Streamを使ってWebサイトに動画アップロード&再生機能を埋め込む手順は以下のようになります。

  1. Amazon Web Services(AWS)にサインアップしてAmazon S3を利用できるようにしておきます。
  2. PandaにSign Upします。
  3. CloudというS3のバケットに1対1で対応したエンコーディングした動画を管理するグループを作成。
  4. Panda StreamのAPI Access用のキーとCloud IDを取得。
  5. ここから本番。実際にWebサイトに組み込むためのプログラミングをします。


<実際にWebサイトに組み込むための前準備>

手順(1)〜(4)のCloud作成までは簡単なのでさらっと説明します。

まず、Amazon S3を用意しましょう。AWSのアカウントがなければ以下から申し込んで、Amazon S3を使えるようにしましょう。アップロードした動画やエンコード後の動画、および動画から抽出されたサムネイル画像はAmazon S3上に保存されます。

http://aws.amazon.com/jp/

次にPanda Streamのサイトへいってサインアップしましょう。

http://www.pandastream.com/

サインアップしたら、Home画面のメニューに「API Access」という項目があるので、そのリンクをたどると以下のような画面になります。

Pandaapikey

ここで、
 ・Access Key
 ・Secret Key
 ・API URL(api.pandastream.com)

があることを確認しましょう。これはPanda StreamのAPIを使うときに必要になります。

次にメニューの「Clouds」というメニューがありますのでここをたどり、「Create new cloud」をクリックし、Cloudを作成します。Cloudの名前とパーミッションおよびS3のバケット、Access Key、Secret Keyを設定します。すると、以下の画面のようにCloudが作成されるので、作成後に左上に表示されているIDがCloud IDというもので、この後、APIアクセスで使うので控えておきましょう。

Cloud_id

いろいろサインアップするものがあって大変ですが、これで準備完了です。


<WEBサイトに動画アップロード機能を埋め込む>

Panda Streamのサイトにサンプルコードがアップされているので、それを見たり、動かしたりするのが入門にはちょうどよいです。

http://www.pandastream.com/docs/sample_apps

ここでは、その中のエッセンスだけ抜き出して、アップロードから再生までどんなコードを書けばよいか解説していきます。

まず、アップロード画面のHTML/JavaScript側を準備します。
HTMLは以下のように記述します。actionの遷移先は適当に変えてください。
最初のhiddenはこのあと説明するJavaScriptによりアップロードに成功後に動画のIDが埋め込まれ、次の画面に遷移するときのパラメータになります。

 

   

   

 

JavaScriptはPandaが提供しているJavaScriptライブラリ、jquery.panda-uploaderを使います。jQuery本体を先に読み込んで、Pandaのアップローダーライブラリを読み込みます。

  
  

  


アップローダーの読み込み後、前準備で取得したPanda Stream用のaccess_key、cloud_idなどともにsignatureを書く必要があります。これはサーバサイドで値を作成します。サーバサイド用にはいろいろな言語用にクライアントライブラリがあるので、言語ごとのPandaライブラリを使うことで、signatureを取得できます。ライブラリは以下にあります。

http://www.pandastream.com/docs/client_libs

Ruby, PHP, Pythonなどのライブラリがあります。環境が整っていてよいですが、Java版やPerl版がなかったりもします。
たとえば、Pythonならば以下のような処理でJSON形式でsignatureなど取得できますので、JavaScriptに埋めてください。

panda = Panda(
    api_host='api.pandastream.com',
    cloud_id='Cloud ID',
    access_key='Panda API用のAccess Key',
    secret_key='Panda API用のSecret Key',
    )
python_access_details = panda.signed_params('POST', '/videos.json')
# 以下のようなJSONが取得できます。
#{'access_key': 'xxxxxxxxxxxxxxx', 'timestamp': '2012-01-19T15:01:43.268760+00:00', 'cloud_id': 'xxxxxxxxxxxxxxxxxxxx', 'signature': 'U8OOrnBlYekV3FjyyAIZj1H0kVZlAATn8O/uI07mEMI='}

ここまでのHTML, JavaScript, サーバサイドの処理を組み合わせて、アップロード処理が作れます。説明したパーツを組み合わせると以下のようにプログレスバーが出てくる動画アップロード機能が作れます。

Upload_progress

 アップロードが終わるとコンテンツはS3に登録され、Panda Streamのサーバ側でエンコーディング処理が始まります。同時に、HTMLにhiddenでpanda_video_idとしたところに、動画再生のためのIDがセットされ、フォームのアクション先にpanda_video_idがパラメータとなり画面遷移します。

アップロードされた動画はPanda Streamの画面でも以下のように作成したCloudのDashboardでエンコード中の動画が確認できます。

Dashboard


<WEBサイトで動画を再生する>

さて、アップロードされた動画はエンコードに多少時間がかかります。エンコードのフォーマットはPandaに作成したCloudごとにプロファイルを作成できます。デフォルトはmp4/h.264です。PandaのAPIを使ってエンコードの進捗が何パーセントかも取得できますが、それについては今日は省きます。

動画のエンコードが終わったら、PandaStreamのDashboard上でも再生できますが、アップロード時に取得できたpanda_video_idをパラメータにencodings APIを呼び出し、エンコードされたS3上のファイルパスを取得します。エンコーディングのプロファイルごとにファイルパスや拡張子、スクリーンショットのパスが取得できます。Pythonだと以下のようなコードで取得できます。

    # panda_video_idはアップロード時に取得したID。pandaはPandaクラスのインスタンス
    panda_encodings = json.loads(panda.get("/videos/%s/encodings.json" % panda_video_id))

    encoding = None
 
 # エンコーディングのプロファイルごとに取得できる
    for panda_encoding in panda_encodings:
        if panda_encoding['extname'] == '.mp4' and panda_encoding['status'] == 'success':
            encoding = {
                'id'     : panda_encoding['id'],
                'width'  : panda_encoding['width'],
                'height' : panda_encoding['height'],
                # 以下のようにすると動画のURLやスクリーンショットのjpegのURLが取得できる
                'url'    : "http://%s.s3.amazonaws.com/%s%s" % (’S3のバケット名', panda_encoding['id'], panda_encoding['extname']),
                'screenshot_url' : "http://%s.s3.amazonaws.com/%s_4.jpg" % ('S3のバケット名', panda_encoding['id']),
            }

取得したurlやscreenshot_urlをHTML上で以下のように埋め込みます。ここではHTML5で再生する例を示します。

サーバ側で取得したスクリーンショットのURLや動画ファイルのURLをそれぞれ埋め込めば再生できると思います。もちろん、エンコードが終わってからですが。

かんたんな使い方は以上になります。Panda StreamのDocumentにいろいろな情報があるので、ここから先はそちらを見るようお願いします。


<気になる価格体系>

そういえば、価格について触れないといけないですね。以下のページに記載がありますが、同時に処理できるエンコーダの数により価格が決まります。

http://www.pandastream.com/pricing_and_signup

フリー版はエンコーダは他の人とシェア、ファイルサイズの上限が10MBです。有料版は1つの専用のエンコーダ(dedicated encoder)あたり月間99ドルです。たとえば、3つのエンコーダならば397ドルです。エンコーダの数は同時にエンコードできる動画の数と考えてください。実際には占有できるCPU coreのようです。あと有料版はファイルサイズの上限が5GBのファイルの動画までエンコードできます。


<PaaS上でのプログラミング>

今回はPanda Streamという動画アップロード、エンコード、再生を実現するためのサービスの紹介をしましたが、こういった特定の機能を提供するサービスは動画のエンコードだけでもZencoder(http://zencoder.com/ )など他のサービスがあります。他にも画像変換、メール、PDF生成など、スクラッチで作ると手がかかる機能の実現が最近は外部サービスを使うことで可能になってきているようですね。

Heroku、DotCloudなどのPaaSにより、簡単にアプリケーションをクラウド上にデプロイできるようになりましたが、動画処理やサムネイル生成など特別な機能の実現をしようというときにこれまでと同じやり方ではできないものもあり、少し頭を悩ます部分です。

そういった問題への解決策がPanda Streamのようにアプリケーションの特定の機能を提供するサービス群なのかな、と思います。

クラウド環境でのプログラミングは主にWEB、アプリケーションサーバ用のCPU/DBを提供するHerokuなどのPaaSとその周辺を補強するサービス群を使いこなすことがポイントになるんだろうと思い始めています。

PaaSを使ったアプリケーション構築スタイルをこれから追求しないといけない分野ですね。


<まとめ>

Panda Streamという動画アップロード、エンコード、再生機能を簡単にWebサイトに組み込めます。

こういったサービスは他にもたくさんあるので、今後もたくさん試してみてこのブログ上で共有していきたいと思います。

2011年8月18日 (木)

Amazon SESをbotoを使ってPythonで操作する

前回の記事に引き続き、Amazon SESについてです。

今回はPythonでbotoを使って実際にAmazon SESを使ってメールを送信したり、メール送信結果の統計情報を取得してみようと思います。


■ botoを利用する

botoはAmazon SESなどAmazon Web Servicesのプロダクト群のAPIを操作できるPython用のライブラリです。

インストール方法については以前、Amazon S3を操作する方法の記事で書いたので、以下を参照してください。

http://blog.flect.co.jp/cto/2011/08/pythonamazon-s3-d36b.html


■ Amazon SESを使えるようにする

botoを使う前にAmazon SESのページへ行き、サービス利用を申し込んでおいてください。

申し込んだ直後に利用できる環境はサンドボックス環境と呼ばれる環境です。1日に200通までの送信、および認証されたメールアドレスにしか送受信できない、という制限があります。サンドボックス環境で十分に検証をしたら、以下からプロダクション環境利用の申請をします。

https://aws-portal.amazon.com/gp/aws/html-forms-controller/contactus/SESAccessRequest


■ SESへの接続

では、これから実際に使ってみます。
環境変数としてAWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEYをあらかじめセットしておけばプログラム中にアクセスキー、秘密キーを記述する必要はありません。以下のような感じで接続できます。

from boto.ses.connection import SESConnection
conn = SESConnection()

ホストの指定も以下のように一応できますが、現状では東海岸しかホストされていないので、あまり指定する意味はないです。

# SESへの接続用オブジェクト作成。現在は東海岸しかホストされていない。
conn = SESConnection(host='email.us-east-1.amazonaws.com')

■ メールアドレスの認証

先ほども記載した通り、Amazon SESでは認証されたメールアドレスしか、送受信できません。
Amazon SESにはVerifyEmailAddressというAPIが用意されていて、これを使うことでメールアドレスの認証ができます。以下のようにして呼び出します。

conn = SESConnection()
conn.verify_email_address('o-masaoki@flect.co.jp')

このAPIを呼び出すと、対象のメールアドレスに認証確認のメールが届き、メール本文内のURLをクリックすると認証成功となります。ListVefiriedEmailAddressesというAPIを呼び出すことで認証済みのメールアドレスのリストを取得できます。たとえば、以下のようにすると呼び出せます。

# 認証済みアドレスの取得(細かいチェック省いています)
response = conn.list_verified_email_addresses()
result = response['ListVerifiedEmailAddressesResponse']['ListVerifiedEmailAddressesResult']
addresses = result['VerifiedEmailAddresses']

for address in addresses:
   print address


■ メールの送信

メールの送信はSendEmail APIを使います。使い方は以下のような感じです。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from boto.ses.connection import SESConnection

# SESへの接続用オブジェクト作成。
conn = SESConnection()

# 送信先のtoのアドレスリスト
to_addresses = [
   'o-masaoki+sestest01@flect.co.jp',
   'o-masaoki+sestest02@flect.co.jp',
   'o-masaoki+sestest03@flect.co.jp',
   ]

# reply to headerにセットするアドレス
reply_addresses = ['o-masaoki@flect.co.jp']

# SendMail APIを呼び出す
conn.send_email('o-masaoki@flect.co.jp' # from
               ,u'テスト'               # subject
               ,u'本文のテスト'          # body
               ,to_addresses           # toのアドレスリスト
               ,cc_addresses=[]        # ccのアドレスリスト
               ,bcc_addresses=[]       # bccのアドレスリスト
               ,reply_addresses=reply_addresses        # Reply-toヘッダの設定
               ,return_path='masaoki.ohashi@gmail.com' # bounceメールを返す場所
               )

上記のサンプルは日本語が書かれていますが、送信元のプログラムがMac OS
X、受信側のメーラーがGMailという環境では日本語がUTF-8で表示されました。他の環境ではダメかもしれません。SendEmail APIのリファレンスは以下ですが、主要なパラメータについて上記サンプル中のコメントに説明を記載しておきました。

http://docs.amazonwebservices.com/ses/latest/APIReferene/index.html?API_SendEmail.html

SendEmail APIはパラメータにReply-toやbounceメールを指定すると、自動的にメールのヘッダに付与してくれます。文字コードのエンコードの指定やヘッダの細かい制御等を行うにはSendRawEmailというAPIがあります。これについてはまたの機会に書きたいと思います。


■ 統計情報の取得

前回の記事で、ユーザはバウンスメール数などの統計情報のフィードバックを見られる旨を記載しましたが、それが次に説明するGetSendStatistics APIです。この機能と次に説明するQuotaの機能が、Amazon SESのすごく特徴的なところで、Amazon SESの配信性能の仕組みを支えている部分のひとつになります。

まず、GetSendStatistics APIです。このAPIでは15分インターバルで集計される送信メールの統計情報(15分ごとの統計情報のグループをDataPointと言うようです)を取得できます。サンプルは以下の通りです。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from boto.ses.connection import SESConnection

# SESへの接続用オブジェクト作成。現在は東海岸しかホストされていない。
conn = SESConnection(host='email.us-east-1.amazonaws.com')

# 15分間隔ポイントで統計情報を作成している
response = conn.get_send_statistics()

result = response['GetSendStatisticsResponse']['GetSendStatisticsResult']
points = result['SendDataPoints']

for p in points:
   print '----------------------'
   print 'time of data point : %s' % p['Timestamp']

   # 配信を試みたメール数(配信数)
   print 'delivery attempts : %s' % p['DeliveryAttempts']

   # 何かしらの原因で不達になったバウンスメール数
   print 'bounces : %s' % p['Bounces']

   # 苦情として連絡されたメール数(受信者から拒否されたメール数)
   print 'complaints : %s' % p['Complaints']

   # Amazon SESサービスにより拒否されたメール数。SESのフィルタリングにひっかかったものと思われる。
   print 'rejects : %s' % p['Rejects']

print '----------------------'

取得できるのは配信試行数(DeliveryAttempts)、バウンスメール数(Bounces)、受信側から拒否された数(Complaints)、Amazon SESのフィルタリングにより送信拒否となった数(Rejects)の4つの数値とDataPointのタイムスタンプが取得できます。上記のサンプルプログラムだと以下のように出力されます。

----------------------
time of data point : 2011-08-15T02:38:00Z
delivery attempts : 17
bounces : 0
complaints : 0
rejects : 0
----------------------
time of data point : 2011-08-11T15:23:00Z
delivery attempts : 1
bounces : 0
complaints : 0
rejects : 0
----------------------
time of data point : 2011-08-17T15:38:00Z
delivery attempts : 3
bounces : 0
complaints : 0
rejects : 0
----------------------

ユーザはこれを見て、bounces、complaints, rejectsを減らすことによって配信メールの品質を向上させていく必要があります。Amazon SESにはじかれたメールはAPI経由で、バウンスメールはReturn-Pathのメールから判断できるので、それらの内容をよく確認し、配信リストの修正、品質向上に取り組むといいと思います。


■ 配信割当量(Quota)情報の取得

サンドボックス環境だと固定の数値、プロダクション環境だと、送信したメール数、品質によって徐々にアップしていきます。取得できるAPIはGetSendQuotaで、以下のように使います。

# SESへの接続まではGetSendStatisticsのサンプルと同じです。
response = conn.get_send_quota()
result = response['GetSendQuotaResponse']['GetSendQuotaResult']

# 24時間以内に送信できるメール数
print 'max 24 hour send : %s' % result['Max24HourSend']

# 24時間以内に送信したメール数
print 'sent last hour send : %s' % result['SentLast24Hours']

# 1秒間に送信できるメール数
print 'max send rate : %s' % result['MaxSendRate']

上記のサンプルの通り、24時間以内に送信できるメール数、実際に24時間以内に送信したメール数、それと1秒間に何通送信できるか、という割当情報が取得できます。GetSendStatistics APIで品質チェックをして、品質向上と配信数の向上に努めたら、徐々にこの割当がアップしていくようです。なんだか、レベル上げみたいですね。

 

■ まとめ

前回の記事に引き続き、今回はAmazon SESのAPIをbotoライブラリを使ってPythonでプログラミングする場合の使い方をまとめました。ただ送信するだけではなく、SESからフィードバックをもらい、より高品質なメール配信ができるようにしてもらいたい、というAWS側からの意思みたいなものを感じるAPIですね。

AWSはまだまだ使っていないプロダクトも多いので、今後もいろいろいじってみて実運用につなげていきたいと思います。

■ おまけ ~中途採用のお知らせ~

フレクトではAWSやSalesforceを使ったソリューション事業の拡大をしております。クラウド環境を使った開発に興味がある人はぜひご連絡いただければと思います。以下、採用ページです。

http://www.flect.co.jp/recruit/index.html

2011年8月17日 (水)

Amazon SESとメール配信の難しさについて調べたこと

Amazon SESについて簡単に調査、試用してみたので記事にしてみます。
記事は2回構成で、今回はAmazon SESの特徴をドキュメントベースに調査したものをまとめました。次回、実際にAmazon SESでプログラミングした記事をアップします。

■ 動機と調べたことの概要

メール配信のソリューションはいろいろなベンダーが、魅力的なサービスを展開していますが、初期費用が高かったり、ランニングも場合によってはけっこう高かったりと、やや高価な感が否めません。(そうではないものもあると思いますが)

そう思っていたところでAmazon SESを見つけたので調べてみました。(前からAmazon SES自体は知ってましたが、調べるのが今になってしましまいした・・・)

Amazon SESは初期費用はいらず従量課金の低コストで提供されるメール送信サービスです。1000通あたり0.1ドルと非常にリーズナブルです。

費用以外では、確実にメールを届ける配信性能を売りとしています。メールを送信すれば届くというのは意外と当たり前のようなことですが、大量にメールを送るときに確実にすべての受信者に届けるのはかなり難しいです。

なので、どの辺が難しいか、Amazon SESは何を配信性能とはどういうことなのかメール配信について初心者の自分の理解するのためにまとめてみました。

■ 確実にメール配信するために必要なこと

Eメールを使わないネットサービス、業務システムは少ないと思います。特にコンシューマ向けのPC、モバイル向けのWebサイトではメールマガジンなど大量にメールを送信しなければいけないケースが少なくないです。

メールを送る処理は1通の送信ならばそんなに難しくないですが、マーケティング要素が強い大量メールの配信というのはかなり難しいです。たとえば、以下のような要件をクリアしないといけません。

 ・バウンスメールの処理をきちんとしてISP等でブロックされないようにする
   例)存在しないメールアドレスに対してISPにメールを大量に送るとブロックされたりする

 ・送信できなかった場合の再送処理
   例)IPベースでISPでブロックされた場合、別のIPから送りなおすなど

 ・送信量の制御をしてISPの制御にひっかからないようにする
   例)モバイルのキャリア(ドコモ)などに短い時間で大量に送信すると
      送信ブロックをされることがあるなど

 ・複数台構成で信頼性の高いメール配信サーバを容易する

 ・SPF/Sender IDなどの設定

もう、なんかこれだけで大変そうです。おなかいっぱいですね。

■ スパムフィルタがメール配信を難しくする大きな原因

次に、なぜこんなにメール配信がこんな難しい要件になっているか、についてです。

その原因はどうやらISPのスパムフィルタにあるようです。つまり、届くべきメールまでスパム扱いされているということが原因のようです。

たとえば、情報ソースはやや古いですが、以下のような記事があります。

・メールの0.71~1.02%は「ただ消えて無くなる」~Microsoft研究者らが論文
 

上の記事では以下のような原因でメールは届かなくなるケースが多いとあります。

  •  ISPにて送信元のIPアドレスをフィルタリングしてホワイトリストにないか、ブラックリストに入っていたら、メールを除去
  •  (一か所から)一定量以上(おそらく大量)のメールが送信されている場合
  •  HTMLメールかつ特定の文面が含まれている場合

また、モバイルサイトを開発した人ならば見たことがある人も多いドコモの以下のページも参考になります。

http://www.nttdocomo.co.jp/service/communication/imode_mail/notice/mass_send/index.html

これによると、ドコモ側としては主として

  •  過剰なSMTPセッション数
  •  1セッション(短時間)の送信数
  •  大量の宛先不明を含むメール

などが検知されると、迷惑なメール配信業者という扱いをするようですね。

 

まとめると、

  •  メール不達の原因の大きなものはISPなどにスパム扱いされてしまいブロックされている
  •  短時間に大量のメール送信、宛先不明のアドレスに大量送信、をするとスパム扱いされやすい(下手するとブラックリストに入ってしまう)

ということのようですね。

■ Amazon SESの配信の信頼性を向上させる仕組み

ここで、やっとAmazon SESについて調べたことに入ります。(前置きが長いですね、すみません)

Amazon SESにはメール配信の信頼性を向上させるためにAmazon SES自体を外部ISPからの信頼性を損ねないよう、ISPのスパムフィルタにひっかからないようにする仕組みがあるとのことです。

具体的には、主に以下の手段で配信されるメールを高品質であるよう保ちます。

  1.  Amazon SESのフィルタリングによりスパムメールの可能性が高いメールは外部へ配信しない。
  2.  ユーザにメール品質向上のためのフィードバック情報を提供し、品質向上できるようにする。
  3.  ユーザには急激なメール送信の増加をさせず、段階的にユーザのメール送信量を増やすようにし、継続的に高品質なメールを配信するユーザのみ大量メール配信できるようにする。

まず(1)ですが、スパムメールやその他低品質なメールはAmazon SES自体がそれらのメールを外部に送信しないばかりか、ひどい場合はアカウントの停止もあるとのことで、徹底して低品質なメールを外部に出さないようにしています。

次に(2)では、ユーザに対してバウンスメールの数、苦情メールの数など統計情報を提供し、ユーザ自身がそれらの統計情報を元にメールの品質を向上できるようにします。(具体的なAPIは次の記事で紹介します)

最後の(3)ですが、たとえば、プロダクション環境では最初は24時間に1000通のみの配信ができますが、高品質なメール配信を続けるとその制限が徐々に緩和され、10日間かけて10,000通程度メールが送れるようになります。その間、Amazon SESはそのユーザから送られるメールについて高品質なメールを送りつづけているか評価し、制限の緩和を判断するようです。
(参考:http://docs.amazonwebservices.com/ses/latest/DeveloperGuide/)

これらのことを実施することにより、Amazon SES自体から発信されるメールが各ISPのブロックにかからないようになり、高い配信性能が保たれるということのようです。

■ 他サービスとの比較や気になる点

Amazon SESは「開発者のみなさんも品質向上に協力してね!」という姿勢が他社のサービスと比較すると強く感じます。セルフサービス要素が強い感じ。

国内にある他のメール配信サービス業者などでは、基本的には法人による契約なので、Amazon SESと同様の取り組みなどは必要ないのだと思います。

他のメール配信業者はより高価ですが、配信性能だけではなく、開封情報の取得や不達の管理や、ドコモ等キャリアへのメール配信など、Amazon SESにはない機能も備えているので、実は単純には価格では比較できるものではなく、それぞれの用途によって使い分けましょう、という話になるのかと思います。当たり前ですが。

実運用を見据えると、やはり国内用途ではドコモ、au、ソフトバンクの携帯メールへの配信は大丈夫かな、というのが気になります。たとえば、配信性能は満たしたとしても、配信の高速化(並列化)などはどれくらいできるのだろうか、、、など。機会があったら調べてみたいと思います。

■ まとめと次回

Amazon SES自体はまだベータ版で、導入例などもどれくらいあるかわからないので、当社でも実戦投入はまだ先なのかなとは思いますが、初期費用なく、ランニングも少ない、そして簡単なAPIで使え、配信性能もよい、ということでいつか実戦投入を模索したいな、と思います。

次回は実際にAmazon SESのAPIを使ってプログラミングをしてみたまとめなどを書きたいと思います。

■ おまけ ~中途採用のお知らせ~

フレクトではAWSやSalesforceを使ったソリューション事業の拡大をしております。クラウド環境を使った開発に興味がある人はぜひご連絡いただければと思います。以下、採用ページです。

http://www.flect.co.jp/recruit/index.html

2011年8月10日 (水)

PythonでのAmazon S3プログラミング

ちょっとした事情があり、PythonでAmazon Web Servicesを操作するプログラムを作ることになりました。PythonでAWSを操作するにはbotoがよさそうです。

PythonでAWSプログラミングの手始めにメジャーなサービスであるAmazon S3でbotoの使い方を学習してみました。

■ botoについて

botoはAWSの公式SDKではありませんが、以下のAWSのAPIに対応したライブラリです。

  • Simple Storage Service (S3)
  • SimpleQueue Service (SQS)
  • Elastic Compute Cloud (EC2)
  • Mechanical Turk
  • SimpleDB
  • CloudFront
  • CloudWatch
  • AutoScale
  • Elastic Load Balancer (ELB)
  • Virtual Private Cloud (VPC)
  • Elastic Map Reduce (EMR)
  • Relational Data Service (RDS)
  • Simple Notification Server (SNS)
  • Identity and Access Management (IAM)
  • Route53 DNS Service (route53)

公式SDKに迫るような対応サービスの多さですね。
それだけではなく、Google Storageにも対応しています。PythonとGoogle関連サービスとの相性がよいからでしょうか...。単純にS3とインタフェースが近しいからというのもありそうですが。

まあ、とにかくしっかりAWSプログラミングができそうなライブラリです。

■ botoインストール

Linux、Mac OS Xなどではeasy_installがあれば以下のような感じで簡単インストールです。オススメです。

% sudo easy_install boto

あるいは、ここからダウンロードして、展開して
以下のような感じでインストールでもよいかと。(easy_installつかったほうが簡単だと思いますが・・・)

% wget http://boto.googlecode.com/files/boto-2.0.tar.gz
% cd boto-2.0
% sudo python setup.py install

■ S3への接続とアクセスキー、秘密キーについて

環境変数としてAWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEYをあらかじめセットしておけばプログラム中にアクセスキー、秘密キーを記述する必要はありません。以下のような感じで接続できます。

from boto.s3.connection import S3Connection
conn = S3Connection() # プログラム中にキー情報は書かない

もし、プログラム中に記述する場合は以下のようにAWSとのAWSAuthConnectionを継承したクラス(この場合S3Connection)のインスタンスを作成するのですが、そこで引数にキー情報を入れます。

from boto.s3.connection import S3Connection
conn = S3Connection('アクセスキー', '秘密キー')

■ バケット情報取得とバケット作成

すべてのバケット情報を取得して、バケット名を出力すると以下のような感じになります。簡単ですね。

from boto.s3.connection import S3Connection

conn = S3Connection()

# すべてのバケットを取得する。
buckets = conn.get_all_buckets()

# バケット名出力
for bucket in buckets:
    print bucket.name

続いて、バケットの有無を判定して、あればバケットオブジェクト取得、なければバケットオブジェクトを作成する、というのは以下のようにすれば動きます。ちなみにバケットのリージョンは東京リージョンを指定しています。

bucket_name = 'flect-bucket-tokyo'

# バケットがあるかどうかを調べる、なければ作成する。
if bucket_name in conn:
    bucket = conn.get_bucket(bucket_name)
else:
    bucket =  conn.create_bucket(bucket_name, location=Location.APNortheast) # 東京リージョン指定

補足ですが、リージョン情報を取得するのは以下のように書けばできます。

from boto.s3.connection import Location
print dir(Location)
# 出力例→ ['APNortheast', 'APSoutheast', 'DEFAULT', 'EU', 'USWest', '__doc__', '__module__']

■ オブジェクトの作成と削除

続いて、先ほど作成したバケットにオブジェクトを出し入れしてみます。
まずは単純なテキストから。以下のように出し入れできます。

# キーからオブジェクトの取得/保存操作には以下のインポートが必要
from boto.s3.key import Key

# 中略

k = Key(bucket)
k.key = 'text_content' # キー指定
k.set_contents_from_string('text content') # テキストをオブジェクトとして保存
text_content = k.get_contents_as_string()  # 保存したオブジェクトのテキスト取得

また、ファイルとしてアップロード/ダウンロードも簡単です。
以下はローカルの画像ファイルをアップし、別名でダウンロードする例です。
(bucketはバケットオブジェクトが取得されているものとします)

# ファイルをS3に保存する
k = Key(bucket)
k.key = 'sample_image.jpg'
k.set_contents_from_filename('./sample.jpg')

# ファイルをS3から取得して、指定したファイル名に保存
k.get_contents_to_filename('./download.jpg')

■ まとめと次の課題

内容が著しく浅い、という感はぬぐえないですが、今回はPythonでAWSを触る感触を学習してみたかった、ということでS3を操作するプログラムを通してbotoの使い方になじんでみました。

よく使っているからという理由でS3から学習してみたのですが、今回はAmazon SESなどほかに動かしたいサービスのウォーミングアップなので次回以降いろいろなサービスを動かしてみたいと思います。

2011年1月27日 (木)

セールスフォース、Google、AmazonのPaaSの比較とSIer視点からの印象

最近、Amazon Beanstalkが発表されて、PaaS領域が一段と盛り上がってきました。

業務系に強いForce.com、ソーシャルアプリなどコンシューマ系サービスに強いGoogle App Engineあたりが有名でしたが、最近は選択肢がかなり増えました。
この記事を書いている最中にもちょうど以下のような記事がでていて、PaaSに対する注目度が高いことがうかがえます。

http://www.publickey1.jp/blog/11/google_app_engine_paas.html

http://www.publickey1.jp/blog/11/_java_paas.html

上記の記事でだいたいよいところ、難しいところがまとまっているのですが、今日はGoogle App Engine、Force.com、AWSのサービス、それぞれ実サービスの運用や受託開発で使った経験や、SIerでマネージャをする立場から、自分なりの比較と印象を書いてみようと思います。まず、セールスフォース、Google、Amazonが提供、あるいは関連が強いPaaSについて言語、利用するDB、インフラ運営元、そしてサービス提供会社を一覧にしてみました。

◆ 各プラットフォームと言語、DBなどの比較

名前言語DBインフラ環境サービス提供会社
Google App Engine(GAE) Python, Java BigTable Google Google
Force.com Apex, VisualForce Force.comのDB(Database.com) Force.com(セールスフォース) セールスフォース
VMForce Java(Spring Framework) Force.comのDB(Database.com) Force.com(セールスフォース) セールスフォース
Heroku Ruby(Ruby on Railsなど) PostgreSQL(標準)など Amazon セールスフォース
Amazon Beanstalk Java Amazon RDS(MySQL), Amazon SimpleDBなど Amazon Amazon
Engine Yard Ruby(Ruby on Railsなど) MySQL(標準)など Amazon Engine Yard(いずれAmazon?)

注目すべきはGAEはDBが独自であること、Force.comはApex、VisualForceという独自の言語でアプリケーション構築をする必要があることでしょう。(Force.comのDBは独自といえば独自ですが、RDBの考え方でだいたい操作できます)

それに対して、Amazon上で動くPaaSは言語、DBともに、オンプレミス環境からの差分はかなり少ないと思ってよさそうです。こういった特徴を踏まえたうえで、「よいところ」と「気をつけるべきところ、気になるところ」を私の所感でまとめてみました。

◆ 特徴まとめ

PaaS分類よいところ気になるところ
Google App Engine(GAE) ・アプリケーションサーバだけでなく、データベースまで自動スケールするところ。

・Java/Pythonといった汎用性の高い言語で開発できること

・非常に安い。1日あたりのQuota制限があるが、ある程度のボリュームまでは無料と考えてもよい。範囲を超えても安い。

・個人で勉強しやすい。情報も多いし、安い。
・物理的にどこにデータが置かれているかなどブラックボックス(悪いこととは限らない)で、どう動いているか見通しがきかないことがあること

・BigTableという、RDBとは違う考え方が求められるデータベースを使うため、使いこなすにはかなりの学習コストが発生すること。開発者の確保もやや難しいこと
Amazon系(Heroku、Engine Yard、Beanstalk) ・Java(Tomcat)やRuby on Railsなど、オンプレミスの環境で培われた技術を用いてアプリケーションが構築できること。

・開発者が確保しやすい。

・Amazon EC2、RDS、S3、CloudFrontなどPaaSの構成要素となるコンポーネントが独立したサービスとなっており、自由自在に組み合わせられる。
・GAE、Force.comと比べるとRDBやストレージなどのバックアップ等、ミドル、インフラの運用面的なことを多少意識しないといけないこと

・アプリケーション部分についてはForce.comのように何かベースとなる強力な機能があるわけではないので、その開発コストはオンプレミスに近い感じになること
Force.com系 ・GUI操作(Point&Click)だけで、かなりのカスタム機能が作成でき、型にはまると生産性がかなり高いこと。

・認証系の機能、ユーザの権限制御、ワークフローなどスクラッチ実装をするとけっこう面倒な機能が既に存在すること。

・国内、海外問わずたくさんの大手企業が業務システムに使っており、セキュリティ含め、実績面では信頼しやすい
・Apexなど独自技術の学習コストがかかる

・ストレージ容量やレコード件数、外部からのAPI呼び出しの最大数の制限など、「容量の大きいデータ」、「件数の多いデータ」への制限は強いこと

・ライセンス費用がユーザ単位であり、GAEやAmazon系の課金と違うのは注意が必要なこと

上の色をつけているところが、もっともメリットとして特色があるところです。

◆ 用途に合うと高い効果を出すGAEとForce.com

GAEとForce.comは「制約は強いが、型にはまると高い効果を出す」と考えるとよいと思います。

GAEはデータベースが独自なので「Joinをさける」「集合関数は使わないで、レコードに持つ」などRDBとは違う考え方で開発が必要です。しかし、アプリケーションサーバだけでなく、データベースまでもが自動スケールするので、アクセス量が読めないアプリケーション(ソーシャルアプリなど) では、独自技術を習得するコストを払ってもメリットを得られる可能性があります。

Force.comについては、GAEに比べると書籍等少なく、個人で勉強する人も少なそうなので、知らない人も多そうですが、GAEと同じく型にはまると強いPaaSと考えています。
GAEやAmazon系のPaaSが提供している機能はデータストアやストレージのAPIなど、どんなアプリケーションでも共通で使いそうなものが中心です。それに対して、Force.comは会社などの組織で使うアプリケーション構築に必要な機能が最初から揃っています。たとえば、ユーザの発行、権限管理、ワークフローなど。認証処理とか権限制御など、受託案件では「毎度おなじみ」の機能ですが、けっこう作るのは毎度面倒、な機能です。そういったものが最初からそろっていて、しかも簡単にカスタマイズできるというのは、はまるとすごい生産性です。

実際、フレクトでも案件管理や勤怠管理、経費申請などいくつかの業務アプリをバンバン作っていった実績があります。セールスフォースが得意とするCRMだけでなく、多くの業務系アプリの開発が高い生産性でできる実感を持っています。

一方でForce.comはユーザごとにライセンス費用がかかったり、CPUやストレージをちょっと使うと意外と早く制限がくるなど、特有の制約事項があり、注意しながらの開発が必要です。

両方とも制約が強く、はまると効果テキメンっといった感じですが、学習コストがかかるので開発者のアサインが意外と難しい、といったデメリットがあります。

◆ 既存のオープンな技術をベースにスピーディに作れるAmazon系

HerokuやBeanstalkなどのPaaSはRuby on Rails、Java(Tomcat)、PostgreSQL、MySQLなどのオンプレミス環境でも存在しているオープンな技術をベースとしていて、それらを使ったシステムをすばやく作れるというメリットがあります。アプリケーションサーバ、RDB、ストレージなどはAWSや各PaaSが提供してくれているサービスをそのまま使えばいいので、インフラ、ミドルの心配をせずに、Web開発のスタンダードな技術であるJava、Ruby、MySQLなどを使って開発できるというのがGoodです。オンプレミス環境と同じで、なおかつオープンな技術をベースにしているものが多いので学習コストも低く、開発者のアサインも容易です。

コンシューマが使うWeb系のシステム構築で、とにかく早くリリースしたい、早く作りたい、という場合には、Amazon系のPaaSが有力な選択肢になります。

実際、フレクトでもPaaSではありませんが、Amazon EC2、S3などを使ってモバイルサイト(http://bhmb.jp/)を開発、運用しており、開発時のプラットフォーム選択の決め手はクラウドの恩恵をうけつつ、既存技術をベースにすばやく開発したい、というものでした。

◆ SIer視点からの認知度、信頼度の印象

SIerである私たちにとって、多く接するお客様はユーザ企業の情報システム部門の方々です。そういった企業の方々からも選択肢として認知されているのはセールスフォースがやはり一番という印象です。多くの企業で導入されたということはコンプライアンス観点などからもクリアした案件が多いということで、この辺はお客様に提案するプラットフォームとしてはSIer的にはGoodです。

Amazon系のサービスやGoogle App Engineもまったく認知されていないというわけではないのですが、積極的に選択肢にあがってくるケースが多いかというと必ずしもそうではなさそうな気もします。Amazon系やGAEの事例もサービス企業が内製でそのメリットを活かして作っているケースが多く見受けられます。企業の情報システム部門からの認知度、信頼度という点は、今後の向上に期待というところです。

もちろん、私が知る範囲でもお客様によってはAmazon系やGAEなどの技術に長けていて、すごく活用されているというケースもあります。ただ、全体で見るとまだ低い割合なのかな、と思います。

◆ 今後の気になるところはPHPのPaaS

SI案件というとJava系が多いと思いますが、Web絡みになるとPHPの比重が高くなります。そういう点ではPHP系のPaaSは有名なものは見当たらないですね。注目としてはPHP Fog(http://www.phpfog.com/)が、Heroku的立ち位置を目指しているようなので今後どうなるか気になるところです。

※ 【追記】 AzureでPHPできるとご指摘いただきました。ちょっと調べてみます。

◆ まとめと中途採用募集のお知らせ

VMForceなどリストアップしていながら、あまり言及していないものがあり、すみません。また今度調査してみます。

最後に、フレクトはセールスフォースのプラットフォームやAWSを使ったインテグレーション事業を拡大中です。最新のPaaSを使った開発に興味のある人はぜひご連絡いただければと思います。採用エントリーは以下からです。お待ちしております。

http://www.flect.co.jp/recruit/

2010年12月14日 (火)

AWS SDK for JavaでAmazon SimpleDBを使う


前回、Amazon SimpleDBの記事で、コマンドラインから操作する方法を紹介しましたが、
今日はJavaから使う方法を紹介したいと思います。

■ AWS SDK for Java

JavaからAmazon Web Servicesを操作するには、Amazon純正のSDKである
「AWS SDK for Java」を使います。以下からダウンロードできます。

http://aws.amazon.com/sdkforjava/


■ 使うための準備

AWS SDK for Javaの現在のバージョンは1.0.14なので、aws-java-sdk-1.0.14.jarというファイルがダウンロードできると思います。これをクラスパスに通しましょう。
また、CommonsのHTTPクライアントライブラリやCODECライブラリが必要になるので
それぞれ以下からダウンロードしてクラスパスに通しましょう。

http://hc.apache.org/httpclient-3.x/
http://commons.apache.org/codec/


■ 最初にアクセスキー、秘密キーのセットアップ

最初にアクセスキーと秘密キーを指定して、AWSCredentialという認証情報を持つインタフェースを実装しているBasicAWSCredentialsクラスのインスタンスを作成します。
そして、認証情報を引数にしてAmazonSimpleDBClientというのクライアントクラスの
インスタンスを作成します。AmazonSimpleDBClientは同期アクセス版のクライアントです。
非同期版は別にあるのですが、今回は同期版で進めます。

// アクセスキーと秘密キーは任意のものを指定してください。
String accessKey = "xxxxxxxxxxxxxxx";
String secretKey = "xxxxxxxxxxxxxxx";

AmazonSimpleDB sdb = new AmazonSimpleDBClient(new BasicAWSCredentials(accessKey, secretKey));


■ エンドポイントURLでRegionの指定

アメリカ東海岸、西海岸、シンガポールなど、どこのSimpleDBサーバを使うか指定します。

sdb.setEndpoint("sdb.us-west-1.amazonaws.com");

今回はアメリカ西海岸を使うことにします。各Regionのエンドポイントは以下にあります。

http://docs.amazonwebservices.com/AmazonSimpleDB/latest/DeveloperGuide/index.html?Endpoints.html


■ ドメインを作成(CreateDomain)

ここまでで準備ができたのでドメイン作成から始めてみます。
"book"というドメインを作ってみます。以下の通りです。簡単ですね。

sdb.createDomain(new CreateDomainRequest("book"));


■ アイテムを作ってみます(PutAttributes)

"book"というドメインに、キーをISBN(4873110963)として、属性には"name"と"author"を入れて
アイテムを作ってみます。

sdb.putAttributes(new PutAttributesRequest()
.withDomainName("book")
.withItemName("4873110963")
.withAttributes(
new ReplaceableAttribute("name","Programming Perl", true),
new ReplaceableAttribute("author","Larry Wall", true)
)
);

けっこう簡単ですね。


■ アイテムを1件取得します(GetAttributes)

先ほど作成したアイテムを取得します。GetAttributesというAPIを使います。
ドメインとアイテムのキー(ItemName)を指定して取得します。取得できた値は
GetAttributesResultというクラスのインスタンスに格納されるので、以下のように
ループをまわすと属性情報が取得できます。

GetAttributesResult getAttrResult = 
sdb.getAttributes(
new GetAttributesRequest()
.withDomainName("book")
.withItemName("4873110963")
);

// 属性情報を表示します
for (Attribute attr : getAttrResult.getAttributes()) {
System.out.println("name = " + attr.getName() + ", value =  " + attr.getValue());
}


■ Select文でアイテムを取得します。

以下のようにSelect文を指定して、値を取得することができます。
取得できる値はItemクラスのインスタンスに格納されて返って来ます。
これも簡単にいきますね。

String query = "select name, author from book where author like \"Larry%\" ";
for (Item item : sdb.select(new SelectRequest(query)).getItems()) {
    System.out.println("------------");
    System.out.println("Item Name = " + item.getName());
    for (Attribute attribute : item.getAttributes()) {
        System.out.println("\tAttribute");
        System.out.println("\t\tName = " + attribute.getName());
        System.out.println("\t\tValue = " + attribute.getValue());
    }
}


■ アイテムの削除(DeleteAttributes)

あるアイテムの、ある属性でけ削除したい場合はDeleteAttributesというAPIを使います。
bookドメインのあるアイテムの"author"という属性を削除したい場合、以下のように
書きます。

sdb.deleteAttributes(new DeleteAttributesRequest("book", "4873110963")
.withAttributes(new Attribute().withName("author")));

また、あるアイテムをひとつまるごと消したい場合は、以下のように
属性を指定しないで、DeleteAttributesを呼び出します。

sdb.deleteAttributes(new DeleteAttributesRequest("book", "4873110963"));


■ Consistent Readの指定

Amazon SimpleDBでは一貫性読み込みのオプションをつけて、アイテムを読み込むことが
できます。これも非常に簡単でGetAttributesのAPIでwithConsistentReadをtrueに設定するだけです。

sdb.getAttributes(new GetAttributesRequest().withDomainName("book").withItemName("4873110963").withConsistentRead(true));


■ ドメイン削除

最後にドメイン削除です。以下のような感じであっさりです。

sdb.deleteDomain(new DeleteDomainRequest("book"));


■ まとめ

今回はAWS SDK for Javaを使ってAmazon SimpleDBを操作する方法の主要な箇所をだいたい書きました。この記事自体は技術的には内容は薄いですが、簡単なお勉強には役に立つかもしれません。

次回からはAmazon Web Servicesからはちょっと離れたクラウドネタを書いてみようかと
思います。

採用情報

株式会社フレクトでは、事業拡大のため、
・Salesforce/Force.comのアプリケーション開発
・HerokuやAWSなどのクラウドプラットフォーム上での
Webアプリケーション開発
エンジニア、マネージャーを募集中です。

未経験でも、これからクラウドをやってみたい方、
是非ご応募下さい。

フレクト採用ページへ

会社紹介

株式会社フレクトは、
認定コンサルタント
認定上級デベロッパー
認定デベロッパー
が在籍している、セールスフォースパートナーです。
また、heroku partnersにも登録されています。
herokuパートナー
株式会社フレクトのSalesforce/Force.com
導入支援サービス
弊社の認定プロフェッショナルが支援致します。
・Visualforce/Apexによるアプリ開発
・Salesforceと連携するWebアプリ開発
も承っております。
セールスフォースご検討の際は、
お気軽にお問合せください。

2024年4月

  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30        
ブログ powered by TypePad