HTTP Load Balancerを使って、Host名やURIによって接続するインスタンス(群)を切り替える

本当は前日夜には投稿したかったけど時間足りなかったすみません。
GCP アドベントカレンダー始まるお

フロントエンジニアとして働いていたつもりが、いつの間にかインフラエンジニアをやっている大橋です。

GCPのHTTP Load Balancer(HTTP LB)使ってますか？
[kazunori279](http://qiita.com/kazunori279/items/8d2417c8510021c697e7)さんが[「GoogleのHTTPロードバランサーの破壊力があり過ぎる #gcpja」](http://qiita.com/kazunori279/items/8d2417c8510021c697e7)って記事を書いてから、
だいぶ時間が立ち、
Limited PreviewからBetaリリースにになり誰でも利用が可能になりました。

主な機能としてはL7レイヤーのロードバランシング機能を提供しており、
クロスリージョンでのロードバランシングや、アクセス元の地域を見てロードバランシング先インスタンスを変える等の機能を持っています。

GCPアドベントカレンダー1発めはこのHTTP Load Balancerのもう一つの強力な機能、
「Content-based Load Balancing」を紹介したいと思います。

なおHTTP LBの破壊力、性能は上の記事で十分わかるのでそちらを読んでください。

## Content-based Load Balancing is 何?

「Content-based Load Balancing」は同一IPへのアクセスにおいて、ホスト名やパス等のURLによって、接続先インスタンス(群)を変える事ができるHTTP LBの機能です。
イメージは「LB & バーチャルホスト & リバースプロキシ」的な感じですね。
１つのIPアドレスを効率的に利用できると共に、サーバの配置がかなり自由になる機能です。

ここで言うインスタンス群とはBackend Serviceと呼ばれるGCEのInstance Groupをまとめたもので、
Content-based Load BalancingではUrlMapと呼ばれるリソースに、どのパス(またはホスト)の時にどのBackend Serviceを利用するかを設定することができます。

全体像を書くと以下の様なイメージです。
左側のユーザがアクセスするURL(例えば/index.html)によってロードバランシング先のインスタンス群(Static-Service)を振り分けることができます。
もちろんHTTP LBが元々持っているクロスリージョンでのロードバランシングはそのまま利用でき、
例えばAsiaからのアクセスはasia-eastリージョンのインスタンス群へ、
USからのアクセスはus-centralリージョンのインスタンス群へロードバランシングすることが来ます。

![Network diagram.png](https://qiita-image-store.s3.amazonaws.com/0/2965/e00d1ac2-4932-b6ea-7c54-ca47091773f4.png "Network diagram.png")

## 出てくる用語

HTTP LBを触るとさまざまな用語が出てくるので念のためまとめて置きます。

* GlobalForwardingRule
  * HTTP LBに対して、IP、ポート、プロトコルを設定するリソース
  * 現状だと80ポートのみ対応しており、つまりHTTP LBは現状(2014/11/30現在)HTTPでのアクセスしかサポートしていない
    * 今後近いうちにサポートされると思いますが。
  * HTTP LBに対して複数設定できる
  * TargetHttpProxyへの参照を持つ
* TargetHttpProxy
  * UrlMapとGlobalForwardingRuleをひもづける*だけ*のリソース
  * コレにより、UrlMapが複数のIP(~GlobalForwadingRule)を持つことができるようになる
* UrlMap
  * HTTP LBに対して、ホスト名やURLからどのBackend Serviceへアクセスさせるかを設定するリソース
  * HTTP LBに対して1つだけ設定できる
     * UI上名称の設定などはできず、HTTP LB名と同じ名称が付けられる
     * API上HTTP LBの一覧を取ることはできず、HTTP LBの一覧を取得する場合はこのUrlMapの一覧を取得することになる。
     * つまりほぼコレがHTTP LBと同義といえる
  * default serviceという通常アクセスさせるBackend Serviceを指定でき、Content-based HTTP Load Balancingを利用しない場合はdefault serviceのみ設定されている 
* Backend Service
  * Instance Groupをまとめた物で、対象のInstance Groupのヘルスチェックや、Load Balancingする量(RPSやCPU、単純なパーセンテージなど)を設定できる。
* InstanceGroup
  * Compute Engineのインスタンスをまとめたもの。
  * Instance Group Managerを利用すると、autoscalerなども利用できる 


## 実際に設定してみる

ほとんど画面上で設定できるので実際にやっていってみましょう。
今回は2インスタンスを建て、同一IPへのアクセスで片一方は/api/index.htmlでアクセスされるように、もう一方は/assets/hoge.jsでアクセスされるようにしてみます。

### 1. インスタンスを立てる

2インスタンスを適当に立てます。docker使いたいのでcoreosにしています。

![instances.png](https://qiita-image-store.s3.amazonaws.com/0/2965/4eaa962b-98fa-1849-5afa-d8bafab61a4e.png "instances.png")

それぞれ80ポートをあけて、webサーバ(nginx)を建てます。

```bash
[local] $ gcloud ssh --project "your-project" --zone "asia-east1-c" web1
[web1] $ docker pull nginx
[web1] $ mkdir -p html/web1/
[web1] $ echo "web1" > html/index.html
[web1] $ echo "web1" > html/web1/index.html
[web1] $ docker run -d -v $(pwd)/html:/usr/share/nginx/html -p 80:80 --name web nginx
[web1] $ exit
[local] $ gcloud ssh --project "your-project" --zone "asia-east1-c" web2
[web2] $ docker pull nginx
[web2] $ mkdir -p html/web2/
[web2] $ echo "web2" > html/index.html
[web2] $ echo "web2" > html/web2/index.html
[web2] $ docker run -d -v $(pwd)/html:/usr/share/nginx/html -p 80:80 --name web nginx
[web2] $ exit

```

web1側は http://{web1}/web1/index.htmlを
web2側は http://{web2}/web2/index.htmlのみ置いておきます。


### 2. インスタンスグループ作る

それぞれにインスタンスグループを作ります。
インスタンスグループはインスタンスの一覧画面の上部のボタンから作るか、
左メニューの「インスタンスグループメニュー」から作れます。

![create_ig-button.png](https://qiita-image-store.s3.amazonaws.com/0/2965/67b2e789-ee78-dce7-74a3-4e40a3b86808.png "create_ig-button.png")


今回は1インスタンスに付き1つインスタンスグループを作ります。

![create_ig.png](https://qiita-image-store.s3.amazonaws.com/0/2965/d136748f-13aa-c06a-cfb1-a9de487f1d18.png "create_ig.png")


### 3. HTTP LBを作る

次にHTTP LBを作ります。
まず左メニューから「HTTP 負荷分散」を選択します。
![network.png](https://qiita-image-store.s3.amazonaws.com/0/2965/82dbdd61-bebc-cef1-f1e9-9d0bd3bc9913.png "network.png")


任意の名前で新規作成します。
![http-log.png](https://qiita-image-store.s3.amazonaws.com/0/2965/bfd7de8c-2e56-9a6e-1fdb-c20f2cd7a38d.png "http-log.png")

![lb.png](https://qiita-image-store.s3.amazonaws.com/0/2965/6e33b69a-c91a-6e17-8b2b-8eedc11a1120.png "lb.png")


### 4. GlobalForwardingRuleを作る

上に続いてGlobalForwardingRuleを作ります。

![GlobalForwardingRule.png](https://qiita-image-store.s3.amazonaws.com/0/2965/ad49676f-c7e2-0fb7-3f00-26ca4ee0f5cf.png "GlobalForwardingRule.png")

これも任意の名前で良いです。また現状はIPぐらいしか設定ができません。

![make-rule.png](https://qiita-image-store.s3.amazonaws.com/0/2965/95769732-c3a1-95e1-b826-e79de690b217.png "make-rule.png")

### 5. Backend Serviceの設定 & 新規作成を行う

次にBackend Serviceの設定を行います。
今回は1インスタンスグループに付き1つBackend Serviceを使いますが、
Http LBを作った際に1つ自動的に作成されているため、片一方はそれを利用します。

まず自動で作られた側にインスタンスグループを設定します。

![default-bs.png](https://qiita-image-store.s3.amazonaws.com/0/2965/9c066687-84ee-b11c-9ba6-2563f0d7bcf6.png "default-bs.png")

編集ボタンを押し、インスタンスグループを設定します。
今回は先にインスタンスグループを作成しているので、「既存のインスタンスグループ」を選択し、設定を行って下さい。
分散モードや、最大使用率は特に気にしなくてよいです。

![web1-bk.png](https://qiita-image-store.s3.amazonaws.com/0/2965/eefe85dd-a702-ae5e-2d2c-329c9d89381d.png "web1-bk.png")

一つ目を作成したら2つ目のBackend Serviceを作成するために、
HTTP LBの画面から「バックエンドサービスを追加」を選択します。
![make-bk2.png](https://qiita-image-store.s3.amazonaws.com/0/2965/794ef842-1348-b245-d966-9db5ce2b5927.png "make-bk2.png")


1つ目と同じようにweb2のインスタンスグループを設定します。
![web2-bk.png](https://qiita-image-store.s3.amazonaws.com/0/2965/8c191081-b491-2ccf-35a3-7adf7873321a.png "web2-bk.png")


### 6. Url-Mapを設定する

2つ目のBackend Serviceを作成すると、
Url-Mapの設定画面が表示されます。
別のURLセットを追加をクリックし、パスに対するBackend Serviceを設定します。

![url-map.png](https://qiita-image-store.s3.amazonaws.com/0/2965/33414a63-ee44-3a1a-6e51-3e0a1183ff8b.png "url-map.png")

![url-map-path.png](https://qiita-image-store.s3.amazonaws.com/0/2965/0f4bc878-9cb1-0bdb-b152-8c40d8260c87.png "url-map-path.png")

### テスト

これで設定は完了です。
HTTP LBの画面右下にBackend Serviceごとのヘルスチェックが見れるので
全て正常になるまで待ちます。

![healthcheck.png](https://qiita-image-store.s3.amazonaws.com/0/2965/555c9da1-64e1-d6ed-7703-643d765a5745.png "healthcheck.png")

では実際にアクセスしてみましょう

HTTP LBのIPは画面左下に有ります。

![ip.png](https://qiita-image-store.s3.amazonaws.com/0/2965/af58abc2-6137-e0aa-70ec-042e58f23b8d.png "ip.png")

まず普通にアクセスすると「デフォルト」バックエンドサービスに設定されていたweb1にアクセスされます。

![1.png](https://qiita-image-store.s3.amazonaws.com/0/2965/6494cdd5-776b-6349-c6af-f07cd2488d16.png "1.png")

次に`/web1`へアクセスするとパスの設定の通りweb1にアクセスされます。

![2.png](https://qiita-image-store.s3.amazonaws.com/0/2965/39029b7b-ebc4-def7-2cdf-bf3fbeb4e340.png "2.png")

次に`/web2`へアクセスするとパスで振り分けされweb2にアクセスされます。

![3.png](https://qiita-image-store.s3.amazonaws.com/0/2965/7bbf611a-eda9-d608-f889-399530ebc48b.png "3.png")


これでテストは完了です

# まとめ

実際に今回ぐらいのケースですと、nginxなどのリバースプロキシで同じことができます。
ただ、LBをつけたり、リージョン間ロードバランシングなどをしようと思うとnginxではかなり辛いです。
また非常に沢山のアクセスがある場合などもHTTP LBでは余裕でさばけるでしょう。

そしてHTTP LBはnginxと違い再起動など必要なく、設定をどんどん変えることができ、
Blue-Green Deploymentをやる場合など「一部のパスのapiだけ切り離してバージョンをあげる」のようないかつい事もサーバ再起動無しに実現することが可能になります。

このへんはクラウドとして素敵ですね。