概要
Private VPC(or サブネット)に配置したAPIサーバをAPI Gateway経由でインターネットに公開す方法を解説する。
APIサーバの設計
システム要件からAPIは次のように分類した。
- 公開API: インターネットに公開する必要のあるAPI
- 非公開API: インターネットに公開する必要のないAPI。Privateサブネットからのみアクセス可能
設計レベルで考慮したこと。
- 認証周りをCognitoで統一したい
- APIにキャッシュの仕組みを取り入れたい
- 仕様はOpen APIで書いてるので、Swaggerでバリデーションしたい
- 外部APIに関してはリクエスト制限を設けたい
API AggregatorとしてはAWSが提供するAPI GatewayのほかKong等もあるが、今回は上記全てに対応でき、かつTerraformのリソースも揃っているAPI Gatewayを採用した。
初期設計
APIサーバはECSで構築し、APIのタイプごとにサービスを分けて、ALBのパスベースルーティングでターゲットグループを複数作成した。
この方法だとパス単位でクラスタ上で稼働するタスク数を調整することができ、柔軟なリソース変更が可能となる。
当初、API Gatewayは図のALB手前に置けば良いと思ってたが、どうやらProxyできるエンドポイントはインターネットに公開しておく必要があるので、internal ALBは指定できないらしい。
API GatewayとNLBの統合
2017年の年末にAWSは API GatewayとPrivate VPCとのエンドポイント統合 をサポートした。これによりAPI Gateway経由でPrivateサブネット下のAPIサーバとの統合が可能となった。
大雑把に言うと次のような流れである。
- NLBを作成
- NLB配下にインスタンスを作成
- TCPでHTTP/HTTPSポートを監視
- API GatewayでVPC Linkを作成 (NLBを指定)
- API Gatewayでエンドポイントを作成。統合タイプでVPCリンクを指定
詳しくは 【新機能】API Gateway VPC integrationを使ってみた #reinvent の記事が参考になる。
ただしこの方法で有効となるのは API Gateway -> NLB -> EC2
という構成であって、更にNLBから ALB -> ECS
と繋げることができない。
NLBからALBに接続するには更に工夫が必要で、詳しくは Using static IP addresses for Application Load Balancers に書かれているが、要約すると次のような流れとなる。
- ALBのIPをチェックするLambdaファンクションを作成 (※ALBは定期的にIPが変わる)
- CloudWatch Eventでファンクションを毎分実行
- IPの更新があればNLBに紐付くターゲットグループ(ALBのIP)が更新される
NLBにインスタンスではなくIPを紐付けるというのがポイント。
当初この仕組みだとALBのIPが変わった直後は接続不能になるのではないかと思ったけど、ちゃんと記載があった。ALBはIP更新後もしばらく古いIPへの接続が有効らしい。
INVOCATIONS_BEFORE_DEREGISTRATION lets you configure the number of times an IP address can not be in the DNS results before we will deregister it. In normal operation, the IP address of an ALB continues to be available after it is removed from DNS for a short period. The NLB health check will detect failed ALB IP addresses if we miss one, so immediately de-registering is not a risk to our traffic. The default value is set to 3, which causes an ALB IP address to be deregistered only after it is missing from the DNS result for 3 minutes.
という訳で、API周りの最終的なデザインはこのような構成となった。
速度比較
参考までにAPI Gatewayを経由した場合としてない場合の速度比較を載せておく。
API Gateway経由
$ ab -n 300 -c 100 https://***/
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking ***
Completed 100 requests
Completed 200 requests
Completed 300 requests
Finished 300 requests
Server Software:
Server Hostname: ***
Server Port: 443
SSL/TLS Protocol: ***
TLS Server Name: ***
Document Path: ***
Document Length: 36 bytes
Concurrency Level: 100
Time taken for tests: 7.290 seconds
Complete requests: 300
Failed requests: 0
Non-2xx responses: 300
Total transferred: 139800 bytes
HTML transferred: 10800 bytes
Requests per second: 41.15 [#/sec] (mean)
Time per request: 2430.120 [ms] (mean)
Time per request: 24.301 [ms] (mean, across all concurrent requests)
Transfer rate: 18.73 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 65 385 359.7 199 1465
Processing: 23 1338 1846.2 139 4945
Waiting: 23 1323 1854.8 102 4945
Total: 107 1724 2000.5 509 6281
Percentage of the requests served within a certain time (ms)
50% 509
66% 1339
75% 3957
80% 4427
90% 5019
95% 5256
98% 6082
99% 6206
100% 6281 (longest request)
ALB経由
$ ab -n 300 -c 100 http://***/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking ***
Completed 100 requests
Completed 200 requests
Completed 300 requests
Finished 300 requests
Server Software:
Server Hostname: ***
Server Port: 80
Document Path: /
Document Length: 36 bytes
Concurrency Level: 100
Time taken for tests: 6.514 seconds
Complete requests: 300
Failed requests: 0
Write errors: 0
Non-2xx responses: 300
Total transferred: 78900 bytes
HTML transferred: 10800 bytes
Requests per second: 46.05 [#/sec] (mean)
Time per request: 2171.357 [ms] (mean)
Time per request: 21.714 [ms] (mean, across all concurrent requests)
Transfer rate: 11.83 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 2 3 0.9 3 7
Processing: 7 1484 1926.0 13 6055
Waiting: 7 1484 1926.0 13 6055
Total: 10 1487 1926.2 16 6057
Percentage of the requests served within a certain time (ms)
50% 16
66% 2274
75% 3469
80% 3762
90% 4337
95% 4722
98% 5971
99% 6046
100% 6057 (longest request)