#author("2019-02-01T06:13:13+00:00","","")
#author("2019-03-15T13:13:58+00:00","","")
[[AWSメモ]] >
* AWS Lambda のローカル開発手順 [#tb98f5c9]
#setlinebreak(on);

#contents
-- 関連
--- [[AWSローカル開発環境の構築]]
--- [[LocalStackでAWSローカル開発]]
--- [[AWSのサービスをJenkinsでデプロイする#ローカルで動作確認>AWSのサービスをJenkinsでデプロイする#b41a391f]]
-- 参考
--- http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/test-sam-local.html
--- https://aws.amazon.com/jp/blogs/news/new-aws-sam-local-beta-build-and-test-serverless-applications-locally/

** 事前準備 [#c2fe10a0]
#html(<div style="padding-left:20px;">)

aws-sdk と AWS CLI をインストールしておく。
[[AWSローカル開発環境の構築]] を参照
#html(</div>)

** SAM Local のインストール [#ve75d1ce]
#html(<div style="padding-left:20px;">)

#myterm2(){{
# NPMでインストール
npm install -g aws-sam-local

# インストール(バージョン)確認
sam --version

}}
#html(</div>)

** Dockerのインストール [#ua6e3fad]
#html(<div style="padding-left:20px;">)

「SAM Local は docker-lambda というカスタマイズされた Docker イメージを自動的に提供します」という事らしい。
http://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/docker-basics.html

インストールガイドに従ってインストール
https://docs.docker.com/engine/installation/

#html(</div>)


** DynamoDB ローカルのインストール [#oc49f3aa]
#html(<div style="padding-left:20px;">)

参考)
http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBLocal.html

https://s3.ap-south-1.amazonaws.com/dynamodb-local-mumbai/dynamodb_local_latest.zip からダウンロード、解凍する。

DymanoDBの開始
#myterm2(){{
java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb
}}
※デフォルトでは localhost:8000 で動作する。
※AWS CLI で エンドポイント指定するか、http://localhost:8000/shell から各種操作が可能。

#html(</div>)

** ローカルIPアドレスの確認 [#xf96ff89]
#html(<div style="padding-left:20px;">)

上記で起動した DynamoDB はホストOS上の 8000ポートで動作しているが、
sam local は docker上で動作する為、sam local から ローカルへの DynamoDB には
localhost:8000 という指定ではアクセスはできない。

なので、ローカルホストの IPアドレスを確認し、lo0 に 127.0.0.1 (localhost) 以外のアドレスが割り当たっていない場合は、以下の通り割り当てる。

#myterm2(){{
sudo ifconfig lo0 alias 10.20.30.40/24
}}
#html(<span>※10.20.30.40/24 は 割り当てる IPアドレスとサブネットマスク</span>)

#html(</div>)


** SAM localの 主なCLI オペレーション [#f99d3fa8]
#html(<div style="padding-left:20px;">)

|コマンド|説明|補足|h
|start-api|すべての Lambda 関数をホストするローカル HTTP サーバーを作成する。||
|invoke|ローカル Lambda 関数を一度呼び出し、呼び出しの完了後に終了する。||
|generate-event|疑似サーバーレスイベントを生成する。||
|validate|テンプレートを検証する。||
|package および deploy|AM アプリケーションのパッケージングおよびデプロイメントを行う|http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/serverless-deploy-wt.html#serverless-deploy|
#html(</div>)

** SAM Local によるシンプルなアプリケーションの構築 [#y6b906ae]
#html(<div style="padding-left:10px;">)
参考
http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/test-sam-local.html#sam-cli-simple-app
#html(</div>)

*** Lambda関数の作成 [#s4cc70b1]
#html(<div style="padding-left:20px;">)

#TODO(handler を分けた方が良さそう)
https://github.com/awslabs/serverless-application-model/tree/master/examples/2016-10-31/api_backend

book.js
#mycode2(){{
'use strict';
var AWS = require('aws-sdk');

var dynamoOpt = {apiVersion: '2012-08-10'};
if (process.env.DYNAMO_REGION && process.env.DYNAMO_END_POINT) {
    dynamoOpt.region = process.env.DYNAMO_REGION;
    dynamoOpt.endpoint = process.env.DYNAMO_END_POINT;
}
var documentClient = new AWS.DynamoDB.DocumentClient(dynamoOpt);

const createResponse = (callback, statusCode, body) => {
    var res = { 
        "statusCode": statusCode,
        "body": JSON.stringify(body)
    }   
    callback(null, res);
}

exports.handler = (event, context, callback) => {
    var req = event.body || event;
    if (typeof(req) === "string" && req !== "") {
      req = JSON.parse(req);
    }   
    var id = false;
    if (event.pathParameters){
        id = event.pathParameters.id || false;
    }   
    switch(event.httpMethod){
        case "GET":
            if(id && id > 0) {
                var params = { 
                  TableName : 'Books',
                  FilterExpression : 'id = :id',
                  ExpressionAttributeValues : {':id' : id }
                };
                documentClient.scan(params, function(err, data) {
                   if (err) {
                        console.log(err);
                        createResponse(callback, 500, { "msg": "Get Error!", "err": err});
                   } else {
                        createResponse(callback, 200, data);
                        console.log(data);
                   }
                });
                return;  
            } else {
                var params = { 
                  TableName : 'Books'
                };
                documentClient.scan(params, function(err, data) {
                   if (err) {
                        console.log(err);
                        createResponse(callback, 500, { "msg": "List Error!", "err": err});
                   } else {
                        createResponse(callback, 200, data);
                        console.log(data);
                   }
                });
            }
            break;
        case "POST": 
            var params = { 
                TableName : 'Books',
                Item: {
                    "id" : req.id,
                    "isbn" : req.isbn,
                    "title" : req.title,
                    "price" : parseInt(req.price,10)
                }
            };
            documentClient.put(params, function(err, data) {
                if (err) {
                    console.log(err);
                    createResponse(callback, 500, { "msg": "Create Error!", "err": err, "req": req});
                } else {
                    createResponse(callback, 200, { "msg": "Create OK!"});
                }
            });
            break;
        case "PUT":
            var params = {
                TableName : 'Books',
                Key: { "id" : id },
                ExpressionAttributeNames: {
                    "#isbn" : "isbn",
                    "#title" : "title",
                    "#price" : "price"
                },
                ExpressionAttributeValues: {
                    ":isbn" : req.isbn,
                    ":title" : req.title,
                    ":price" : parseInt(req.price, 10)
                },
                UpdateExpression: "SET #isbn = :isbn, #title = :title, #price = :price"
            };
            documentClient.update(params, function(err, data) {
                if (err) {
                    console.log(err);
                    console.log(data);
                    createResponse(callback, 500, { "msg": "Update Error!", "err": err});
                } else {
                    createResponse(callback, 200, { "msg": "Update OK!"});
                }
            });
            break;
        case "DELETE":
            var params = {
                TableName : 'Books',
                Key: { "id" : id }
            };
            documentClient.delete(params, function(err, data) {
                if (err) {
                    console.log(err);
                    console.log(data);
                    createResponse(callback, 500, { "msg": "Delete Error!", "err": err});
                } else {
                    createResponse(callback, 200, { "msg": "Delete OK!"});
                }
            });
            break;
        default:
            console.log("Error: unsupported HTTP method (" + event.httpMethod + ")");
            createResponse(callback, 501, { "msg": "Error: unsupported HTTP method (" + event.httpMethod + ")" } );
    }
}
}}
#html(</div>)

*** テンプレートの作成 [#d025e069]
#html(<div style="padding-left:20px;">)
template-local.yml
#mycode2(){{
AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Test serverless application.

Resources:

  BookFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: book.handler
      Runtime: nodejs6.10
      Environment:
        Variables:
          DYNAMO_END_POINT: http://10.20.30.40:8000         #上記で確認/設定したローカルのIPアドレス
          DYNAMO_REGION: localhost
      Events:
        List:
          Type: Api
          Properties:
            Path: /book
            Method: get
        Create:
          Type: Api
          Properties:
            Path: /book
            Method: post
        Update:
          Type: Api
          Properties:
            Path: /book/{id}
            Method: put
        Delete:
          Type: Api
          Properties:
            Path: /book/{id}
            Method: delete
}}
#html(</div>)


*** APIサーバー開始 [#w5014e32]
#html(<div style="padding-left:20px;">)
#myterm2(){{
sam local start-api --template template-local.yml
}}
#html(</div>)

*** 動作確認 [#oad393b7]
#html(<div style="padding-left:20px;">)

一覧
#myterm2(){{
curl -v http://localhost:3000/book
}}

登録
#myterm2(){{
curl -v -XPOST --data '{ "id" : "A01", "isbn" : "ABC001", "title" : "テスト001", "price": 1008 }' http://localhost:3000/book
}}

キー指定検索
#myterm2(){{
curl -v http://localhost:3000/book/A01
}}

更新(キー指定)
#myterm2(){{
curl -v -XPUT --data '{ "id" : "A01", "isbn" : "ABC001", "title" : "テスト001", "price": 2016 }' http://localhost:3000/book/A01
}}

削除(キー指定)
#myterm2(){{
curl -v -XDELETE http://localhost:3000/book/A01
}}
#html(</div>)

** パッケージ化とデプロイ [#idc328e9]
#TODO


** サンプル/参考URL [#j3d6d130]
#html(<div style="padding-left:20px;">)

*** aws-sdk から DynamoDB を利用するサンプル [#cba97dbc]
http://docs.aws.amazon.com/ja_jp/sdk-for-javascript/v2/developer-guide/dynamodb-examples.html

*** aws-sdk から Lambda を利用するサンプル [#cb542160]
http://docs.aws.amazon.com/ja_jp/sdk-for-javascript/v2/developer-guide/lambda-examples.html

*** AWS SAM Local(ベータ版) &#8211; サーバーレスアプリケーションをローカルに構築してテストする [#b988ed40]
https://aws.amazon.com/jp/blogs/news/new-aws-sam-local-beta-build-and-test-serverless-applications-locally/

*** SAM ローカルを使用してサーバーレスアプリケーションをローカルでテストする [#df541c69]
http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/test-sam-local.html
http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/test-sam-local.html#sam-cli-simple-app

*** Lambda ベースのアプリケーションをデプロイする [#o0c6a710]
http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/deploying-lambda-apps.html

*** 独自のサーバーレスアプリケーションを作成する(パッケージ化とデプロイ) [#k6fc6af3]
http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/serverless-deploy-wt.html

*** Lambda ベースのアプリケーションのデプロイメントを自動化する [#x244e615]
http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/automating-deployment.html

#html(</div>)



トップ   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS