[Sails.js] AWS S3にファイルアップロード・ダウンロード

Sails.js ver 1.0で、AWS S3 にファイルをアップロード・ダウンロードしてみたので、メモします。

ダウンロード(AWS-SDK)

AWSが提供しているaws-sdkを利用します。
AWSの認証情報は、config/awsCredentials.jsonというファイルに入れておく。

config/awsCredentials.json
{
  "accessKeyId": "your access key",
  "secretAccessKey": "your secret key",
  "region": "ap-northeast-1"
}
api/controllers/FileController.js
const AWS = require('aws-sdk');

downloadFile: function (req, res) {
  File.findOne(req.params.id)
  .then((file) => {
    AWS.config.loadFromPath('./config/awsCredential.json');
    var s3 = new AWS.S3();
    var params = {
      Key: file.fd,
      Bucket: 'files-bucket'
    };
    s3.getObject(params, (err, data) => {
      if (err) {
        return res.serverError(err);
      }
      res.attachment(orderFile.filename);
      res.send(data.Body);
    });
  })
  .catch((err) => {
    res.serverError(err);
  });
},

アップロード(skipper-s3)

Sailsはデフォルトでskipprerというbody parserを使っており、skipperのS3アップロード用のアダプターが提供されています。(skipper-s3
これを使うと、S3へのアップロードは簡単に実装できました。

npm install skipper-s3 --save
※ Sailsではskipperはインストール済み

api/controllers/FileController.js
uploadFile: function (req, res) {
  var file = req.file('file');
  file.upload({
    adapter: require('skipper-s3'),
    key: sails.config.custom.awsKey,
    secret: sails.config.custom.awsSecret,
    bucket: 'files-bucket'
  }, (err, uploadedFiles) => {
    if (err) {
      res.serverError(err);
    }
    sails.log("FILES:",uploadedFiles)
    res.ok();
  });
}

AWSの認証情報をsails.config.customに入れます。

config/custom.js
const fs = require('fs');

const awsCredentialFile = 'config/awsCredential.json';
const awsCredential = JSON.parse(fs.readFileSync(awsCredentialFile, 'utf8'));

module.exports = awsCredential;

参考情報

続きを読む

Amazon Athena の API を使ってみた (2017/05)

http://docs.aws.amazon.com/athena/latest/ug/release-notes.html#may-18-2017

2017年5月18日に Amazon Athena にて API が公開されました。
managed presto としての魅力を感じつつも API が存在しないということで original の presto を使っている人にとっては魅力が薄かったサービスですが、 API が公開されることでできることも増えました。

この記事では、 Amazon Athena の API の使い勝手について概観してみようと思います。

API で出来ること

公式ドキュメントにあるように、 API 経由で出来ることは以下になります。

http://docs.aws.amazon.com/athena/latest/APIReference/API_Operations.html

  • BatchGetNamedQuery
  • BatchGetQueryExecution
  • CreateNamedQuery
  • DeleteNamedQuery
  • GetNamedQuery
  • GetQueryExecution
  • GetQueryResults
  • ListNamedQueries
  • ListQueryExecutions
  • StartQueryExecution
  • StopQueryExecution

いくつか API がありますが、出来ることは大別して以下の3つです。

  • Query の実行に関する操作

    • ***QueryExecution
  • Query の実行結果に関する操作
    • GetQueryResults
  • NamedQuery (SavedQuery) に関する操作
    • ***NamedQuery

AWS CLI のセットアップ

この記事では Amazon Athena の API の呼び出しはすべて cli 経由で例示します。
Amazon Athena の API 呼び出しを行うために、あらかじめ awscli を最新版にアップデートしておきます。

$ pip install -U awscli --ignore-installed six
(snip.)
$ aws --version
aws-cli/1.11.90 Python/2.7.10 Darwin/16.5.0 botocore/1.5.53

また、現時点(2017年5月23日)では Amazon Athena は Tokyo Region に来ていないため、region の設定を Athena が動作する region に設定しておく必要があります。

$ cat ~/.aws/config
[default]
region = us-east-1

サポートしていない region を指定して Amazon Athena の cli を実行すると、処理がフリーズし応答がなくなります。(これは、エラーメッセージが表示された方が親切だと思います。)

QueryExecution

StartQueryExecution

http://docs.aws.amazon.com/athena/latest/APIReference/API_StartQueryExecution.html

任意の Presto Query を実行します。
以下からの例では、再現性を考慮しあらかじめ最初から用意されているサンプルデータベースのテーブルを対象にします。
(sampledb.elb_logs)

$ aws athena start-query-execution 
  --query-string 'select * from sampledb.elb_logs limit 1;' 
  --result-configuration OutputLocation=s3://hogehoge/athena-execution-result/
{
    "QueryExecutionId": ".........."
}

--query-string に実行する Presto Query を指定します。
--result-configuration OutputLocation=..... で指定した S3 Bucket に実行結果を保存します。

API の結果として返却される QueryExecxutionId という値を使用して、当該 Query については以後操作することになります。

GetQueryExecution

http://docs.aws.amazon.com/athena/latest/APIReference/API_GetQueryExecution.html

実行した Query の状態などの情報を取得します。

$ aws athena get-query-execution 
  --query-execution-id ()
$ aws athena get-query-execution 
>   --query-execution-id ..........
{
    "QueryExecution": {
        "Status": {
            "SubmissionDateTime": 1495539953.596, 
            "State": "SUCCEEDED", 
            "CompletionDateTime": 1495539955.596
        }, 
        "Query": "select * from sampledb.elb_logs limit 10", 
        "Statistics": {
            "DataScannedInBytes": 850058, 
            "EngineExecutionTimeInMillis": 1651
        }, 
        "ResultConfiguration": {
            "OutputLocation": "s3://hogehoge/athena-execution-result/...........csv"
        }, 
        "QueryExecutionId": ".........."
    }
}

この API の結果からは、Query の現在の状況(実行中か、完了しているか)、開始 / 終了時間などが取得できます。
状態の種類については以下公式ドキュメントに記載されています。
http://docs.aws.amazon.com/athena/latest/APIReference/API_QueryExecutionStatus.html

QUEUED | RUNNING | SUCCEEDED | FAILED | CANCELLED

個人的に、当該 API を cli から使用するときは、 StartQueryExecutionGetQueryExecutionjq コマンドを用いて pipe で繋いで、正しく実行されているかどうかをひと目で確認できるようにしています。
(毎回手で実行するのは手間なので、 warpper shell を用意しています)

$ aws athena get-query-execution 
  --query-execution-id  
  `aws athena start-query-execution --query-string 'select * from kuso_query;' --result-configuration OutputLocation=s3://hogehoge/athena-execution-result/ | jq -r '.QueryExecutionId'`
{
    "QueryExecution": {
        "Status": {
            "SubmissionDateTime": 1495540557.77, 
            "State": "FAILED", 
            "CompletionDateTime": 1495540557.914, 
            "StateChangeReason": "Database, table or column name not found. Please check your query."
        }, 
        "Query": "select * from kuso_query", 
        "Statistics": {
            "DataScannedInBytes": 0, 
            "EngineExecutionTimeInMillis": 67
        },
(snip.)
}

ListQueryExecutions

http://docs.aws.amazon.com/athena/latest/APIReference/API_ListQueryExecutions.html

過去に実行した Query の履歴が取得できます。
特に request parameter で検索条件が指摘できないため、基本的に登録された日時が新しいものから順に取得されます。

$ aws athena list-query-executions 
  --max-results 3
{
    "NextToken": "......", 
    "QueryExecutionIds": [
        "........", 
        "........", 
        "........"
    ]
}

こちらも結果として QueryExecutionId しか返却されず可読性が悪いので、以下のように jq で pipe してみます。
GetQueryExecution には複数の QueryExecutionId が渡せる BatchGetQueryExecution が存在するのでこちらを用います。

$ aws athena batch-get-query-execution  
  --query-execution-ids 
  `aws athena list-query-executions --max-results 3 | jq -r ".QueryExecutionIds[]" | tr 'n' ' '`
{
    "UnprocessedQueryExecutionIds": [], 
    "QueryExecutions": [
        {
            "Status": {
                "SubmissionDateTime": 1495540557.77, 
                "State": "FAILED", 
                "CompletionDateTime": 1495540557.914, 
                "StateChangeReason": "Database, table or column name not found. Please check your query."
            }, 
            "Query": "select * from kuso_query", 
(snip.)
    ]
}

このような形で、直近の Query の実行状況を 1 liner で確認することができます。

GetQueryResults

http://docs.aws.amazon.com/athena/latest/APIReference/API_GetQueryResults.html

実行した Query のデータを取得することができる API です。

$ aws athena get-query-results  
  --query-execution-id ........
{
  "ResultSet": {
    "Rows": [
      {
        "Data": [
          {
            "VarCharValue": "request_timestamp"
          },
          {
            "VarCharValue": "elb_name"
          },
          {
            "VarCharValue": "request_ip"
          },
(snip.)
        ]
      },
      {
        "Data": [
          {
            "VarCharValue": "2015-01-06T12:00:01.612598Z"
          },
          {
            "VarCharValue": "elb_demo_006"
          },
          {
            "VarCharValue": "243.72.152.87"
          },
(snip.)
    "ResultSetMetadata": {
      "ColumnInfo": [
        {
          "Scale": 0,
          "Name": "request_timestamp",
          "Nullable": "UNKNOWN",
          "TableName": "",
          "Precision": 1073741824,
          "Label": "request_timestamp",
          "CaseSensitive": true,
          "SchemaName": "",
          "Type": "varchar",
          "CatalogName": "hive"
        },
(snip.)
}

ただしこの結果内容は、プログラムで扱う分にはまだ良いですが、awscli から扱うには直感的な内容とはい言えないです。

awscli からは、StartQueryExecution 時に指定した OutputLocation から S3 経由で取得する、という方が楽かもしれません。

$ aws s3 cp  
  `aws athena get-query-execution --query-execution-id ........ | jq -r ".QueryExecution.ResultConfiguration.OutputLocation"` ./result.csv

download: s3://hogehoge/athena-execution-result/...........csv to ./result.csv

***NamedQuery

NamedQuery という用語が聞きなれなかったので何を指しているかよくわからなかったのですが、これは Amazon Athena の画面上では Saved Query と表現されているもののようです。

Athena ではよく使う Query などをあらかじめ登録しておくことができる機能がありますが、当該 API はその SavedQuery を操作する API になります。

CreateNamedQuery

http://docs.aws.amazon.com/athena/latest/APIReference/API_CreateNamedQuery.html

$ aws athena create-named-query 
  --name test --description 'for test'  
  --database sampledb  
  --query-string 'select * from sampledb.elb_logs limit 10;' 
{
    "NamedQueryId": "........"
}

QueryExecution と同じような形で NamedQueryId という ID が返却されます。

ListNamedQueries

http://docs.aws.amazon.com/athena/latest/APIReference/API_ListNamedQueries.html

登録されている SavedQuery (NamedQuery) の一覧を取得できます。
特に request parameter で検索条件が指摘できないため、基本的に登録された日時が新しいものから順に取得されます。namedescription などで絞込はできません。

$ aws athena list-named-queries 
  --max-results 3
{
    "NamedQueryIds": [
        "........", 
        "........", 
        "........"
    ], 
    "NextToken": "....."
}

GetNamedQuery

http://docs.aws.amazon.com/athena/latest/APIReference/API_GetNamedQuery.html

あらかじめ登録されている SavedQuery (NamedQuery) の情報を取得できます。
NamedQueryId のみを検索条件に指定可能で、 namedescription などで絞込はできません。

NamedQueryId があらかじめわかっている場合は、以下のような形で pipe で繋ぐことで Query を 1 liner で発行することは一応できます。

$ aws athena start-query-execution 
  --query-string "`aws athena get-named-query --named-query-id ........ | jq -r ".NamedQuery.QueryString"`"  
  --result-configuration OutputLocation=s3://hogehoge/athena-execution-result/
{
    "QueryExecutionId": "........"
}

個人的な感想

ここまで、だらだらと各 API について awscli の例をもとに書いてきました。

いままで JDBC 経由、もしくは Amazon Athena の web console 経由でしか使用ができなかった状況にくらべると格段と可能性は広がったように思えますが、個人的には以下の点で物足りなさを感じています。

  • 全体的に API のインターフェースが気がきいてない
  • ListQueryExecutions API で所定の条件で絞込、ならびに並び替えができない
    • たとえば、実行中の Query だけ取得する、実行時間が長い Query を取得する、ということが API 単体ではできない。
  • ListQueryExecutions の返却結果が QueryExecutionId だけで、情報量が少ない
  • GetQueryResults の使い勝手が悪い
  • 基本的に API の数が少ない
  • このタイミングで Amazon Athena 自身の機能拡張は特に無かった

などなど。

正直、今の機能では積極的にシステム・サービスに組み込んで行くには不足している点が多いと思いますが、期待されているサービスでもありますので今後の進化を期待したいと思います。

個人的に想像しているユースケース

Amazon Athena の API が出る、という話を聞いて、個人的に以下のようなユースケースで使いたいなと感がていました。蛇足になりますが、以下列挙します。

実行時間が長い Query を検知して stop する

現状の API では使い勝手が良くないですが、以下の API を組み合わせることで実現可能です。

  • ListQueryExecutions
  • BatchGetQueryExecution
  • StopQueryExecution

where 句に partition が指定されていない Query を検知して stop する

2017年5月22日現在、Amazon Athena は Tokyo Region に来ていないため、 Tokyo Region の S3 を Athena で使用する場合はどうしても転送量が発生します。

ものすごい巨大なデータ群が入っている S3 Bucket を data source にしている場合、partition を設定していなかったり、もしくは Query に partition 情報が含まれていない場合、膨大な転送量が発生してクラウド破産をしてしまう恐れがあります。
そのため、partition が指定されていない Query を検知し次第、stop をする、というようなユースケースが考えられます。

本来は、Amazon Athena 単体でこのような機能が備わっていてほしいですが、 API を用いることで実現することは可能です。

レポーティングバッチ

おそらくアプリケーション使用用途で一番うれしいのはこのケースだと思います。

レポーティングバッチから Amazon Athena を呼び出して何かしらのレポート処理を行いたい場合、JDBC 経由で対話的に Amazon Athena に繋ぐしか無い状況下では、 Query 実行結果に引きづられてずっとプロセスが待機する必要があったと思います(作りによりますが)

API が提供されたことで、 Query を submit する処理と結果を polling する処理を別に分けることもできますし、そもそも標準機能で実行結果を自動的に S3 にアップできるようにもなりました。

まとめにかえて

長々と書いてきましたが、Amazon Athena は期待の大きいサービスでもありますので、本体の機能も、API の機能についても、より使いやすいものに進化してもらえると、ユーザーとしては嬉しく感じます。

続きを読む

いますぐ使う CloudFront

CloudFrontとは

台数不明で性能不明ですが、グローバルに配置された、キャッシュサーバー。

効果

CloudFrontをリバースプロキシキャッシュとして立ててみました。お問い合わせページなど動的ページを除いて、ほぼ全部のリクエストをCloudFrontが捌いてくれてます。

d0ff6181-11f1-d277-eee8-2d5999566133.jpg

※効果には個人差がございます

課金ポイント

  • 料金 – Amazon CloudFront | AWS

    • データ転送料金
    • キャッシュクリア料金
      • 1ファイル1回クリアが、月間1000回までは無料。以降は0.005 USD

        • リリースとかでこまめに大量のファイルをクリアすると、金かかる
        • キャッシュ有効期限は24時間。24時間ほっとけるならキャッシュクリア料金かからない

用語整理

  • ひとつのCloudFrontは「ディストリビューション」。

    • EC2やRDSが「インスタンス」と呼んだように。
  • キャッシュルールは「ビヘイビア」
  • キャッシュ元データを配信するサーバーを「オリジン」
    • ELB、EC2、S3、その他のサーバー
  • キャッシュクリアは「インバリデート」
    • 「無効化リクエスト」と書いてある文書もある

CloudFront の設置場所

CloudFront無しの構成

EC2のローカルディスクにすべてがあります。静的コンテンツ、動的ページ、すべてのアクセスを、EC2が捌く必要があります。ApacheとかNginxでキャッシュを効かせると、負荷は軽くなるかも。みたいな涙ぐましいノウハウがあったのです。

f65e1219-60fe-d83f-c483-73b133b04544.jpg

横に置く

昔のCloudFrontは、GETとHEADしか受け付けなかったため、JS/CSS/画像/添付ファイルなどを配信するS3を別立てにして、その手前にCloudFrontを置いていました。HTMLの実装では、cssとかjs、画像のタグに書くのリンクを xxxxxxxx.cloudfront.com にしておくことで、こうできます。図ではS3に置くことにしていますが、リリースでのCSSやJSの同期とか、何かと状況が複雑になりがちです。

40904824-cf46-b636-e04c-ee2462471b96.jpg

前に置く

CloudFrontの2013年10月のアップデート から、すべてのHTTPメソッドを受けてくれるため、ウェブアプリサーバーの手前に置くことができます。この場合は、静的コンテンツはCloudFrontのキャッシュでリクエストを捌き、動的ページはCloudFrontはスルーさせて、EC2で処理させます。

626b87d4-abd6-5ba8-6835-b3319f2722c0.jpg

今からやるなら「前に置く」構成

CloudFront無しの構成に導入するなら、断然「前に置く」構成です。

ウェブアプリのソース改修不要で、CloudFrontを適切に設定して配置するだけでOKなので、面倒がないです。

ただし、特定のページだけIP制限してたりすると、ApacheやNginxの設定を変更する必要があります。

とりあえずCloudFrontを立てる

必須項目だけ埋めて、あとで直せばOKです。

  • AWSコンソールにはいる
  • CloudFrontのページに行く
  • Create Distribution
    • Webを選ぶ(RTMPは動画配信とかに使う用)

      • Origin Settings

        • Origin Domain Name

          • ELBエンドポイントURL、BeanstalkエンドポイントURL、S3エンドポイントURL、EC2 DNS名など
          • IPアドレスでなければOK
      • Default Cache Behavior Settings
        • あとで変えるので放置
      • Distribution Settings
        • Alternate Domain Names(CNAMEs)

          • このディストリビューションに当てる予定のドメイン名。
          • 「前に置く」構成なら、これまでELBに当てていたドメイン名を指定。
          • 「横に置く」構成なら、空欄でOK
      • 他はあとで変えればOKなので放置
      • Create Distributionボタン押す
  • ディストリビューションは全世界に分散して立つのと、微妙にダサい仕様のため、しばらく時間がかかります

ビヘイビアの掟

  • ビヘイビアリストの上から順に評価されます。
  • Default (*)は、
    • 一番下から動かせません。
    • 削除できません。
    • どのビヘイビアにも当たらなかった場合のため存在します。
  • パスパターンにマッチしたら、そのビヘイビアだけに従って、キャッシュを見たり、オリジンにスルーしたりする
    • なので、ビヘイビアの上下の並び順は重要

ビヘイビアの設定方針

下記のどちらか。後からでも変更はできますが、どっちで行くかを考えるために、先に切り分けておくと良いです。

  • Default (*)を「キャッシュする」で書く。他のパスパターンは「キャッシュしない」で書く。
  • Default (*)を「キャッシュしない」で書く。他のパスパターンは「キャッシュする」で書く。

キャッシュしないページの設定

  • Path Pattern

    • 仮に http://hoge.example.com/contact/piyo.jpg みたいなとき

      • /contact/piyo.jpg
      • /contact/*.jpg
      • *.jpb
    • みたいに、そのキャッシュルールを適用するパスパターンを指定します。
  • Allowed HTTP Methods
    • 全部入りのを指定
  • Forward Headers
    • 「all」を指定
  • Object Caching
    • Customize
    • TTL(min, max, default)
      • ぜんぶゼロを指定
  • Forward Cookies
    • 「all」を指定
  • Query String Forwarding and Caching
    • 「forward all, cache based on all」を指定

キャッシュするページの設定

  • Path Pattern

    • 仮に http://hoge.example.com/contact/piyo.jpg みたいなとき

      • /contact/piyo.jpg
      • /contact/*.jpg
      • *.jpb
    • みたいに、そのキャッシュルールを適用するパスパターンを指定します。
  • Allowed HTTP Methods
    • GET,HEAD を指定
  • Forward Headers
    • 「Host」は必須。他にも必要なものがあれば追加。
  • Object Caching
    • Use Origin Cache Headers
    • Customizeにして、TTLを入れてもOK
  • Forward Cookies
  • Query String Forwarding and Caching
    • 「forward all, cache based on all」を指定

DNS設定

ビヘイビアふくめて、ディストリビューションの設定が完成したら、DNSの設定を書き換えます。

ディストリビューションには、「d1lxxxxxxxxx.cloudfront.net」のような、一意なドメイン名が発行されます。

Alternate Domain Names (CNAMEs)に入れたドメイン名のCNAMEとして、ディストリビューションのドメイン名を向けた、DNS CNAMEレコードを作成します。

動作確認

サイトにアクセスして、期待したとおりにビヘイビアが設定できているか、確認しましょう。

ChromeのデベロッパーツールのNetworkタブで、個々のファイルのレスポンスヘッダーに下記のようなのがあれば、CloudFrontを経由しています。

Via:1.1 41f313008af830d498dcb13814523bd7.cloudfront.net (CloudFront)
X-Amz-Cf-Id:xcP_6KiTFG_guNA9dRA-KOW6pg740-3mP1SvSrt2NqKGndWGPJKVuA==
X-Cache:Hit from cloudfront

X-Cacheに、キャッシュヒットしたかしてないかが記載されます。HitとMiss、ほかにもいくつかありますが、、、

  • X-Cache:Hit from cloudfront

    • CloudFrontにあるキャッシュが返っています
  • X-Cache:Miss from cloudfront
    • CloudFrontにキャッシュがなく、オリジンから返っています

HitとMissが想定と異なる場合は、ビヘイビアの調整が必要です。がんばりましょう。

その他、TIPS

制限、仕様

導入前に、CloudFrontというプロダクトの制限と仕様が、プロダクトの制限と仕様にマッチするのか、検討が必要です。

参考文書

続きを読む

AWS Lambdaから実行したEC2上のPythonでモジュールがimportできない

S3に動画ファイルが配置されたことを契機にLambdaでEC2上のPythonをキックし、
ffmpy経由でffmpegを起動し動画を処理するようなプログラムを作っていたのですが、
どうもffmpyが正常にimportできないので、現状と解決した際の情報共有に書き残します。

環境

実行用コードは下記のようにec2-userのホームディレクトリ配下に配置されています。

/home/ec2-user/hoge/
├── MAIN1_KICKED_BY_LAMBDA.py # メインメソッド
├── fuga
│   └── hogefuga.py           # 動画編集用モジュール
...

下記の設定をrootユーザに対して行いました。

  • pyenv -> Anaconda3.4.1 (Python 3.6)
  • pip install ffmpy
  • ffmpegをインストール (下記スクリプト)
ffmpegcpl.sh
#!/bin/sh

sudo yum -y install autoconf automake cmake freetype-devel gcc gcc-c++ git libtool make mercurial nasm pkgconfig zlib-devel

mkdir ~/ffmpeg_sources

#Yasm
cd ~/ffmpeg_sources
git clone --depth 1 git://github.com/yasm/yasm.git
cd yasm
autoreconf -fiv
./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin"
make
make install
make distclean

#libx264
cd ~/ffmpeg_sources
git clone --depth 1 git://git.videolan.org/x264
cd x264
PKG_CONFIG_PATH="$HOME/ffmpeg_build/lib/pkgconfig" ./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin" --enable-static
make
make install
make distclean

#libx265
cd ~/ffmpeg_sources
hg clone https://bitbucket.org/multicoreware/x265
cd ~/ffmpeg_sources/x265/build/linux
cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX="$HOME/ffmpeg_build" -DENABLE_SHARED:bool=off ../../source
make
make install

#libfdk_aac
cd ~/ffmpeg_sources
git clone --depth 1 git://git.code.sf.net/p/opencore-amr/fdk-aac
cd fdk-aac
autoreconf -fiv
./configure --prefix="$HOME/ffmpeg_build" --disable-shared
make
make install
make distclean

#libmp3lame
cd ~/ffmpeg_sources
curl -L -O http://downloads.sourceforge.net/project/lame/lame/3.99/lame-3.99.5.tar.gz
tar xzvf lame-3.99.5.tar.gz
cd lame-3.99.5
./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/bin" --disable-shared --enable-nasm
make
make install
make distclean

#libopus
cd ~/ffmpeg_sources
git clone http://git.opus-codec.org/opus.git
cd opus
autoreconf -fiv
./configure --prefix="$HOME/ffmpeg_build" --disable-shared
make
make install
make distclean

#libogg
cd ~/ffmpeg_sources
curl -O http://downloads.xiph.org/releases/ogg/libogg-1.3.2.tar.gz
tar xzvf libogg-1.3.2.tar.gz
cd libogg-1.3.2
./configure --prefix="$HOME/ffmpeg_build" --disable-shared
make
make install
make distclean

#libvorbis
cd ~/ffmpeg_sources
curl -O http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.4.tar.gz
tar xzvf libvorbis-1.3.4.tar.gz
cd libvorbis-1.3.4
LDFLAGS="-L$HOME/ffmeg_build/lib" CPPFLAGS="-I$HOME/ffmpeg_build/include" ./configure --prefix="$HOME/ffmpeg_build" --with-ogg="$HOME/ffmpeg_build" --disable-shared
make
make install
make distclean

#libvpx
cd ~/ffmpeg_sources
git clone --depth 1 https://chromium.googlesource.com/webm/libvpx.git
cd libvpx
./configure --prefix="$HOME/ffmpeg_build" --disable-examples
make
make install
make clean

#FFmpeg
cd ~/ffmpeg_sources
git clone http://source.ffmpeg.org/git/ffmpeg.git
cd ffmpeg
PKG_CONFIG_PATH="$HOME/ffmpeg_build/lib/pkgconfig" ./configure --prefix="$HOME/ffmpeg_build" --extra-cflags="-I$HOME/ffmpeg_build/include" --extra-ldflags="-L$HOME/ffmpeg_build/lib" --bindir="$HOME/bin" --pkg-config-flags="--static" --enable-gpl --enable-nonfree --enable-libfdk-aac --enable-libfreetype --enable-libmp3lame --enable-libopus --enable-libvorbis --enable-libvpx --enable-libx264 --enable-libx265
make
make install
make distclean
hash -r

現状・試したこと

実行するコードについては、公開を控えさせてください。。。
Lambdaから実行したときのログは下記のとおりで、どうやらec2-userのpyenv(存在しない)を見にいって落ちているようです。

Traceback (most recent call last):
  File "/home/ec2-user/hoge/MAIN1_KICKED_BY_LAMBDA.py", line 88, in <module>
    hogefuga.exeExtractWav(TMP_DIRECTORY2 + '/', nameonly)
  File "/home/ec2-user/hoge/fuga/hogefuga.py", line 175, in exeExtractWav
    extractWav(filename)
  File "/home/ec2-user/hoge/fuga/hogefuga.py", line 162, in extractWav
    ff.run()
  File "/home/ec2-user/.pyenv/versions/anaconda3-4.3.1/lib/python3.6/site-packages/ffmpy.py", line 99, in run
    raise FFExecutableNotFoundError("Executable '{0}' not found".format(self.executable))
ffmpy.FFExecutableNotFoundError: Executable 'ffmpeg' not found

SSH接続しrootで直接実行した際はroot側のpyenvを見にいき問題なく実行されるのですが、解決策としては以下の2つでしょうか。

  • ec2-user環境にも同様にpyenv, ffmpeg環境を構築する
  • 実行ファイル類をrootのホーム配下へ移動する

そもそも直接実行とLambda実行でパスが変わる原因を明らかにしないとすっきりしませんが・・・。

(2017.05.23 17:12追記)

  • ec2-user環境にも同様にpyenv, ffmpeg環境を構築する
  • 実行ファイル類をrootのホーム配下へ移動する

両方試したものの、下記のエラーが出て終了。

----------ERROR-------
failed to run commands: exit status 1
Traceback (most recent call last):
  File "MAIN1_KICKED_BY_LAMBDA.py", line 17, in <module>
    import formatchg
  File "/root/fuga/hoge/fugahoge.py", line 10, in <module>
    from ffmpy import FFmpeg
ImportError: No module named ffmpy

続きを読む

awsサービス cloudformationからlambdaをキックさせる

cloudformationでインスタンスなどが立ち上げ終わった後に、マネジメントコンソールで管理しているlambdaを叩きたいなと思って実行したものです。

カスタムリソース

カスタムリソースというのが用意されています。これを使います。

こいつは何か?

AWSのCloudFormationのリソースタイプとして使用できないリソースを含める必要があるときに、それらのリソースは、カスタムリソースを使用して含めることができるらしい。つまりこの方法によりすべての関連リソースを1つのスタックで管理できるという優れものです。
今回だとて使用できないリソースとしてlambdaが当たりますがSQSとかもそうらしいです。以下のような感じで記述できます。Test1、Test2のように任意の文字列も渡せます。

"CustomResource" : {
  "Type" : "AWS::CloudFormation::CustomResource",
    "Properties" : {
        "ServiceToken": "指定のARN",
        "Test1": {"Ref": "InstanceName"},
        "Test2": {"Ref": "TargetGroupName"}
    }
}

使い方

lambdaをcloudformatinで使うにはあるものを呼ばないといけないらしい。それがcfn-responseモジュールと呼ばれるものだ。そしてこれを使うときの注意書きとして以下が記述してあります。

cfn-response モジュールは、ZipFile プロパティを使用してソースコードを作成した場合にのみ使用できます。S3 バケットに保存されたソースコードには使用できません。S3 バケットのコードでは、独自の関数を作成してレスポンスを送信する必要があります。

どういうことかというとcloudformationでlambdaを使うときは以下のようにコードを直書きしないと使えませんよということ。

"ZipFile": { "Fn::Join": ["", [
  "var response = require('cfn-response');",
  "exports.handler = function(event, context) {",
  "  var input = parseInt(event.ResourceProperties.Input);",
  "  var responseData = {Value: input * 5};",
  "  response.send(event, context, response.SUCCESS, responseData);",
  "};"

あれ??マネジメントコンソールですでに記述したやつを使いたかったのに・・・・
github調べてみたら出てきた

https://github.com/LukeMizuhashi/cfn-response/blob/c5bb51ada6110f4e84d7a8bad7799fd90e0b440d/index.js#L11

解決方法

実際に書いた。githubのほぼコピペなので迷うことありません。

cloudformation側

"CustomResource" : {
  "Type" : "AWS::CloudFormation::CustomResource",
    "Properties" : {
        "ServiceToken": "lambdaのARN",
        "Test1": {"Ref": "InstanceName"},
        "Test2": {"Ref": "TargetGroupName"}
    }
}

そうすると”lambdaのARN”で指定したlambdaを叩きに行く。そしてlambda側で以下を入れておく。

lambda側

var SUCCESS = "SUCCESS";
var FAILED = "FAILED";

var cfnResponse = function(event, context, responseStatus, responseData, physicalResourceId) {

    var responseBody = JSON.stringify({
        Status: responseStatus,
        Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,
        PhysicalResourceId: physicalResourceId || context.logStreamName,
        StackId: event.StackId,
        RequestId: event.RequestId,
        LogicalResourceId: event.LogicalResourceId,
        Data: responseData
    });

    console.log("Response body:n", responseBody);

    var https = require("https");
    var url = require("url");

    var parsedUrl = url.parse(event.ResponseURL);
    var options = {
        hostname: parsedUrl.hostname,
        port: 443,
        path: parsedUrl.path,
        method: "PUT",
        headers: {
            "content-type": "",
            "content-length": responseBody.length
        }
    };

    var request = https.request(options, function(response) {
        console.log("Status code: " + response.statusCode);
        console.log("Status message: " + response.statusMessage);
        context.done();
    });

    request.on("error", function(error) {
        console.log("send(..) failed executing https.request(..): " + error);
        context.done();
    });

    request.write(responseBody);
    request.end();
}

exports.handler = (event, context, callback) => {
    // TODO implement
    if(event['RequestType'] == 'Delete'){
        cfnResponse(event, context, SUCCESS, {});
    }
    console.log(event);
    cfnResponse(event, context, SUCCESS, {});
};

これをlambda側に書いていないとインプログレスのまま一時間くらい止まってしまい、立ち上げ直さないといけない感じになる。

結果

cloudformationで環境を作った後に、lambdaを使うことに成功した。
その時にカスタマーリソールなるものを用いた、ちゃんとS3のcloudformationのタスクのレスポンスとして結果を返さないといけないので注意しましょ!!!

続きを読む

Hive(EMR)+S3でMoving data to… Failed with exception Unable to move source s3…が出る場合の対処

ちゃんとログ見ればすぐ気づくことだったけど…

EMRでHadoop+Hive。

CREATE TABLE [[テーブル名]] STORED AS ORC location 's3://[[s3パス]]' TBLPROPERTIES('EXTERNAL'='TRUE', 'orc.compress'='zlib') AS SELECT * FROM [[テーブル名]]
Moving data to directory s3://[[s3パス]]
Failed with exception Unable to move source s3://[[s3パス]]/hoge/fuga...

INSERT INTO テーブル名 SELECT…

は通るのになんでやねん。と思ってたら。

No Such File or Dir s3://[[s3パス]]/hoge

えっ

まさかと思い手動でhogeオブジェクトをs3上に作成。
通るようになりました。

s3のputとかはオブジェクトまでのパスがなくても勝手に作ってくれるからそこを疑いもしなかったのと、EMR上でのHiveの詳細なログがターミナルで操作した場合どこに吐かれるのかわからんかったせいでドハマりした。

最終的には全く別チームが作っていたEMRにjdbc接続して(HiveServer2)HQL投げるって機構があったおかげで、/mnt/var/log/hive/hiveserer2.logにバックトレースが吐かれたので気づけた。
ターミナルからやるとここに吐かれないからわからないんだよね… どっかに吐かれるのかなあ

続きを読む

AmazonAthenaをCLIから使う

よーやくCLIから使えるようになりました。
早速試しましょう

前提条件

  • S3にQuery用のダミーデータが入っているものとします
  • S3にAthenaの実行結果保存用ディレクトリが作成されているものとします
  • Athenaは us-east-1 を使用して実行しています

実行環境

コマンド
aws --version
結果
aws-cli/1.11.89 Python/3.5.0 Darwin/16.5.0 botocore/1.5.52

手元にコマンドがない場合はアップデートしましょう

アップデート
sudo pip install -U awscli --ignore-installed six
  • 筆者の環境ではDatapipelinを使用してDynamoDBのあるテーブルデータをS3にバックアップしたものをサンプルデータとして使用しています。
  • DATABASEに関しては以前Javaからの接続の際検証で作成した dynamodb という名前のDATABASEが存在します。
  • テーブルとして、DynamoDBのJSONデータに対応するように作成した dynamodb.sample1 を使用します。

Queryの実行

コマンド
aws athena start-query-execution 
    --query-string 'select count(*) from dynamodb.sample1;' 
    --result-configuration Database=dynamodb 
    --result-configuration OutputLocation=s3://xxxxxxxxxxxxxxxxxxxxxxxxxxx/cli-test/
{
    "QueryExecutionId": "xxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

結果の取得

コマンド
aws athena get-query-results 
    --query-execution-id "xxxxxxxxxxxxxxxxxxxxxxxxxxx"
結果
{
    "ResultSet": {
        "ResultSetMetadata": {
            "ColumnInfo": [
                {
                    "CatalogName": "hive",
                    "Name": "_col0",
                    "Scale": 0,
                    "SchemaName": "",
                    "Precision": 19,
                    "Type": "bigint",
                    "CaseSensitive": false,
                    "Nullable": "UNKNOWN",
                    "Label": "_col0",
                    "TableName": ""
                }
            ]
        },
        "Rows": [
            {
                "Data": [
                    {
                        "VarCharValue": "_col0"
                    }
                ]
            },
            {
                "Data": [
                    {
                        "VarCharValue": "500000"
                    }
                ]
            }
        ]
    }
}

実行したQueryの詳細を取得する

コマンド
aws athena get-query-execution 
    --query-execution-id  "xxxxxxxxxxxxxxxxxxxxxxxxxxx"
結果
{
    "QueryExecution": {
        "Status": {
            "SubmissionDateTime": 1495440535.706,
            "State": "SUCCEEDED",
            "CompletionDateTime": 1495440549.995
        },
        "QueryExecutionId": "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
        "Statistics": {
            "DataScannedInBytes": 71512882,
            "EngineExecutionTimeInMillis": 13930
        },
        "Query": "select count(*) from dynamodb.sample1",
        "ResultConfiguration": {
            "OutputLocation": "s3://xxxxxxxxxxxxxxxxxxxxxxxxxxx/cli-test/xxxxxxxxxxxxxxxxxxxxxxxxxxx.csv"
        }
    }
}

格納先のオブジェクトパスが取得できます。

これでAthenaを簡単に使える!東京リージョンまだだけど、S3を介してやり取り出来るのでまあまあ使えるんじゃないかという所感。

続きを読む

デプロイ方法のメモ

はじめてデプロイ作業したのでその時につまづいたところをまとめました。

pipのインストール

pipはPythonのパッケージ管理システムです
標準で入ってるらしいですが、自分のPCには入ってなかったので、
入れ方から使い方までメモしました。

Pythonが入ってることを確認しましょう

$ python -V

次にpipが入ってるか確認しましょう。

$ python -m pip -V

python初めての人入っていないので、
https://bootstrap.pypa.io/get-pip.py
ここからpip.pyをダウンロードしてください。

$ sudo python get-pip.py

でインストール。入ったことを確認したら

$ sudo pip install -U pip

でpipをアップデートします。

aws-cli をインストールする

コマンドラインからAWSを操作できる公式のコマンドラインツールです。
http://docs.aws.amazon.com/ja_jp/streams/latest/dev/kinesis-tutorial-cli-installation.html

$ sudo pip install awscli

でインストールできますが、

DEPRECATION: Uninstalling a distutils installed project (six) has been deprecated and will be removed in a future version. This is due to the fact that uninstalling a distutils project will only partially uninstall the project.

というエラーが出ました。どうやらSixというのが邪魔してるみたいです。
SixはPython 2と3の互換性ライブラリのことです。

$ sudo pip install awscli --upgrade --ignore-installed six

これでインストールできます。

セットアップ

aws configure

と打つと対話形式で
AWSの設定を打ち込んでいきます。

AWS Access Key ID [None]:アクセスキーID
AWS Secret Access Key [None]:シークレットアクセスキー
Default region name [None]: ap-northeast-1は東京のこと
Default output format [None]:無視

~ $ aws s3 ls

で確認

あとは、公開したいサイトのディレクトリーに入り
deploy.shファイルを作成し。

./deploy.sh

でファイル一覧がでてきて
yesで
デプロイ完了です。

続きを読む