前提
システム開発をしていると「A機能はLambdaで作るべきで、B機能はコンテナで作るべきで、でもそれぞれ実はURIは同じにしておく方がREST的にキレイになるんだよね」なことがある。…いや、あまりないか?ともあれ、そんな時にはnginxとかでプロキシを作って振り分けても良いのだけど、せっかくパブリッククラウドを使っているのだからマネージドサービス使ってみたいよね、と思ってやってみた。
Spring BootなWebアプリの動いているコンテナと、Lambda関数がそれぞれあるような構成。
Lambda関数
この記事のために作り直すのも面倒なので、以下の記事で作ったものを転用する。
Amazon API Gateway/ALBのバックエンドで動くLambda関数をJava(Eclipse+maven)で実装する
Spring BootなWebアプリの動いているコンテナ
あまりこだわりもないので、適当なパラメータを受けてJSONを返すコンテナを作ってみる。
リソースは/person
。
パラメータは、クエリかリクエストボディ(JSON)でid
を読み込んで、それに対応した名前と年齢をJSONで返すような仕様で。
package com.example;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
@RestController
public class WebController {
@RequestMapping("/person")
@ResponseBody
public Person response(
@RequestBody(required = false) Person person,
@RequestParam(name="id", required = false) String reqParamId) {
Person response = null;
String id = null;
if( reqParamId != null ){
id = reqParamId;
}else{
id = Integer.toString(person.getId());
}
if(id.equals("11111")) {
response = new Person(Integer.parseInt(id), "Taro", 35);
}else if(id.equals("22222")) {
response = new Person(Integer.parseInt(id), "Hanako", 32);
}
return response;
}
}
package com.example;
public class Person {
private int Id;
private String Name;
private int Age;
// 適当な getter, setter
// 中略
public Person(int id, String name, int age) {
Id = id;
Name = name;
Age = age;
}
}
pom.xml は spring-boot-starter, spring-boot-starter-web があればOK。
ビルドは適当にmvn packageしてから適当なDockerfileでdocker buildして、ECRにpushする。
この辺も以下の過去記事を参考に。
AWSでSpringBootベースのWebアプリを起動してみる(Docker on EC2編/ECS+Fargate編)
API Gatewayの設定
リソース | メソッド | 転送先 |
---|---|---|
LambdaTest | ANY | Lambda関数 |
person | GET | Spring BootなWebアプリ |
person | POST | Lambda関数 |
person | PUT | Spring BootなWebアプリ |
手でポチポチ設定するのも面倒なので、以下のSwagger+API Gateway拡張をインポートする。
分かりにくいかもしれないけど、インポートはここから可能。
---
swagger: "2.0"
info:
description: "Created by AWS Lambda"
version: "2020-04-25T10:56:39Z"
title: "API GatewayのAPI名"
host: "[API Gatewayのドメイン]"
basePath: "/default"
schemes:
- "https"
paths:
/LambdaTest:
x-amazon-apigateway-any-method:
responses:
200:
description: "200 response"
x-amazon-apigateway-integration:
uri: "[Lambda関数のARN]"
responses:
.*:
statusCode: "200"
passthroughBehavior: "when_no_match"
httpMethod: "POST"
type: "aws_proxy"
/person:
get:
produces:
- "application/json"
responses:
200:
description: "200 response"
schema:
$ref: "#/definitions/Empty"
x-amazon-apigateway-integration:
uri: "http://[NLBのドメイン]/person"
responses:
default:
statusCode: "200"
passthroughBehavior: "when_no_match"
connectionType: "VPC_LINK"
connectionId: "[VPCLinkのID]"
httpMethod: "GET"
type: "http_proxy"
post:
produces:
- "application/json"
responses:
200:
description: "200 response"
schema:
$ref: "#/definitions/Empty"
x-amazon-apigateway-integration:
uri: "[Lambda関数のARN]"
responses:
default:
statusCode: "200"
passthroughBehavior: "when_no_match"
httpMethod: "POST"
contentHandling: "CONVERT_TO_TEXT"
type: "aws_proxy"
put:
produces:
- "application/json"
responses:
200:
description: "200 response"
schema:
$ref: "#/definitions/Empty"
x-amazon-apigateway-integration:
uri: "http://[NLBのドメイン]/person"
responses:
default:
statusCode: "200"
passthroughBehavior: "when_no_match"
connectionType: "VPC_LINK"
connectionId: "[VPCLinkのID]"
httpMethod: "GET"
type: "http_proxy"
definitions:
Empty:
type: "object"
title: "Empty Schema"
コンテナについては、内部向けのHTTPサーバを統合する場合は、VPCLinkで繋いだNLBにぶら下げる必要がある。この辺の記事を参考にしよう。ほぼこの記事とやってることは同じだけど気にしない………。
さて、ここまでやったら、curlで動作確認をしてみると、期待通りの動作になっているのが分かる。
$ curl --include -X GET https://[API Gatewayのドメイン]/default/LambdaTest
HTTP/2 200
date: Sat, 25 Apr 2020 12:52:11 GMT
content-type: application/json
content-length: 42
x-amzn-requestid: ********
x-amz-apigw-id: ********
x-amzn-trace-id: ********
{"greetings":"Hello from Lambda Ver 1.11"}
$ curl --include -X POST https://[API Gatewayのドメイン]/default/person
HTTP/2 200
date: Sat, 25 Apr 2020 12:54:16 GMT
content-type: application/json
content-length: 42
x-amzn-requestid: ********
x-amz-apigw-id: ********
x-amzn-trace-id: ********
{"greetings":"Hello from Lambda Ver 1.11"}
$ curl --include -X GET https://[API Gatewayのドメイン]/default/person?id=11111
HTTP/2 200
date: Sat, 25 Apr 2020 12:55:24 GMT
content-type: application/json
content-length: 35
x-amzn-requestid: ********
x-amzn-remapped-connection: keep-alive
x-amz-apigw-id: ********
x-amzn-remapped-date: Sat, 25 Apr 2020 12:55:24 GMT
{"name":"Taro","id":11111,"age":35}[
$ curl --include -X PUT -H 'Content-Type:application/json' -d '{"id":"11111"}' https://[API Gatewayのドメイン]/default/person?id=11111
HTTP/2 200
date: Sat, 25 Apr 2020 12:56:33 GMT
content-type: application/json
content-length: 35
x-amzn-requestid: ********
x-amzn-remapped-connection: keep-alive
x-amz-apigw-id: ********
x-amzn-remapped-date: Sat, 25 Apr 2020 12:56:32 GMT
{"name":"Taro","id":11111,"age":35}