DynamoDB Local Viewerに機能追加(スキーマ情報取得、ソート・フィルタ、データ削除)

以前作成したDynamoDBのLocal Viewerですが、現状の機能だと色々と不足してきたため更新をかけました。以前の記事はこちら。

DynamoDB Local用のViewerをSpring Bootベースで作ってみた

当時はScanメソッドでとりあえずデータを取ってましたが、今回は以下に対応させました。

  • テーブルの詳細情報確認
  • 指定したテーブルの中身の削除(制限あり)
  • 指定したテーブルの削除
  • テーブルデータのソートと絞り込み

結果はこちらに登録してあります。

https://github.com/kojiisd/dynamodb-local-view

テーブルの詳細情報確認

テーブル名をクリックした際にスキーマ情報を取得できるようにしました。

スクリーンショット 2017-04-21 7.10.54.png

こんな感じのダイアログを出すようにしています。

スクリーンショット 2017-04-21 7.11.21.png

指定したテーブルの中身の削除(制限あり)

個人的には一番欲しかった機能です。ローカルでDynamoDBを使った動作確認をしている際に、いちいちテーブルをドロップしてから作り直すのをスクリプトを組んで実施するのが面倒でした。なので「Clear」をクリックすれば中身をからにしてくれる機能を作りました。

スクリーンショット 2017-04-21 7.12.01.png

以下は削除後のスキーマ情報です。tableSizeBytesやitemCountが0になっているのがわかります。
ただいくつか制限があり、以下の実装のようにいくつかテーブル情報を引き継いでいません。この辺りうまい方法を知っている人がいればぜひ知りたいです。

CreateTableRequest createRequest = new CreateTableRequest().withTableName(tableName)
        .withAttributeDefinitions(describeResult.getTable().getAttributeDefinitions())
        .withKeySchema(describeResult.getTable().getKeySchema())
        .withProvisionedThroughput(new ProvisionedThroughput()
                .withReadCapacityUnits(describeResult.getTable().getProvisionedThroughput().getReadCapacityUnits())
                .withWriteCapacityUnits(describeResult.getTable().getProvisionedThroughput().getWriteCapacityUnits()));

スクリーンショット 2017-04-21 7.11.49.png

どんな仕組みにしたかはこちらに書きました。

Local DynamoDBのテーブルデータを削除したいときの実装(テーブル削除→作成)

指定したテーブルの削除

スクリーンショット 2017-04-21 7.13.16.png

スクリーンショット 2017-04-21 7.13.40.png

テーブルデータのソートと絞り込み

Scanのページには、各ヘッダで並び替えができるように、また検索結果にフィルタをかけられるように簡易のフィルタ機能をつけました。

スクリーンショット 2017-04-21 9.06.23.png

まとめ

そろそろQuery機能もつけて、実際のAWS ConsoleでのDynamoDBのユーザビリティを再現したいと思います。

続きを読む

DynamoDB Local用のViewerをSpring Bootベースで作ってみた

DynamoDB Localって便利ですよね。実際にAmazon DynamoDBにつなぎに行かなくてもCRUD操作をローカル環境で試せるので重宝しています。

java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb

このコマンドでDynamoDBがシミユレートできてしまうのだから、とても便利。

が、、、投入したデータの確認、となると一手間かかってしまいます。localhost:8000/shell(デフォルトの画面URL)で見れるこの画面、ありがたいのですが、今ひとつ物足りないですよね。

スクリーンショット 2017-01-04 19.54.49.png

やっぱり本家のこういう画面が欲しいなあ、、、と。こういうテーブル一覧とか、、、

スクリーンショット 2017-01-04 19.45.06.png

各テーブルの詳細画面とか。

スクリーンショット 2017-01-04 19.56.45.png

とりあえず目下自分が困っていたのは投入したデータの確認が容易にできないことだったので、DynamoDB Localにつなぎに行ってデータの中身を可視化してくれるViewerを作成しました。

結果

こちらに登録してあります。ご自由にお使いください。起動方法などはREADME.mdをご覧ください。

https://github.com/kojiisd/dynamodb-local-view

かなり力技で実装している部分があります。ソートやフィルタは実装していません。Pull Requestしてくれる方、大歓迎です。またデータ増えた時にパフォーマンス大丈夫かとかその辺は、知らんがな(´・ω・`)。今回の要件ではそんなにローカルでデータ保持しない想定だからダイジョーブ。

Mavenでのビルドから、Javaコマンドで実行できます。

$ mvn clean package
$ java -jar target/dynamodb-view-0.0.1-SNAPSHOT.jar

前提

  • 2016-05-17_1.0バージョンのDynamoDB Local jarファイルを利用しました。
  • Spring Boot + AngularJSで開発しました。

機能と画面

listTablesscanしかしていません。画面も2画面しか作っていません。

1. テーブル一覧画面

DynamoDB Localを起動した状態でアプリケーションから接続に行きます。 localhost:8080 でページが見れます。この画面は現在DynamoDB Localに保存されているテーブルの一覧になります。

スクリーンショット 2017-01-22 16.35.32.png

各テーブルにはリンクを付与しており、クリックするとそのテーブルのデータ一覧が見れます。

2. テーブル詳細画面

こんな感じになっています。DynamoDBは列指向ですので、動的に列が増えたとしても、対応していないレコードは空で表示するような仕組みにしています。

スクリーンショット 2017-01-22 16.35.22.png

この例ではsensor_idカラムとtimestampカラムをそれぞれ「HASH」、「RANGE」にしてテーブル作成しています。

テーブル作成のサンプルデータ

今回のテーブル(sample_table3)を表示するためのテーブル作成定義とデータサンプルは以下になります。

とりあえずこのままDynamoDB Localの管理画面(localhost:8000/shellでアクセスできるアレ)に投入すれば、テーブルの作成はできます。

CreateTable用データ定義
var params = {
    TableName: "sample_table3",
    KeySchema: [
        {
            AttributeName: "sensor_id",
            KeyType: "HASH"
        },
        {
            AttributeName: "timestamp",
            KeyType: "RANGE"
        }
    ],
    AttributeDefinitions: [
        {
            AttributeName: "sensor_id",
            AttributeType: "S"
        },
        {
            AttributeName: "timestamp",
            AttributeType: "S"
        }
    ],
    ProvisionedThroughput: {
        ReadCapacityUnits: 1, 
        WriteCapacityUnits: 1
    }
};

dynamodb.createTable(params, function(err, data) {
    if (err) ppJson(err); // an error occurred
    else ppJson(data); // successful response
});

データ作成はこんな感じでできます。

サンプルデータ用定義
var params = {
    TableName: "sample_table3",
    Item: { 
        sensor_id: "acceleration-sensor01",
        accelerationX: 1.254,
        accelerationY: 0.001,
        accelerationZ: 0.178,
        timestamp: "2017-01-22T10:01:24"
    },
    ReturnValues: "NONE",
    ReturnConsumedCapacity: "NONE",
    ReturnItemCollectionMetrics: "NONE"
};
docClient.put(params, function(err, data) {
    if (err) ppJson(err); // an error occurred
    else ppJson(data); // successful response
});

最後に

今回のツールのおかげで、個人的にDynamoDB Localに保持したデータの確認がかなり容易になりました。このツールを使ってバシバシデータの確認をしてもらえたら、と思います。

おまけ(少し困ったところ)

今回列指向のデータベースに対するアクセスでしたので、各レコードが持っている異なるカラムを、どのようにJava側でデータ保持して、画面側に渡し、表示するのがいいのか、については少し苦戦しました。

結果として以下のようにしました。

  1. [サーバ] 一旦値が空のキーのみのMapを作成する → カラムの過不足をなくす
  2. [サーバ] データが存在する部分だけ値を入れる → データが存在しない箇所は空文字になる
  3. [クライアント] ヘッダ作成部とデータ表示部でテーブルの作り方に一工夫

[サーバ] 一旦値が空のキーのみのMapを作成する

この辺りの実装ですね。少し力技です。画面側の表示を簡単にするためにサーバ側に処理を多めに入れています。

private Map<String, String> createEmptyColumnMap(ScanResult scanResult) {
    Map<String, String> columnMap = new LinkedHashMap<String, String>();
    for (Map<String, AttributeValue> valueMap : scanResult.getItems()) {
        for (Map.Entry<String, AttributeValue> valueMapEntry : valueMap.entrySet()) {
            columnMap.put(valueMapEntry.getKey(), StringUtils.EMPTY);
        }
    }
    return columnMap;
}

[サーバ] データが存在する部分だけ値を入れる

ここも力技。今回いくつかの型には(全部ではない)対応しましたが、これってDynamoDBのAPIを利用してもっとうまくできないですかね?

private String extractColumnValue(AttributeValue value) {
    String result = StringUtils.EMPTY;

    if (value == null) {
        return result;
    }

    if (value.getBOOL() != null) {
        result = value.getBOOL().toString();
    } else if (value.getB() != null) {
        result = value.getB().toString();
    } else if (value.getN() != null) {
        result = value.getN();
    } else if (value.getS() != null) {
        result = value.getS();
    } else if (value.getM() != null) {
        ObjectMapper mapper = new ObjectMapper();
        try {
            result = mapper.writeValueAsString(value.getM());
        } catch (JsonProcessingException ex) {
            // TODO Exception handling
            ex.printStackTrace();
            result = StringUtils.EMPTY;
        }
    } else if (value.getSS() != null) {
        result = String.join(VALUE_SEPARATOR, value.getSS().toArray(new String[0]));
    } else if (value.getNS() != null) {
        result = String.join(VALUE_SEPARATOR, value.getNS().toArray(new String[0]));
    } else if (value.getBS() != null) {
        result = String.join(VALUE_SEPARATOR, value.getBS().toArray(new String[0]));
    }
    return result;
}

[クライアント] ヘッダ作成部とデータ表示部でテーブルの作り方に一工夫

ng-repeatを使ってAngularJS側でパッとテーブル表示したかったのですが、カラム名が動的に変わるのでこんな感じの実装になりました。limitTo:を使ってほぼ無理矢理です。これもよりスマートなやり方があればぜひ知りたいです。

<table class="table table-condensed table-bordered table-striped">
    <thead>
        <tr ng-repeat="data in datas | limitTo:1">
            <th>No</th>
            <th ng-repeat="(key, val) in data">{{ key }}</th>
        </tr>
    </thead>
    <tbody>
      <tr ng-repeat="data in datas">
          <td>{{ $index + 1 }}</td>
          <td ng-repeat="(key, val) in data">{{ val }}</td>
      </tr>
    </tbody>
</table>

続きを読む

Serverless アプリケーションをローカルで開発する

AWSに代表されるServerless Architectureはクラウド上での動作が前提ですが、Serverless Frameworkのプラグインを用いることにより、ローカル環境でも動作させることが可能になるのでご紹介します。AWSにデプロイすることなく開発が可能になるので、より素早く開発ができます。また、AWSのアカウントを持っていない方もServerlessの世界を体験できるかと思います。ここではじゃんけんを行うAPIの開発を通して、ローカルでの開発方法を説明します。完成版のソースコードは以下にあります

構成

API Gateway、Lambda、DynamoDBを用いたアーキテクチャをここでは想定します。ローカル開発環境ではそれぞれ serverless-offline、javascriptファイル、DynamoDB Local が対応します。

Screen Shot 2016-12-23 at 14.55.06.png

環境

  • macOS sierra
  • Node.js v4.6.2
  • Serverless Framework v1.4

プロジェクトの作成

Serverless Frameworkを用いて開発するのでインストールを行い、新しいサービスを作成します。

$ npm install -g serverless
$ mkdir serverless-janken
$ sls create -t aws-nodejs -n serverless-janken

関連するパッケージのインストール

API Gatewayの代用として利用する serverless-offline プラグイン、Serverless FrameworkからDynamoDB Localを操作できるようにする serverless-dynamodb-local プラグインをインストールします。

$ npm install aws-sdk
$ npm install --save-dev serverless-offline
$ npm install --save-dev serverless-dynamodb-local

インストール後、Serverless Frameworkからプラグインとして利用できるように設定に記入します。

$ vi serverless.yml
# service: serverless-janken の下に以下を追記
plugins: 
 - serverless-dynamodb-local
 - serverless-offline

DynamoDB Local のインストール

serverless-dynamodb-local プラグインを利用してDynamoDB Localをインストールします。

$ sls dynamodb install

DynamoDB Local テーブルの定義

DynamoDB Localで利用するテーブルを定義します。サンプルとして、プレイヤー名とUnixtimeをキーとするテーブルを作成しました。

$ mkdir migrations
$ vi migrations/jankens.json
# 下記内容で保存する
{
    "Table": {
        "TableName": "jankens",
        "KeySchema": [{
            "AttributeName": "player",
            "KeyType": "HASH"
        }, {
            "AttributeName": "unixtime",
            "KeyType": "RANGE"
        }],
        "AttributeDefinitions": [{
            "AttributeName": "player",
            "AttributeType": "S"
        }, {
            "AttributeName": "unixtime",
            "AttributeType": "N"
        }],
        "ProvisionedThroughput": {
            "ReadCapacityUnits": 1,
            "WriteCapacityUnits": 1
        }
    },
    "Seeds": [{
        "player": "user1",
        "unixtime": 1482418800,
        "player_hand": "rock",
        "computer_hand": "paper",
        "judge": "lose"
    }]
}

DynamoDB Local の起動

DynamoDB Local起動時にテーブルの作成とシードデータの挿入を行うため、 serverless.yml に設定を入れます。

serverless.yml
# service: serverless-janken の下に以下を追記
custom:
  dynamodb:
    start:
      port: 8000
      inMemory: true
      migration: true
    migration:
      dir: ./migrations

DynamoDB Localを起動します。

$ sls dynamodb start
Dynamodb Local Started, Visit: http://localhost:8000/shell

Table creation completed for table: jankens
Seed running complete for table: jankens

ブラウザで http://localhost:8000/shell にアクセスし、テーブルの中身を確認します。左側のエディタに下記を記入し、再生ボタンを押します。

var params = {
    TableName: 'jankens',
};
dynamodb.scan(params, function(err, data) {
    if (err) ppJson(err);
    else ppJson(data);
});

図のように右側にシードデータの内容が確認できれば、テーブルの作成&シードの挿入が完了しています。

Screen Shot 2016-12-23 at 15.08.59.png

Lambdaの開発

データベースの設定ができたので、ロジック部分を書いていきます。handler.js を開いて以下の内容で保存します。じゃんけんを行うAPI playJanken とじゃんけん結果を参照するAPI listJankens のためのロジックを書いています。このコードはAWSでもローカルでも動作が可能なように、 event.isOffline を見て接続するDynamoDBを切り替えています。

hander.js
"use strict";

var AWS = require("aws-sdk");

var judgeJanken = function (a, b) {
    var c = (a - b + 3) % 3;
    if (c === 0) return "draw";
    if (c === 2) return "win";
    return "lose";
}

var getDynamoClient = function (event) {
    var dynamodb = null;
    if ("isOffline" in event && event.isOffline) {
        dynamodb = new AWS.DynamoDB.DocumentClient({
            region: "localhost",
            endpoint: "http://localhost:8000"
        });
    } else { 
        dynamodb = new AWS.DynamoDB.DocumentClient();
    }
    return dynamodb;
}

module.exports.playJanken = function (event, context, callback) {
    console.log("Received event:", JSON.stringify(event, null, 2));
    console.log("Received context:", JSON.stringify(context, null, 2));

    var dynamodb    = getDynamoClient(event);
    var date        = new Date();
    var unixtime    = Math.floor(date.getTime() /1000);

    var hand        = ["rock", "scissors", "paper"];
    var player_name = event.queryStringParameters.name;
    var player_hand = event.queryStringParameters.hand;
    var player      = hand.indexOf(player_hand);
    var computer    = Math.floor( Math.random() * 3) ;
    var judge       = judgeJanken(player, computer);

    var params = {
        TableName: "jankens",
        Item: {
            player: player_name,
            unixtime: unixtime,
            player_hand: player_hand,
            computer_hand: hand[computer],
            judge: judge
        }
    };

    dynamodb.put(params, function(err) {
        var response = {statusCode: null, body: null};
        if (err) {
            console.log(err);
            response.statusCode = 500;
            response.body = {code: 500, message: "PutItem Error"};
        } else {
            response.statusCode = 200;
            response.body = JSON.stringify({
                player: player_hand,
                computer: hand[computer],
                unixtime: unixtime,
                judge: judge
            });
        }
        callback(null, response);
    });
};

module.exports.listJankens = function (event, context, callback) {
    console.log("Received event:", JSON.stringify(event, null, 2));
    console.log("Received context:", JSON.stringify(context, null, 2));

    var dynamodb = getDynamoClient(event);
    var params   = { TableName: "jankens" };

    dynamodb.scan(params, function(err, data) {
        var response = {statusCode: null, body: null};
        if (err) {
            console.log(err);
            response.statusCode = 500;
            response.body = {code: 500, message: "ScanItem Error"};
        } else if ("Items" in data) {
            response.statusCode = 200;
            response.body = JSON.stringify({jankens: data["Items"]});
        }
        callback(null, response);
    });
};

API Gatewayの設定

最後に前項で作成したロジックを呼ぶエンドポイントを作成するために serverless.yml に設定を入れます。

  • GET /jankens… じゃんけん結果の参照
  • POST /jankens… じゃんけんを行い結果をDynamoDB Localに保存
serverless.yml
service: serverless-janken

custom:
  dynamodb:
    start:
      port: 8000
      inMemory: true
      migration: true
    migration:
        dir: ./migrations

plugins:
  - serverless-dynamodb-local
  - serverless-offline

provider:
  name: aws
  runtime: nodejs4.3

functions:
  playJanken:
    handler: handler.playJanken
    events:
      - http:
          path: jankens
          method: post
  listJankens:
    handler: handler.listJankens
    events:
      - http:
          path: jankens
          method: get

テスト

以上で、データベース、ロジック、エンドポイントがそろったのでローカルで起動させて利用してみます。

$ sls offline

別のシェルでcurlでAPIを叩いて利用してみます。うまくいかない場合は sls dynamodb start でDynamoDB Localが起動していることを確認してください。

$ curl 'http://localhost:3000/jankens?hand=rock&name=test' -X POST
{"player":"rock","computer":"scissors","unixtime":1482469235,"judge":"win"}
$ curl 'http://localhost:3000/jankens'
{"jankens":[{"unixtime":1482469235,"player_hand":"rock","judge":"win","player":"test","computer_hand":"scissors"},{"unixtime":1482418800,"player_hand":"rock","judge":"lose","player":"user1","computer_hand":"paper"}]}

AWSにデプロイ

ローカルで開発ができたら、AWS上にデプロイします。AWSで動かすには、DynamoDBのテーブル定義と、IAMロールの設定が必要でしたので、serverless.yml に追記して下記のようにします。

serverless.yml
service: serverless-janken

custom:
  dynamodb:
    start:
      port: 8000
      inMemory: true
      migration: true
    migration:
        dir: ./migrations

plugins:
  - serverless-dynamodb-local
  - serverless-offline

package:
  exclude:
    - node_modules/**
    - migrations/**
    - .git/**

provider:
  name: aws
  runtime: nodejs4.3
  # DynamoDBの利用の許可
  iamRoleStatements:
    -  Effect: 'Allow'
       Action:
         - 'dynamodb:PutItem'
         - 'dynamodb:Scan'
       Resource: '*'

functions:
  playJanken:
    handler: handler.playJanken
    events:
      - http:
          path: jankens
          method: post
  listJankens:
    handler: handler.listJankens
    events:
      - http:
          path: jankens
          method: get

# DynamoDB Tableの作成
resources:
  Resources:
    JankensTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: jankens
        KeySchema:
          - AttributeName: player
            KeyType: HASH
          - AttributeName: unixtime
            KeyType: RANGE
        AttributeDefinitions:
          - AttributeName: player
            AttributeType: S
          - AttributeName: unixtime
            AttributeType: N
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1

最後にServerless Commandでデプロイします。

$ sls deploy
Serverless: Packaging service...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading service .zip file to S3 (1.81 KB)...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
......................
Serverless: Stack update finished...
Service Information
service: serverless-janken
stage: dev
region: ***
api keys:
  None
endpoints:
  POST - https://***.amazonaws.com/dev/jankens
  GET - https://***.amazonaws.com/dev/jankens
functions:
  serverless-janken-dev-playJanken: arn:aws:lambda:***:***:function:serverless-janken-dev-playJanken
  serverless-janken-dev-listJankens: arn:aws:lambda:***:***:function:serverless-janken-dev-listJankens

以上でローカルで開発したServerless アプリケーションをAWSにデプロイができました。

続きを読む

【メモ】AWSクラウドロードショウ大阪 # パネルセッション関西企業によるAWSで実現した「IoTビジネス」

事例紹介

  1. グッディ本部

    • ビーコンを使ってお客様や従業員の動線を可視化、バックヤード作業を効率化
  2. ダイドードリンコ
    • スマホを介してユーザーと自販機を繋ぐ新サービスを開発中

パネリスト各社のIoTビジネス

オプテックスのセンシングによるIoTビジネス

目的:センサの付加価値を高める -> 新たなビジネス価値やソリューションの創造

  1. 膨大なデータからセンサ側でデータを選別
  2. クラウドにデータをアップロード
  3. データを元に挙動を制御

オプテックスのセンサ

温度など特定の値を取得するのではなく、「侵入者検知」など、ある目的のためのセンサ

ビジネスモデル

  1. 端末機器販売型(従来)
  2. アライアンス型ソリューション(クラウドの発達によって可能)
  3. 完結型ソリューション(自らサービスを提供)

クラウド連携でサービス提供者と協業することがメイン

セーフメーターデータサービス

運転データの取得(加速度から運転の安全度を算出する独自アルゴリズム)
ドライブレコーダでは事故は減らない

ラトックシステム WiFi学習リモコン

PC・スマホの周辺機器メーカー

WiFi学習リモコンとは

スマホからWiFi経由で接続し、家電操作する機器
AWSを使うことで外出先からも家電の操作が可能

システム構成

  1. スマホアプリからCognitoの認証や、エンドポイントの作成、AWS IoTの証明書の作成など、IoTの下準備を行う
  2. 学習リモコンとAWSがMQTTで接続確立

スマホからの通信

  1. スマホアプリからコマンド発行DynamoDBに書き込み
  2. DynamoDBの書き込みイベントでLambdaをキック
  3. LambdaかたIoT MQTTをコール、学習リモコンへ指令を送る
  4. 学習リモコンからの応答をでLambdaをキック
  5. DynamoDBに応答を書き込むとともに、SNSでスマホにプッシュ通知

今後の展望

  • Alexaを使って音声認識で操作
  • ものづくりからサービスへ

ピクセラ 家庭向けIoT事業

ハードウェアからソフトウェアまで自社開発
IoT、翻訳、AR/VRにも参入

Conteホームサービス

スマートホームサービス
一人暮らし向け、高齢者見守り用など用途に向けたプランを選択可能

「ドアが開いたらLEDライトを点灯する」といったシナリオを組み立てることで、簡単に機器同士での連携ができる

AWS活用のメリット

求められる要素

  • セキュリティ

    • ユーザー認証
    • 秘匿性
  • 双方向通信
    • Websocket
  • スケーリング
    • 2016-08からELBもWebsocket対応
  • メンテナンス性

REST APIならAPIゲートウェイとLambdaを使える
双方向通信はAWS IoTのMQTT通信で実現

メリット

  • スモールスタートが可能
  • フルマネージド(保守運用の手間がへる)
  • カスタマイズ性

サーバレスアーキテクチャの開発フローの確立

Swaggerの活用

  • REST APIのモデリング
  • API ゲートウェイにインポート可能

Lambdaコードのテスト

  • DynamoDB localやdockerを利用した単体テスト環境

デプロイ

  • Cloud Formation, CLIを使って自動化

    • APIゲートウェイのステージ、Lambdaのaliasを使った環境切り替え、バージョン管理

今後の展開

  • 対応家電の追加
  • 対応ゲートウェイの追加

個人的所感

サーバレスアーキテクチャの開発フローが一番面白かったのにサクッと終わってしまった…
Lambdaはテストがデプロイ前のテストがやりづらいことに定評があるのでノウハウをしっかりと聞きたかったところ。
M-1グランプリの事例よりもピクセラの方がサーバーレス度高いやん!

続きを読む

Amazon Linuxコンテナイメージは AWS Lambda ネイティブライブラリ(.so)問題を解決するし財布にも優しい

全世界が泣いた!Amazon Linux Container Image!

New Amazon Linux Container Image for Cloud and On-Premises Workloads | AWS Blog
https://aws.amazon.com/jp/blogs/aws/new-amazon-linux-container-image-for-cloud-and-on-premises-workloads/

ついに手元に Amazon Linux が!!!

AWS Lambda でネイティブライブラリ(.so)を参照するpythonパッケージをベンダリングするのに使ってみる

Amazon Linux Container Imageを使えば
今までネイティブライブラリ(.so)を使うpythonパッケージを作るのに
EC2でAmazon Linuxを起動して作ってたところの手間が減る!
お財布にも優しい!

準備

コンテナイメージを pull しておく

Amazon Linux Container Image – Amazon ECR
http://docs.aws.amazon.com/AmazonECR/latest/userguide/amazon_linux_container_image.html

を参照。

確認してみる。

$ docker run -it 137112412989.dkr.ecr.us-west-2.amazonaws.com/amazonlinux:latest cat  /etc/system-release
Amazon Linux AMI release 2016.09
$

確かにAmazon Linux! (感涙)

サンプルプログラム

lambdaの中で持っている画像をリサイズして
/tmp/ に出力して
lambdaの中に置きっぱなしで終了する
意味のないプログラムです。
Serverless Framework v1です。

https://github.com/imura81gt/lambda-and-amazon-linux-container/tree/qiita

各種ファイルについて補足すると

一覧

$ tree -FL 1 test/
test
├── Dockerfile  # Amazon Linuxコンテナ
├── event.json  # ServerlessFramework
├── handler.py  # ServerlessFramework
├── lena_std.tif # 画像ファイル
├── requirements.txt # pipでインストールするパッケージ(pip freeze >> requirements.txt)
├── serverless.yml  # ServerlessFramework
├── setup.cfg  # Amazon LinuxにPillowをインストールするのに必要だった
└── yum.list # Pillowをインストールするのに必要なyum repo管理のパッケージ

1 directory, 8 files

Dockerfile

FROM 137112412989.dkr.ecr.us-west-2.amazonaws.com/amazonlinux:latest

RUN mkdir /app

WORKDIR /app

# lambdaは python2.7で動く
RUN yum install python27 python27-pip python27-devel python27-libs gcc  -y

ADD . .
# pipでインストールするパッケージが依存しているモノは yum.list に書いとく
# ONBUILD RUN とかにすればよかったかも
RUN yum install $(echo $(cat yum.list)) -y

CMD pip install --no-cache-dir -r /app/requirements.txt -t /app/vendor

NGパターン :bomb: ネイティブライブラリを使うpythonパッケージをOSX上でpipインストールしたら…

まちがって OSX 上で pip install したモノをそのまま Lambda にアップロードしたら…

$ cd test
$ # OSXやCentOS上で pip install した
$ # ネイティブライブラリを使うパッケージ
$ # (ex.Pillow)はLambda上では動かない。
$ pip install -r requirements.txt -t vendor/
$ $(npm bin)/sls deploy
$ $(npm bin)/sls invoke -f hello

エラーが出る

{
    "errorMessage": "Unable to import module 'handler'"

}

ログを見ると

$ $(npm bin)/sls logs -f hello

Unable to import module 'handler': /var/task/vendor/PIL/_imaging.so: invalid ELF header と出てます。

START RequestId: fea3d675-a59c-11e6-9977-9db5192e5233 Version: $LATEST
Unable to import module 'handler': /var/task/vendor/PIL/_imaging.so: invalid ELF header

END RequestId: fea3d675-a59c-11e6-9977-9db5192e5233
REPORT RequestId: fea3d675-a59c-11e6-9977-9db5192e5233  Duration: 0.24 ms   Billed Duration: 100 ms     Memory Size: 1024 MB    Max Memory Used: 32 MB

OKパターン:star: Amazon Linux Contaier Imageを使う

Amazon Linux Container Images をpullしておく
http://docs.aws.amazon.com/AmazonECR/latest/userguide/amazon_linux_container_image.html

$ cd test
$ rm -rf vendor/
$ docker build . -t amazon-py
$ docker run -it -v $(pwd)/vendor:/app/vendor:rw -t amazon-py
$ $(npm bin)/sls deploy
$ $(npm bin)/sls invoke -t hello

実行結果は

null

ログにもエラーなし

$ $(npm bin)/sls logs -f hello
START RequestId: e9003709-a59e-11e6-b541-0f7fe552a859 Version: $LATEST
END RequestId: e9003709-a59e-11e6-b541-0f7fe552a859
REPORT RequestId: e9003709-a59e-11e6-b541-0f7fe552a859  Duration: 169.37 ms Billed Duration: 200 ms     Memory Size: 1024 MB    Max Memory Used: 33 MB

期待

DynamoDB Local みたいな感じで、 Lambda Localが誕生するといいなぁ。

続きを読む

MacにAWS DynamoDB localを入れてRails runnerからアクセスしてみる

DynamoDBがローカルで実行できるらしいので開発用に試してみた。

ダウンロード・展開

wget http://dynamodb-local.s3-website-us-west-2.amazonaws.com/dynamodb_local_latest.zip #ダウンロード
unzip dynamodb_local_latest.zip #展開

起動

java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb #デフォルトだとPort8000で起動

接続

# aws cliがインストールされていれば、endpointを指定するだけで使える
local$ aws dynamodb list-tables --endpoint-url http://localhost:8000
{
    "TableNames": [
        "test"
    ]
}

Railsから使う

runnerでやってみた。
※Rails 4.2.6

Gemfile
#SDKをGemで追加
gem 'aws-sdk'
lib/tasks/dynamo.rb
gem 'aws-sdk-core'

class Tasks::Dynamo
  def self.execute
    dynamo = Aws::DynamoDB::Client.new(
        endpoint: 'http://localhost:8000' #ローカルに対して
    )

    options = {
        table_name: "products",
        attribute_definitions: [
            {
                attribute_name: "product_code",
                attribute_type: "S"
            }
        ],
        key_schema: [
            {
                attribute_name: "product_code",
                key_type: "HASH"
            }
        ],
        provisioned_throughput: {
            read_capacity_units:  1,
            write_capacity_units:  1
        }
    }

    dynamo.create_table(options) #Table作成

    p dynamo.describe_table({table_name: "products"}) #テーブル情報取得
  end
end
local$ rails runner "Tasks::Dynamo.execute"
Running via Spring preloader in process 6438
#<struct Aws::DynamoDB::Types::DescribeTableOutput table=#<struct Aws::DynamoDB::Types::TableDescription attribute_definitions=[#<struct Aws::DynamoDB::Types::AttributeDefinition attribute_name="product_code", attribute_type="S">], table_name="products", key_schema=[#<struct Aws::DynamoDB::Types::KeySchemaElement attribute_name="product_code", key_type="HASH">], table_status="ACTIVE", creation_date_time=2016-10-31 08:20:37 +0000, provisioned_throughput=#<struct Aws::DynamoDB::Types::ProvisionedThroughputDescription last_increase_date_time=1970-01-01 00:00:00 +0000, last_decrease_date_time=1970-01-01 00:00:00 +0000, number_of_decreases_today=0, read_capacity_units=1, write_capacity_units=1>, table_size_bytes=0, item_count=0, table_arn="arn:aws:dynamodb:ddblocal:000000000000:table/products", local_secondary_indexes=nil, global_secondary_indexes=nil, stream_specification=nil, latest_stream_label=nil, latest_stream_arn=nil>>

無事表示された。

続きを読む

DynamoDBをlocal環境で動かしてみる

Dynamodbをlocal環境で動かすメモ

AWS上で使っているDynamodbに対して色々開発段階でデバッグなどしたい!
でも、アクセスしまくって課金されるのは困る。
みたいな時、Dynamodbには「DynamoDB Local」という、ローカルマシン内で稼働するdownloadable version of DynamoDBが公開されています。

公式のドキュメントに詳しく書かれています。
日本語版もあります。

環境

OSX 10.11.4
Java 1.8.0_101
IntelliJ IDEA 2016.1.4
Homebrew 1.0.8 もしくは pipとか(今回はHomebrewを使っています)

DynamodbLocalのセットアップ

[Step1]DynamodbLocalをダウンロード

ドキュメントにもある通りいずれかからダウンロード。

形式 URL
tar.gz 形式 http://dynamodb-local.s3-website-us-west-2.amazonaws.com/dynamodb_local_latest.tar.gz
zip 形式 http://dynamodb-local.s3-website-us-west-2.amazonaws.com/dynamodb_local_latest.zip

[Step2]任意のディレクトリ下で展開し、そのディレクトリ下に移動

こんな感じです
スクリーンショット 2016-10-27 8.33.10.png

[Step3]DynamodbLocalを起動

ここも、ドキュメントにあるようにまずはDynamodbLocalを起動してみます。

$ java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb
Initializing DynamoDB Local with the following configuration:
Port:   8000
InMemory:   false
DbPath: null
SharedDb:   true
shouldDelayTransientStatuses:   false
CorsParams: *

これで起動は完了です。終了する場合はCtrl+Cです。

AWS コマンドラインインターフェイス (CLI)のインストール

[Step1]インストールします

公式ドキュメントではpipでaws-cliをインストールしていますが、homebrewで今回はインストールします。
これによって、コマンドラインで、Dynamodbのテーブルを作成したり、確認したりいろいろできるようになります。

$ sudo brew install awscli
Password:
~~~~省略~~~~
==> Renamed Formulae
stash-cli -> atlassian-cli
==> Deleted Formulae
dwarf                       jsdoc-toolkit               puddletag                   sonar-runner
homebrew/science/minc       libmarisa                   qbzr                        treeline

==> Downloading https://homebrew.bintray.com/bottles/awscli-1.11.10.el_capitan.bottle.tar.gz
######################################################################## 100.0%
==> Pouring awscli-1.11.10.el_capitan.bottle.tar.gz
==> Caveats
The "examples" directory has been installed to:
  /usr/local/share/awscli/examples

Before using aws-cli, you need to tell it about your AWS credentials.
The quickest way to do this is to run:
  aws configure

More information:
  https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html
  https://pypi.python.org/pypi/awscli#getting-started

Bash completion has been installed to:
  /usr/local/etc/bash_completion.d

zsh completion has been installed to:
  /usr/local/share/zsh/site-functions
==> Summary
🍺  /usr/local/Cellar/awscli/1.11.10: 3,459 files, 28.4M

[Step2]aws configureの設定

http://www.task-notes.com/entry/20141026/1414322858
などを参考に。

$aws configure
AWS Access Key ID [None]: accesskey
AWS Secret Access Key [None]: secretkey
Default region name [None]: localhost:8000
Default output format [None]: 

[Step3]DynamoDB JavaScript consoleを立ち上げる

DynamodbLocalを起動している状態で、
http://localhost:8000/shell/
を開くと、以下のようなDynamoDB JavaScript consoleが開きます。

詳細な使い方についてはこちらがかなりわかりやすかったです。
http://dev.classmethod.jp/cloud/aws/dynamodb-local-shell-console/

[Step4]DynamodbLocalのtableをaws-cliで出力

$ aws dynamodb list-tables --endpoint-url http://localhost:8000
{
    "TableNames": []
}

ここではまだ何もtableが登録されていないのでtableは空です

IntelliJ IDEAでDynamodbを触るプロジェクトを作ってみる(Java)

[Step1]IntelliJをダウンロード・インストールします

Comunity(無償)でも問題ないと思いますが(使ったことないですが)学生はUltimateも無料なのでそちらを使いましょう!
https://www.jetbrains.com/idea/
違いについてはこちらにまとまっています

[Step2]IntelliJを起動

[Step2]プロジェクトを作成(Create New Project)

Mavenを選択しProjectSDKはJava1.8にしました。


[Step3]テーブル作成のプロジェクトを作る

以下のStepではこのGettingStartedを参考にしています。というかコードはそのままです。
https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/gettingstartedguide/GettingStarted.Java.html

Javaファイルを作成します

作成先で以下のコードをコピペしてみましょう。

MoviesCreateTable.java
import java.util.Arrays;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
import com.amazonaws.services.dynamodbv2.model.KeyType;
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType;

public class MoviesCreateTable {

    public static void main(String[] args) throws Exception {

        AmazonDynamoDBClient client = new AmazonDynamoDBClient()
                .withEndpoint("http://localhost:8000");

        DynamoDB dynamoDB = new DynamoDB(client);

        String tableName = "Movies";

        try {
            System.out.println("Attempting to create table; please wait...");
            Table table = dynamoDB.createTable(tableName,
                    Arrays.asList(
                            new KeySchemaElement("year", KeyType.HASH),  //Partition key
                            new KeySchemaElement("title", KeyType.RANGE)), //Sort key
                    Arrays.asList(
                            new AttributeDefinition("year", ScalarAttributeType.N),
                            new AttributeDefinition("title", ScalarAttributeType.S)),
                    new ProvisionedThroughput(10L, 10L));
            table.waitForActive();
            System.out.println("Success.  Table status: " + table.getDescription().getTableStatus());

        } catch (Exception e) {
            System.err.println("Unable to create table: ");
            System.err.println(e.getMessage());
        }

    }
}

真っ赤になりました。

Add Maven Dependency… を行います

全て選択しAddします

右上にImport Changesとでてくるのでクリックすると

赤いのが消えます!

これはpom.xmlを確認してもらえれば分かりますが

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>hiroki11x</groupId>
    <artifactId>DynamodbSample</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-dynamodb</artifactId>
            <version>1.11.41</version>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-dynamodb</artifactId>
            <version>1.11.41</version>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-dynamodb</artifactId>
            <version>1.11.41</version>
        </dependency>
    </dependencies>
</project>

みたいにプロジェクトが依存するライブラリが指定されています。

現段階ではaws-cliでテーブルのリストを出力すると

$ aws dynamodb list-tables --endpoint-url http://localhost:8000
{
    "TableNames": []
}

ですが、先ほどのMoviesCreateTable.javaを実行すると

$ aws dynamodb list-tables --endpoint-url http://localhost:8000
{
    "TableNames": [
        "Movies"
    ]
}

Moviesが追加されました!
あとはGettingStartedに色々あるので引き続きこの記事に追加していこうと思います。

参考

続きを読む

AWS DynamoDB local の実行とアクセス方法(WindowsのGUIクライアントからも見れる!)

ダウンロードとインストール

AWS Documentation: コンピュータでの DynamoDB の実行

bash
wget http://dynamodb-local.s3-website-us-west-2.amazonaws.com/dynamodb_local_latest.tar.gz
tar xzvf dynamodb_local_latest.tar.gz

起動

java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb

  • -dbPathを指定すると、DBファイル格納フォルダを指定できるため、Vagrantでフォルダ共有とかする場合は便利。
  • -sharedDbをつけると指定されたリージョンごとのDBファイルではなく、単一のファイルにデータが格納される。

アクセス方法

CLIから

AWS Documentation:ローカルエンドポイントの設定

aws dynamodb list-tables --endpoint-url http://localhost:8000

JavaScript(node)SDKから

aws_sdk
var AWS = require("aws-sdk");
AWS.config.endpoint = new AWS.Endpoint('http://localhost:8000');

var db = new AWS.DynamoDB();
db.listTables(function(err, data) {
  console.log(data.TableNames);
});

dynamooseモジュールから

with_dynamoose
var dynamoose = require("dynamoose");
dynamoose.AWS.config.endpoint = new dynamoose.AWS.Endpoint('http://localhost:8000');

var Cat = dynamoose.model('Cat', { id: Number, name: String });
var garfield = new Cat({id: 666, name: 'Garfield'});
garfield.save();
Cat.get(666)
.then(function (badCat) {
  console.log('Never trust a smiling cat. - ' + badCat.name);
});

GUI(SQLite browser)から

stackoverflow:dynamodb-client-in-local-with-ui-like-phpmyadmin

DynamoDB LocalはSQLiteをバックグランドで使っているのでSQLite Database Browser上で中を見ることが出来るようです!!!!!!

  1. ローカルのDynamoDBをデータベースパスを指定して起動
    java -Djava.library.path=. -jar DynamoDBLocal.jar -dbPath /vagrant_data/dynamodb_data

  2. SQLite Database Browserをダウンロードしてインストール

  3. SQLite Database Browserを起動、Open DatabaseからDynamoDB起動時に指定したパスのXXX.dbファイルを開く

  • Windows上のVagrantで起動しているDynamoDB localでも、Vagrantの共有フォルダにDBファイルを出力するようにしておけば、GUIで見れるし、SQLが使えます。
  • DynamoDB localのデフォルトでは、起動したjarファイルと同じ場所にDBファイルが出力されます。

Javascript Shellから

チュートリアル: DynamoDB の基本的な運用

インタラクティブなチュートリアルが用意されています。優し~い!

  • ブラウザから以下にアクセスするとWebからJSのコードでDynamoDB localをいじれます
    http://localhost:8000/shell

  • Vagrantや、リモートの場合は動いているサーバーのURLに適宜変えてくださいませ~。


続きを読む