NginxでリクエストをLXCコンテナに振り分ける

# やりたいこと
* `blog.abcang.net`と`abcang.net`宛のリクエストを`web`コンテナに転送
* `blog.abcang.net`と`abcang.net`でそれぞれ別のページが表示される
* `app.abcang.net`宛のリクエストを`app`コンテナに転送
* 登録されていないサブドメインでのアクセスは`abcang.net`に転送
* コンテナを増やすたびに設定ファイルを追加したくない

# やったこと

以下のように設定ファイルを編集する。
各コンテナは普段通りの設定をすればいい。

```:/etc/nginx/conf.d/default.conf
map $http_host $cname {
    hostnames;
    abcang.net          web;
    blog.abcang.net     web;
    app.abcang.net      app;
    default             0;
}

server {
    listen       80 default_server;
    server_name  abcang.net;

    # 登録されていないホストはリダイレクト                            
    if ($cname = 0) {
        rewrite ^ http://abcang.net$request_uri? last;
    }

    # 内部DNSで名前解決して転送                                       
    location / {
        resolver dns.abcang.net valid=3600s;
        proxy_pass http://$cname;
        # 接続元情報を維持                                            
        proxy_set_header Host            $host;
        proxy_set_header X-Real-IP       $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_max_temp_file_size 0;

        # Websocket                                                   
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_http_version 1.1;
    }
}

```

# 説明
## ドメインからコンテナを指定

```:
map $http_host $cname {
    hostnames;
    abcang.net          web;
    blog.abcang.net     web;
    app.abcang.net      app;
    default             0;
}
```

左にアクセス元のドメインを、右にコンテナの名前を書いていく。
新しくコンテナを追加した場合はこの部分を増やせば良い。
`default`は一致しなかったものがマッチして、`0`が設定されるようにしている。

## 登録されていないホストはリダイレクト

```:
    # 登録されていないホストはリダイレクト                            
    if ($cname = 0) {
        rewrite ^ http://abcang.net$request_uri? last;
    }
```

先のmapでの設定で、登録されていない場合は`$cname`が`0`になることがわかる。
それを検知したら`rewrite`でリダイレクトするようにしている。
`last`をつけるとそこで処理を停止してくれるらしい。

## 各コンテナに転送

```:
    # 内部DNSで名前解決して転送                                       
    location / {
        resolver dns.abcang.net valid=3600s;
        proxy_pass http://$cname;
        # 接続元情報を維持                                            
        proxy_set_header Host            $host;
        proxy_set_header X-Real-IP       $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_max_temp_file_size 0;

        # Websocket                                                   
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_http_version 1.1;
    }
```

`resolver`の部分で内部DNS(LXCで使用されているもの)を指定する。
`valid`をつけると再度DNSに問い合わせる時間を指定できるみたい。
`proxy_pass`で転送先を指定しているが、ドメイン部分を変数にしておかないとnginx起動時以降名前解決してくれないらしい。
後は接続元の情報をヘッダに付けてあげたり、WebSocketも使えるようにしてあげたり。

# 参考
* [Module ngx_http_map_module](http://nginx.org/en/docs/http/ngx_http_map_module.html)
* [Nginxでproxy_passにホスト名を書いた時の名前解決のタイミング - (ひ)メモ](http://d.hatena.ne.jp/hirose31/20131112/1384251646)
* [Nginxのリバースプロキシ機能を使って唯一のグローバルIPを有効活用する - 動かざることバグの如し](http://thr3a.hatenablog.com/entry/20150210/1423556429)