API GatewayとAPIサーバの連携

## 概要

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](https://konghq.com/)等もあるが、今回は上記全てに対応でき、かつTerraformのリソースも揃っているAPI Gatewayを採用した。

## 初期設計

APIサーバはECSで構築し、APIのタイプごとにサービスを分けて、ALBのパスベースルーティングでターゲットグループを複数作成した。
この方法だとパス単位でクラスタ上で稼働するタスク数を調整することができ、柔軟なリソース変更が可能となる。
![Untitled Diagram-Page-2 (5).png](https://qiita-image-store.s3.amazonaws.com/0/27454/ca4461b0-96f0-7219-4818-556d618dcc1c.png)

当初、API Gatewayは図のALB手前に置けば良いと思ってたが、どうやらProxyできるエンドポイントはインターネットに公開しておく必要があるので、internal ALBは指定できないらしい。

## API GatewayとNLBの統合

2017年の年末にAWSは [API GatewayとPrivate VPCとのエンドポイント統合](https://aws.amazon.com/jp/about-aws/whats-new/2017/11/amazon-api-gateway-supports-endpoint-integrations-with-private-vpcs/) をサポートした。これによりAPI Gateway経由でPrivateサブネット下のAPIサーバとの統合が可能となった。
大雑把に言うと次のような流れである。

1. NLBを作成
2. NLB配下にインスタンスを作成
3. TCPでHTTP/HTTPSポートを監視
4. API GatewayでVPC Linkを作成 (NLBを指定)
5. API Gatewayでエンドポイントを作成。統合タイプでVPCリンクを指定

詳しくは [【新機能】API Gateway VPC integrationを使ってみた #reinvent](https://dev.classmethod.jp/cloud/aws/aws-reinvent-apigateway-vpc-integration/) の記事が参考になる。

ただしこの方法で有効となるのは　`API Gateway -> NLB -> EC2` という構成であって、更にNLBから `ALB -> ECS` と繋げることができない。
NLBからALBに接続するには更に工夫が必要で、詳しくは [Using static IP addresses for Application Load Balancers](https://aws.amazon.com/jp/blogs/networking-and-content-delivery/using-static-ip-addresses-for-application-load-balancers/) に書かれているが、要約すると次のような流れとなる。

1. ALBのIPをチェックするLambdaファンクションを作成 (※ALBは定期的にIPが変わる)
2. CloudWatch Eventでファンクションを毎分実行
3. 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周りの最終的なデザインはこのような構成となった。

![Untitled Diagram-Page-1 (4).png](https://qiita-image-store.s3.amazonaws.com/0/27454/b66fbfc8-070d-5394-f347-8a5b91458118.png)

## 速度比較

参考までに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)
```