プロキシサーバーchoconでAWS APIへの接続を高速化する

2017/06/08 「AWS CLIの実行」に注意書きを追加

ども、大瀧です。
先週開催されたAWS Summit Tokyoでは、メルカリSREの@kazeburoさんのセッションにてプロキシサーバーchoconを活用したリージョン間アクセスの高速化事例が紹介されました。


プロキシ元であるクライアントとプロキシ先のAPIエンドポイントとのHTTPSセッションを集約することで、Keepaliveによる高コストなHTTPSネゴシエーションの最小化やHTTP/2による効率の良いデータ転送を狙うもののようです *1。AWSのRESTful APIはちょうどHTTPSですので、choconをAWS APIの呼び出しに使えないかなぁと思い、試してみた様子をご紹介します *2

動作検証環境

  • OS : macOS Sierra
  • golang : バージョン1.7

セットアップ

GitHubのREADMEに従い、choconをインストールします。Makefileがglideに依存するので、先にHomebrewでインストールしました。make glideでもイケるかもしれません。

$ brew install glide
==> Installing glide
==> Downloading https://homebrew.bintray.com/bottles/glide-0.12.3.sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring glide-0.12.3.sierra.bottle.tar.gz
?  /usr/local/Cellar/glide/0.12.3: 6 files, 10MB
$ go get -u github.com/kazeburo/chocon
$ cd .go/src/github.com/kazeburo/chocon/
$ make bundle
glide install
[INFO]	Downloading dependencies. Please wait...
[INFO]	--> Fetching github.com/lestrrat/go-apache-logformat.
[INFO]	--> Fetching github.com/satori/go.uuid.
[INFO]	--> Fetching github.com/lestrrat/go-server-starter-listener.
[INFO]	--> Fetching github.com/renstrom/shortuuid.
  :
$ make
go build -ldflags "-X main.Version=0.7.0" chocon.go
$ chocon -h
Usage:
  chocon [OPTIONS]

Application Options:
  -l, --listen=             address to bind (default: 0.0.0.0)
  -p, --port=               Port number to bind (default: 3000)
      --access-log-dir=     directory to store logfiles
      --access-log-rotate=  Number of day before remove logs (default: 30)
  -v, --version             Show version
  -c, --keepalive-conns=    maximun keepalive connections for upstream (default: 2)
      --read-timeout=       timeout of reading request (default: 30)
      --write-timeout=      timeout of writing response (default: 90)
      --proxy-read-timeout= timeout of reading response from upstream (default: 60)

Help Options:
  -h, --help                Show this help message
$

これでOKです。

choconの実行

choconコマンドで実行します。今回はこのあとのAWS CLIのコマンドラインをシンプルにしたかったので、sudoを使って80番ポートをListenしました。デフォルトの3000番などであればsudoによる実行は不要です。

$ sudo chocon -p 80
Password: Macのユーザーのパスワードを入力
(なにも表示されない)

他のターミナルからcURLでアクセスしてみると

$ curl localhost
$

以下のように、choconを実行する端末にアクセスログ(今回はプロキシアクセスのための情報が足りないため400エラー)が表示され、動いている様子がわかります。

$ sudo chocon -p 80
Password: 
[::1]:49265 - - [06/Jun/2017:00:04:31 +0900] "GET / HTTP/1.1" 400 - "localhost" 0.52.441796 6oFvpnTv5EgEm8zHJvQCC5

AWS CLIの実行

では、今回はAWS APIにアクセスするためにAWS CLIを利用してみます。choconはリクエストのHostヘッダを以下の規則で見て、プロキシ先を動的に決定します。

<プロキシ先ドメイン名>.ccnproxy-{http|https}

今回はAmazon S3バージニアリージョン(s3.amazonaws.com)へのアクセスを試してみたいので、/etc/hostsファイルに以下のように追加します。

127.0.0.1 s3.amazonaws.com.ccnproxy-https

これで、s3.amazonaws.com.ccnproxy-httpsのアクセスが127.0.0.1(ローカルホストのchocon)を向き、それがchoconに届くとs3.amazonaws.comのHTTPSへプロキシされるわけです。

それでは試してみましょう(AWS CLIにはあらかじめAPIキーなどを設定済みです)。

$ aws s3 ls --endpoint http://s3.amazonaws.com.ccnproxy-https
2016-12-01 17:30:39 aws-athena-query-results-XXXXXXXXXXXX-us-west-2
  :
$

お、AWSからのレスポンスは正常に返ってきているようです。choconを実行するターミナルを見てみると...

127.0.0.1:49296 - - [06/Jun/2017:00:12:02 +0900] "GET / HTTP/1.1" 200 8472 "s3.amazonaws.com.ccnproxy-https" 1.723.21232 KZmTLy6tE5g2UMydCx9eg8

それらしいログが残っていますね!ソースを見ると9カラム目(1.723.21232)がオリジンとのリクエスト/レスポンス送受信にかかった時間に見えます。 なので、同じawsコマンドを何度か試してアクセスが早くなるか様子を見てみましょう。

127.0.0.1:49371 - - [06/Jun/2017:00:18:10 +0900] "GET / HTTP/1.1" 200 8472 "s3.amazonaws.com.ccnproxy-https" 1.331.463509 JLmGMKUiMgahtzsUyR5sUY
127.0.0.1:49374 - - [06/Jun/2017:00:18:13 +0900] "GET / HTTP/1.1" 200 8472 "s3.amazonaws.com.ccnproxy-https" 0.419.019149 K3qUtdo5zPXWKT6imCVh6Y
127.0.0.1:49376 - - [06/Jun/2017:00:18:15 +0900] "GET / HTTP/1.1" 200 8472 "s3.amazonaws.com.ccnproxy-https" 0.626.820031 o7SCdEYV8PQgG5gRf96Bq3
127.0.0.1:49378 - - [06/Jun/2017:00:18:17 +0900] "GET / HTTP/1.1" 200 8472 "s3.amazonaws.com.ccnproxy-https" 0.573.527977 5e9f4vGBPQWYf8eYd38zWZ
127.0.0.1:49380 - - [06/Jun/2017:00:18:19 +0900] "GET / HTTP/1.1" 200 8472 "s3.amazonaws.com.ccnproxy-https" 0.424.053241 Dvsz3yjXg7T8dD3Mo8wtAn
127.0.0.1:49382 - - [06/Jun/2017:00:18:21 +0900] "GET / HTTP/1.1" 200 8472 "s3.amazonaws.com.ccnproxy-https" 0.325.698352 8DdyYhewJQ9oTLrtVCyRQH
127.0.0.1:49384 - - [06/Jun/2017:00:18:23 +0900] "GET / HTTP/1.1" 200 8472 "s3.amazonaws.com.ccnproxy-https" 0.124.282789 rYU4Am5kAS7SBzgT9SQs9L
127.0.0.1:49386 - - [06/Jun/2017:00:18:25 +0900] "GET / HTTP/1.1" 200 8472 "s3.amazonaws.com.ccnproxy-https" 0.10.013781 J8jtkQQLoiETLVHnSqr9Md
127.0.0.1:49390 - - [06/Jun/2017:00:18:27 +0900] "GET / HTTP/1.1" 200 8472 "s3.amazonaws.com.ccnproxy-https" 0.35.882492 LC9eN6sPz25EaLotTEGvhB
127.0.0.1:49392 - - [06/Jun/2017:00:18:28 +0900] "GET / HTTP/1.1" 200 8472 "s3.amazonaws.com.ccnproxy-https" 0.976.821096 J3KVb6AQpUsRRkcGrbgrsb

2回目以降の時間が短くなっていることが分かりますね!KeepaliveでS3 APIとのコネクションを維持することで個々のリクエスト/レスポンス処理が高速化できているんじゃないでしょうか。

AWSのAPIはサービスごとにリクエストの規則が異なるため、サービスによってはエラーになることがあります。今回S3は通りましたが、DynamoDBではAWS V4署名に関するエラーでAWS CLIを実行することができませんでした。

AWS環境にデプロイするなら

今回はローカルのMacで試してみましたが、AWS環境で構成するのであればいろいろ工夫のしがいがありそうです。クライアント-chocon間のリクエストは通常のHTTPですので、ELBを間に噛ませてchoconプロキシインスタンスを冗長構成にするのがいいかなと思います。また、他のAWSサービスやリージョンに対応させるとなるとhostsファイルではしんどくなってくると思うので、発表スライドにあるような内部DNSを利用するのが良いかなと思います。ただし、Route 53はピリオドを含むサブドメインのワイルドカードが表現できないので、dnsmasqなどサブドメイン表現が柔軟にできるものをEC2のローカルに用意しちゃうのがおすすめです。

まとめ

choconを利用して、AWS APIへのアクセスを高速化する様子をご紹介しました。DynamoDBやKinesis、S3など頻繁にデータをやり取りするサービスで大きな効力を発揮するのではないでしょうか。一方でそれらのサービスはDNSのTTLが短くDNSによってAWS側ノードの負荷分散を意識しているので、choconサーバーのインスタンス数をある程度用意しないと、choconプロキシ-AWS間で負荷が偏りボトルネックになる恐れもあります。

あとどうでもいいことですが、ソフトウェア名は某幼児向け番組の物知りキャラから来ているんですかね。気になるー。

参考URL

脚注

  1. 用途は異なりますが、IoT向けのSORACOM Beamと建て付けが近い感じがします。
  2. この1枚前のスライドに「AWS SDKもendpointを切り替えることで利用可能」って書いてあるんですけどね(^^;