API Gatewayでのみパブリックアクセス可能なAPIをECSで構築する方法です。構成はこの図のようになります。
ECSのロードバランサにはApplication Load Balancer(ALB)の方を使用することも多いと思いますが、この図のNetwork Load Balancer(NLB)をALBに変えることはできません。API GatewayからVPC内のプライベートAPIに接続するにはVPC Linkを介する必要があり、VPC LinkはNLBにしか対応していないためです。
NLBだとALBのように細かい制御はできませんが、そこはAPI Gatewayの側で行えば問題ないでしょう。
前提ECSタスク
タスクに登録されているコンテナの定義は以下のようになっており、
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F43355%2Facc8a805-da8f-5255-17d7-b16daa484ee7.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=8fe3803f06fb3f7c2e879ba98c57f114)
http://サーバ名:8080/hello
にGETでアクセスすると Hello3
と返すだけのものです。
NLBの作成
最初にロードバランサーを作成します。
ロードバランサーの設定
「スキーム」に「内部」を指定し、サブネットもプライベートサブネットを指定することで、内部向けNLBとして作成します。
API GatewayからNLBまでの通信が保護されているという明確な情報が見つけられなかったため、この手順ではリスナーは一応TLSにしておきます。べつに通常のTCPリスナーでも問題ないかもしれません。通常のTCPリスナーなら、ドメイン登録やTLS証明書の取得は不要になります。
セキュリティ設定の構成
TLSの場合、あとでNLBに割り当てるドメイン名に対応する証明書を割り当てておきます。
この例では、*.app.{秘密}.net
のワイルドカード証明書を指定しました。
ルーティングの設定
ルーティングの設定では、ターゲットの種類を IP
、プロトコルを TCP
、ポートを転送先のポート(この例では 8080
)にしておきます。
ターゲットの登録
ターゲットは何も登録しません。
作成完了
エラーが出なければOKです。
NLBをDNSに登録
NLBをDNSに登録します。Route 53 を使う場合は、Create Record Set
からAliasとして登録します。
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F43355%2F6bfc303d-0b2c-2b7e-35a0-70cb451401a9.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=fc6365bbf116bbe23bf8149242f5bc88)
この例では、app-1.app.{秘密}.net
ドメインをNLBに割り当てました。
ECSの設定
ECSコンソールから、対象のサービスを作成します。
サービス設定
このあたりは何でもかまいません。例ではFargateを使用しています。
ネットワーク構成
サブネットはプライベートを選択します。今回の構成とは関係ありませんが、ECSインスタンスは外部にアクセスできる必要があるため1、パブリックIPをDISABLED
にする場合は、NATゲートウェイなどが別途必要です。
セキュリティグループの設定では、NLBのターゲットがIP
なので、NLBのIPアドレス範囲からアクセス可能なように制限を行います。2
ELBタイプでNetwork Load Balancerを選択します。
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F43355%2Fda048dc0-8b2b-2f3c-d844-08e03dfa285a.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=14d5f9dbbf384ebabb3e11505f57009a)
ELB名で作成したNLBを選択し、アクセス振り分け先のコンテナを選択して「ELBへの追加」ボタンを押します。
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F43355%2Fa43b1757-f1f2-f4c9-0d3c-dfa9d7486b4a.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=06f7b1f602a0dd8629558e5ee2e55254)
ターゲットグループ名で、作成済みのターゲットグループを選択します。
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F43355%2F98268147-d7f0-56c1-ac08-43621ef16440.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=b7533c795c0db2dbaeb8acf719f84872)
他の項目は勝手に設定されます。
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F43355%2F7994eb76-87d7-b965-f979-578427e90115.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=2c4d99092a3d09bdf292fa4763f48f7d)
作成完了
後の項目は任意で設定し、サービスを登録します。タスクが正常に起動すればOKです。
![image.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F43355%2F04fa0cf7-4e49-2da5-7a2f-c7427ddfa01f.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=3c8eb15e9d558697e4dd36ca0b7aec66)
プライベートネットワーク内からは、NLBに割り当てたドメインでAPIを呼び出すことができるようになります。
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F43355%2F26054991-ec13-947f-036d-699ef1a3f9c0.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=8aa0cc4ec2f92c111e006ef022d15c42)
API Gatewayの設定
NLBは外部からアクセスできないため、この時点では外部からAPIにアクセスできません。API Gatewayを経由して外部からアクセスできるように設定を行います。
VPC Linkの作成
API Gatewayのコンソールの「VPC リンク」メニューから、作成したNLBを対象とするVPCリンクを作成します。
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F43355%2Ffbb5513c-185a-1583-1dfe-c66f131e65dc.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=3346e31241ad546ce9f32d91f73f6f74)
ステータスが「利用可能」になるのを待ちます。数分時間がかかります。
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F43355%2F9092571e-e215-28a2-8837-da6025607b79.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=f65431e8495cd6557abc8304aa8d73fc)
APIメソッドの設定
API GatewayのAPIメソッドの設定で、NLB経由でECSタスクにアクセスするよう設定します。
「統合タイプ」で「VPC リンク」を選択し、「VPC リンク」で作成したVPCリンクを選択します。そして、エンドポイントURLに https://{NLBに割り当てたドメイン}/{APIのパス}
を指定します。
設定したら、デプロイして有効化します。
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F43355%2Fe380bddd-9982-762a-1d9d-5bd0ce3d026f.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=a9634888452c4d3d241c58f55d29bb50)
発行された呼び出しURLを確認します。
呼び出し確認
curl
コマンドなどで、発行された呼び出しURLにアクセスしてみます。
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F43355%2Fd91c2f0a-86ce-7071-834c-5e42165aa7dc.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=02f5286ed990b658a700554ccb0d88e1)
どうしてもALBでロードバランスしたい場合
API Gateway経由からしかアクセスさせたくないが、どうしてもNLBではなくALBでロードバランスしたい場合に考えられるやり方です。
内部向けALBの場合
以下で紹介されているように NLB->ALB の構成とし、ALBのIPアドレス変更ごとにNLBの宛先を変更するようにします。
ただ、AWSのブログには「プロダクションで使う前に必ずテストしてね!」と書いてあるので、本当にちゃんと安定して動くのかはよくわかりません…。
公開ALBの場合
この場合、公開ALBを、API Gatewayのみからアクセス可能にする必要があります。2019年3月時点では、以下の方法が考えられます。
- API Gateway からバックエンドの公開ALBへのリクエストに、
X-API-Key: {ランダムで推測不可能な文字列}
のように適当なHTTPヘッダで認証情報をセットします。 - ALB側でそのHTTPヘッダの値をチェックし、API Gateway側にセットした文字列と同じ文字列が無ければアクセスを拒否するようにします。以下の方法があります。
認証情報を渡すので、HTTPS前提です。API全体を単一のプレーンテキストで守り、AWSコンソール上から値が丸見えなので、あんまり安全な感じもしません。
-
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ECS_instances.html "コンテナインスタンスには、Amazon ECS サービスエンドポイントと通信するために外部ネットワークアクセスが必要なため、コンテナインスタンスにパブリック IP アドレスがない場合は、NAT (ネットワークアドレス変換) を使用してこのアクセスを提供する必要があります。" ↩
-
https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/network/load-balancer-target-groups.html#target-type "ターゲットを IP アドレスで指定する場合、送信元 IP アドレスはロードーバランサノードのプライベート IP アドレスとなります。" ↩