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 の機能についても、より使いやすいものに進化してもらえると、ユーザーとしては嬉しく感じます。

続きを読む

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で
デプロイ完了です。

続きを読む

AWS CLIを使ってS3上にあるファイル数とファイルサイズの合計を取得する

Amazon Athenaの料金はスキャンしたデータ量で決まるので、特定のフォルダ以下のファイルサイズの合計をAWS CLIで取得する方法を調べたのですが忘れそうなのでメモしておきます。以下のコマンドでファイル一覧の後に […] 続きを読む

AWS CLIからAthenaのクエリを実行してみた

AWS CLIがAthena対応したので、試してみました。
(いや〜JDBC接続とかめんどかった・・・)

利用環境はMacです

AWS CLIをバージョンアップ

利用していたAWS CLIのバージョンは1.11.88でした

$ aws --version
aws-cli/1.11.88 Python/2.7.10 Darwin/15.6.0 botocore/1.5.51
$ aws help | grep athena

もちろんAthenaが入っていないのでバージョンアップします
–ignore-installed sixをつけないとバージョンアップできないのはなんとかならないかな・・・

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

バージョンアップ完了

$ aws --version
aws-cli/1.11.89 Python/2.7.10 Darwin/15.6.0 botocore/1.5.52
$ aws help | grep athena
       o athena

ドキュメントを眺める

コマンド一覧

  • batch-get-named-query
  • batch-get-query-execution
  • create-named-query
  • delete-named-query
  • get-named-query
  • get-query-execution
  • get-query-results
  • list-named-queries
  • list-query-executions
  • start-query-execution
  • stop-query-execution

コマンド実行してみる

本日時点ではAthenaが東京リージョンに来てないので、コマンドに–region us-east-1(バージニアリージョン)を指定。(諸事情でprofileは使わない)
すでに、CloudFrontとCloudTrailのログをAthena上に配置
Athenaを使ってAWSのログを集計する

start-query-execution

クエリを実行するコマンド。
必須パラメータ
–query-string:Stringでクエリを記述。FROM句にDB名は必須。
–result-configuration:結果を出力するS3バケットを指定

$ aws --region us-east-1 athena start-query-execution 
    --query-string "SELECT * FROM aws_logs.cloudfront_log limit 10;" 
    --result-configuration OutputLocation=s3://athena-output
{
    "QueryExecutionId": "9d5f2f3a-e80f-4807-ab6c-35139924d374"
}

get-query-results

クエリの実行結果を見る

$ aws --region us-east-1 athena get-query-results 
    --query-execution-id 9d5f2f3a-e80f-4807-ab6c-35139924d374
{
    "ResultSet": {
        "Rows": [
            {
                "Data": [
                    {
                        "VarCharValue": "date"
                    }, 
                    {
                        "VarCharValue": "time"
                    }, 
                    {
                        "VarCharValue": "xedgelocation"
                    }, 
                    {
                        "VarCharValue": "scbytes"
                    }, 
                    {
                        "VarCharValue": "cip"
                    }, 
                    {
                        "VarCharValue": "csmethod"
                    }, 
                    {
                        "VarCharValue": "cshost"
                    }, 
                    {
                        "VarCharValue": "csuristem"
                    }, 
                    {
                        "VarCharValue": "scstatus"
                    }, 
                    {
                        "VarCharValue": "csreferer"
                    }, 
                    {
                        "VarCharValue": "csuseragent"
                    }, 
                    {
                        "VarCharValue": "csuriquery"
                    }, 
                    {
                        "VarCharValue": "cscookie"
                    }, 
                    {
                        "VarCharValue": "xedgeresulttype"
                    }, 
                    {
                        "VarCharValue": "xedgerequestid"
                    }, 
                    {
                        "VarCharValue": "xhostheader"
                    }, 
                    {
                        "VarCharValue": "csprotocol"
                    }, 
                    {
                        "VarCharValue": "csbytes"
                    }, 
                    {
                        "VarCharValue": "timetaken"
                    }, 
                    {
                        "VarCharValue": "xforwardedfor"
                    }, 
                    {
                        "VarCharValue": "sslprotocol"
                    }, 
                    {
                        "VarCharValue": "sslcipher"
                    }, 
                    {
                        "VarCharValue": "xedgeresponseresulttype"
                    }, 
                    {
                        "VarCharValue": "csprotocolversion"
                    }
                ]
            }, 
            {
                "Data": [
                    {
                        "VarCharValue": "2017-03-11"
                    }, 
                    {
                        "VarCharValue": "07:09:39"
                    }, 
                    {
                        "VarCharValue": "NRT20"
                    }, 
                    {
                        "VarCharValue": "485"
                    }, 
                    {
                        "VarCharValue": "182.251.62.172"
                    }, 
                    {
                        "VarCharValue": "GET"
                    }, 
                    {
                        "VarCharValue": "d296z2px268if9.cloudfront.net"
                    }, 
                    {
                        "VarCharValue": "/sample"
                    }, 
                    {
                        "VarCharValue": "200"
                    }, 
                    {
                        "VarCharValue": "-"
                    }, 
                    {
                        "VarCharValue": "curl/7.43.0"
                    }, 
                    {
                        "VarCharValue": "-"
                    }, 
                    {
                        "VarCharValue": "-"
                    }, 
                    {
                        "VarCharValue": "Miss"
                    }, 
                    {
                        "VarCharValue": "7_kRmqTCtndlAsdecditmwIL3kPgVKjsqBggBEFSu68_tsTGWAVK-g=="
                    }, 
                    {
                        "VarCharValue": "d296z2px268if9.cloudfront.net"
                    }, 
                    {
                        "VarCharValue": "https"
                    }, 
                    {
                        "VarCharValue": "99"
                    }, 
                    {}, 
                    {
                        "VarCharValue": "-"
                    }, 
                    {
                        "VarCharValue": "TLSv1.2"
                    }, 
                    {
                        "VarCharValue": "ECDHE-RSA-AES128-GCM-SHA256"
                    }, 
                    {
                        "VarCharValue": "Miss"
                    }, 
                    {
                        "VarCharValue": "HTTP/1.1"
                    }
                ]
            }, 
・・・

これ使いにくい・・・

get-query-execution

クエリ実行結果(成功/失敗)等の情報を取得する
start-query-executionで実行した結果を取得
必須パラメータ
–query-execution-id:実行時に表示されるQueryExecutionIdを指定

$ aws --region us-east-1 athena get-query-execution 
    --query-execution-id 9d5f2f3a-e80f-4807-ab6c-35139924d374
{
    "QueryExecution": {
        "Status": {
            "SubmissionDateTime": 1495269759.131, 
            "State": "SUCCEEDED", 
            "CompletionDateTime": 1495269762.711
        }, 
        "Query": "SELECT * FROM aws_logs.cloudfront_log limit 10", 
        "Statistics": {
            "DataScannedInBytes": 1454, 
            "EngineExecutionTimeInMillis": 3475
        }, 
        "ResultConfiguration": {
            "OutputLocation": "s3://athena-output/3fbf61dd-866e-4de6-9ba4-56cfdb671964.csv"
        }, 
        "QueryExecutionId": "3fbf61dd-866e-4de6-9ba4-56cfdb671964"
    }
}

StatusにSUCCEEDEDと表示されるので成功している
結果は実行時に指定したS3バケット内にCSVで保存される→OutputLocation

スクリプトを組むならこんな感じ?

athena-query.sh
#!/bin/bash

OutputBucket=athena-output # 出力バケット
# クエリ実行
queryId=$(aws --region us-east-1 athena start-query-execution 
    --query-string "SELECT * FROM aws_logs.cloudfront_log limit 10;" 
    --result-configuration OutputLocation=s3://$OutputBucket 
    | jq -r '.QueryExecutionId')
# 結果の確認
status=$(aws --region us-east-1 athena get-query-execution 
    --query-execution-id $queryId 
    | jq -r '.QueryExecution.Status.State')

if [ "$status" == "SUCCEEDED" ]; then
    aws s3 cp s3://${OutputBucket}/${queryId}.csv .
else
    echo "Query Error!"
fi

cat ${queryId}.csv

まとめ

  • 待望のAthenaがAWS CLIに対応しました。
  • BigQueryはbqコマンドで実行できたので、足並み揃い始めた感じでしょうか
  • もうすぐ東京リージョンに来そうな感じなので、期待大です(Comming Soonってなってるので、AWS Summit Tokyoで発表!?)
  • 出力結果がもっといい感じに見れるといいですね

続きを読む

EC2 instance起動時にtagをつけるTagSpecifications

AWSCLIでEC2 instance起動時に同時にタグをつける方法としては、instance起動してinstance-idを取得しておいて、パイプでつないでtagをつけたり、スクリプトの中で後でタグ付けする方法があったと思います。
http://kurochan-note.hatenablog.jp/entry/2017/01/08/220155

AWSCLI EC2 Run-Instanceのなかに–tag-specificationsというoptionが入って、run-instancesの中でタグが作成できるようになりました。地味なアップデートかもしれませんが、結構うれしいです。

instanceの詳細はjsonに記述して、下記のように指定して実行します。

aws ec2 run-instances --cli-input-json file://instance.json

EC2は山ほど設定項目があるので、generate-cli-skeltonでフォーマットを出力して、必要な項目だけ入力して、不必要なものは消すとinstanceの詳細を記述したjsonの完成です。Gitにでも入れておきましょう。
http://docs.aws.amazon.com/cli/latest/userguide/generate-cli-skeleton.html

aws ec2 run-instances --generate-cli-skeleton

Instanceの設定詳細を記述したjsonサンプル

instance.json
{
    "ImageId": "<image-id>",
    "KeyName": "<my-key>",
    "SecurityGroupIds": [
        "<my-sgid>"
    ],
    "InstanceType": "<instance-type>",
    "BlockDeviceMappings": [
        {
            "VirtualName": "Root",
            "DeviceName": "/dev/sda1",
            "Ebs": {
                "VolumeSize": 100,
                "DeleteOnTermination": true,
                "VolumeType": "gp2"
            }
        }
    ],
    "Monitoring": {
        "Enabled": false
    },
    "SubnetId": "<subnet-id>",
    "DisableApiTermination": false,
    "IamInstanceProfile": {
        "Name": "<instance-iam-role>"
    },
    "TagSpecifications":[
        {
            "ResourceType": "instance",
            "Tags": [
              {
                "Key": "Name",
                "Value": "<server-name>"
              },
              {
                "Key": "ClusterName",
                "Value": "<cluster-name>"
              },
              {
                "Key": "Application",
                "Value": "<myapp>"
              },
              {
                "Key": "CostCenter",
                "Value": "<my-cost-center>"
              },
              {
                "Key": "Environment",
                "Value": "Test"
              },
              {
                "Key": "User",
                "Value": "<user-name>"
              }
            ]
        },
        {
          "ResourceType": "volume",
          "Tags": [
            {
              "Key": "Device",
              "Value": "<device-name>"
            },
{
              "Key": "CostCenter",
              "Value": "<my-cost-center>"
            },
            {
              "Key": "backup_key",
              "Value": "true"
            }
          ]
        }
    ]
}

続きを読む

AWSアカウントを作成して、はじめにすること

一番最初にしておきたいことをまとめていきます。

1.MFA(Multi-Factor Authentication)の設定

AWSアカウントを作成するとrootでログインされます。
rootが乗っ取られれないようにMFAの設定は必ずやっておきたいと思います。
以下は、その手順です。

MFAデバイスの用意

公式のMFAページ https://aws.amazon.com/jp/iam/details/mfa/

仮想MFAデバイス

専用のMFAデバイスを購入するほどでもないので、手元のスマートフォンを仮想MFAデバイスとして使用したいと思います。
以下から、MFAアプリケーションをインストールします。

Kobito.PBL4mn.png

IAM(Identity & Access Management)コンソールを表示

  • 次にIAMコンソールからMFAの設定をしていきたいと思います。

Kobito.xZW6Wa.png

「ルートアカウントのMFAを有効化」を選択する

Kobito.EGJ2EL.png

Kobito.qF3Fsw.png

用意した仮想MFAデバイスでQRコードを読み込むと数字が表示されます。
時間が経つと数字が変わるので気をつけてください。

「認証コード1」に数字を入力したら、次に表示される数字を「認証コード2」に入力して「仮想MFAの有効化」をクリックしてください。

再ログインする

次回移行ログインするときは、MFAの確認を要求されるので仮想MFAデバイスに表示されている数字を入力してください。
※ MFAの仮想デバイスの管理には気をつけてください。

2.ユーザーアカウント(IAMユーザー)を作成する

通常使用するようにアカウントを作成したいと思います。

Kobito.JtGo41.png

IAMコンソールの←メニューから「ユーザー」を選択して、「ユーザーを追加」をクリックする

Kobito.x1CcjW.png

ユーザ名を入力して、完了画面を表示する。
Kobito.Kn6G2n.png
アクセスキー、シークレットアクセスキー、パスワードを保持して、別ブラウザでログインをする。

Kobito.SEtuFi.png

最後に権限を付与する

3.CloudTrailの設定

操作ログを記録するようにしたいと思います。

Kobito.A9Mtrw.png

Kobito.x9PIKJ.png

4.開発環境の設定

CLIをローカルの環境にインストールします。

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.
    Uninstalling six-1.4.1:

以下のコマンドで無事にインストール完了
こちらを参考:https://saku.io/failed-to-install-awscli-on-mac/

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

続きを読む

EC2のボリューム(EBS)容量拡張方法検証 (AmazonLinux)

結論を3行で

検証

【遂に来た!】EBS でボリュームサイズを変更できるようになりました(ボリュームタイプ変更も) | Developers.IO を参考に、稼働中のインスタンスにアタッチ済みのボリュームのサイズを増やしてみます。

今回ボリュームを増やしたいインスタンスはこのような感じです。

インスタンス的には/dev/xvdaというところに30GBのボリュームがあります。

ec2-user@ip-172-31-10-224 ~ $  df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       30G  3.8G   26G  13% /
devtmpfs        490M   56K  490M   1% /dev
tmpfs           499M     0  499M   0% /dev/shm

ec2-user@ip-172-31-10-224 ~ $  lsblk
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda    202:0    0  30G  0 disk
└─xvda1 202:1    0  30G  0 part /

ほとんど元記事のとおりですが以下のような感じです。

スクリーンショット_2017-05-18_11_30_15.png

Modify Volumeを押します

スクリーンショット 2017-05-18 11.30.31.png

今回は 30GB -> 100GBにします

スクリーンショット 2017-05-18 11.32.51.png

パフォーマンスが変更されるまで時間がかかるとのこと。。yesを押す。

スクリーンショット 2017-05-18 11.33.00.png

完了。

スクリーンショット 2017-05-18 11.34.08.png

ボリュームの状態は optimizing...0% という表示になりますが、もうこの状態で ディスクの拡張は終わっています。

スクリーンショット 2017-05-18 11.34.46.png

awscli的には $ aws ec2 describe-volumes-modifications と叩くと進捗が表示されます(引数なしでOK)

$ aws ec2 describe-volumes-modifications
{
    "VolumesModifications": [
        {
            "TargetSize": 100,
            "TargetVolumeType": "gp2",
            "ModificationState": "optimizing",
            "VolumeId": "vol-0e92fb2e26dfd9687",
            "TargetIops": 300,
            "StartTime": "2017-05-18T02:34:07.151Z",
            "Progress": 0,
            "OriginalVolumeType": "gp2",
            "OriginalIops": 100,
            "OriginalSize": 30
        }
    ]
}

"Progress": 0 ですが、lsblk を叩くともう反映されていることがわかります。

ec2-user@ip-172-31-3-117 ~ $  lsblk
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
xvda    202:0    0  100G  0 disk             # <- 100になってる
└─xvda1 202:1    0   30G  0 part /

次に resize2fs すればよいのですが、以下のような感じで怒られます。

resize2fs 1.42.12 (29-Aug-2014)
resize2fs: Device or resource busy while trying to open /dev/xvda
Couldn't find valid filesystem superblock.

今回はパーティションの設定がされているためと思われます。パーティションを利用している場合の設定はこちら。http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/storage_expand_partition.html
ただルートパーティションの場合は面倒そうなので、起動時に実行されるresize2fsに任せることにしました。

見たところAmazonLinuxの場合 /etc/cloud/cloud.cfg.d/00_defaults.cfg の中で resize2fs の記述があるので、再起動時に実行されるようです。

こうして、何も考えずにrebootすることにより、dfの結果が変わりました :tada:

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       99G  9.1G   90G  10% /           # <- 99GBに増えてる
devtmpfs        490M   56K  490M   1% /dev
tmpfs           499M     0  499M   0% /dev/shm

続きを読む

boto3でec2をdescribeして値を取り出す

どうもLambda(Python)初心者の若松です。

唐突ですが、みなさんはLambda使っていますか?

サーバが無くてもコードが動く。
魅力的ですよねぇ。

やっぱり時代はServerlessだと。
インフラでも言語の一つくらい使えるようになれと。

そんな流れに逆らわないゆとり世代ど真ん中の私は、とうとうLambdaに踏み込んだわけです。

前置きが長くなりましたが、インフラ屋が四苦八苦しながら戯言言ってんなぁぐらいの気持ちで生暖かく見ていただければと思います。

やりたいこと

特定のIPを持っているインスタンスのインスタンスIDを取得したい。

環境

AWS Lambda
Python 3.6

第一形態

コード

とりあえずインスタンス情報を全部取り出してみようかと思い、以下のコードを書きました。

import boto3

def lambda_handler(event, context):

    ec2 = boto3.client('ec2')

    responce = ec2.describe_instances()

    return responce

結果

returnで以下のエラーが返って失敗

{
  "errorMessage": "An error occurred during JSON serialization of response",
  "errorType": "datetime.datetime(2017, 5, 11, 5, 15, 59, tzinfo=tzlocal()) is not JSON serializable"
}

結果そのままではJSONとして扱えないみたいです。

調査

boto3のリファレンスを呼んでみると、どうやら describe_instances の返り値は dict(辞書)型 というもののようです。
とりあえずprintで出力したほうがよさげなので、コードを変更することにしました。

第二形態

コード

とりあえず出力をプリント文に変更

import boto3

def lambda_handler(event, context):

    ec2 = boto3.client('ec2')

    responce = ec2.describe_instances()

    print(responce)

    return

結果

ログに以下が出力されました。
AWSCLIの結果でよく見るあれですね。

{
    "Reservations": [
        {
            "OwnerId": "xxxxxxxxxxxx", 
            "ReservationId": "r-xxxxxxxxxxxxxxxxx", 
            "Groups": [], 
            "Instances": [
                {
                    "Monitoring": {
                        "State": "disabled"
                    }, 
                    "PublicDnsName": "ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com", 
                    "Platform": "xxxxxxx", 
                    "State": {
                        "Code": 80, 
                        "Name": "stopped"
                    }, 
                    "EbsOptimized": false, 
                    "LaunchTime": "xxxx-xx-xxxxx:xx:xx.xxxx", 
                    "PublicIpAddress": "xxx.xxx.xxx.xxx", 
                    "PrivateIpAddress": "xxx.xxx.xxx.xxx", 
                    "ProductCodes": [], 
                    "VpcId": "vpc-xxxxxxx", 
                    "StateTransitionReason": "", 
                    "InstanceId": "i-xxxxxxxxxxxxxxxxx", 
                    "EnaSupport": true, 
                    "ImageId": "ami-xxxxxxxx", 
以下略

調査

次はIPで絞りたいと思います。
AWSCLIでいうところの--filterはboto3にもあるらしいのでそれを使うことにしました。

第三形態

コード

特定のIPを持っているインスタンスに絞るため、Filtersを追加

import boto3

def lambda_handler(event, context):

    ec2 = boto3.client('ec2')

    responce = ec2.describe_instances(
        Filters=[{'Name':'network-interface.addresses.private-ip-address','Values':["xxx.xxx.xxx.xxx"]}]
    )

    print(responce)

    return

結果

指定したIPアドレスを持つインスタンスに絞れた。

調査

次は返り値をインスタンスIDに絞ります。
イメージとしてはAWSCLIでいうところの--queryだが、boto3にはないようです。
ここでJSONに変換するだのなんだのと、ネットの情報に踊らされてかなり苦戦しました。
結果的には、dict型はオブジェクトの後に [“hoge”][“fuga”] と書くことで値を取り出せるらしいことがわかりました。

最終形態

コード

import boto3

def lambda_handler(event, context):

    ec2 = boto3.client('ec2')

    responce = ec2.describe_instances(
        Filters=[{'Name':'network-interface.addresses.private-ip-address','Values':["xxx.xxx.xxx.xxx"]}]
    )["Reservations"][0]["Instances"][0]["InstanceId"]

    print(responce)

    return

結果

めでたくインスタンスIDの取得に成功しました。
リストある際には[0]と明示的に0を書く必要があるというところに気をつけたいところです。
※AWSCLIの--queryでは[]と省略して書けるため

まとめ

いかがでしたでしょうか。
やってる内容は非常に初歩的な内容ですが、いくつかハマった箇所があったので備忘録的にまとめました。

意外と簡単に動かせるので、是非Tryしてみてください。
Lambda怖くない。

続きを読む