nelmoの日記帳

エンジニア見習いの備忘録とかです。

「エンジニアのためのデータ可視化[実践]入門」を読んだ

業務ではまだ触れていないけれど、少しづつ増えてきそうなので勉強を始めている。

エンジニアのための データ可視化[実践]入門 ~D3.jsによるWebの可視化 (Software Design plus)

エンジニアのための データ可視化[実践]入門 ~D3.jsによるWebの可視化 (Software Design plus)

以下、読書メモ。

感想

副題に「D3.jsによるWebの可視化」とあるけれどD3.jsはあまり本題ではなく、データ分析の目的とその目的を達成するためのアプローチ、そこから得られた結果をいかに理解しやすく伝えるかに主眼の置かれた本だと感じた。

特に勉強になったのは4章「何を可視化すべきか」と6章「探索的データ解析入門」。

4章ではKGI(Key Goal Indicator)とKPI(Key Performance Indicator)の設計について、Webサービスに典型的な例を基にした解説。 KPIの乱立はやりがちだと思うけれど、そういう時はKGIとの関連性を考えて必要な物に絞り込んで利用しないと効果が減ってしまう。「なぜそれをKPIに設定するのか」というのをKGIと照らしあわせて考えることが重要。 KPIの設計・データの利用方針についても勉強になった。実際に利用する時に読み返す章になると思う。 また、「仮説検証的なアプローチ」と「探索的なアプローチ」という2つを区別してデータ分析にとりかかるのは非常に重要。

  • 仮説検証的なアプローチ : 設定した仮説が正しいかどうかをデータを基に裏付けをとるための可視化
  • 探索的なアプローチ : データを様々な切り口から可視化することで問題を発見したり仮説設定の切り口とする

自分でも混同しないように気をつけないといけない。

6章では、上に述べた「探索的なアプローチ」を実施する際の概念と手法の解説が記載されていた。データを可視化した時に、中央値・分散・平均等のどこを中止するべきなのか、可視化する時にどういった図が有効なのかを学べた。

D3.jsは、、、はい、JS勉強します。でもブラウザ上でSVGを操作して可視化できるというのはかなりイノベーティブなことだと思うし、利用できる状況はかなり多そう。

JSはすぐにカオスなコードになってしまうので一度AltJSをしっかり勉強しなきゃと思いつつ手が付けられていない状況。CoffeeScriptとかTypeScriptとかあるけど、今から勉強するならどれがいいんですかね。

まとめ

いい本だと思いました(小学生並みの感想)。

RubyでHashの配列を単純な配列にしたい時はArray#mapを使う

Ruby

array_of_hash =[
  { :value => 0 }, 
  { :value => 1 },
  { :value => 2 },
]

っていうのがあった時に

[0, 1, 2]

っていう形に整形したいことがありました。

PHPでこの類のことをやる時はひたすらforeach回してたんだけど、Rubyならもっと簡単にできるんじゃないか?と思って調べたらこういうやり方ができそう。

array_of_hash =[
  { :value => 0 }, 
  { :value => 1 },
  { :value => 2 },
]

array_of_hash.map(&:values).flatten # => [0,1,2]

AWSでmicroインスタンスを使っていたら想定以上に課金された話

かなり痛い目を見たので、自戒の意味も込めてメモ。

事象

  • 今年4月に,社内向けにある用途のアプリケーションサーバとして、microインスタンス上にRails + Nginx + MySQLの環境を作成。
  • 6月でそのアプリの用途が終わったが、お上からの要望で7月いっぱいは24hで稼働させておくことに。
  • 7/31にサーバをTerminate.

見積もりでは、利用料金は数ドルで済むと考えていました。実際、6月まではその通りでした。

ですが、7月分の利用料金を確認してみると、なんと80ドルも課金されていました。

上記80ドルすべてがEBSのI/Oに対しての料金でした。 インスタンスの稼働時間自体は無料範囲内です。

7月はほとんど使っていないのに、なぜ?と考えてみました。

原因

調査をしようにもサーバ自体はTerminateしているので、手の打ちようがありません。ここから先は全て私の推測です。

もうお気づきの方もいるかと思いますが、メモリが不足してswap領域に書き込みが発生しまくったのが原因ではないかと考えています。

613MBしかメモリがないmicroインスタンス上でRails + MySQLを動かすと、当然メモリは不足します。で、swapにガリガリI/Oが発生します。

利用中も何度か再起動したりしていましたが、7月に入ってからは放置状態が続いていました。ですので、放置された分だけひたすらメモリを食い続けてしまったのだと思います。

対策

今回の件はもうどうしようもないですが、今後の教訓としては

  • (当然ですが)swapに対してもEBSのI/O課金が発生する!
  • microインスタンスで無停止ロングランはさせない!

これに尽きるのではないかと思います。

microで80ドル払うなら、m1.smallを一ヶ月フルで稼働させた方が安いです。

とはいってもやはりmicroの低料金は魅力的なので、使っていない場合はシャットダウンする、という鉄則を守り、適宜OS/ミドルウェアの停止/再起動をした方がいいと思います。

もう少し大きい規模の話ならクラウド破産するところだった、という話でした。

AWS ELBのProxy Protocolを触ってみた

Amazon Web Serviceブログに7/31付で発表がありましたが、ELBでProxy Protocolがサポートされました。

これにより、TCP接続の負荷分散でも、接続元IPアドレスの取得が可能になったようです。

Proxy Protocol?

ELBでは、(2013/08/04現在)以下のプロトコルをサポートしています。

HTTPとHTTPSに関してはX-Forwarded-Forを見ることで接続元クライアントのIPアドレスを知ることができましたが、TCPの負荷分散ではヘッダが付与されないため、取得することができませんでした。

しかし、先日追加されたELB Proxy Protocolで、この機能を有効化した場合、TCPデータ内に接続元IPアドレス情報が付加されるようになりました。

というわけで、ELBの設定方法と接続元IPアドレスの取得方法について調べてみました。

検証

以下の手順で検証しました。(かなり簡易的な検証となっています)

  1. EC2上にインスタンスを2台作成し、各サーバにMySQLをインストール
  2. ELBを作成し、作成したインスタンスを関連づける
  3. 作成したELBに対し、Proxy Protocolを有効にする
  4. telnetを叩き、通信をEC2インスタンス上でtcpdumpして取得する

MySQLインストール

特筆することはないです。いつも通りさくっと。

$ sudo yum -y install mysql-server
$ sudo service mysqld start

ELB作成

ELB自体の作成は、Management Consoleからさくっとやってしまいます。 EC2に作成したインスタンスMySQLポートに分散するように設定しましょう。

f:id:Canelmo:20130804161444j:plain

最初の画面で設定したBalancer Nameは次の手順で利用します。

Proxy Protocolを有効にする

ここが本番です。

Proxy Protocolの有効化、Management Consoleからは行うことができません。 現状はAPI経由でのみ行うことができるようです。

ELBのDocumentationでは、旧来の(?)CLIが使われているので、私はPython版CLIで検証してみました。

まず、Policyの情報を取得します。

$ aws elb describe-load-balancer-policy-types | jq '.PolicyTypeDescriptions[] | select(.PolicyTypeName=="ProxyProtocolPolicyType")'
{
  "PolicyAttributeTypeDescriptions": [
    {
      "Cardinality": "ONE",
      "AttributeName": "ProxyProtocol",
      "AttributeType": "Boolean"
    }
  ],
  "PolicyTypeName": "ProxyProtocolPolicyType",
  "Description": "Policy that controls whether to include the IP address and port of the originating request for TCP messages. This policy operates on TCP/SSL listeners only"
}

ProxyProtocolPolicyTypeの設定値をTrueにしてあげればいいようです。 ので、次のコマンドを実行。

オプションの--load-balancer-name の部分は、さっき作成したELBにつけた名前を設定して下さい。

--policy-nameはこのpolicyの名前になるものです。今回は[proxy-protocol-policy]としています。

$ aws elb create-load-balancer-policy          \
    --policy-name proxy-protocol-policy        \
    --load-balancer-name proxy-protocol        \
    --policy-type-name ProxyProtocolPolicyType \
    --policy-attributes '[{"attribute_name":"ProxyProtocol","attribute_value":"True"}]'

{
    "ResponseMetadata": {
        "RequestId": "eadec4c1-fcc9-11e2-9925-39cfc649e8ce"
    }
}

今作成したPolicyを、既存のELB配下のインスタンス群の3306番ポートに対して適用します。

$ aws elb set-load-balancer-policies-for-backend-server \
    --load-balancer-name proxy-protocol                 \
    --policy-name proxy-protocol-policy                 \
    --instance-port 3306

{
    "ResponseMetadata": {
        "RequestId": "2dd9fbcf-fcca-11e2-9925-39cfc649e8ce"
    }
}

$ aws elb describe-load-balancers
(...前略)
        "Policies": {
            "LBCookieStickinessPolicies": [],
            "AppCookieStickinessPolicies": [],
            "OtherPolicies": [
                "proxy-protocol-policy"
            ]
        },
        "Scheme": "internet-facing",
        "VPCId": "vpc-62cb0a0a",
        "CanonicalHostedZoneNameID": "Z2YN17T5R711GT",
        "BackendServerDescriptions": [
            {
                "PolicyNames": [
                    "proxy-protocol-policy"
                ],
                "InstancePort": 3306
            }
        ],
(...後略)

これで、Proxy Protocolが有効になりました。

telnet & tcpdump

皆さん大好きtcpdumpで、接続元のIPアドレスがとれているかどうか確認してみます。

$ sudo tcpdump -w packet.dump tcp port 3306

ローカルのマシンから、telnetで接続を確認します。

$ telnet proxy-protocol-1621762185.ap-northeast-1.elb.amazonaws.com 3306

で、キャプチャしたものをWiresharkで見てみます。

f:id:Canelmo:20130804161602p:plain

モザイクで編集してしまいましたが、IPアドレスと、ポート番号(のようなもの)が2つずつあります。

モザイクの黒っぽい部分に、接続元のIPアドレスが書いてあります。白っぽいモザイクの方には、プライベート帯のIPアドレスがありました。ELBの内部アドレスでしょうか。

疑問点

ここまでやって本日は時間切れ。

残った疑問点としては、

  • 2つめのIPアドレスとポート番号の正体
  • 自前アプリケーションからの取得方法

ここらへんはまた近いうちに。

vagrant-berkshelfが上手にインストールされない事象に遭遇

vagrant-berkshelfプラグインをインストールしていたら, はまったのでメモ。

手順はこちらのブログを参考に。

EC2のChef SandboxをVagrant+BerkShelfでさくっと作る|クラスメソッド株式会社 開発ブログ

環境

状況

vagrantはインストール済の状態から、vagrant-berkshelfを新規インストールしようとしました。

$ gem install berkshelf
$ vagrant plugin install vagrant-berkshelf

ここまではうまくいきました。

$ vagrant -v

すると、

/Applications/Vagrant/bin/../embedded/gems/bin/vagrant: No such file or directory - sysctl hw.ncpu
Failed to load the "vagrant-berkshelf" plugin. View logs for more details.

Vagrant version 1.2.4

対応

軽くググると、

https://github.com/RiotGames/vagrant-berkshelf/issues/47

を発見したので、手元で実行

$ VAGRANT_LOG=DEBUG vagrant -v

(中略)
ERROR root: Failed to load plugin: vagrant-berkshelf
ERROR root:  -- Error: #<NoMethodError: undefined method `[]' for nil:NilClass>
ERROR root:  -- Backtrace:
ERROR root: /Users/nelmo/.vagrant.d/gems/gems/celluloid-0.14.1/lib/celluloid/cpu_counter.rb:7:in `<module:CPUCounter>'
/Users/nelmo/.vagrant.d/gems/gems/celluloid-0.14.1/lib/celluloid/cpu_counter.rb:4:in `<module:Celluloid>'
/Users/nelmo/.vagrant.d/gems/gems/celluloid-0.14.1/lib/celluloid/cpu_counter.rb:3:in `<top (required)>'
/Applications/Vagrant/embedded/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
/Applications/Vagrant/embedded/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
/Users/nelmo/.vagrant.d/gems/gems/celluloid-0.14.1/lib/celluloid.rb:497:in `<top (required)>'
/Applications/Vagrant/embedded/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
/Applications/Vagrant/embedded/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
/Users/nelmo/.vagrant.d/gems/gems/berkshelf-2.0.7/lib/berkshelf.rb:3:in `<top (required)>'
/Applications/Vagrant/embedded/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
/Applications/Vagrant/embedded/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
/Users/nelmo/.vagrant.d/gems/gems/vagrant-berkshelf-1.3.3/lib/berkshelf/vagrant.rb:11:in `<top (required)>'
/Applications/Vagrant/embedded/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
/Applications/Vagrant/embedded/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
/Users/nelmo/.vagrant.d/gems/gems/vagrant-berkshelf-1.3.3/lib/vagrant-berkshelf.rb:1:in `<top (required)>'
/Applications/Vagrant/embedded/lib/ruby/1.9.1/rubygems/custom_require.rb:60:in `require'
/Applications/Vagrant/embedded/lib/ruby/1.9.1/rubygems/custom_require.rb:60:in `rescue in require'
/Applications/Vagrant/embedded/lib/ruby/1.9.1/rubygems/custom_require.rb:35:in `require'
/Applications/Vagrant/embedded/gems/gems/vagrant-1.2.4/lib/vagrant.rb:186:in `require_plugin'
/Applications/Vagrant/embedded/gems/gems/vagrant-1.2.4/lib/vagrant/environment.rb:768:in `block in load_plugins'
/Applications/Vagrant/embedded/gems/gems/vagrant-1.2.4/lib/vagrant/environment.rb:765:in `each'
/Applications/Vagrant/embedded/gems/gems/vagrant-1.2.4/lib/vagrant/environment.rb:765:in `load_plugins'
/Applications/Vagrant/embedded/gems/gems/vagrant-1.2.4/lib/vagrant/environment.rb:132:in `initialize'
/Applications/Vagrant/embedded/gems/gems/vagrant-1.2.4/bin/vagrant:62:in `new'
/Applications/Vagrant/embedded/gems/gems/vagrant-1.2.4/bin/vagrant:62:in `<top (required)>'
/Applications/Vagrant/bin/../embedded/gems/bin/vagrant:23:in `load'
/Applications/Vagrant/bin/../embedded/gems/bin/vagrant:23:in `<main>'
 INFO interface: error: Failed to load the "vagrant-berkshelf" plugin. View logs for more details.
(後略)

とのこと。

スタックとレースの一番上のcpu_counter.rbを見てみると、 当該行あたりに,

module Celluloid
  module CPUCounter
    case RbConfig::CONFIG['host_os'][/^[A-Za-z]+/]
    when 'darwin'
      @cores = Integer(`sysctl hw.ncpu`[/\d+/]) # この行

sysctlを実行してるんですね。

ここで確認してみると、(なぜか)/usr/sbinにPATHが通っていないことが判明。そのせいでsysctlコマンドが実行できていなかったようです。

/usr/sbinにPATHを通してあげた結果、

$ vagrant -v
Vagrant version 1.2.4

無事vagrant-berkshelfがloadされました。

あまりない事象かと思いますが、同じところで詰まっている方は一度PATHを参照してみて下さい。

AWS Game Day Tokyo 2013に参加してきた #awsgameday #jawsug

AWS Game day Tokyo

6/8(土)に開催された、[AWS Game Day Tokyo 2013]に参加してきました。

「AWS Game Day Tokyo 2013」 日本初!対戦型システム信頼性向上策を体感しよう!

AWS SAの皆様、面白い体験をありがとうございました!

簡単なルール

バッチ処理システムをAWS上に構築した後、他チームのAWSアカウントのPower User(IAM以外へのFull Control)をもらい、他チームのシステムをめちゃくちゃにする。 その上で、

  • 最もEvilな攻撃をしたチーム(Most Evil Break)
  • 最も強靭な防御をしたチーム(Most Awesome Fix)
  • 最も面白い画像を出力したチーム(Funniest Image)

の3つが表彰対象となります。

システム構成概要

画像加工バッチ処理クラスタを構築しました。 簡単にシステム説明すると、以下のような感じ。

                    |--->S3
 SQS -> Batch(EC2) -|
                    |--->SQS
  • 画像URLをSQS Inputとして渡す
  • Batchクラスタが画像をモンタージュ加工
  • 加工した画像をS3にアップロードし、完了メッセージのQueueを流す

という感じの流れになります。

参加前はWebアプリケーションが攻撃対象かなー、なんて考えていましたが、かなり裏をかかれた感じです。

諸事情あり、構築自体はほとんどCloudFormationでポチポチするだけでだったので、どうやって守りを固めるかが大事になりました。

攻撃

実行した攻撃は、主に下の4つです。

  1. S3のBucketPolicyをいじる

  2. AutoScalingをいじる

  3. SQSのConfigurationを変更する

  4. SQSにおかしなqueueを流す

1については、BucketPolicyでAll Denyにしました。これはバレやすいかなー、とも思いましたが、攻撃の一つとして決行。

2はAutoScalingのScheduled policyを変更して1分おきに全てのインスタンスがterminateされるようにしたり(この方法は向かいの席の方に教えて頂きました)、 launchConfigurationいじってインスタンスサイズをかえてみたりしました。

3はqueueのDelivery DelayをInput, Outputともに最大の15分に設定して、いつまで経ってもキューが消化されない状態を作りました。

4については、相方の@さんに調査をしてもらいながら、私が個人のAWSアカウントにApacheを立てて設定かえて、無限RedirectするURLをqueueに流し込むことにしました。 また、バッチ処理の実行スクリプトの挙動を見て、渡すURL文字列にOSコマンドを入れこめることがわかったので、それでchmodやkillを流したりしました(SQS Injection...)

CloudWatchとAutoscalingを連動させて、ある閾値に達したらサーバを自動でシャットダウンさせる案も考えたんですが、時間が足りなかった・・・ autoscalingのcliに慣れていなかったせいで、2にかなり時間を使ってしまいました。

参加前は、SecurityGroupとかでネットワークを不通にさせることも考えていましたが、EC2インスタンスへのInbound通信がほとんどない構成だったので断念しました。non-VPC環境ではOutboundの設定がいじれないですしね。

被攻撃 & 修復

40分間の攻撃が終わったら、今度は攻撃を受けた自システムを修復する時間でした。 私のシステムが受けた攻撃は、SQSとbucketの名前が変わっているという、比較的単純かつ気づきにくいものでした。 が、バッチプロセスが落ちる現象の修復に手間取ってしまい、時間内に実行できた画像処理は一件だけという結果に。。。 修復がうまくできなかったことは、大きな反省点です。

振り返り&懇親会

その後は、他のチームと手の内を明かし合う時間でした。 Cloudwatchの案はかなり好評で、時間が足りなかったことが悔やまれました。(負け惜しみ) ですが、無限Redirectが結構いい評価だったようなのでよかったです。 SQSInjectionに対する対策されていたチームがあるのには驚きました。とてもそこまでできる余裕がなかったです。。。 そして、出力できた唯一の画像が、まさかのFunniest Image賞に!www その画像がこちら。

f:id:Canelmo:20130609143831j:plain

終了後の懇親会も、様々な話が聞けてよかったです。

終わってから思ったこと

SQSのPermissionをいじって、外部アカウントからのenqueue/dequeueを許可してしまえば、外部マシンからSQSに対して継続的に好き勝手やることができるので、攻撃の一つとして試しておくべきだったと思いました。

これから

AWSを真剣に触り始めて半年ほどで、初めてAWSの勉強会(と呼んでいいかわかりませんが)に参加しました。 CloudFormationはまともに使ったことがないサービスでしたが、強力さが痛いほどわかりました。

一番印象的だったのは、Milesがプレゼンで

  • 最適な基盤状態を定義しておいて、いつでもその状態に戻せるようにしておく
  • 基盤は"Repair"ではなく"Rebuild"してしまえばいい

と言っていたことです。Milesの話に関しては@さんのブログが参考になります。 http://uncloudedeyes.hatenablog.com/entry/2013/06/09/001620

とにかく得る物が多い一日でした。

相方の@さん、ありがとうございました!

次回のGame Dayもぜひ参加したいですね。今度こそMost Evil Attackの称号を手に入れます!

AWS認定試験を受験してきた。

先日からAWSの認定試験の提供が開始しました。
AWS Certification

まだ英語版のみの提供ですが、今後数ヶ月で日本語版も提供されるとのこと。 試験科目も順次追加されていくようです。

ということでAWS Certified Solution Architect Associate Level examを 受験して、合格してきました。 正答率は67%で、決して誇れる数字ではないですが 次の試験が始まったら、次の目標はそれですね。