Amazon S3, Google Cloud Storageなど各種オブジェクトストレージの料金比較まとめ (2017/04/30)

少し前までクラウドに様々な環境を提供してくれるサービスというとはほぼAWS一択だった印象がありますが、近年はGoogleやMicrosoftもどんどん力を入れてきていますよね。

開発者として選択肢が増えるのは嬉しい限りです。

そんなクラウドサービスの中でもAmazon S3に代表されるようなオブジェクトストレージは、運用コストを考える必要がなく容量を気にせずどんどんファイルを保存していける非常に便利なサービスなので利用している方もとても多いのではないでしょうか。

各種ストレージの料金を比較する機会があったのでこちらにもまとめてみようと思います。

比較するサービス

上記の3つに加えて、国産の

これら2つのサービスを対象としています。

比較を行う項目は

  • ストレージ料金
  • 転送料金 (アップロード)
  • 転送料金 (ダウンロード)
  • GET, POSTなど各種リクエストにかかる料金

です。

転送料金 (アップロード)とは読んでそのまま、ストレージにファイルをアップロードする時にかかる料金です。

転送料金 (ダウンロード)はストレージから外部にファイルを取り出す時にかかる料金で、webサイトで画像を配信する時などの料金もこれにあたります。

なお、日本円で表記のあるサービスについてはそのままの表記を、USドルで表記のあるサービスについては併せて日本円での表記もしてありますが、こちらは僕が2017/04/30時点での為替レート $1 = 111.52円 で計算して勝手に付け加えたものです。

注意点

Amazon, Google, Microsoftのものはオプションとして

  • データセンターの場所
  • どれだけ耐久性を保証するか (≒どれだけ手厚くバックアップをとるか)
  • どれだけ頻繁な読み書きを前提とするか

などの項目の選択によって料金が変わってきますが、ここでは汎用ストレージとして一般的なオプションを選んでいます。

各社で指定可能なオプションの種類は異なりますし、バックアップの取り方、ネットワークの速度なども各社それぞれなので、必ずしも同一条件での比較ではない点にご注意ください。

各サービスの料金

Amazon S3

https://aws.amazon.com/jp/s3/pricing/

アジアパシフィック(東京リージョン) / スタンダードストレージ

項目 単位 価格
ストレージ 1GB / 月 $0.025 (2.788円)
アップロード 1GB 無料
ダウンロード 1GB $0.140 (15.613円)
PUT / COPY / POST / LIST リクエスト 10000回 $0.047 (5.241円)
GET およびその他のリクエスト 10000回 $0.0037 (0.413円)
DELETE リクエスト 10000回 無料

ダウンロードについては送信先が

  • 同一リージョンのEC2, CloudFrontの場合は 無料
  • 別のAWSリージョンの場合は $0.09(10.037円) / 1GB

という料金体系になり、AWS内部での利用ならばお得になるようです。

また、ストレージ/ダウンロード共に一定の利用量を超えた分は単価が安くなります

Google Cloud Storage

https://cloud.google.com/storage/pricing

asia-northeast1(日本リージョン) / Regional Storage

項目 単位 価格
ストレージ 1GB / 月 $0.023 (2.565円)
アップロード 1GB 無料
ダウンロード 1GB $0.14 (15.613円)
GET service / GET Bucket(オブジェクトのリスト) / PUT / POST リクエスト 10000回 $0.05 (5.576円)
GET Bucket(バケット設定) / GET Object / HEAD リクエスト 10000回 $0.004 (0.446円)
DELETE リクエスト 10000回 無料

ダウンロードについては、送信先が

  • 香港を除く中国の場合は $0.23 (25.65円) / 1GB
  • オーストラリアの場合は $0.19 (21.189円) / 1GB
  • 同一ロケーションまたはサブロケーションにあるGoogle Cloud Platformサービスの場合は 無料 (1)

になるようです。また、一定の利用量を超えた分は単価が安くなります

(1) 同一リージョン内サービスの場合は無料と捉えて問題ないのですが、Google Cloud Platformにはサービスロケーションについてマルチリージョン、リージョン2種類の概念がありAWSのものと若干異なります。詳しくは公式サイトを参照してください。

Microsoft Azure Storage

東日本リージョン / Blob Storage / LRS-HOT

項目 単位 価格
ストレージ 1GB / 月 2.04円
アップロード 1GB 無料
ダウンロード 1GB 14.08円
PUT / LIST / Create Container リクエスト 10000回 5.10円
GET およびその他のリクエスト 10000回 0.41円
DELETE リクエスト 10000回 無料

ダウンロードについては、

  • 最初の5GB/月までは 無料
  • 送信先が同一リージョンにあるAzureサービスの場合は 無料

となっています。

また、ストレージ/ダウンロード共に一定の利用量を超えた分は単価が安くなります

さくらのクラウド オブジェクトストレージ

http://cloud.sakura.ad.jp/specification/object-storage/

項目 単位 価格
ストレージ 1GB / 月 5.4円
アップロード 1GB 無料
ダウンロード 1GB 10.8円
ダウンロード (キャッシュ配信) 1GB 5.4円

これまで挙げた3つのサービスが完全に従量課金型だったのと異なり、さくらのオブジェクトストレージは基本料金が540円かかります。

かわりに最初の100GB/月まではストレージに従量課金がされず、基本料金のみが発生する形になっています。

100GBを超えた分に関して上記の表にある単価で料金が発生していきます。

キャッシュ配信というのはこちらに記載がある通り、さくらが東京に用意しているキャッシュサーバーに一時的にコンテンツをキャッシュし配信する仕組みです。

特に操作をせずとも自動的にキャッシュ配信用のURLが発行され、そちらにアクセスするだけでキャッシュ配信となるようです。

ダウンロードについては、

  • 通常のダウンロードで送信先がさくらのサービスの場合は 無料
  • 通常のダウンロード/キャッシュ配信共に最初の10GB/月までは 無料

となっています。

ストレージについては一定の利用量を超えた分は単価が安くなります

各種操作については特に記載がなかったので今のところ回数による従量課金はされず、単純に転送量に対してのものになるようです。

ConoHa オブジェクトストレージ

https://www.conoha.jp/faq/object-storage/

項目 単位 価格
ストレージ 1GB / 月 4.5円
アップロード 1GB 無料
ダウンロード 1GB 無料

上記URLに記載がある通りConoHaのオブジェクトストレージは従量課金型ではなく、100GB単位で容量を追加していく料金固定型になっています。

表では比較のために1GB単位の料金を記載していますが、本来は月100GB / 450円です。

また、転送量についても従量課金はされず全て無料となっています。

各種操作についても課金はされないようです。

まとめ

ざっくりとした料金の感覚

全サービス共通

  • アップロード → 無料
  • 各種操作 → めちゃ安い
  • 同一リージョン内または同一サービスへのダウンロード → 無料

海外サービス

  • ストレージ → 2.5円/1GB ぐらい
  • 外部へのダウンロード → 15円/1GB ぐらい

国内サービス

  • ストレージ → 5円/1GB ぐらい
  • 外部へのダウンロード → 10円/1GB ぐらい。ConoHaは無料

海外サービスについて

海外のクラウドサービスは3社ともだいたい同じような料金ですね。

先発のAWSの料金が最も高く、後発のAzureが一番安いようです。ダウンロードも最初の5GBだけ無料枠があります。

なんだか吉野家/松屋/すき家の牛丼戦争みたいです。

Google Cloud Storageのみ中国やオーストラリアへのダウンロードが極端に高いのはどうしてなのでしょうか?

Googleのビジネス的にコストが高くなってしまっている国なんでしょうかね、不思議です。

各種操作にかかる料金は10000回で数円程度なので特殊なユースケース以外は無視しても誤差の範囲で済みそうな感じです。

ストレージの料金は単価が転送の数分の一ですし事前にそれなりに利用量の予測がつくので計算しやすいですが、気になるのはなんといっても外部への配信料であるダウンロード転送料ですよね。

外部に公開するような用途で利用するのでなければ予測しやすいかと思いますが、だいたい15円/1GB程度のスタートなのでwebサービスを運営していてユーザがスケールするとあっとういう間に料金もスケールしてしまいます。

(例えば、クライアント側でのキャッシュを一切考慮に入れずに100万PV/dayで1PVあたり3MBの転送が発生する場合、3MB * 100万PV * 30日で1ヶ月に9万GBの転送が行われ、約15円 * 9万GBで135万円の転送料金が発生します。)

ユーザ数の予測自体なかなか難しいところなので、ここをどう見積もるかは少し工夫が必要になりそうですね。

国内サービスについて

さくら/ConoHaの両サービスはストレージの単価が海外サービスと比べると2倍程度お高くなっていますが、ダウンロードについてはだいぶ安くなっています。さくらはだいたい2/3ぐらいの単価ですね。

さくらは上でも少し書いたようにデフォルトでCDNのようにキャッシュ配信を行う専用URLが発行され、それを介した配信なら単価がさらに半分になるという仕組みがあります。海外サービスと比較すると1/3程度の単価になります。

キャッシュの更新時間は10分間ということですから、外部に公開するURLをキャッシュ配信用のものに限定することで「ファイルの更新には最長10分かかる代わりに転送料を半額にする」という運用も可能そうです。

もちろんアクセスの度に常にストレージから正直にファイルを配信するような仕組みにはせずに何らかのキャッシュ機構を挟んだりCDNを利用するなどして転送料の節約やパフォーマンスの向上をはかることも多いかと思いますが、デフォルトで転送料を節約する仕組みがあると少し嬉しいですね。

ConoHaに至っては無料となっていますが、これはおそらくそこまで大規模なユースケースは想定していないということなのでしょう。ConoHaで想定する利用規模を超えた場合は転送速度が絞られたり、なんらかの制限が加わりそうな気がします。

それでも個人で利用したりする場合はどれだけダウンロードが増加しても課金が発生しないというのは安心ですね。

どのストレージを使うにしてもしっかり事前にユースケースに合わせた計画を立てて有効に利用したいところです。

おまけ

オブジェクトストレージ系のクラウドサービスを利用する際に気になる転送料まわりの知見をまとめた記事へのリンクを集めてみました。併せてご活用ください。


データ転送量が多いと、S3結構高いのよ

S3の料金を見積もってみたお話です。


Amazon S3の利用料節約を考えてみる
Amazon S3の利用料節約を考えてみる(の結果)

S3 + CloudFrontで運用していた構成のCloudFront部分をさくらのVPS上に実装したキャッシュサーバに置き換えて転送料を80%以上削減できたお話です。キャッシュサーバはApacheで実装されています。


Cloudfrontより1/3も安いと噂のkeyCDNを試してみた

ファイルの配信用CDNに安価なものを採用して転送料を削減するお話です。


CloudFlareとAmazonS3を連携させてリクエストを抑える

CDNに基本無料のCloudFlareを利用して転送料を削減するお話です。


S3のZipファイルをRangeでバイナリアクセスして料金を節約

大量のメールアーカイブ運用のお話です。S3は1リクエスト毎にリクエスト自体へも課金があり、大量のファイルを細かくPOSTしてしまうと料金が高くなってしまう問題を回避しています。


Amazon Glacierでクラウド破産しないために

S3のオプションの1つに取り出すのに時間とコストがかかるようになる代わりに大体1/3程度の単価でファイルを保存できるようになるGlacierというものがあります。
頻繁には参照しないけど長期的に保管しておきたい大量のログや写真などを保存しておくのに便利なのですが、使い方を間違えると逆にとんでもない料金がかかるよ、というお話です。

続きを読む

多段CNAMEとパフォーマンス、およびAWS Route53のエイリアスレコードについて

概要

CNAMEレコードの設定値(参照先1)に、別の箇所でCNAMEとして設定されたドメイン名を指定することはできるか。できたとして、問題は無いのか気になったので調べた。

言葉にするとわかりにくいが、下記のような設定が可能かどうかだ。

web2.example.con. IN CNAME web1.example.com. 
web1.example.com. IN CNAME www.example.com.
www.example.com. IN A  192.0.2.1

“web2″のCNAMEで”web1″が指定され、”web1″のCNAMEで”www”が指定されるよな、CNAMEが連鎖する設定を「多段CNAME」とこの記事では呼ぶ。2

そして、多段CNAMEの短所を補うAWS Route53のエイリアスレコード(Aliasレコード)について最後に紹介する。

結論

  • 多段CNAMEの設定は可能
  • ただし、パフォーマンスに問題があるため避けたほうがよい。使う場合も多段の階層を深くしない
  • AWSでは、エイリアスレコード(Aliasレコード)を使える場合は使ったほうがよい

多段CNAMEは可能か

直感的に多段CNAMEは問題を起こしそうな気がしなくもないが、実際のところどうなのか。

DNSの基本的な仕様はRFCは1034と1035にまとめられているが3、いずれも多段CNAMEを禁止する記述は見当たらなかった。
下記の『CNAMEとパフォーマンス』でも軽く触れたが、RFC 1034に書かれているネームサーバとリゾルバの動作を見ても、問題なさそうだ。

ただし、多段CNAMEは循環的な参照を作りうる4ので、キャッシュDNSサーバ側でなんらかの制限が必要になる。
(BINDでは再帰問い合わせの回数に制限があるらしい)

CNAMEの循環的な参照については、RFCでも触れられている。

The amount of work which a resolver will do in response to a
client request must be limited to guard against errors in the
database, such as circular CNAME references, and operational
problems, such as network partition which prevents the
resolver from accessing the name servers it needs. While
local limits on the number of times a resolver will retransmit
a particular query to a particular name server address are
essential, the resolver should have a global per-request
counter to limit work on a single request.
(RFC 1034)

深すぎる多段CNAMEは、キャッシュDNSによる問い合わせが打ち切られる可能性があるため、避けるべきといえる。

(余談)CNAMEにおける制限

CNAMEに関する制限事項といえば、よく知られた話だが「CNAMEは他のリソースデータと共存できない」というものがある。

If a CNAME RR is present at a node, no other data should be present;
(RFC 1034より引用)

この件に関してはRFC 1912が詳しく、このRFC内の例を用いると、下記の設定が許可されない。(NOT ALLOWED)

           podunk.xx.      IN      NS      ns1
                           IN      NS      ns2
                           IN      CNAME   mary
           mary            IN      A       1.2.3.4

podunk.xx.にCNAMEを指定すると同時に、NSとしても指定しているため、「no other data should be present」に反している。

修正するには、下記のようする。

           podunk.xx.      IN      NS      ns1
                           IN      NS      ns2
                           IN      A       1.2.3.4
           mary            IN      A       1.2.3.4

CNAMEをAレコードに変更している。

CNAMEとパフォーマンス

CNAMEをつかうことで、問い合わせのパフォーマンスが低下する。

なぜなら、キャッシュDNSサーバはCNAMEの応答を受け取ったとき、その受け取ったドメイン名についての問い合わせを始めから実行する必要があるためだ。

つまり、キャッシュDNSサーバが”www.example.com” のAレコードを問い合わせている最中で、「www.example.com の正規名はwww.example.org である」と知った場合、今までの問い合わせを打ち切って”www.example.org” の問い合わせを実行する必要がある。

通常の2回分の問い合わせが行われることになり、あきらかに問い合わせの効率が下がる。
さらにいうと、CNAMEの段数が増えれば増えるほど、問い合わせの回数は増えていく。

このときの動作の詳細は、RFC 1034の『5.5.2 Aliases』を読むとよい。

ただし、CNAMEを多段に用いてもパフォーマンスの低下が少ないケースもある。それは、自身のゾーン内、もしくは委譲先のゾーンでCNAMEが解決される場合だ。
この点については、同じくRFC 1034の『4.3.2. Algorithm』を読んでほしい。

AWS Route53のエイリアスレコードについて

エイリアスレコード(Aliasレコード)とは

エイリアスレコードは、CNAMEとよく似た機能を提供するが、その弱点をカバーする。

まず、エイリアスレコードを知らない人のために、AWS公式ドキュメントから引用する。

Amazon Route 53 は “エイリアス” レコードを提供します (Amazon Route 53 固有の仮想レコード)。[中略]エイリアスレコードは CNAME レコードのように機能するので、DNS 名(example.com)を別の「ターゲット」DNS 名(elb1234.elb.amazonaws.com)にマッピングできます。それらはリソルバーに表示されていないという点で、CNAME レコードと異なります。リソルバーには、A レコードと結果として生じるターゲットレコードの IP アドレスだけが表示されます。

(AWS Route53ドキュメント、『よくある質問』より引用)

先ほど述べた通りだが、キャッシュDNSのサーバがCNAMEの応答を受け取ったときの動作は、返されたドメイン名に対する問い合わせを再度実行する。
これが問い合わせのコストの増加となり、問い合わせのパフォーマンス(主にクライアントへ応答を返す時間)が悪化してしまう。

しかし、エイリアスレコードを使うことで、Aレコードと同じコストでCNAMEと同様の機能を使うことができる。

エイリアスレコードとCNAME

ELBを作成すると、my-loadbalancer-1234567890.us-west-2.elb.amazonaws.comのようなドメイン名がAWSから発行される。

このドメイン名は人間にとってわかりにくいため、”my-web.example.com” のようなドメイン名を別名でつけることがある。

このとき、CNAMEを使った場合、キャッシュDNSサーバは”my-web.example.com” の問い合わせ中にCNAMEとして”my-loadbalancer-1234567890.us-west-2.elb.amazonaws.com” を受け取り、”my-loadbalancer-1234567890.us-west-2.elb.amazonaws.com”の問い合わせをルートドメインから行うことになる。

一方でエイリアスレコードを使った場合、CNAMEの応答は行われず、一連の”my-web.example.com” の問い合わせの中で、”my-loadbalancer-1234567890.us-west-2.elb.amazonaws.com” のAレコードが返される。

エイリアスレコードを使用するための条件

なぜこのようなことができるかというと、参照先のドメイン名の解決をRoute 53内部で行なっているためだ。

したがって、エイリアスレコードを使用するには2つの条件を満たす必要がある。

  • ユーザはRoute 53を使うこと(そもそもエイリアスレコードはAWSの拡張機能)
  • 参照先が、AWSの限定されたサービスであること(CloudFront, ELB, 静的ウェブサイトとしてのS3など)

エイリアスレコードを使った場合のデメリットは特に無いので、使用できるケースであれば積極的に使うべきだ。

エイリアスレコードの詳細については、Route 53のドキュメントの『エイリアスリソースレコードセットと非エイリアスリソースレコードセットの選択』を読んでほしい。

参照先として指定できるドメイン名についても、このドキュメント内に書いてある。

(余談)パフォーマンスではない、エイリアスレコードのメリット

私が思うエイリアスレコードの最大のメリットはパフォーマンスだが、AWSのドキュメントでは触れられていない。

『よくある質問』には下記のように書いてある。

静的なウェブサイトをホスティングするよう設定されている CloudFront ディストリビューションおよび S3 バケットには、CNAME を使用する代わりに、CloudFront ディストリビューションまたは S3 ウェブサイトバケットにマッピングされる “エイリアス” レコードを作成することを推奨します。エイリアスレコードには 2 つの利点があります。1 つは、CNAME とは異なり、エイリアスレコードは zone apex (例えば、www.example.com ではなく example.com) に対しても作成できることです。もう 1 つは、エイリアスレコードへのクエリは無料であることです。

また、AWS Trusted AdvisorでAWSの設定をチェックしたときに、エイリアスレコードを使える箇所でCNAMEを使っていた場合にワーニングになる。

参考文献

  1. RFC 1034 『DOMAIN NAMES – CONCEPTS AND FACILITIES

  2. RFC 1035『DOMAIN NAMES – IMPLEMENTATION AND SPECIFICATION』
  3. RFC 1912『Common DNS Operational and Configuration Errors』
  4. AWS Route53ドキュメント
  5. DNSのRFCの歩き方( http://dnsops.jp/event/20120831/DNS-RFC-PRIMER-2.pdf )
    • 記事を書いてから気づいたが、DNSのRFCを自力で読む前に目を通すと良さそう

  1. CNAMEは”canonical name”なので”正規名”などどしたほうが正確だが、わかりやすさを重視して”参照先”とした 

  2. 「多段CNAME」をググるとそれなりにヒットするので、ある程度一般的な言葉かもしれない 

  3. 関連するRFCは他にもある. 参考文献の3を参照 

  4. 「web.example.com -> www.example.com -> web.example.com …」のような循環的な参照 

続きを読む

lambda入門(Node)③ – API Gatewayを使ってslackからのリクエストをlambdaで受けられるようにする

第3回になりました。

過去のはこちら。

今回は、ようやくやりたいことに近づいて来まして
slackからのリクエストをlambdaで受けられるようにしたいと思います。

何か調べていくと、どうもAPI Gatewayを使うと良い感じぽい。
まずはAPI Gatewayについて予習を。

API Gateway

どんな役割をしてくれるのか

APIのエンドポイントとして待ち構える玄関として使える
現在はhoge/*のようなパスを/hoge/{proxy+}として設定できる
プロキシリソースなるものらしい
post, putの振り分けなどが簡単になった

課金体系

受信した API 呼び出しと、送出したデータ量に対して発生

serverless frameworkでの設定

serverless.yml
functions:
  bookStore:
    handler: books/store.store
    events:
      - http:
          path: books
          method: get
          cors: true

これだけ。
デプロイ実行すると

$ serverless deploy -v --stage dev
Serverless: Packaging service...
・
・
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Deployment - ApiGatewayDeployment1490662361327
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Deployment - ApiGatewayDeployment1490662361327
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Deployment - ApiGatewayDeployment1490662361327
・
・
Service Information
service: testProject
stage: dev
region: ap-northeast-1
api keys:
  None
endpoints:
  GET - https://xxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/books
functions:
  bookStore: testProject-dev-bookStore

エンドポイントが作られた。。!

レスポンスを送れるか試してみる

$ curl https://xxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/books
{"message":"ok",...........}

うん、okそう、すごい。ymlに書くだけで何でも設定してくれちゃう。

Slackから応答させるようにする

ここで今回の本題。
作成したエンドポイントに対してスラックがリクエストを送って
slack上にメッセージが返ってくるようにする。

全体像

スクリーンショット 2017-04-29 15.17.57.png

やることとしては、リクエストパラメータを解析してデータを保存。
保存した結果を返す

book.js

'use strict';

const slackAuthorizer = require('../authorizer/slackAuthorizer');
const parser = require('../service/queryParser');

const bookSave = require('../useCase/book/save.js');

module.exports.book = (event, context, callback) => {
  const queryParser = new parser(event.body);
  const authorizer = new slackAuthorizer(queryParser.parseToken());

  /**
   * 認証
   */
  if (!authorizer.authorize()) {
    context.done('Unauthorized');
  }

  /**
   * @Todo ここで lambda function の振り分けを行いたい
   */
  bookSave(event, (error, result) => {});

  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'ok',
    }),
  };

  callback(null, response);
};

同期で処理を行いたいので asyncを使って処理をブロックごとに実行する

save.js

'use strict';

const AWS = require('aws-sdk');
const dynamoDB = new AWS.DynamoDB.DocumentClient();
const bookTable = process.env.bookTable;

const uuidV1 = require('uuid/v1');

const webhookUrl = process.env.slack_webhook_url;
const request = require('request');

const dateTime = require('node-datetime');
const dt = dateTime.create();
const insertDate = dt.format('Y-m-d H:M:S');

const async = require('async');

const parser = require('../../service/queryParser');

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

  const queryParser = new parser(event.body);

  const key = uuidV1();

  async.series([
    function(callback) {

      /**
       * データを保存
       */
      dynamoDB.put({
        'TableName': bookTable,
        'Item': {
          'id': key,
          'title': queryParser.parseText(),
          'insert_date': insertDate,
        },
      }, function(err, data) {
        callback(null, "saved");
      });
    },
    function(callback) {

      /**
       * 保存したものを取り出して
       * 結果を返す
       */
      dynamoDB.get({
        TableName: bookTable,
        Key: {
          id: key,
        },
      }, function(err, data) {
        if (!err) {
          const response = {
            text: ``${data.Item.title}` is Saved !!`,
          };
        
          /**
           * webhook でチャンネルにメッセージを返す
           */
          request.post(webhookUrl, {
            form: {
              payload: JSON.stringify(response),
            },
          }, (err, response, body) => {
            callback(null, 'getData');
          });
        }
      });
    },
  ], function(err, results) {
    if (err) {
      throw err;
    }
  });
};

slash commandから送られてきたパラメータをパースするために
query-stringモジュールを使い、ラップした

queryParser.js
'use strict';

const queryStringParser = require('query-string');

module.exports = class queryParser {

  constructor (queryString) {
    this.queryString = queryString;
  }

  parseToken () {
    return queryStringParser.parse(this.queryString).token;
  }

  parseText () {
    return queryStringParser.parse(this.queryString).text;
  }
};

slashコマンドの設定は省略します。
最初の api gatewayで設定されたエンドポイントのURLを
設定してあげればいいので

実際に動かすとこんな感じです。

4b4b9687608f58545ef2b4536a7166c2.gif

ソースは汚いかもしれませんが、saverlessフレームワークで
エンドポイント作成やdb、外部サービスの連携が簡単に出来ました

もっとキレイに書けるようにjsの筋力をつけていかねば。。笑

続きを読む

RDSのIAM認証 (aws−cli編) 【cloudpack大阪ブログ】

cloudpack大阪の佐々木です。

RDSの認証をIAMでできるという話です。
http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html

基本的な設定はこちらを参照
http://qiita.com/Pampus/items/18b45330b990927652fd

認証に必要なTokenをaws-cliで取得できるようになってました。

aws-cliをアップデートします。

$ sudo pip install -U awscli
$ aws --version
aws-cli/1.11.81 Python/2.7.12 Linux/4.9.17-8.31.amzn1.x86_64 botocore/1.5.44

aws rds generate-db-auth-token コマンドが使えるようになっています。

NAME
       generate-db-auth-token -

DESCRIPTION
       Generates an auth token used to connect to a db with IAM credentials.

SYNOPSIS
            generate-db-auth-token
          --hostname <value>
          --port <value>
          --username <value>

OPTIONS
       --hostname (string) The hostname of the database to connect to.

       --port (integer) The port number the database is listening on.

       --username (string) The username to log in as.

実行するとトークンが発行されます。

$ aws rds generate-db-auth-token --hostname test.xxxxxxxxxx.ap-northeast-1.rds.amazonaws.com --port 3306 --username testuser --region ap-northeast-1
test.xxxxxxxxxx.ap-northeast-1.rds.amazonaws.com:3306/?Action=connect&DBUser=testuser&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&X-Amz-Date=20170427T134947Z&X-Amz-SignedHeaders=host&X-Amz-Security-Token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&X-Amz-Credential=xxxxxxxxxxxxxxxxxxxxxx2Faws4_request&X-Amz-Signature=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

これをパスワードにして接続します。

$ mysql -u testuser -h test.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com -p`aws rds generate-db-auth-token --hostname test.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com --port 3306 --username testuser --region ap-northeast-1` --ssl-ca=rds-combined-ca-bundle.pem
ERROR 2059 (HY000): Authentication plugin 'mysql_clear_password' cannot be loaded: plugin not enabled

んん?

--enable-cleartext-plugin ってのがいるらしいです。
https://dev.mysql.com/doc/refman/5.6/ja/cleartext-authentication-plugin.html

$ mysql -u testuser -h test.xxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com -p`aws rds generate-db-auth-token --hostname test.xxxxxxxxxx.ap-northeast-1.rds.amazonaws.com --port 3306 --username testuser --region ap-northeast-1` --ssl-ca=rds-combined-ca-bundle.pem --enable-cleartext-plugin
Welcome to the MySQL monitor.  Commands end with ; or g.
Your MySQL connection id is 711
Server version: 5.7.16-log MySQL Community Server (GPL)

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.

mysql>

続きを読む

LambdaでRDSチュートリアル

AWSのAPI GatewayとLambdaとRDSを用いて、RDSに保存されているデータを外部に公開するAPIを作成することを想定して、まずは、LambdaからRDSに接続する

想定するテーブル構造

# テーブル名: link_clicks
| カラム名   | description       | 
| path      | urlのパス         |
| clicks    | リンクのクリック数   |
| stat_date | 集計日             |

1. Lambda関数を作成する

lambdaを用いて、RDS(mysql)に接続して、クエリパラメータ(path)に応じた情報を引き出して返すものプログラムをpythonで作成する

1.1 pythonパッケージ(api)を作る

mkdir api

1.2 pythonの依存パッケージをインストールする

mysqlへの接続のためにPyMySQLをインストールする

pip install PyMySQL -t api

1.3 メイン関数を記載する

メイン関数(handler)記載ファイルと設定ファイルを用意する
(YOUR_XXXは置換する)

api/api.py
# -*- coding: utf-8 -*-
import sys
import logging
import rds_config
import pymysql
import datetime as DT
import json

#rds settings
rds_host  = rds_config.db_host
name = rds_config.db_username
password = rds_config.db_password
db_name = rds_config.db_name


logger = logging.getLogger()
logger.setLevel(logging.INFO)

try:
    mysql_client = pymysql.connect(rds_host, user=name, passwd=password, db=db_name, connect_timeout=5)
except:
    logger.error("ERROR: Unexpected error: Could not connect to MySql instance.")
    sys.exit()

logger.info("SUCCESS: Connection to RDS mysql instance succeeded")

def handler(event, context):
    """
    This function fetches content from mysql RDS instance
    """

    path = event["path"]

    query = "select sum(clicks) from link_clicks where path = '%(path)s' group by path" % dict(path=path)
    logger.info(query)

    results = []
    with mysql_client.cursor() as cur:
        cur.execute(query)
        for row in cur:
            clicks = int(row[0])
            results.append({"clicks": clicks})

    return json.dumps(results)
api/rds_config.py
db_host = "YOUR_HOST"
db_username = "YOUR_USERNAME"
db_password = "YOUR_PASSWORD"
db_name = "YOUR_DB_NAME"

1.4 lambdaにプログラムをアップロード

deploy.sh
# zip作成
(cd api && zip -r - *) > api.zip

# create lambda function
## YOUR_ROLEにはlambda-vpc-execution-roleを指定すること
aws lambda create-function \
--region YOUR_REGION \
--function-name api  \
--zip-file fileb://api.zip \
--role YOUR_ROLE \
--handler api.handler \
--runtime python2.7 \
--vpc-config SubnetIds=YOUR_SUBNET_IDS,SecurityGroupIds=YOUR_SECURITY_GROUP_ID

# update lambda function
aws lambda update-function-code \
--region YOUR_REGION \
--function-name  api  \
--zip-file fileb://api.zip

2. Lambda関数を確認する

2.1 Lambda関数がアップロードされたことを確認する

以下にアクセスして、apiファンクションが追加されたことを確認する
https://ap-northeast-1.console.aws.amazon.com/lambda/home?region=ap-northeast-1#/functions?display=list

apiファンクションが追加されてる.png

2.2 テストイベントを設定する

actions > configure test event からテストイベントを登録する
設定したものは以下の通り

{
  "path": "/11111"
}

testイベント設定.png
テストイベントとしてpathを設定.png

2.3 テストする

上記画像の save&test をクリックしてテストする
テスト結果が表示される.png

最後に

以上の手順で、lambda => RDSに接続して、データを参照できるようになりました
詳細はawsのドキュメントにも書いてあるので、そちらを参考にしてください
http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/vpc-rds-create-lambda-function.html

続きを読む

IAM Database AuthenticationをPythonから試す

背景

RDS for MySQLとAuroraのDB接続にIAMが使えるようになったそうです。
Manage access to your RDS for MySQL and Amazon Aurora databases using AWS IAM
IAM Database Authentication for MySQL and Amazon Aurora
サンプルがJavaだったのでPythonから試してみました。

環境

  • Amazon Linux 2017.03
  • Aurora 1.12
  • Python 2.7.12
  • boto3-1.4.4

準備

Aurora

RDSのクラスターから「クラスターの変更」を開き「IAMのDB認証を有効にする」を「はい」に設定します。Auroraの場合db.t2.smallはIAMデータベース認証をサポートしていないためdb.t2.medium以上で試してください。
RDS_·_AWS_Console.png

DBユーザーの作成

IAMアクセス用のDBユーザーを作成し、必要な権限を付与します。

mysql> CREATE USER iam_auth_user@'testdb-cluster.cluster-abcdefghijkl.ap-northeast-1.rds.amazonaws.com' IDENTIFIED WITH AWSAuthenticationPlugin as 'RDS';
mysql> GRANT SELECT ON `testdb`.* TO iam_auth_user@'%';

公開鍵のダウンロード

IAMデータベース認証はSSL接続が必須という事なので公開鍵をダウンロードしてec2上の適当なパスに配置しておきます
http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Aurora.Overview.html#Aurora.Overview.Security.SSL

IAM

ドキュメントを参考にIAM Roleに権限を付与します。リソースIDはクラスターのものを指定しています。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "rds-db:connect"
      ],
      "Resource": [
        "arn:aws:rds-db:ap-northeast-1:12345678:dbuser:cluster-12ABC34DEFG5HIJ6KLMNOP78QR/iam_auth_user"
      ]
    }
  ]
}

接続

iam_db_auth.py
#  -*- coding: utf-8 -*-
from __future__ import print_function
import boto3
import mysql.connector
from mysql.connector.constants import ClientFlag

rds = boto3.client('rds', region_name='ap-northeast-1')

user = 'iam_auth_user'
host = 'testdb-cluster.cluster-abcdefghijkl.ap-northeast-1.rds.amazonaws.com'
db_auth_token = rds.generate_db_auth_token(host, 3306, user, 'ap-northeast-1')

config = {
    'user': user,
    'password': db_auth_token,
    'host': host,
    'db': 'testdb',
    'client_flags': [ClientFlag.SSL],
    'ssl_ca': 'rds-combined-ca-bundle.pem'
}

cnx = mysql.connector.connect(**config)
cur = cnx.cursor(buffered=True)

cur.execute('SELECT AURORA_VERSION();')
print(cur.fetchone())

cur.close()
cnx.close()
$ python iam_db_auth.py 
[(u'1.12',)]

以上です。

参考

続きを読む

WordPressのサーバがメモリ不足で落ちたので、SWAP領域を追加して応急処置

はじめに

先日、ブログサイトとして運用していたWordPressのサーバがメモリ不足で落ちたので、実際にやった対処法をご紹介します。
原因は、深夜にヘッダー画像をアップロードしたり、切り取ったりを繰り返していたら、MySQLが死んだみたいです。

EC2 t2.microのインスタンスはWordPressのサーバに使用すると、ぎりぎり落ちるか落ちないかのスペックみたいですね。

# service mysqld status
mysqld dead but subsys locked

tail -100 /var/log/mysqld.log
// 一部抜粋
2017-02-07 16:41:15 17577 [ERROR] InnoDB: Cannot allocate memory for the buffer pool
2017-02-07 16:41:15 17577 [ERROR] Plugin 'InnoDB' init function returned error.
2017-02-07 16:41:15 17577 [ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed.
2017-02-07 16:41:15 17577 [ERROR] Unknown/unsupported storage engine: InnoDB
2017-02-07 16:41:15 17577 [ERROR] Aborting

インフラ構成

EC2 t2.microインスタンス1台構成
AmazonLinux
Apache2.2 MySQL5.5 PHP5.6

SWAPとは

ハードディスクなどの補助記憶装置を利用して使用可能なメモリ容量を増やすOSの機能の一つ。
ハードディスク上に「スワップファイル」あるいは「スワップ領域」と呼ばれる専用の保存領域を用意して、メモリ容量が不足してきたら現在使われていないプログラム(プロセス)を一時的にスワップファイルに書き出して消去し、占有していたメモリを開放する。メモリからハードディスクに退避する動作を「スワップアウト」(swap-out)、ハードディスクからメモリに書き戻す動作を「スワップイン」(swap-in)という。

http://e-words.jp/w/%E3%82%B9%E3%83%AF%E3%83%83%E3%83%97.html
より引用

作業内容

とりあえずMySQLの再起動を試しましたが、出来ず。

# service mysqld restart
Stopping mysqld:                                           [  OK  ]
Starting mysqld:                                           [FAILED]

freeコマンドで現在のメモリの仕様状況を確認します。

# free
             total       used       free     shared    buffers     cached
Mem:       1019280     560060     459220      54268       9936      96820
-/+ buffers/cache:     453304     565976
Swap:            0          0          0

SWAP領域の追加

ddコマンドで、SWAP用ファイルを作成する。
※中身が0で埋め尽くされた、1Gbyteのswapfile1という名前のファイルができます。

# dd if=/dev/zero of=/swapfile1 bs=1G count=1

パーミッションを設定

# chmod 600 /swapfile1

SWAP領域の作成

# mkswap /swapfile1
Setting up swapspace version 1, size = 1048572 KiB
no label, UUID=67eb1ae1-16bf-4ed2-9b4c-3808cd59f6d3

SWAP領域を有効化する

# swapon /swapfile1

freeコマンドでSWAP領域が適用されたことを確認

# free
             total       used       free     shared    buffers     cached
Mem:       1019280     949668      69612      54268       3052     481712
-/+ buffers/cache:     464904     554376
Swap:      1048572          0    1048572

MySQLの起動

# service mysqld start
Starting mysqld:                                           [  OK  ]

終わりに

最初MySQLが再起動出来なかった時、正直めっちゃ焦りました笑
なんとか原因がわかって、対処出来てよかったです。
SWAP領域を確保しておくだけで、ある程度の負荷には耐えられるようになるので、サーバのスペックを変更せずに対処出来てオススメです。

読んでいただきありがとうございました!

続きを読む

【勉強会メモ】GitHubとクラスメソッドの勉強会〜GitHub x AWSの最新DevOps事情〜

参加した勉強会のメモです。

開催日: 2017年4月26日
GitHubとクラスメソッドの勉強会〜GitHub x AWSの最新DevOps事情〜
ハッシュタグ: #github_method

How we ship GitHub

発表者

GitHub 池田尚史さん(@ikeike443)
チーム開発実践入門GitHubツールビルディングの著者
 

近年起きていること

  • アジャイル:三角形は逆さまに

    • 従来はScope(やりたいこと)をFIXし、Time, Resourceをそれにあわせて決めていた
    • 近年は先にTime, ResourceをFIX, Scopeを柔軟に
  • VCS:コラボレーションがやりやすくなった
    • Git,GItHubなどの分散リポジトリ
  • 継続的デリバリー
    • GitHubは「継続的デプロイメント」を目指す

      • 本番リリースまで自動化

GitHubはどうしているか

  • 一日に数十回デプロイ
  • リモートワークが盛ん
  • GitHub Flow
  • Hubot(ChatOps)

GitHub Flow

  • Masterを常にデプロイ可能な状態に保つ
  • 必ずFeatureブランチを作り、その生存期間を短く保つ
  • 溜め込まずすぐにPR、フィードバックをもらう
  • デプロイして問題のないもののみMasterにマージ
  • PRは議論の場

Hubotの利用

なぜChatOpsなのか?

  • shared console
  • 誰がやっても同じ結果
  • 他人がやっているのを見て学べる
  • リモートワークに最適
  • スマホでデプロイも可能に
  • いつだれがどんな操作をしたかすべて残る

本番リリース

  • PRを本番にリリースしてからMasterブランチにマージ(※Masterにマージしてからデプロイではない)
  • 本番デプロイ依頼をキューイング(Hubotがキュー管理)
  • DataDocを利用してグラフでチェック

様々なことをすべてチャットで

  • 営業的な見積もり
  • お遊びコマンド

DevOpsとAWS

発表者

クラスメソッド 千葉淳さん(@iron_breaker)

資料

https://www.slideshare.net/ssuser8125c5/test-75422283?ref=http://dev.classmethod.jp/cloud/github-cm-study-aws-and-devops/

今日話すこと

  • AWSにおけるDevOpsサービスの話

DevOpsとは?

  • 開発者と運用者が協力したフロー
  • 安全に素早くたくさんリリースするための仕組み
    • 属人化の排除

      • デリバリの自動化
      • コードで管理
    • 開発の効率化
      • チームコラボレーション
      • ちいさくたくさんリリース
    • 安全なリリース
      • デプロイの自動化(B/Gデプロイメント、切り戻し)
      • フローの整備(承認とか)
    • モニタリング

DevOpsの目的

  • ビジネスに使う時間を増やす

CI/CDとは?

CIとは?

  • ビルド、テストの自動化
  • リポジトリ→ビルド→Unitテスト

CDとは?

  • CIに加えて、デプロイまで
  • ステージング環境→統合テスト→本番環境

AWSサービスの紹介

  • たくさんあったので資料参照

DevOps時の注意

  • プロビジョニングとデプロイは別管理にする(ライフサイクルが違う)

GitHub EnterpriseとAWSを組み合わせたCDP

発表者

クラスメソッド 中山幸治さん

資料

https://speakerdeck.com/knakayama/github-classmethod-study-20170426

※途中退席したためメモなし

続きを読む