簡単なWebアプリケーションを作っていてALBの設定で詰まったという話です。
AWS上のAPIサーバーとWebアプリケーションサーバーの2台構成で、JavaScriptでもAPIの特定のエンドポイントだけを利用したいとき、特定のエンドポイント(/api/hogehoge/
)のときだけAPIサーバーに、それ以外はRailsのサーバーに振り分ける設定をしていました。
そのとき、「ALB(Application Load Balancer)を利用すれば簡単に作業が終わるよ」というアドバイスを先輩からいただき、その通り設定して試してみました。
公式ドキュメントにも次のような記述があります。
アプリケーションロードバランサーはHTTPヘッダーにアクセスし、リクエストを異なるバックエンドにルーティングすることができます。例えば、/apiがURLパスに含まれているリクエストを1つのサーバグループ(これをターゲットグループと呼びます)に送り、/mobileが含まれるリクエストをもう1つのグループに送りたいとします。こういった手法でリクエストをルーティングすることで、複数のマイクロサービスをそれぞれ独立して実行しスケールさせることができます。
ところが、確かに簡単に設定できたのですが、およそ半分のリクエストがタイムアウトするようになってしまいました。公式のトラブルシューティングに従って、
$ for X in 'seq60'; do curl -Ik -w "HTTPCode=%{http_code} TotalTime=%{time_total}\n"
$ http://***************.elb.amazonaws.com/api/hogehoge/ -so /dev/null; done
上のようなcurlコマンドを何度か実行すると、確かに504エラーが返って来ていることが確認できました。
HTTPCode=200 TotalTime=0.142
HTTPCode=504 TotalTime=10.256
APIの呼び出し(/api/hogehoge/
のエンドポイント)でも、アプリケーションでも同様だったので、サーバー側の問題ではなさそうです。
よくよく調べると、
- (ALBを含む)ELBでは複数アベイラビリティゾーンを設定した場合、それぞれにインスタンスが必要
- ALBでは(今のところ)複数アベイラビリティゾーンを設定する必要があるらしい
ということが原因だったみたいです。
異なるアベイラビリティゾーンに空のサブネットを作って指定していたのですが、指定したインスタンスのあるアベイラビリティゾーンにLBが自動で振り分けてくれるものだと勝手に思い込んでいました…。
今回はサーバーの台数も必要ないので、結局、通常のELBを使い、「アプリケーションサーバーに入れてあるnginxで、/api/hogehoge/
のエンドポイントのみAPIサーバーを呼び出す」という実装をしようと思います。
10/28 追記
今日設定しなおしたところ、タイムアウトを起こさなくなっており、「あれ?」と思ってしばらく後にまた試したところ、またタイムアウトが発生するようになりました。
昨日はきれいに1回おきでエラーが返ってきていて、微妙に挙動が違うのでよくわからないのですが、できる限り調べてみます。
まず、digコマンドでIPを調べます。
$ dig test-********.ap-northeast-1.elb.amazonaws.com
;; QUESTION SECTION:
;test-********.ap-northeast-1.elb.amazonaws.com. IN A
;; ANSWER SECTION:
test-********.ap-northeast-1.elb.amazonaws.com. 59 IN A 54.***.***.189
test-********.ap-northeast-1.elb.amazonaws.com. 59 IN A 54.***.***.150
このうち、.150
のほうのIPアドレスが504エラーを出しており、おそらく空のサブネットにあるLBのほうがエラーを出していることが分かります。
もう片方(.189
)のほうはきちんと通信できていました。
ただし、実行するタイミングにより、.150
のIPアドレスが存在したりしなかったりしていました。おそらくオートスケーリングの仕組みが働いているのでしょう。
$ curl -vs 54.***.***.150 > /dev/null
* Rebuilt URL to: 54.***.***.150/
* Trying 54.***.***.150...
* Connected to 54.***.***.150 (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: 54.***.***.150
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 504 Gateway Time-out
< Server: awselb/2.0
< Date: Fri, 28 Oct 2016 02:43:11 GMT
< Content-Type: text/html
< Content-Length: 181
< Connection: keep-alive
<
{ [181 bytes data]
* Connection #0 to host 54.***.***.150 left intact
ここで、以下の2つの項目を調べてみます。
- 空のサブネットにEC2インスタンスを立て、railsのサーバーに疎通できるか確認する
- フローログを取得する
まずは、「ALB用に作った空のサブネットからrailsのサーバーに通信する設定ができていないんじゃないか」ということを確認するために、(NACLやルートテーブルの設定に問題が無さそうなことを確認した後で)空のサブネットの中にEC2インスタンスを立てて確認してみます。
と思ったら、.150
のIPアドレスが消えてしまいました。ログを見て検証するのもいったんは難しそうです…。