Bind 9.10のGeoIP機能を使って、リクエスト元に近いサーバのIPを返す

# はじめに
海外から日本のサーバに接続すると、予想外の遅さに少し驚くことがあります。
例えば、ロンドンから日本のサーバにHTTPでアクセスをすると、明らかにワンテンポ遅い感じです。
試しにhttpingコマンドでqiita.comへのHTTP接続を計測してみたところ、547msの平均応答速度でした。

仮に日本以外に海外でも展開するサービスを作るとしたら、もっと応答時間を早くしたいものです。

```bash
$ httping -c 3 qiita.com
PING qiita.com:80 (/):
connected to 54.248.127.202:80 (386 bytes), seq=0 time=579.29 ms
connected to 54.248.127.202:80 (386 bytes), seq=1 time=530.49 ms
connected to 54.248.127.202:80 (386 bytes), seq=2 time=533.82 ms
--- http://qiita.com/ ping statistics ---
3 connects, 3 ok, 0.00% failed, time 4645ms
round-trip min/avg/max = 530.5/547.9/579.3 ms
```
これは、予算に余裕があれば、[Amazon Route 53](https://aws.amazon.com/jp/route53/)や[Amazon CloudFront CDN](https://aws.amazon.com/jp/cloudfront/), [CloudFlare](https://www.cloudflare.com/)等を使えばすぐに解決できる問題ですけど…。
ConoHaにもGeoDNS機能があり、コスパも高いし魅力的なのですが、リージョンにヨーロッパがないのが残念。

 - [ConoHaのチュートリアル - DNS(GeoDNS)を使う](https://www.conoha.jp/conoben/archives/9071)

# オレオレCDN
 1. 海外の格安VPSを活用して、複数の大陸にサーバを設置
 2. DNSでの名前解決で、リクエスト元に近いサーバのIPを返せばOK

[Bind 9.10 の新機能](https://www.isc.org/bind-9-10-new-features/#)として、GeoIP機能が搭載されており、クライアントのIPの国・都市・タイムゾーンなどを元に、名前解決の結果を分ける事ができます。
この機能を使って、例えばヨーロッパの利用者はヨーロッパのサーバのIP、アジアの利用者は日本のサーバのIPを返すように構成すれば良さそうです。

## 構成
| リクエスト元 | 近いサーバの場所 |
|---|---|
|ヨーロッパ・アフリカ・中東・ロシア|ロンドン|
|日本・韓国・中国・東南アジア|東京|
|北米・南米・その他|ニューヨーク|

## 海外格安VPSを活用する (Vultr編)
最近、格安海外VPSの[Vultr](http://www.vultr.com/?ref=6863518)を使い始めました。
Vultrを選んだ理由は、ArchLinuxをインストールできる事と安い事なのですが、ふと簡易CDNとしても使えるのではないかと思い立ちました。

[Vultr](http://www.vultr.com/?ref=6863518)は、DigitalOcean的な立ち位置のVPS業者ですが、東京にもリージョンがある、月$5の最安プランでのサーバのメモリ容量が768MBとDigitalOceanより256MB多い(その代りSSD容量は5GB少ない)のがより魅力的に感じました。
オフィシャルの[クーポン](https://www.vultr.com/coupons/)もあり、2016年1月現在では$50(ただし2ヵ月で消える)のクレジットが貰えるようです。

- [Vultrのリージョン一覧](https://www.vultr.com/locations/)

ヨーロッパと日本、アメリカに3台サーバを設置したとして、月$15です。
転送量は月1TBまで無料。
小規模のスタートアップや個人で運用するサービスには、なかなか良いのではないでしょうか。

# 動作確認環境
 - Ubuntu 14.04 64-bit
 - BIND 9.10.3

# Bind 9.10

## インストール

- [BIND 9.10 PPA](https://launchpad.net/~mgrocock/+archive/ubuntu/bind9)

```bash:ubuntu14.04-add-bind9.10-ppa.sh
#!/bin/bash
sudo add-apt-repository ppa:mgrocock/bind9
sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 083F021DDC682B55
sudo apt-get up{dat,grad}e -y
```

BindでGeoIP機能が有効かを確認

```bash
$ named -V | sed -r 's/ +/\n/g'|grep geoip
'--with-geoip=/usr'
# OK
```


GeoIPデータベースを入れる

- [Ubuntu パッケージ検索結果 -- geoip-database](http://packages.ubuntu.com/search?keywords=geoip-database)

```shell-session
# $ sudo apt-get install geoip-database
or
$ sudo apt-get install geoip-database-contrib
```
※geoip-databaseの方は内容が古いようで、一部のIPの国を認識できなかったので、geoip-database-contribの方がお勧めです。

## 設定
試しに、日本と英国、二カ国からのアクセスに対して設定してみます。

- 「example.com」への名前解決要求

 - クライアントのIPが日本 ⇒ 東京サーバのIPを返す

 - クライアントのIPが英国 ⇒ ロンドンサーバのIPを返す

```conf:/etc/bind/named.conf.options
/* 地域 */
acl "jp" { geoip country JP; };                                                                                    
acl "uk" { geoip country GB; }; 

/* 中略 */

/* 追記 */
options {
    geoip-directory "/usr/share/GeoIP";
};
```
国コードには、[ISO-3166-1](https://ja.wikipedia.org/wiki/ISO_3166-1)の2文字、又は3文字のコードを指定します。

geoip ディレクティブには、他にも都市やタイムゾーンも指定できるとのことです。
[Chapter 6. BIND 9 Configuration Reference](ftp://ftp.isc.org/isc/bind/9.10.0b2/doc/arm/Bv9ARM.ch06.html) より抜粋:

```text
geoip [db database] field value

# examples
geoip country US;
geoip country JAP;
geoip db country country Canada;
geoip db region region WA;
geoip city "San Francisco";
geoip region Oklahoma;
geoip postal 95062;
geoip tz "America/Los_Angeles";
geoip org "Internet Systems Consortium";
```

次にゾーンファイルを更新します。`
match-clients に、先ほど定義した国に対応したaclを指定するだけです。

```text:/etc/bind/named.conf.default-zones
view "jp" {
 match-clients { jp; };
 zone "example.com" {
  type master;
  file "/etc/bind/example.com.tokyo.zone";
  /* 以下略 */
 };
};

view "uk" {
 match-clients { uk; };
 zone "example.com" {
  type master;
  file "/etc/bind/example.com.london.zone";
  /* 以下略 */
 };
};

view "other" {
 zone "example.com" {
  type master;
  file "/etc/bind/example.com.other.zone";
  /* 以下略 */
 };
};
```

ゾーンファイルの example.com.london.zone の内容は、特筆することはなくごく普通です。
IPのAレコードが国ごとに違うだけ。

## デバッグ
国と対応させたviewが正しく識別されているかをログで確認します。

参考: [BINDのviewを用いた内部向けDNS構築メモ](http://koyhogetech.hatenablog.com/entry/20101213/internal_dns)

```text:named.conf.options
logging {
 channel queries_file {
  file "/var/log/named/queries.log"
  versions 3 size 5m;
  severity dynamic;
  print-time yes;
 };
};
```

各国のクライアントからテストでDNSクエリを送ります。
※1.2.3.4は本当は上で設定したIP

```bash
$ nslookup examples.com 1.2.3.4 
```

ログを確認します。

```text:/var/log/named/queries.log
08-Jan-2016 8:45:10.527 client 104.238.171.177#39341 (example.com): view uk: query: example.com IN A + (172.31.22.25)
08-Jan-2016 8:49:30.593 client 202.157.182.142#2043 (example.com): view other: query: example.com IN SOA -T (172.31.22.25)
08-Jan-2016 8:54:06.839 client 157.192.160.134#40687 (example.com): view jp: query: example.com IN A + (172.31.22.25)
```
*OK*

あとは、aclでの国定義を増やしていき、クライアントの国に対応したサーバを更に適切に返せるように詰めていけば良さそうです。


# 参考

- [Using the GeoIP Features in BIND 9.10 | Internet Systems Consortium Knowledge Base](https://kb.isc.org/article/AA-01149/190/Using-the-GeoIP-Features-in-BIND-9.10.html)

- [Chapter 6. BIND 9 Configuration Reference](ftp://ftp.isc.org/isc/bind/9.10.0b2/doc/arm/Bv9ARM.ch06.html)

- [BIND 9.10 Administrator Reference Manual (PDF)](ftp://ftp.isc.org/isc/bind9/cur/9.10/doc/arm/Bv9ARM.pdf)