gentooでびっくりしたこと

3号です。gentooで、

sudo emerge --ask rmagick

とかしたら、依存でRubyも入るんですね。そしたらbundlerが動かなくなったんですね。調べてみたらRUBYOPT=-rauto_gemがセットされてた。一時間くらい何が起きたかわからなかった。rmagickもgemで入れたらいいだけなんだけどね。

似たような被害に遭っている方はいるようですが、みなさまはrvm関連のようです。私は別ケースでしたが。gentooでRubyを扱うときはご注意を。

なんでGentoo使ってるの?

言えねえ。

Vagrantのshell provisionerはシェルスクリプト以外も指定出来る

1号による以下の「記事数/はてブ数の可視化」によりビリとして肩身が狭い2号です。

Vagrantのprovisionerは各種あり、大体はChef, Ansible, Puppet等の構成管理ツールが使われることが多いためそれほど登場の機会の無いshell provisioner、名前の通りシェルスクリプトに記載した内容に沿ってVMをprovisioningしていきますが、実はこの実行はshebangを見ているため、シェルスクリプト以外を指定することが出来ます。

「男なら黙ってオールPythonでprovisioningだ!」という硬派な方は、例えば以下のようなyumのAPIを直接叩くスクリプトを書き

#!/usr/bin/python
import yum

yb=yum.YumBase()
searchlist=['name']
arg=['vim-enhanced']
matches = yb.searchGenerator(searchlist,arg)
for (package, matched_value) in matches :
    if package.name == 'vim-enhanced' : yb.install(package)
    yb.buildTransaction()
    yb.processTransaction()

Vagrantfileに以下の様に指定することで

config.vm.provision :shell do |shell|
  shell.path = "provision.py"
end

邪道なシェルスクリプトを使うことなくvimをインストールすることが出来るというわけです。多分誰もやらんだろうけど。

はてなAPIとS3を使って隊員ごとのブクマ数をブログパーツ化してみた

このブログは複数人で同一アカウントを使って書いていて、だれが書いたかはカテゴリに『X号』ってのをつけて区別してます。

開設から2週間たって記事もたまってきたので、隊員ごとの記事数、はてブ数を表示するブログパーツを作ってみました。
サイドバーの下のほうにこんな感じでついてるやつです。

f:id:oretachino:20141213181650p:plain

今のところスマホだと出ません(´・ω・`)

実装について

はてなのAPIで記事情報とブックマーク数を取得してS3にアップロード、ブログパーツから読み込む、という感じで作ってみました。

はてなブログの記事URLとカテゴリははてなブログAtomPubで取得しました。
Basic認証であればcurlコマンドで試せて、こんな感じにXMLで記事リストが取れます。

$ curl --user oretachino:<APIキー> https://blog.hatena.ne.jp/oretachino/oretachino.hatenablog.com/atom/entry

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"
      xmlns:app="http://www.w3.org/2007/app">

  <link rel="first" href="https://blog.hatena.ne.jp/oretachino/oretachino.hatenablog.com/atom/entry" />
  <link rel="next" href="https://blog.hatena.ne.jp/oretachino/oretachino.hatenablog.com/atom/entry?page=1417683554" />
  
  <title>俺たちのブログ</title>
  <link rel="alternate" href="http://blog.oretachino.com/"/>
...
  <entry>
<id>tag:blog.hatena.ne.jp,2013:blog-oretachino-8454420450075581217-8454420450076797777</id>
<link rel="edit" href="https://blog.hatena.ne.jp/oretachino/oretachino.hatenablog.com/atom/entry/8454420450076797777"/>
<link rel="alternate" type="text/html" href="http://blog.oretachino.com/entry/2014/12/12/032757"/>
<author><name>oretachino</name></author>
<title>test-unitさえあればご飯大盛り三杯はイケる</title>
<updated>2014-12-12T03:27:57+09:00</updated>
<published>2014-12-12T03:27:57+09:00</published>
<app:edited>2014-12-12T11:52:39+09:00</app:edited>
<summary type="text">rspec3 に疲弊している皆さんこんにちは。今日は rspec3 に疲れた皆さんへ憩いの場として test-unit gem のお話を提供したいと思います。 test-unit gem について 「Rubyのテスティングフレームワークの歴史(2014年版)」の記事に詳しく書いて…</summary>
<content type="text/x-markdown">rspec3 に疲弊している皆さんこんにちは。今日は rspec3 に疲れた皆さんへ憩いの場として test-unit gem のお話を提供したいと思います。

## test-unit gem について
...
<category term="7号" />

<app:control>
  <app:draft>no</app:draft>
</app:control>

  </entry>
  
  <entry>
...

記事ごとのはてブ数は、はてなブックマーク件数取得APIを使って取得します。

$ curl 'http://api.b.st-hatena.com/entry.counts?url=http://blog.oretachino.com/entry/2014/12/04/175914&url=http://blog.oretachino.com/entry/2014/12/05/001837'
{
  "http://blog.oretachino.com/entry/2014/12/04/175914":0,
  "http://blog.oretachino.com/entry/2014/12/05/001837":191
}

この2つのAPIを使って、隊員ごとのブックマーク数を取得、S3にアップロードするrubyスクリプトをやっつけ気味に作成してみました。

#!/usr/bin/env ruby
require 'uri'
require 'net/http'
require 'rexml/document'
require 'json'
require 'aws-sdk'
require 'pp'

# はてなブログAPI
BLOG_USERNAME = 'oretachino'
BLOG_API_KEY = '<APIキー>'
BLOG_DOMAIN = 'oretachino.hatenablog.com'
BLOG_API_ENDPOINT = "https://blog.hatena.ne.jp/#{BLOG_USERNAME}/#{BLOG_DOMAIN}/atom/entry"

# はてなブックマーク
BOOKMARK_API_ENDPOINT =  'http://api.b.st-hatena.com/entry.counts'

# AWS
AWS_S3_BUCKET = 'oretachino'
AWS_ACCESS_KEY_ID = '<アクセスキー>'
AWS_SECRET_ACCESS_KEY = '<シークレットアクセスキー>'

# はてなブログのエントリ取得
# TODO: タイトルとかいらん情報取ってる
def get_entries
  entries = {}
  next_endpoint = BLOG_API_ENDPOINT

  while next_endpoint do
    uri = URI.parse next_endpoint
    req = Net::HTTP::Get.new uri.request_uri
    req.basic_auth BLOG_USERNAME, BLOG_API_KEY
    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true)  do |http|
      http.request(req)
    end
  
    doc = REXML::Document.new(res.body)
    doc.elements.each("feed/entry") do |entry|
      next if entry.elements["app:control/app:draft"].text == 'yes'
  
      url = entry.elements["link[@rel='alternate']"].attributes["href"]
      title = entry.elements["title"].text
      categories = []
      entry.elements.each("category") do |category|
        categories << category.attributes["term"]
      end
  
      entries[url] = {}
      entries[url][:title] = title
      entries[url][:account] = categories.find{|c| c =~ /^\d+$/}
    end
  
    next_link = doc.elements["feed/link[@rel='next']"]
    next_endpoint = next_link ? next_link.attributes["href"] : nil
  end

  entries
end

# ブックマーク数の取得
# TODO: 1回で取得できる件数は最大50件なので、記事増えたらエラーになる
def get_bookmark_counts(urls)
  uri = URI.parse BOOKMARK_API_ENDPOINT
  path = uri.request_uri + '?' + urls.map{|url| "url=#{URI.escape(url)}"}.join('&')
  req = Net::HTTP::Get.new path
  res = Net::HTTP.start(uri.host, uri.port)  do |http|
    http.request(req)
  end
  JSON.parse(res.body)
end

# S3に書き込み
def write_s3_object(path, json)
  AWS.config(
    access_key_id: AWS_ACCESS_KEY_ID,
    secret_access_key: AWS_SECRET_ACCESS_KEY,
    region: 'ap-northeast-1'
  )
  s3 = AWS::S3.new
  
  bucket = s3.buckets[AWS_S3_BUCKET]
  object = bucket.objects[path]
  object.write(json, acl: :public_read, content_type: 'application/json')
end

entries = get_entries
bookmark_counts = get_bookmark_counts(entries.keys)
bookmark_counts.each do |url, count|
  entries[url][:bookmark_count] = count
end

counts_by_account = {}
entries.each do |url, entry|
  counts_by_account[entry[:account]] ||= {
    entry_count: 0,
    bookmark_count: 0
  }
  counts_by_account[entry[:account]][:entry_count] += 1
  counts_by_account[entry[:account]][:bookmark_count] += entry[:bookmark_count]
end

write_s3_object('counts_by_account.json', counts_by_account.sort.to_json)

実行すると、こんな感じでS3上にJSONファイルが作成されます。

このスクリプトをテキトウなサーバで15分毎に実行するようにしてます。

[1gou@batch01 ~]$ crontab -l
*/15 * * * * /home/1gou/bin/update_oretachino_counts.rb

S3のバケットは事前に作成しておきます。
設定としてはStatic Website Hostingの有効化と、今回はJSONのまま外部JavaScriptで利用するので、Cross Origin Resource Sharing (CORS)設定も入れておきます。CORS設定はS3バケットのプロパティから『Permissions → Add CORS Configuration → デフォルトのままSave』するだけです。

あとは、はてなブログのデザイン設定で、サイドバーのモジュール追加から『HTML』を選択してコードを貼り付けると作業完了です。

f:id:oretachino:20141213182919p:plain

貼り付けたHTMLの中身はこんな感じです。

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<script type="text/javascript">
$(function() {
  $.ajax({
    type: "GET",
    url: "https://s3-ap-northeast-1.amazonaws.com/oretachino/counts_by_account.json",
    dataType: "json",
    success: function(data) {
      $.each(data, function(){
        var account = this[0];
        var entry_count = this[1].entry_count;
        var bookmark_count = this[1].bookmark_count;

        var category_link = "archive/category/" + account;
        var text = account + " : " + entry_count + " / " + bookmark_count;
        var ul = $("div#hatena-module-counts-by-account > ul");

        ul.append("<li><a href='" + category_link + "'>" + text + "</a></li>");
      });
    }
  });
});
</script>

<div id='hatena-module-counts-by-account'><ul></ul></div>

jqueryをそのまま読み込んでて行儀悪いですね。

まとめ

というわけで、はてなのAPIとS3を使って簡単な初ブログパーツを作ってみました。

しかし6号、7号のはてブ数すげえな・・・

test-unitさえあればご飯大盛り三杯はイケる

rspec3 に疲弊している皆さんこんにちは。今日は rspec3 に疲れた皆さんへ憩いの場として test-unit gem のお話を提供したいと思います。

test-unit gem について

Rubyのテスティングフレームワークの歴史(2014年版)」の記事に詳しく書いてありますが、test-unit gem は古来にあった test/unit とも minitest とも違う第3の(第4?5?よくわかりません)の単体テストライブラリです。

Ruby コミッタでもある @kou 氏が精力的にメンテナンスを続けてくれており、 Ruby 2.2 に標準で bundle される今をときめくテスティングライブラリです。

古来の test/unit に比べて多くの機能が入っており、大変便利になっています。

テストの書き方

本来ならばここで、テストの記法について語るのでしょうが、@repeatedly 氏が以前に書いてくれた 「Test::Unit でテストを書く」の記事が大変参考になるので、そちらにおまかせします。

本記事では、それに対する補完として、test-unit のコマンドラインオプションについて記載します。

ファイルを指定してテストを実行する。

rspec では、以下のようにファイルを指定してテストを実行できました。

rspec spec/xxx_spec.rb

これは test-unit でもできます。require 'test/unit' しているファイルを直接指定して、ruby スクリプトとして実行すれば OK です。

# test/test_xxx.rb
require 'test/unit'
class TestXXX < Test::Unit::TestCase
end
$ ruby test/test_xxx.rb

ちなみに、require してクラス定義しているだけなのに、なんでスクリプト実行されるの?メソッド呼んでないよ?という気になりますが、どうやら以下のような挙動になっているらしいです。

上の例では、テストクラスを「定義しただけ」で、テストが実行されています。 これは、require 'test/unit'した時に Test::Unit.autorunを実行し ているためです。その結果、終了時の後処理として実行されるようになってい ます。

cf. http://docs.ruby-lang.org/ja/2.1.0/library/test=2funit.html (正確には見るべきドキュメントが違いますが似たような挙動でしょう)

ヘルプ

-h オプションでヘルプを参照できます。test-unit には rspec のようなコマンドはないので、これも require 'unit/test' しているファイルを指定して、-h オプションを指定します。

ruby test/test_xxx.rb -h

とりあえずヘルプを全部貼っておきます。

Test::Unit automatic runner.
Usage: test_xxx.rb [options] [-- untouched arguments]
    -r, --runner=RUNNER              Use the given RUNNER.
                                     (c[onsole], e[macs], x[ml])
        --collector=COLLECTOR        Use the given COLLECTOR.
                                     (de[scendant], di[r], l[oad], o[bject]_space)
    -n, --name=NAME                  Runs tests matching NAME.
                                     (patterns may be used).
        --ignore-name=NAME           Ignores tests matching NAME.
                                     (patterns may be used).
    -t, --testcase=TESTCASE          Runs tests in TestCases matching TESTCASE.
                                     (patterns may be used).
        --ignore-testcase=TESTCASE   Ignores tests in TestCases matching TESTCASE.
                                     (patterns may be used).
        --location=LOCATION          Runs tests that defined in LOCATION.
                                     LOCATION is one of PATH:LINE, PATH or LINE
        --attribute=EXPRESSION       Runs tests that matches EXPRESSION.
                                     EXPRESSION is evaluated as Ruby's expression.
                                     Test attribute name can be used with no receiver in EXPRESSION.
                                     EXPRESSION examples:
                                       !slow
                                       tag == 'important' and !slow
        --[no-]priority-mode         Runs some tests based on their priority.
        --default-priority=PRIORITY  Uses PRIORITY as default priority
                                     (h[igh], i[mportant], l[ow], m[ust], ne[ver], no[rmal])
    -I, --load-path=DIR[:DIR...]     Appends directory list to $LOAD_PATH.
        --color-scheme=SCHEME        Use SCHEME as color scheme.
                                     (d[efault])
        --config=FILE                Use YAML fomat FILE content as configuration file.
        --order=ORDER                Run tests in a test case in ORDER order.
                                     (a[lphabetic], d[efined], r[andom])
        --max-diff-target-string-size=SIZE
                                     Shows diff if both expected result string size and actual result string size are less than or equal SIZE in bytes.
                                     (1000)
    -v, --verbose=[LEVEL]            Set the output level (default is verbose).
                                     (important-only, n[ormal], p[rogress], s[ilent], v[erbose])
        --[no-]use-color=[auto]      Uses color output
                                     (default is auto)
        --progress-row-max=MAX       Uses MAX as max terminal width for progress mark
                                     (default is auto)
        --no-show-detail-immediately Shows not passed test details immediately.
                                     (default is yes)
        --output-file-descriptor=FD  Outputs to file descriptor FD
        --                           Stop processing options so that the
                                     remaining options will be passed to the
                                     test.
    -h, --help                       Display this help.

Deprecated options:
        --console                    Console runner (use --runner).

行番号を指定して実行する

rspec の時は、以下のようにして行番号を指定して、特定のテストを実行することができました。

$ rspec spec/xxx_spec.rb:12

これができないテスティングフレームワークへの移行などできるはずもないわけですが、test-unit gem でもできます!--location オプションに行番号を指定してテストを実行することができます。

$ ruby test/test_xxx.rb --location 12

やった! ╭( ・ㅂ・)و ̑̑

テスト名を指定して実行する

--name オプションで特定のテスト(メソッド)を指定してテストを実行することができます。

# test/test_xxx.rb
require 'test/unit'
class TestXXX < Test::Unit::TestCase
  def test_foo
    assert_true(true)
  end
  def test_bar
    assert_false(true)
  end
end
$ ruby test/test_xxx.rb --name test_foo

test_foo のテストだけが実行されます。注意点としては、以下のような test 文字列 do; end の記法を使うと --name オプションを使用できなくなるところです。

# test/test_xxx.rb
require 'test/unit'
class TestXXX < Test::Unit::TestCase
  test "test_foo" do
    assert_true(true)
  end
  test "test_bar" do
    assert_false(true)
  end
end

--location 使えればなんとかなると思っているので、個人的にはあまり気にしていません。

タグを指定して実行する

rspec にはタグという機能があって、タグを指定してテストをフィルタリングして実行することができました。

# stack_spec.rb
describe Stack do
  context "when new" do
    it '#empty?', :current => true do
      should be_empty
    end
    it '#items', :type => 'getter' do
      should have(0).items
    end
  end
end
$ rspec stack_spec.rb --tag current
$ rspec stack_spec.rb --tag type:getter

test-unit gem には属性という機能があって、属性をつけてフィルタリングすることができます。

require 'test/unit'
class TestXXX < Test::Unit::TestCase
  attribute :current, true
  test "test_foo" do
    assert_true(true)
  end
  attribute :type, 'getter'
  test "test_bar" do
    assert_false(true)
  end
end
$ ruby test_xxx.rb --attribute current
$ ruby test_xxx.rb --attribute 'type == "getter"'

Ruby の文法で複雑な条件も指定できます。

$ ruby test_xxx.rb --attribute 'current || type == "getter"'

まとめ

rspec ではできたオプションを test-unit gem ではどのように指定するのか解説してみました。 test-unit gem には他にも priority 指定の機能などもあったりするので、是非調べてみてください!

ドキュメントページ にもこの辺書いてあるといいんだどなぁ|ω・`)チラ

scpとrsyncでパスのケツスラッシュのありなしの挙動を毎回迷うのでメモ

scpとrsync、ケツにスラッシュがあるかないか毎回確認するのでメモしておく。
個人的にはscp使わないで rsync option from/ dest/
両方共ケツスラッシュ付けてやってる。

from/files_dirdest/files_dirに持って行きたい

% tree .
.
├── dest
└── from
    └── files_dir
        └── file

3 directories, 1 file
%

まとめ

PATH scp rsync
from/files_dir/ dest/ NG NG
from/files_dir dest/ NG OK
from/files_dir/ dest/files_dir/ OK OK
from/files_dir dest/files_dir OK NG
from/files_dir/ dest/files_dir OK OK
from/files_dir dest/files_dir/ OK NG

結論

scpもrsyncも明示的に送り元と送り先ディレクトリを指定して
ケツスラを付けようという話。


scp

持っていくディレクトリにケツスラあり

NG

% scp -r from/files_dir/ dest/
%
% tree .
.
├── dest
│   └── file
└── from
    └── files_dir
        └── file

3 directories, 2 files
%

持っていくディレクトリにケツスラなし

NG

% scp -r from/files_dir dest/
%
% tree .
.
├── dest
│   └── file
└── from
    └── files_dir
        └── file

3 directories, 2 files
%

持っていくディレクトリを明記してケツスラあり

OK

% scp -r from/files_dir/ dest/files_dir/
%
% tree .
.
├── dest
│   └── files_dir
│       └── file
└── from
    └── files_dir
        └── file

4 directories, 2 files
%

持っていくディレクトリを明記してケツスラなし

OK

% scp -r from/files_dir dest/files_dir
%
% tree .
.
├── dest
│   └── files_dir
│       └── file
└── from
    └── files_dir
        └── file

4 directories, 2 files
%

持っていくディレクトリを明記してもっていくディレクトリだけケツスラあり

OK

% scp -r from/files_dir/ dest/files_dir
%
% tree .
.
├── dest
│   └── files_dir
│       └── file
└── from
    └── files_dir
        └── file

4 directories, 2 files
%

持っていくディレクトリを明記して送り先のディレクトリだけケツスラあり

OK

% scp -r from/files_dir dest/files_dir/
%
% tree .
.
├── dest
│   └── files_dir
│       └── file
└── from
    └── files_dir
        └── file

4 directories, 2 files
%                                                                                                                                                                              [~/tmp/test]

rsync

持っていくディレクトリにケツスラあり

NG

% rsync -r from/files_dir/ dest/
%
% tree .
.
├── dest
│   └── file
└── from
    └── files_dir
        └── file

3 directories, 2 files
%

持っていくディレクトリにケツスラなし

OK

% rsync -r from/files_dir dest/
%
% tree .
.
├── dest
│   └── files_dir
│       └── file
└── from
    └── files_dir
        └── file

4 directories, 2 files
%

持っていくディレクトリを明記してケツスラあり

OK

% rsync -r from/files_dir/ dest/files_dir/
%
% tree .
.
├── dest
│   └── files_dir
│       └── file
└── from
    └── files_dir
        └── file

4 directories, 2 files
%

持っていくディレクトリを明記してケツスラなし

NG

% rsync -r from/files_dir dest/files_dir
%
% tree .
.
├── dest
│   └── files_dir
│       └── files_dir
│           └── file
└── from
    └── files_dir
        └── file

5 directories, 2 files
%

持っていくディレクトリを明記してもっていくディレクトリだけケツスラあり

OK

% rsync -r from/files_dir/ dest/files_dir
%
% tree .
.
├── dest
│   └── files_dir
│       └── file
└── from
    └── files_dir
        └── file

4 directories, 2 files

持っていくディレクトリを明記して送り先のディレクトリだけケツスラなし

NG

% rsync -r from/files_dir dest/files_dir/
%
% tree .
.
├── dest
│   └── files_dir
│       └── files_dir
│           └── file
└── from
    └── files_dir
        └── file

5 directories, 2 files
%

Sinatraライクなgin-gonicで手軽にGolangを試す。

こんにちわ。5号です。Sinatraライクと書いておきながらあんまりSinatra触ったことが無いので全然違ったらすみません。 Golangを試す際にWebAppサーバ作ってみようとする時に軽量なフレームワークで試せるgin-gonicは良いと思います。

gin-gonic/gin · GitHub

こちらのqiitaで導入方法は詳細に説明されているのと、

Go - Gin Web Frameworkを試してみたよ - Qiita

READMEさえ見たら簡単なサーバはすぐ作れるのですが、せっかくなので今回はGETした時のURIパラメータをそのまま返すWebAppを作ってみます。

main.go

package main

import "github.com/gin-gonic/gin"
func main() {
    router := gin.Default()
    router.GET("/:string", func(c *gin.Context) {
        string := c.Params.ByName("string")
        c.String(200, string)
    })
    router.Run(":8080")
}

こんな感じでmain.goを書いた後にgo get github.com/gin-gonic/ginを実行した後にgo run main.goを実行するとサーバが8080ポートで起動するので

$ curl 127.0.0.1:8080/test
test

という結果が得られるはずです。

じゃあ、POSTリクエストの中身を取得したい場合は、

main.goを以下の様に書き換えます。

package main

import "github.com/gin-gonic/gin"

//POSTされた値を格納するための構造体を定義
type ( TestFormat struct {
    Value string `json:"key"`  //jsonの時にkeyに使われる名称を指定
})

func main() {
    router := gin.Default()

    router.POST("/", func(c *gin.Context) {
        var tmp TestFormat //POSTされた値を受け取るための構造体
        c.Bind(&tmp) //構造体に値をbindする

        c.String(200, tmp.Value) //tmp.Valueでbindされた値を呼び出している
    })

    router.Run(":8080")
}

で、curlで以下の様にPOSTすると

$ curl -X POST -H 'Content-Type: application/json' -d '{"key":"oretachino"}' http://127.0.0.1:8080/
oretachino

とjsonで値に設定した値が返ってくるはずです。もうちょっと込み入った事するなら、取得した値を元に別の処理に引き回して・・・とかをちょろちょろ拡張出来ますね。

という訳で、READMEに書いている事のほんの一部を書いただけですが、気楽にGolangでWebサーバを作れるのでちょっとしたテストとかでまずは試してみるのは良いんじゃないでしょうか。

ELBのIPアドレス個数でサービス規模は推定できるのか

友達と飲んでる時に『ELBのIPアドレス数は動的に変動するから、個数調べるとだいたいのサイト規模がわかる』みたいな話をしたので、実際のところどうなんだろうといくつかのサービスを調べてみました。

こんな感じになりました。

サービス 調査ドメイン IPアドレス個数
Cookpad cookpad.com 8
ランサーズ www.lancers.jp 2
Chatwork chatwork.com 8
日経新聞(モバイル版) mw.nikkei.com 2
東急ハンズ ネットストア hands.net 2
SmartNews www.smartnews.be 6
グノシー platform.gunosy.com 2
パズドラ api-ios.padsv.gungho.jp 8
ドラゴンクエストモンスターズ SL monsters-superlight.dragonquest.jp 4

個数は2~8個っぽい。

んー、まぁなんか、わかるようなわからんようなって感じですねw
そもそも各サイトのアクセス数がよくわからんし(´・ω・`)

調べかた

WEB系はそのままdigして個数を数えるだけです。 CNAME設定だとこんなふうに出てきます。

$ dig www.tokyu-hands.co.jp
...
;; ANSWER SECTION:
www.tokyu-hands.co.jp.  99  IN  CNAME   tokyu-hands-elb-1374513770.ap-northeast-1.elb.amazonaws.com.
tokyu-hands-elb-1374513770.ap-northeast-1.elb.amazonaws.com. 4 IN A 54.249.36.206
tokyu-hands-elb-1374513770.ap-northeast-1.elb.amazonaws.com. 4 IN A 54.64.64.169

たぶんAlias設定だと思うのですが、そのまま出てくる場合は、

$ dig hands.net
...
;; ANSWER SECTION:
hands.net.      59  IN  A   54.64.54.1
hands.net.      59  IN  A   54.178.250.5

IPアドレスが2個以上で、whois見てAmazonのIPアドレスならELBと見なすことにしました。

$ whois 54.64.54.1
...
NetRange:       54.64.0.0 - 54.71.255.255
CIDR:           54.64.0.0/13
NetName:        AMAZON-2011L
NetHandle:      NET-54-64-0-0-1
Parent:         NET54 (NET-54-0-0-0-0)
NetType:        Direct Allocation
OriginAS:
Organization:   Amazon Technologies Inc. (AT-88-Z)
RegDate:        2014-06-20
Updated:        2014-06-20
Ref:            http://whois.arin.net/rest/net/NET-54-64-0-0-1

OrgName:        Amazon Technologies Inc.
OrgId:          AT-88-Z
Address:        410 Terry Ave N.
City:           Seattle
...
OrgTechHandle: ANO24-ARIN
OrgTechName:   Amazon EC2 Network Operations
OrgTechPhone:  +1-206-266-4064
OrgTechEmail:  aes-noc@amazon.com
OrgTechRef:    http://whois.arin.net/rest/poc/ANO24-ARIN
...

スマホアプリについては、自宅のDNSサーバでtcpdumpして、それっぽいドメインをテキトウに当たりつけて調べました。
アクセスの中身は見てません。そして今回はiPhoneだけで調べてます。

例えばWifi繋いでパズドラを起動するとこんな感じで名前解決が走ります。

[root@dns01 ~]# tcpdump port domain and src host 192.168.1.215
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
02:11:39.316918 IP 192.168.1.215.50335 > 192.168.1.103.domain: 30569+ A? init.itunes.apple.com. (39)
02:11:39.573855 IP 192.168.1.215.52657 > 192.168.1.103.domain: 49332+ A? service.gc.apple.com.akadns.net. (49)
02:11:39.827277 IP 192.168.1.215.51331 > 192.168.1.103.domain: 40363+ A? xp.apple.com. (30)
02:11:39.827290 IP 192.168.1.215.55760 > 192.168.1.103.domain: 23294+ A? p19-buy.itunes.apple.com. (42)
02:11:40.799307 IP 192.168.1.215.57506 > 192.168.1.103.domain: 16746+ A? sp.itunes.apple.com. (37)
02:11:46.215691 IP 192.168.1.215.50167 > 192.168.1.103.domain: 61999+ A? patch-pad.gungho.jp. (37)
02:11:46.321042 IP 192.168.1.215.60145 > 192.168.1.103.domain: 25106+ A? www-google-analytics.l.google.com. (51)
02:11:48.771560 IP 192.168.1.215.50013 > 192.168.1.103.domain: 63786+ A? api-ios.padsv.gungho.jp. (41)
...

関係ないけどこれ見てると、各アプリでどのクラウド/DC、CDNを使っているか、広告や解析ツールなどは何を使っているか、などがわかって面白い。

まとめ

サービス規模とELBのIPアドレスの個数に相関はありそうだけど、調べ方がテキトウだし、アクセス数以外にもいろいろ変動要因があると思われるので、あんまアテにならなそう。

追記

そうなのか。後で調べよう。