読者です 読者をやめる 読者になる 読者になる

production.log

ピクスタ株式会社でエンジニアのマネージャーをやっている星直史のブログです。

AWS Rekognitionで画像内の物体名を取得する方法

概要

AWS re:Invent 2016で発表された新機能の中にAWS Rekognitionという画像内の物体、シーン、および顔を検出するサービスが発表されました。
今更感ありますが、Rekognitionで画像内の物体名を取得する方法を書きます。

Lambdaでの処理

'use strict';
const AWS = require("aws-sdk");
AWS.config.region = 'us-west-2';
AWS.config.apiVersions = { 
  rekognition: '2016-06-27',
};
const rekognition = new AWS.Rekognition();

module.exports.handle = (event, context, callback) => {

  var params = { 
    Image: {
      S3Object: {
        Bucket: "BUCKET_NAME",
        Name: "OBJECT_KEY"
      }   
    }   
  };  
  rekognition.detectLabels(params, function(err, data) {
    if (err) console.log(err, err.stack); // an error occurred
    else     console.log(data);           // successful response
  });
};

こんだけ!!
基本的にはS3のバケットとオブジェクトのキーをparamsに指定するだけです。
処理速度は2秒かかることもあるので、バッチ処理以外ではあまり使えなさそうです。
また、当たり前ですが、固有名詞は出てきませんし、返却されるラベルは全て英語です。
そのため、例えば、東京タワーの画像を解析した場合BuildingとかTowerというラベルが返却されます。

注意事項

Rekognitionは使用できるリージョンがとても少ないです。記事投稿時点では下記の3リージョンでしか使用できません。

  • us-east-1
  • us-west-2
  • eu-west-2

Tokyoリージョンに来ることを期待!

まとめ

たった数十行で画像内の物体を検出できるとは、未来に生きている気がしました。

Rekognitionは他にも、人物の顔を認識して傾きとかを検出したり、人物画像同士の比較をし、同一人物かを検出したり、アダルト画像か検出したりできます。価格も100万枚まで1,000枚あたり1.00USDだったり、低価格なのも魅力です。

一方GCPのVision APIはAWSと比較して高機能である代わりに1,000ユニットあたり$1.50 と多少割高になります。

Rekognitionに存在しない機能を使いたい時はGCPを使う感じですかね。

【ServerlessFramework】lambdaでgmを使って画像加工をする方法

概要

ServerlessFrameworkにおいて、Lambdaで画像加工をする場合、Imagemagickかgm(GraphicsMagick)を使用することになります。
AWS公式ドキュメントではgmを使用しているので、今回はgmを使用してサムネイル作成処理について、書こうと思います。

ハマりどころ

gmがrequireできなくてハマった

lambdaのコード中に

const async = require('async');
const gm = require('gm').subClass({ imageMagick: true });

といった感じでrequireしなければならないのですが、単純にこのように記述してデプロイしても、 module not found的な感じでデプロイしてすぐにエラーとなってしまいました。

lambdaをデプロイするとAWSの中で既にmoduleが用意されているものだと勘違いしていました。

改めてAWS公式ドキュメントを読むと.zipを作成しなければならないので、手順に従って回避します。

デプロイパッケージの作成

次にハマったのが、ServerlessFrameworkにおいて、デプロイパッケージの作成です。 AWS公式ドキュメントでは下記の通りにファイルを配置し、.zipを作成せよ。とあります。

CreateThumbnail.js
/node_modules/gm
/node_modules/async

上記の通り.zipを作るのは良いんですが、ServerlessFrameworkの枠組みからいきなり外れてしまうのが問題でした。*1

ただ、sls deploy後の.zipはS3の所定のバケットに配置されることに気づきました。 つまり、ServerlessFrameworkも実は単純を.zipを作ってlambdaにデプロイしているのだとわかりました。

そのため、単純にlambdaの処理が記述されているフォルダの直下で下記を実行すれば/node_modules/が出来上がります。

  • package.jsonを用意
  • yarn add async gm

その後にいつものようにsls deployをすれば完了です。

あとは
aws lambda gm とかで検索するとgmでの処理についての記事が出て来るのでそれを参考に!

まとめ

当たり前ですが、まずは、AWSのドキュメントをしっかり読むこと!!これが重要だと感じました。
他のブログではサムネイル作成処理については書かれているのですが、
ServerlessFrameworkと組み合わせた場合の方法は書かれていない印象だったので、この記事が参考になればと思います。

*1:単純にsls deploy だけで完結しなくなってしまう

なぜ自分はストイックなのかを考えてみた

概要

他の人と話していると、自分はストイックだと言われることが多いので、それはなぜなのか考えてみた。

自分の考えの傾向

自分は物事の多くを投資の対象として捉える癖がある。 それは、金銭、時間、効率などの観点から考えることが多い。

例えば、通勤用のクロスバイクを購入する時はこんな考えだった。

電車の場合: 往復60分、定期代月額5,000円 自転車の場合: 往復40分, 駐輪代月額3,000円、初期購入費用50,000円

  1. 1日20分節約できるので、自分の給与から時給を算出して、20分ぶんの費用が浮く
  2. 運用コスト月額2,000円の差がある
  3. 50,000円 / (1+2のコスト)

を考えた上で、決断する。要は、回収できるの?ということを考えた。 自転車に対する好みはほとんど考慮しておらず、投資効果だけを考えて買った。 そのため、決断は、ほぼ即決。

また、自転車を2年くらい運用すれば、リターンが蓄積されるので、 自分にとっては、そのリターンが最大の喜びになる。

自分にとってストイックとは?

自分の傾向を基に、なぜストイックなのかを考えて、結論まで出してみた。 自分が思うストイックとは、自分という投資対象に自分の資本(金銭、時間など)における比率が高ければ高いほどストイックになっていくのだと思う。

大概の場合、投資は不確定要素や隠れた前提、事実が多いため、資本の投下を悩むことが多い。 また、100%増えるとわかっていても投資収益性が低ければ、投資を渋ることも多い。

しかし、自分に対する投資は、以下の点に置いて不確定要素が少ない。

  1. 自分に対して自分が投資をするので、投資をする側と受ける側の情報の透明性が保たれている
  2. 自分の行動次第で、投資効率や収益性を最大限に高めることができる
  3. 投資をする側と受ける側の利害が完全に一致している

つまり、投資判断における情報を投資家が全て掌握している点、 利率を上げようと思えば、投資家自身でコントロールできる点と投資を受ける側の納得を100%得られる点、 また、投資を受ける側と投資する側の両方が自分であるため、スピーディーに投資判断ができるため、 非常に優れた投資対象であると思う。

※これを読むと超絶ナルシストだと感じるかもしれないが、自分のことは特に優れていると感じていない。ただ、成長の余地は十分にあると踏んでいるため、惜しみなく投資しているって感じ。 ※あとは、ダラダラする時間は、何の利益も生まない時間だから苦手。

結論

自分の成長は投資家である自分に対して責任を持ちたいのと、 リターンは確実に自分(や関係者)に返ってくるため、成長に対して手を緩めることはしたくない。 この思想から来る行動が、他の人から見るとストイックなんだぁ〜と思われる要因なのだろう。

というのが結論。

ゴールデンウィークにペンションひみつ基地でお手伝いをしてきました

概要

ひょんなことから、今年のゴールデンウィークの8日間を栃木県那須塩原市のペンションひみつ基地のお手伝いを住み込みですることになったので、そこで得た学びを書こうと思います。

やったこと(業務内容)

お手伝いとはいえ、学びを得ることが目的だったので、分業化されている(であろう)大規模リゾートホテルではなく、個人経営の小規模ペンションに的を絞りました。
ですので、業務内容としては、ペンションの現場レベルの仕事は、ほぼ全て体験しました。
具体的には下記の通りです。

  • テーブル設営
  • 朝食の調理、配膳、皿洗い
  • ベッドや部屋の清掃
  • ベッドメイキング
  • 風呂場、洗面所、トイレ清掃
  • 掃除機、窓拭き
  • 夕食の調理、配膳、皿洗い

文面で見ると大したことなさそうですが、限られた人的リソースで、短時間で行わなければならないのと、ハイシーズン時の20人前後のお客様が襲来したときは死ねます。
ハードモードの縛りプレイ状態です。
むしろ、全員野球で取り組まなければ成り立たないと思いました。

8日間で得られた学び

自営業のことを学べた

ペンションひみつ基地は、基本的に夫婦2人で経営しており、ハイシーズン時に、パートさんや住み込みアルバイトさんを雇い、経営しています。
逆にオフシーズンの平日などは、比較的のんびりされているようでした。そのため、収支や労働の負荷の浮き沈みが激しく、究極のメリハリだと感じました。

また、自分の中で自営業のイメージは、従業員100人以上を雇い、売り上げも数十億を目指すのが自営業(というか起業)のイメージだったのですが、大きく稼げはしないが、夫婦二人としては余裕で暮らすことができ、仕事以外の時間も楽しめる形があるのだと学びました。
自分の人生の選択肢や考え方が一つ増えたことは大きな学びだと感じました。
那須の自然が豊かなところや、土地柄なのか、時間がゆっくりしており、このような働き方もあるのだと学びました。

あとは、自営業とはいえ、ペンション業界で生き抜くための戦略が感じられました。
ペンションひみつ基地は囲炉裏タイムと呼ばれる宿泊者同士のコミュニケーションの場を設けていたり、
めちゃくちゃ可愛い看板猫(現在は4匹)がいたり、ニジマス釣(リアルにキャッチアンドイート)りや露天風呂で日本酒が楽しめたりするなど*1、単純に宿泊するだけではなく、宿泊者に非日常感を味わせる仕組みを設けていると感じました。
8日間を通して、リピーターの方が一定数いたことや、宿泊予約サイトの管理画面で上位に位置付けていることを見た時は納得感がありました。

初心に帰った

今回は、異業種の仕事に対して、未経験者が一から全てを学びにいくので、完全に新人状態からスタートすることになります。 ウチの会社では勤続年数が比較的長いことや、メンバーに指導する立場にいるので、新人の感覚に戻れたのはとても新鮮でした。

まず、異業種の仕事をするので、単純に知的好奇心が満たされました。
単調な作業の繰り返しといえば繰り返しなのですが、

  • 前回の作業時間より1秒でも短くするためには、どのように動いたら効率的なのか
  • 次の作業を考えた時に、今やるべき作業は何か
  • 上司は何を考えて自分に作業指示を出しているのか
  • 上司のスキルを盗み、自分の行動に反映させ、成長を高速化させる

などなど、新人の頃に重点的に心がけていたことを、今やり直すことで、今の会社で(少なからず)奢っていた自分を恥じ、反省する機会になりました。
これらの作業中に、守破離はだいぶ意識しました。
まずは、ベーシックな作業内容を覚え、体に染み込ませ、
次に、ベーシックな作業内容をいかに改善できるかを考え、
最後に自分のやり方を編み出す。といった感じです。
特に、夕食の片付けにおいては、自分の作業領域を完全に作り出し*2、クローズまでの時間を最短にすべく、最速で動くことができました。

他には、最初の1,2日感の緊張感はすごかったです。
その緊張感とは、上司に「こいつ使えねぇな」と思われたら終わりだ という緊張感です。
当たり前ですが、初日から全力で取り組み、短期間で上司に対する信頼貯金をいかに増やすかを考えて行動しました。
心地よい緊張感で仕事に取り組めたのはよかったです。
信頼残高を積み上げていき、信頼されるまでを短期間で感じ取れたので、仕事の醍醐味の一つを味わえてとても楽しかったです。

異業種の仕事内容に触れることができた

単純に、宿泊業で働くのは初めての体験だったので、何もかもが新鮮で楽しく学び、仕事をすることができました。
特に、宿泊者がチェックアウトした後の清掃はとても興味深かったです。
というのも、普段、自分がホテルなどに泊まるとき、いつも、生活感がなく清潔な部屋だなぁ〜と感じるのですが、それを演出する側に回れたからです。
数時間前までに人が存在したことを感じさせないように、ベッドを整えるのは、だいぶ神経を使う作業であり疲れはするのですが、宿泊施設の提供者としての体験ができたので、とても貴重な時間でした。

また、戦術した生存戦略を実行していく中で、
自分達のサービスが目に見えるお客様に対して直接ぶつけることになるので、お客様のリアルな反応をリアルタイムで感じることができ、 一つの成功や失敗のヒリヒリ感はWebサービスとは全く異なる体験でした。
配膳などの小さな作業*3でも、全てお客様の事を考えぬいて配置/提供されていたので衝撃を受けました。

その他感じたこと

仕事

8日間、住み込みだったんですが、お客様の朝食を作ることから始まり、夕食の片付けで終わるので、1日の生活にリズム感があり体調崩すことなく終えることができたのは良かったです。
ただ、単調といえば単調だった仕事なので、おもしろくないと感じる人は一定数いるのではないかと思います。
でも、自分としては、単調な仕事でも前回の作業より1秒でも早く終わらせたり、効率化について考えたりするので、楽しく仕事をすることができました。

猫様が4匹おり、当初大丈夫かなと心配してたんですが、すごく癒しなりました。むしろ、飼おうか検討しているレベルです。夜寝てる時に毎晩お腹の上に乗って来る猫がいたんですが、懐かれたな〜という感覚が嬉しかったです。
f:id:watasihasitujidesu:20170505120713j:plain f:id:watasihasitujidesu:20170511084829j:plain f:id:watasihasitujidesu:20170503124329j:plain

温泉

温泉ペンションなので、蛇口をひねると温泉が出て来ます。
硫黄の温泉なので、温泉に入っている感(?)がすごくありました。また、毎晩広い風呂に入れるのは、温泉/銭湯好きのオレとしては最高でした。
(水風呂は欲しかったが・・・・ッ!!)

避暑地 那須

避暑地として有名な那須にペンションがあるのですが、日に日に木々が青くなってきたり、川のせせらぎが聞こえるので、心がリフレッシュされる感覚がありました。
何もないことの贅沢感がありました。(普段東京にいるからかも?)
f:id:watasihasitujidesu:20170503134522j:plainf:id:watasihasitujidesu:20170503134300j:plainf:id:watasihasitujidesu:20170503134034j:plainf:id:watasihasitujidesu:20170503124405j:plain

筋肉痛

8日間、全力で働いたら筋肉痛になりました。
立ち仕事が多いので、脚はもちろん、窓拭きで肩周りや腕周りも筋肉痛になりました。
3日目あたりに調子に乗って筋トレしたのは失敗でした。。
ジムでは鍛えることができていないなぁ〜と思ったのと、実用的な筋肉もつけなきゃなぁと感じました。

みんな良い人

ペンションで働いている人は全員良い人でした。
オーナー夫婦はもちろん、パートできてる方まで、とても優しく、仕事ができる人だなぁと感じました。
自分は、心理的安全性が確保されている環境ではないと、パフォーマンスを発揮できないのと、その安全性は一緒に働く人によって左右されることを経験から学んでいます。
繰り返しになりますが、一緒に働く人が親切で優しかったのと、教えるのがうまかったので、自分の仕事がうまくいったんだと思います。
また、パートの方には、出会って1時間で潔癖症だとバレました。何か行動するたびに、めっちゃ手洗うので、それを見られていました。。
あと、ストイックな食生活を話してしまったので、完全に変な人だと思われただろうなと感じてます。パーフェクト食生活をしているだけで変な人ではないことは強調しておきます!

まとめ

スポットで、ですが、異業種で働くことで、さまざまなことを学ぶことができました。

  • 人生における仕事観に少なからず影響があった
  • 初心に帰ることができた
  • 異業種の仕事がどんなものか知れた

また、那須の自然に触れられたことや、本職での仕事の疲労感とは全くことなる疲労感は、とても心地よかったです。
いつものゴールデンウィークはゴロゴロして終わってしまうのですが、今回は何かから学びを得ようと試みたゴールデンウィークであり、 その収穫の多さからとても充実感がありました。

普通の生活をしていたら個人経営のペンションで働くことはありえないと思うので、人生の中でも思い出に残るのではないかと思っています。 あぁ〜楽しかった!
f:id:watasihasitujidesu:20170511084711j:plain

*1:その他、蛇口から温泉が出たり、ハンモック、夏は花火などあります

*2:たと自分では評価しています

*3:作業時間という意味での小さな作業

RedashでAWS DynamoDBに対してDQLを発行しデータ取得する方法

概要

表題の通りです。 RedashでDynamoDBからデータ取得する場合DQLという、SQLライクな書き方でデータを取得することになります。
今回はDynamoのIndexを指定して絞り込みを行う際に、ちょいハマりしたので、メモとして記録します。

AWS DynamoDBの設定

テーブル構造

  • Table name: test_tables
  • Primary partition key: partition_id (Number)
  • Primary sort key: sort_id (Number)

インデックス

  • Name: partition_id-index
  • PartitionKey: partition_id (Number)

DQLの書き方

test_tablespartition_idで絞り込んでデータ抽出したい場合、DQLでは下記の書き方となります。

SELECT *
FROM test_tables
WHERE partition_id = 60515 USING partition_id-index; # USINGでindex名を指定することが重要

WHERE句で属性名だけで条件指定したいところですが、DynamoDBの特性上、インデックスを指定しなければなりません。
そのため、上記のように、partition_idはpartition_id-indexというインデックスやで〜
というように明示させる必要があります。

もし、USINGを書かないと下記エラーが発生します。
No index specified with USING <index>, but multiple possibilities for query: TABLE, partition_id-index

自前で立てたRedash(0.12.0+b2449)を最新1.0.3にupgradeする方法

概要

Redashが0.12.0の時に、AWS AMIから立ち上げたのですが、
この度、メジャーバージョンアップしたので、手動でupgradeした時のメモです。

手順もなにも、ドキュメントがあるのでその通りにやるだけですが、 コマンド実行する際に設定を変更する必要があったので、その説明をします。

手順

ドキュメントの通りwgetし、実行権限を付与。

wget https://raw.githubusercontent.com/getredash/redash/master/bin/upgrade
chmod +x upgrade

その後、処理を走らせます。

sudo ./upgrade

すると、こんなメッセージが出ます。

Before upgrading to this version, please make sure to do the following changes to your /opt/redash/.env file:
    
1. If you have local PostreSQL database, you will need to update the URL from `postgresql://redash` to `postgresql:///redash`.
2. Remove the `REDASH_STATIC_ASSETS_PATH` definition.

Make sure to complete these changes before doing the actual upgrade.

この変更をしなければ、DBが見当たらない旨のエラーが吐かれてアップグレードできないので、 /opt/redash/.envを開き、下記の通り修正します。

  1. postgresql://redashpostgresql:///redashに修正。*1
  2. REDASH_STATIC_ASSETS_PATHを消す。

まとめ

ドキュメントには書かれておらず、実行時に注意してくる感じなので、
英語読めない><
とりあえずy ><
ってやってると見落としまうので、上記修正は忘れず対応しましょう!

*1:/が一つ多くなる

【ServerlessFramework】DynamoDB Streamsでデータの更新をトリガーにLambdaを動かす方法。

概要

表題の通り、DynamoDB Streamsでデータの更新をトリガーにLambdaを動かす方法について説明します。

AWSマネジメントコンソール上でのDynamoDB Streamsの有効化

まず、AWSマネジメントコンソールでStreamsを有効化する必要があります。 DynamoDB > Tables > テーブル選択 > Overview > Stream details > ManageStreamを選択

これを選択すると、ストリームを経由して渡されるデータ構造を選択できるラジオボタンが表示されます。 それぞれの意味は下記の通りです。

  • [Keys only] — 変更された項目のキー属性のみ取得。
  • [New image] — 変更後のデータのみ取得。
  • [Old image] — 変更前のデータのみ取得。
  • [New and old images] — 変更後、変更前のデータの両方取得。

あとは作成すれば、ARNが表示されます。

serverless.ymlの設定

次にserverless.ymlに設定を書き、Lambdaと紐づける必要があります。

provider:
  name: aws
  runtime: nodejs6.10
  stage: dev
  region: us-west-2
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - dynamodb:DescribeStream
        - dynamodb:GetRecords
        - dynamodb:GetShardIterator
        - dynamodb:ListStreams
      Resource:
        - "${AWSマネジメントコンソール上に表示されているARN}"
functions:
  dynamo_stream:
    handler: handler.dynamo_stream
    events:
      - stream:
          arn: "${AWSマネジメントコンソール上に表示されているARN}"
          batchSize: 1
          startingPosition: TRIM_HORIZON
          enabled: true

functions.dynamo_stream.events.stream内の各項目は

batchSize: 一度に処理するストリームの数を表します。1~10000まで指定できます。1以上の値を指定した場合、Lambdaの処理の中でループで処理をする必要があります。 startingPosition: 処理開始するストリームの位置を指定します。最新から取得か最古から取得かのどちらかを指定できます。LATEST | TRIM_HORIZON enabled true: 有効 / 無効を指定 true | false

また、eventの設定以外には、IAMの設定も必要となります。 上記の設定項目は最低限の設定です。これを設定しないとsls deploy -vをした時に下記エラーに見舞われます。

  Serverless Error ---------------------------------------
 
     An error occurred while provisioning your stack: DynamoUnderscorestreamUnderscoreproxyEventSourceMappingDynamodbDevitems
     - Cannot access stream AWSマネジメントコンソール上に表示されているARN
     Please ensure the role can perform the GetRecords, GetShardIterator,
     DescribeStream, and ListStreams Actions on your stream
     in IAM..
 

取得できるJSON

{
  "Records": [
    {
      "eventID": "78h9r97gawegaj7ddnga6e6w",
      "eventName": "MODIFY",
      "eventVersion": "1.1",
      "eventSource": "aws:dynamodb",
      "awsRegion": "us-west-2",
      "dynamodb": {
        "ApproximateCreationDateTime": 1492654321,
        "Keys": {
          "id": {
            "N": "123456789"
          }
        },
        "NewImage": {
          "column_a": {
            "N": "2"
          }
        },
        "OldImage": {
          "column_a": {
            "N": "1"
          }
        },
        "SequenceNumber": "405760000000000123456789",
        "SizeBytes": 123,
        "StreamViewType": "NEW_AND_OLD_IMAGES"
      },
      "eventSourceARN": "AWSマネジメントコンソール上に表示されているARN"
    }
  ]
}

しっかり新旧両方のデータが取得できていますね。

解決できなかった点

  1. ServerlessFrameworkだけで完結できない。 説明の通り、AWS マネジメントコンソールでストリームを追加し、それを元にserverless.ymlを設定しなければなりません。 deploy時に失敗するから気づけるとは思うのですが、漏れが発生する気しかしないです。

  2. 設定できるストリームが1テーブルにつき1つ 当初、レコード追加時のストリーム、Aカラムを更新したときのストリーム….といったように、細かく設定できると思っていたのですが、 試して見た結果、「何かしらの変更が加えられたらJSONに新旧両方のデータを詰めて渡すからよしなにやってね」って感じでした。 DynamoDBにカラムが新規作成されたらLambdaAを、更新の時はLambdaBを実行しようとした場合、テンプレートメソッドパターンや、プロキシパターンなり使わないと収集つかなくなりそうな印象を受けました。

まとめ

  1. AWSマネジメントコンソールでStreamsを有効化
  2. serverless.ymlに設定

この2ステップだけでDynamoDBストリームを受け取りLambdaに渡せるのはやはり便利です。 ただ、マネコンとserverless.ymlの両方に設定を加えなければならないのは漏れが発生しそうなので、もう一声といったところでしょうか。