AWS Lambda & DynamoDBのベストプラクティスを求めて

こんにちはsekitakaです。

serverlessな環境での開発も随分慣れてきましたが、コードの再利用性についてどうしようか悩んでいるので考えたこと&暫定結論を公開します。
ベストプラクティスを模索中なのでコメントでの議論も大歓迎です。

前提条件

共通するDynamoDBのテーブル(例:User)を参照する3つのプロジェクトがある。

  • プロジェクトP1
  • プロジェクトP2
  • プロジェクトJ1

以下のような状況になっている。

  • プロジェクトP1には既にUserデータのデータを取得する関数がある
  • プロジェクトP1,P2はPythonで実装されており、プロジェクトJ1はJavaScriptで実装されている。
  • プロジェクトは全てLambda関数郡としてデプロイされる。

お題

Userテーブルからのデータ取得を各プロジェクトに組み込むにはどんな方法がよいか考える

案1 – 車輪の再発明を恐れない

各プロジェクト内にUserテーブルからのデータ取得用のライブラリをそれぞれ作成する。
この方法は再利用性がプロジェクト内に限定されるかわりに、各プロジェクトで行った修正が他のプロジェクトへ影響しない。
PythonとJavaScriptの両方の言語で実装する必要がある。

案2 – Userテーブル専用のLambda関数を作成する

データアクセス専用のLambda関数を作成する。
1種類の言語でのみデータアクセス用のLambda関数を実装すればよい。
ただしデータアクセス関数を変更した場合、全プロジェクトに影響範囲が広がる。
つまりデータアクセス関数を変更した場合に、各プロジェクトのテストを実行する仕組みが必要になる。

案3 – Userテーブル専用のAPIを作成する

API Gatewayに専用APIを作る。案2とあまり変わらないが、APIGatewayのバージョン管理が使用できそう。
各プロジェクトからバージョン指定でAPIを実行することで、バージョンアップに伴う影響範囲を小さく出来る。

案4 – プロジェクトP1の既存の関数を使用する

案2と似ている。プロジェクトP1の変更がプロジェクトP2,J1に影響するという構造になり混乱が生じやすい気がする。

案5 – 各言語のライブラリを作る

案1に似ているがPythonとJavaScriptの2種類のライブラリを汎用で作成する方法もある。
利用するプロジェクトではnpmのプライベートリポジトリなどを利用して依存関係を解決する。
適切にバージョン管理すれば、バージョンアップに伴う影響も最小限に抑えることができる。

比較

コード量

類似コードを記述(コピペ)する量を比較します。
その場のスピードを出すために案1を使いたいこともありますが、プロジェクトが増えることを考えるとあまりよくないかな。
ただAWSのSDKとして既にライブラリ化されているので、それを利用する部分のコードだけと考えると無くもないかな。

倍率
案1 3(プロジェクトごとに増える)
案2 1
案3 1
案4 0(既存なので)
案5 2(言語毎に増える)

実装規模

新しく開発する必要のある部分の開発工数の比較。

規模
案1
案2
案3
案4
案5

シンプルさ

全体の構造が分かりやすいほどトラブルが起こりにくいので、シンプルさも大切です。

シンプルさ
案1
案2
案3 △(案2よりAPI Gateway分複雑)
案4 ×
案5 ○(作るの大変だけど使うのはシンプル)

点数化

比較したけど何がいいかわからないので点数化してみました。
各要素3点満点にしました。点数が高いほどよい評価です。

倍率 規模 シンプルさ 合計
案1 1 3 3 7
案2 3 2 3 8
案3 3 2 2 7
案4 3 3 1 7
案5 2 1 3 6

ザルみたいな点数化ですが差がつきました。案2がよさそうです。
感覚的にも案2のシンプルなわかりやすさはいい感じに思えます。

とりあえずの結論

案2がよさそうという結論になりました。
他にも方法がありそうでしたらコメントお願いします!

まとめ

1つのモノリシックなアプリを作るのでなく、既存のリソースを利用する場合リソースにどう再利用するか悩みどころが多いですね。
今回の結論も絶対ということはなくスピードや移行コストを考えるとまた違う結論になりそうです。

ノウハウ共有というより脳内垂れ流しですが、色々コメントいただけるとありがたいです。

続きを読む

[モバイルアーキテクチャ ] Cognitoにどこまで任せるべきか?研究してみた

CognitoとはAWSサービスの一つであり、主にモバイルアプリの認証システムとして利用されている。
Cognitoには主に3つの機能があり、それぞれ以下のように自分は解釈している
(ツッコミをいただくために後悔してるので、間違ってたら是非教えてください)

Cognitoのサービス達

Cognito UserPool

概要

emailの確認、SMSによる確認、パスワードの再発行などもやってくれるメールによる認証機能。

メリット

  • ステップが複雑なメール認証機能を実装する手間がない
  • ユーザーの認証ステップの保持、Email, Passwordの暗号化など、面倒なデータを保持せずに済む(AWS管理コンソールにおいて、ユーザーの権限などいじる必要はあるけど)

デメリット

  • 既存のメール認証システムから移行する場合、name, email, phone numberなどはcsvでimportできるが、passwordだけはimportできないので、ユーザーにpasswordの再設定を依頼する必要がある。そのため、移行コストは高い
  • データを自分たちで保持していないため、トラブルシューティングの工数が増す危険がある

Cognito IdentityPool

概要

メール、Google, Facebookなどで認証されたユーザーに対して、特定のAWSサービスへのアクセスを許可する(=認可)の機能。
未認証ユーザーも取り扱い可能で、未認証ユーザーは認証ユーザーと別のrole(権限)を与えることができる

メリット

  • アクセストークンに関する実装をする必要がない
  • サーバーレスアーキテクチャーに移行しやすい(もとからサーバーレスで組むつもりならほぼ必須要件)

デメリット

  • API Gateway経由で、APIを公開する際に、Lambdaならuser情報を取得できるが、HTTPプロキシでELBやEC2を繋いだときは、ユーザー情報が取得できない(?)
  • tokenの受け渡しだけで、未認証ユーザーの照会をすることができない?(idをパラメータに含めないといけない?)

Cognito Sync

概要

Cognito IdentityPoolに存在するユーザーのユーザー設定を保持・同期できるサービス。認証認可とは関係ない。

メリット

機種変時、マルチデバイス対応時に、設定項目を同期するのに便利。

デメリット

今の所見つかってはいないが、iOSでいうUserDefaultに入れるような情報の保持、復元など用途が限定的なので、使わなくていいアプリケーションもたくさんありそう

考察

UserPool

開発コスト

[使わない場合]
– 単純なemail認証であったとしても、rubyだとdeviseなど便利なgemがあるものの、センシティブなデータを扱うために確認工数なども膨れる可能性が高い
– SMS認証も必須だとすると、それをサポートしてるライブラリも少ないため開発工数は爆発的に膨らみそう

[使った場合]
– ライブラリの利用方法を覚える必要があるものの、Mobile Hubなどを利用することでそのコストを格段に下げることができる
– SMS周りは特にハマるらしい。http://qiita.com/aki/items/e35e1bea8c27cfab5e9e
– アクセストークンの検証も忘れがち。http://qiita.com/devalon/items/721ef4bdec80e1e6847c, http://qiita.com/ya-mada/items/154ea6e10f9f788bfdd5

運用コスト

[使わない場合]
– いつでもデータにアクセス可能なので、ユーザー固有の問題に対するトラブルシューティングが比較的容易
– 認証の度にDBへのアクセスが走るためシステムリソースが喰われる
– センシティブなデータにいつでもアクセス可能なため、ルール作りや社内でのアクセス制御が面倒
– UserPoolを使いたくなった時に、ユーザーにpasswordの再設定を依頼する必要がある

[使った場合]
– データを自分たちで保持していないため、トラブルシューティングに時間がかかることもありそう
– AWSのユーザー権限で、アクセス制御が可能なので、そこは簡単
– AWS Cognitoが落ちたら、機能の大半が使えなくなるというリスクを孕んでいる
– export機能はないので、AWS Cognitoから独自の認証機能に移行する場合は、APIを叩いてデータをexportした上で、ユーザーにpasswordの再設定を依頼する必要がある

所感

まだ認証システムを独自で持ってないなら、使うと大幅に開発コストが削減できそう

Cognito IdentityPool

開発コスト

[使わない場合]
– Facebook, Googleなどのuidに相当する情報を保持する必要がある
– クライアントに一時的なアクセストークンを発行する必要がある
– アクセストークンの検証機構を作る必要がある

[使った場合]
– uidの保持をしてくれる
– アクセストークンの発行、検証をawsがよしなにしてくれるが、ec2, elbでそれをやろうとすると、前段にAPI Gatewayをhttp proxyとして置く必要がある
– API Gateway + Lambda ならユーザーの照会までしてくれるが、ec2, elbだとそれができない?
– elb, ec2との相性はわるそうで、未認証ユーザーの照会ができない?
– 上記の理由で構成が複雑化する

運用コスト

[使わない場合]
– uidなどの情報へのアクセス制御をする必要がある
– アクセストークン発行に際して、システムリソースを食う
– 保守しないといけないコードが増える
– サーバーレスアーキテクチャにする際にIdentityPoolに乗り換えるか、STSへのリクエストを独自で頑張るかする必要がある。1ユーザーに認証情報を複数持てるようにしておけば、IdentityPoolのidentityIdをuidとして紐付けできるので、移行コストはそこまで高くないと思う(?)

[使った場合]
– 一部システムを移譲できるものの、構成が複雑になり、全員がクラウドネイティブな考えがない限り、運用が大変になりそう
– クラウドネイティブ的な構成になると、デバッグが大変そう(慣れてないだけ?)

所感

使うことによって、構成が限定されるため初期から使う必要はなさそう。
サービスが大きくなってきて、クラウドを存分に使いたくなったら検討したい

疑問

誰か、もし知ってたらおしえてください

  • API Gateway経由で、APIを公開する際に、Lambdaならuser情報を取得できるが、HTTPプロキシでELBやEC2を繋いだときは、ユーザー情報が取得できないっぽいんですが、何か方法ないですかね?(API Gatewayにおいて、 Integration Type = AWS Service というのがあるけど、これでどうにかできないものか…)

続きを読む

AWS Lambda で動的HTMLコンテンツを配信する(Lambdaプロキシ統合を利用)

静的な内容ならS3に置いてWEBホスティングを有効にすればいいのですが、動的にHTMLを生成する場合にLambdaでHTMLを生成してレスポンスする方法です。この方法だとEC2等のサーバを運用する必要がありません。

Lambda

例として適当なHTMLの内容を返すコードは次のとおりです。

exports.handler = (event, context, callback) => {
  // もしQueryStringから値を取り出したい場合はevent.queryStringParametersから取得する

  // 動的にHTMLの内容を作成
  const html = `
  <html>
    <meta http-equiv="Content-Type" content="text/html" charset="utf-8">
    <body>
      <h1>Test Page</h1>
      こんにちわ!
    </body>
  </html>`;

  // Lambdaプロキシ統合の場合は下記のようなObjectでレスポンスを返せる
  const response = {
    statusCode: 200,
    headers: {
      'Content-Type': 'text/html',
    },
    body: html,
  };

  callback(null, response);
};

API Gateway

Lambdaプロキシ統合の使用にチェックをつけて、先程 作成したLambdaを指定します。

スクリーンショット_2017-06-26_11_05_26.png

動作確認

上記のAPIをAWSのコンソールからデプロイして、

スクリーンショット 2017-06-26 11.12.27.png

ブラウザでアクセスしてみます。

スクリーンショット 2017-06-26 11.14.52.png

補足

Lambdaが起動するのに時間がかかるので、実際の運用で利用する場合はAPI Gatewayでキャッシュを設定するか、CloudFront等のCDNを利用するのがよいかと思います。

以上

続きを読む

AWS LambdaとAPIGatewayのミニマム構築

サーバレスでweb apiを構築できるLambdaとAPIGateway。
これをミニマムな構成で構築してみる

とはいえ認証もなにもない状態だとアレなのでAPI keyを発行した認証は実装する

Lambda

aws consoleからLambdaに入り、Node.jsの6.10を選択、Blank Functionを選択

今回はNodeを選択しているけど、もちろんPythonでもいい

Kobito.XZSrgA.png

ひとまずトリガーはとりあえず作らないで次へ

Kobito.FgA1X1.png

Lambdaの名前を入力し、コードを入力する。
今回はコードはデフォルトのままで進める。

Kobito.R1y4r2.png

スクロールするとさらに設定画面が出てくる
ロール割当だけすれば最小限OK

Kobito.1G5h7o.png

これでLambda関数の作成はOK

API Gateway

APIを定義

aws consoleからAPI gatewayを開く

新しいAPI を選択し、適当な名前を入力

Kobito.46WF6B.png

リソース -> アクション -> メソッドの作成を選択

Kobito.hdwTH2.png

メソッドにPOSTを追加し、Lambda関数を追加する

Kobito.qTpcmT.png

なんかフローみたいのが出てくる。

メソッドリクエストを選択

Kobito.HPvhHq.png

APIキーの必要性にtrueを設定

Kobito.ZD35fr.png

アクション -> デプロイを選択

デプロイするステージを選択(なければ作る)してデプロイ実行

Kobito.tepBZg.png

APIキーの作成

APIキーを選択 -> アクション -> APIキーの作成

Kobito.ilVIPU.png

APIキーの名前、説明を入力。
対象のAPIとステージも一緒に選択。
ここでランダムな文字列のAPIキーが発行されるのでメモしておく(API実行時に必要になる)

Kobito.Xsx7CS.png

使用プランの作成

使用プランを選択 -> 作成を選択

適当な値を入力して使用プランを作成

Kobito.0kronG.png

使用プランにAPIキーを割り当てる
作成したAPIキーをセットする

Kobito.3S94mx.png

実行テスト

postman等からAPIのURLを叩く

headerに x-api-key という名前でAPIkeyを設定すると認証が通り、実行される(はず)

Kobito.IGiR2w.png

続きを読む

AWS独学メモ

頑張って学んでいきます。

サービス俯瞰

コンピューティング関連

サービス名 概要
EC2 仮想サーバー
EC2 Container Service Doker(アプリ実行環境構築ツール)運用サービス
EC2 Container Regstry Dokerイメージ保存・共有サービス。
Elastic Beanstalk .NET/PHP/Python/Ruby/Node.jsアプリを自動でAWSにデプロイ。
Lambda クライアントからのリクエスト発生時に任意プログラミング起動。イベント駆動型サービス。
Auto Scaling CPU使用率等、事前決定条件に応じ、EC2インスタンス増減
Elastic Load Balancing トラフィックに応じ、複数EC2インスタンスに負荷分散

ストレージ・コンテンツ配信

サービス名 概要
S3 ファイルサーバ。画像格納したり。
CloudFront コンテンツ配信ネットワーク。利用者から近い場所から効率よく配信
EBS EC2データを保持するストレージ。EC2のHDD,SSDのような役割。
Elastic File System EC2共有ファイルストレージ
Glacier 低価格ストレージ。仕様頻度低いけど長期保存のバックアップ用。
Import / Export Snowball ペタバイト級の大容量転送サービス。
Storage Gateway オンプレミスとAWSを接続

DB関連

サービス名 概要
RDS DB(MySQL/Oracle/SQL Server/PostgreSQL/Aurora)が利用できる
Database Migration Service 最小限停止時間でDBを移行。オンプレミスのDBサーバからの移行等に用いる
DynamoDB NoSQLデータベスサービス構築/運用。
ElastiCache クラウドでのメモり内キャッシュの管理サービス
Redshift ビッグデータを分析

ネットワーク

サービス名 概要
VPC プライベートネットワーク構築サービス。
Direct Connect オンプレミスのネットワークとAWSのVPCネットワークを直接接続。
Route 53 DNS(ドメイン名とIPアドレスを対応)

開発者用ツール

サービス名 概要
CodeCommit プライベートGit
CodeDeploy 開発アプリを実行環境に自動配置
CodePipeline 継続的デリバリ使用したアプリのリリース

開発ツール

サービス名 概要
CloudWatch AWSリソース監視サービス
CloudFormation テンプレート利用したリソースの作成と管理
CloudTrail ユーザアクティビティとAPI使用状況確認
Config リソースのイベントリ変更の追跡
OpsWorks Chef利用し操作の自動化
Service Catalog 標準化製品の作成と使用
Trusted Advisor パフォーマンスとせきゅりてぃの最適化

セキュリティ

サービス名 概要
IAM AWS認証
Directory Service Active Directoryのホスティングと管理
Inspector アプリのセキュリティ分析
CloudHSM 暗号鍵管理の専用ハードウェア
Key Management Service 暗号鍵作成と管理
WAF 攻撃から保護するファイアウォール

分析

サービス名 概要
EMR Hadoopフレームワーク
Data Pipeline オーケストレーションサービス
Kinesis リアルタイムストリーミングデータとの連携
Machine Learning 機械学習
QuickSight 高速ビジネスインテリジェンスサービス

モバイルサービス

サービス名 概要
Mobile Hub モバイルアプリの構築/テスト/監視
API Gateway RESTful APIの構築/管理
Cofnito ユーザID及びアプリデータの同期
Device Farm iOS/Android/FireOSアプリのテスト
Mobile Analytics アプリ分析の収集/表示/エクスポート
Mobile SDK モバイルソフトウェアの開発キット

アプリケーションサービス

サービス名 概要
AppStream ストリーミングサービス
CloudSearch マネージド型検索サービス
Elastic Transcorder メディアと動画変換
SES Eメール送受信
SNS プッシュ通知サービス
SQS メッセージキューサービス
SWF アプリ同士を連携ワークフローサービス

大企業向け

サービス名 概要
WorkSpaces クラウド上仮想デスクトップパソコンサービス
WorkMail セキュリティ保護、企業向けEメール及びカレンダー
WorkDocs ファイル共有サービス

S3について

用語

用語 意味
バケット データの入れ物
オブジェクト 格納ファイル

ステップ

  1. バケット作成
  2. オブジェクト格納

EC2について

用語

用語 意味
EC2 仮想サーバ。オンプレミスのWindowsサーバやUNIXサーバに相当。
インスタンス 1台の仮想サーバ
EBS(Elastic Block Store) サーバのHDDに相当する仮想ディスク
AMI(Amazon Machine Image) サーバにインストールするOSやミドルウェアやアプリのイメージ。新インスタンスを複数生成時、AMIを利用。
yum パッケージ管理システム
scp(secure copy) SSH機能を用いて、安全にファイル転送する

EC2にSSH接続した

参考ページ1
参考ページ2

ミドルウェアをインストール

yum更新
$ sudo yum -y update
httpdインストール
$ sudo yum  install -y httpd
httpd起動
$ sudo service httpd start
httpd自動起動を確認
$ sudo chkconfig --list httpd
httpd自動起動を設定
$ sudo chkconfig  httpd on
$ sudo chkconfig  httpd off

scp(コンテンツをアップロードする)

【現在ここで躓き中!】
→ 突破!!

参考ページ1

HTTPコンテンツをコピー

HTTPコンテンツのコピー

$ sudo cp /home/ec2-user/index.html /var/www/html/

【現在ここで躓き中!】index.htmlへアクセスできない

続きを読む

Amazon API Gateway+Cognito+JavaScriptでちょっと気をつけること

(2017.6.23現在の情報です)
Amazon API Gatewayを、cognito認証で使うときの注意点です。

リソース設定後、ステージから「SDKの生成」を選ぶことができます。
ただ、このSDK、Cognito認証を想定してないっぽいです。
(認証なし=NONEか、AWS_IAMの認証しか想定してない)

Cognitoで認証する場合、こんな感じです。
※すでにサインイン済みの想定です。

sample.js
AWS.config.region = 'ap-northeast-1'; // Region

var poolData = { UserPoolId: 'ap-northeast-1_xxxxxxxx',
              ClientId: 'xxxxxxx'
};

var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);

if (cognitoUser != null) {
    cognitoUser.getSession(function(err, sessresult) {
         if (sessresult) {
             var idToken = sessresult.getIdToken().getJwtToken();

             var apigClient = apigClientFactory.newClient ();

             var params = {};//必要なら設定
             var body = {};//必要なら設定
             var additionalParams = {
                 headers: {
                    Authorization:idToken //ここが大事
                 }
             }

             apigClient.methodName(params, body, additionalParams)
             .then(function(result){
                  //成功
             }).catch( function(result){
                  //失敗
             });
         }

    });
}


ヘッダーにちゃんとIDトークンを入れるってだけですが。。。
わかんなかったので書いておきます。

最初、IAMのほうの設定の問題かなぁ。。。とか思って色々やってたけど、
そっちはとくに関係なさそうでした。
cognitoとても便利だけど、ちょっとまだ追いついてない感じもしました。

README.mdに書いといて欲しいな。

続きを読む