boscoworks.log

boscoworksのブログ的な

IPアドレスと位置情報をマッピングするテーブルをHadoopを使った解析基盤に作ってみた

概要

データ解析をやっていると、「いつ・誰が・何をした」っていう調査は比較的容易に実現できるものだと実感していますが、「どこで」というキーワードを使った解析は意外に難しい気がしています。
スマートフォンからはGPSを使えばそれなりに取れます(やったことはないけど)。
PCのブラウザからとなると、ユーザから申告してもらうか、IPアドレスからおおよその位置を割り出すしかありません。
今回は、IPアドレスと位置情報を結びつける、データ解析用マッピングテーブルをどうにかして作った話です。

なぜ必要か

データ解析するひとの手元には、どんな形であれログがあると思います。多くはApacheとかNginxとかWebアプリケーションのミドルウェアが出力しているアクセスログだと思います。
これに位置情報があらかじめ付与されている場合は、もうその情報を使えばいいと思います。
fluent-plugin-geoip https://github.com/y-ken/fluent-plugin-geoip 使ってもいいと思います。アクセスログがデカくなっていきそうだけど。
今回は、「(位置情報の付与されていない)アクセスログ」と、以下で作成する「データ解析用マッピングテーブル」をJOINして、新しい解析手法を生み出すことにあります。
例えば、

    • ○○県の20代の男性は、他県に比べて有料会員になりやすい
    • △△市に住んでいるユーザは、特定コンテンツに異様に興味を持っている
    • □□町に住んでいる女性に、今コレが売れている

などなど。

IPから位置情報を取得するAPIサーベイ

IPアドレス 緯度経度 API」とかでググればたくさん出てきます。

抱えている問題

さて、↑のAPIを使えば、IPアドレスと位置情報を結びつけることは誰でもできますが、問題がいくつかあります。

  • リクエスト制限がある (2req/sec とか。1日172800リクエストしか投げられない)
  • 商用利用が有料だったりする (お金がない)
  • HTTPS通信サポートされてない (あるのかもしれないけど見たことはない)
  • そもそも解析対象のデータを余所様にブン投げるのはどうなの (IPアドレス単体なら個人情報じゃないけど、なんか気持ち悪くない?)

freegeoip

freegeoipもIPアドレスから位置情報を取得できるAPIの一つです。
http://freegeoip.net/
無料だし、CSV/JSON/XML形式出力にも対応してる。ただし10000req/hour。
freegeoipの良さはここではないです。
↑のURLの"Limits"に書いてある通り、このシステム、自前のサーバにビルドできるんです。やり放題。

freegeoipのビルド

必要なものはGo/Python/Sqlite/Git。あと少しばかりのやる気と、30分くらいの時間が必要です。
手順も簡単

    • go get
    • go build
    • updatedb (pythonで書かれたDB更新用実行ファイル)
    • freegeoip (goで書かれたアプリケーション実行ファイル)

これだけ。というかREADMEに全部書いてあるので、その通りにやればよいです。かんたん。

API構築後に発生する課題

API用に割けるWebサーバが潤沢にあるとか、リクエスト投げる回数がそんなでもない、って場合は、上のビルドが終わったらAPIたたけばいいと思います。
そういうわけにいかない人もいるでしょう(というかそういう人ばっかりだと思います)

    • APIサーバが貧弱 (鬼のようなリクエストにサーバが耐えられない)
    • 解析対象IPアドレス数が膨大 (だいたいのケースはDISTINCTかけても数多いと思う)

じゃあfreegeoipのDBにリクエスト投げりゃいいじゃんと思う方もいらっしゃるでしょうが、freegeoipのDBはそう簡単にSELECTクエリ投げられるようにできてないのです。

freegeoipのDBのカラクリ

freegeoipには4つのテーブルがあって、それをJOINすることでIPアドレスと位置情報をマッピングすることができます。詳しいスキーマ構成はDESCRIBEしてください。
このDBは全IPアドレス(約43億)が対象となっているはずなのに、モバイルにも搭載可能なサイズで、しかもSqliteで作られている。
あらかじめデータがグループ化された状態で格納されているからです。
例えば、ロングIPアドレスで 1728421888 から 1728422911 までの場合、だいたい以下の情報が1レコードに含まれています。

    • 範囲: 1728421888 - 1728422911
    • 国: Japan
    • 地域: Tokyo
    • 町: Ginza
    • 緯度: 35.6667
    • 経度: 139.7667

この結果、すべてのテーブルをJOINしてDUMPしても、たかだか6万レコードくらいの規模にしかなりません。

MapReduceを使った、IpToGeolocationテーブルの作成

↑のDUMPデータから、シングルプロセスのプログラムにより、ひたすらIPアドレスに対する位置情報のマッピングを行うのは、比較的容易です。
範囲をひたすらforループで展開していけばできます。
今回はデータ解析をしたい(もしくはしている)方が対象なので、大なり小なりの分散処理基盤が手元にある前提で、今回はHadoopに力を借りるとします。
MapReduceを使ってテーブルを作成するのは効率的です。
https://github.com/boscoworks/IpToGeolocation

MapReduceを実行したときのノウハウ

要求されるクラスタ構成

VM7台で構成された技術検証用Hadoopクラスタで試しに実行したら、5台がOSフリーズしてLost Task Trackerになりました。
1Reducerに割り当てられるデータはそんなでもないし、膨大な中間データが生成されるわけでもないので、単純にマシンスペックが要求される仕組みになっていると思います(台数は言えませんがオンプレで構成したクラスタでは順調に動いた)

MapReduceの仕様

1Mapper-65536Reducerの構成で動きます。結果的にデータ量としては6万もいりませんでしたが、やはり1Reducerに割り当てられるデータ量にはかなりのムラがあり、先に述べたLost TT事件にビビったのもあって、慎重に作った感じです。
IPアドレスはドット区切りで4つのデータに分かれていますが、Reducerには上位2階層のIPアドレスごとにデータを割り当てています。なので最大65536(256*256)Reducer。
Reducerでは下位2階層をforループで処理します。この二つの仕様により、1Reducerには最大65536レコードがinputされ、最大65536レコードがoutputされます。

データの特性

freegeoipの仕組みなのか、データには偏りがあるというか、マッピングできないIPアドレスの範囲もあるらしいので、データのムラが気になる方は、もう一個MapReduceをつくって、適当に再分配するといいと思います。今回はめんどくさかったので省略。

成果

上記のMapReduceは、今僕が使えるクラスタで実行したところ、約20分で処理をしました。
みなさんのお手元のクラスタ構成によっては前後すると思いますが、10分を切るような高性能クラスタをお持ちの方は、積極的にお話を聞かせてください。
レコード的には約6億レコードくらいが生成されました。日によって前後すると思いますが、1億レベルでずれることはないと思います。
で、これは結局多いのか少ないのか・・・・。

網羅率

IPアドレス4,294,967,296に対して、約13%程度の網羅率でした。あれー。こんなもんなの?

更新頻度

MaxMindという会社が管理していて、だいたい1週間に1度は更新されているようです。
IPアドレスに対する情報は日々変化するので、その時々によっての位置情報をトレースしたい場合は、定期的にIpToGeolocationを生成するバッチを作って、cronとかで回せばいいと思います。

信頼性

MaxMindはアメリカの会社なので当然アメリカの情報は正確です。誤差50kmのAccuracyで72%だそうです。
対する日本はというと20%。近隣諸国と比較してもAccuracy低めです(中国: 42%、韓国: 23%、台湾:48%)。国内展開するサービスには厳しい結果。
IPアドレスに対するカバレッジも低いですが、日本国内の正確性も低いので、どこまで使い物になるかは微妙ですね。少なくともこれを基軸にビジネス考えるのはやめておいたほうがいいと思います。現段階では。

まとめ

  • ひとまずマッピングテーブルはできた
  • カバレッジと信頼性が低いのが気になる。取得するデータ元を増やしたほうがいいんだろうか
  • Whois情報つけるともっと面白くなりそう
  • これ悪用してネットストーカーしちゃダメだよ