AWS CLI 和 Serverless 都可以用于部署 Serverless 应用
AdministratorAccess
并选中(有最高权限)参考 AWS 命令行界面 安装
pip install awscli
aws configure
然后输入刚才的密钥的 Key 和 Secret,Region 可以选择常用的 region,如us-east-1
npm install -g serverless
或
yarn global add serverless
serverless config credentials --provider aws --key YOUR_KEY --secret YOUR_SECRET
aws-sam-local 是用于本地调试 Serverless 应用的工具
npm install -g aws-sam-local
或
yarn global add aws-sam-local
mvn archetype:generate \
-DgroupId=io.github.helloworlde \
-DartifactId=serverless-spring-boot \
-Dversion=1.0-SNAPSHOT \
-DarchetypeGroupId=com.amazonaws.serverless.archetypes \
-DarchetypeArtifactId=aws-serverless-springboot2-archetype \
-DarchetypeVersion=1.3.1
.
├── README.md
├── build.gradle
├── pom.xml
├── sam.yaml
└── src
├── assembly
│ └── bin.xml
├── main
│ ├── java
│ │ └── io
│ │ └── github
│ │ └── helloworlde
│ │ ├── Application.java
│ │ ├── StreamLambdaHandler.java
│ │ └── controller
│ │ └── PingController.java
│ └── resources
│ └── application.properties
└── test
└── java
└── io
└── github
└── helloworlde
└── StreamLambdaHandlerTest.java
buildscript {
ext {
springBootVersion = '2.1.1.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
archivesBaseName = 'serverless-spring-boot'
group = 'io.github.helloworlde'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
jcenter()
mavenLocal()
mavenCentral()
}
dependencies {
compile(
'org.springframework.boot:spring-boot-starter-web',
'com.amazonaws.serverless:aws-serverless-java-container-spring:[1.0,)',
'io.symphonia:lambda-logging:1.0.1'
)
testCompile("junit:junit:4.12")
}
task buildZip(type: Zip) {
from compileJava
from processResources
into('lib') {
from(configurations.compileClasspath) {
exclude 'tomcat-embed-*'
}
}
}
build.dependsOn buildZip
package io.github.helloworlde.controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import java.util.HashMap;
import java.util.Map;
@RestController
@EnableWebMvc
public class PingController {
@RequestMapping(path = "/ping", method = RequestMethod.GET)
public Map<String, String> ping() {
Map<String, String> pong = new HashMap<>();
pong.put("pong", "Hello, World!");
return pong;
}
}
StreamLamdbaHandler 相当于一个代理层,将访问 Lambda 的流量转发给 SpringBoot应用;handleRequest方法每次请求都会被调用
package io.github.helloworlde;
import com.amazonaws.serverless.exceptions.ContainerInitializationException;
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class StreamLambdaHandler implements RequestStreamHandler {
private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
static {
try {
handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);
} catch (ContainerInitializationException e) {
// if we fail here. We re-throw the exception to force another cold start
e.printStackTrace();
throw new RuntimeException("Could not initialize Spring Boot application", e);
}
}
@Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
throws IOException {
handler.proxyStream(inputStream, outputStream, context);
}
}
sam 用于本地测试Serverless 应用,依赖于 Python 和 Docker
需要注意的是,Resources.ServerlessSpringBootFunction.Properties.CodeUri
这个路径需要根据使用 Gradle 或 Maven 进行修改
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: AWS Serverless Spring Boot 2 API - io.github.helloworlde::serverless-spring-boot
Globals:
Api:
EndpointConfiguration: REGIONAL
Resources:
ServerlessSpringBootFunction:
Type: AWS::Serverless::Function
Properties:
Handler: io.github.helloworlde.StreamLambdaHandler::handleRequest
Runtime: java8
CodeUri: build/distributions/serverless-spring-boot.zip
MemorySize: 512
Policies: AWSLambdaBasicExecutionRole
Timeout: 30
Events:
GetResource:
Type: Api
Properties:
Path: /{proxy+}
Method: any
Outputs:
ServerlessSpringBootApi:
Description: URL for application
Value: !Sub 'https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/ping'
Export:
Name: ServerlessSpringBootApi
gradle clean build
http://localhost:8080/ping
,应当返回结果:{
"pong": "Hello, World!"
}
sam local start-api --template sam.yaml
curl -s http://127.0.0.1:3000/ping
将会看到输出结果:
{"pong":"Hello, World!"}%
service: spring-boot-serverless
provider:
name: aws
runtime: java8
package:
artifact: build/distributions/serverless-spring-boot.zip
functions:
springBootServerless:
handler: io.github.helloworlde.StreamLambdaHandler::handleRequest
events:
- http:
path: /ping
method: get
timeout: 30
sls deploy
日志:
sls deploy
Serverless: Packaging service...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service serverless-spring-boot.zip file to S3 (12.73 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..............
Serverless: Stack update finished...
Service Information
service: spring-boot-serverless
stage: dev
region: us-east-1
stack: spring-boot-serverless-dev
resources: 10
api keys:
None
endpoints:
GET - https://cmu9z3fmd1.execute-api.us-east-1.amazonaws.com/dev/ping
functions:
springBootServerless: spring-boot-serverless-dev-springBootServerless
layers:
None
访问 https://cmu9z3fmd1.execute-api.us-east-1.amazonaws.com/dev/ping
{
"pong": "Hello, World!"
}
上传到 S3 时需要有一个 Bucket 存在,可以在 AWS 服务-S3 中创建
aws s3 mb s3://spring-boot-serverless --region us-east-1
aws cloudformation package --template-file sam.yaml --output-template-file output-sam.yaml --s3-bucket spring-boot-serverless
这个命令执行之后会在项目根目录下生成一个 output-sam.yaml
输出
Uploading to e793ef8960075db672fce0a8ac65f03b 13353411 / 13353411.0 (100.00%)
Successfully packaged artifacts and wrote output template to file output-sam.yaml.
Execute the following command to deploy the packaged template
aws cloudformation deploy --template-file /Users/HelloWood/Dev/Projects/Serverless/serverless-spring-boot/output-sam.yaml --stack-name spring-boot-serverless
aws cloudformation deploy --template-file /Users/HelloWood/Dev/Projects/Serverless/serverless-spring-boot/output-sam.yaml --stack-name spring-boot-serverless-demo --capabilities CAPABILITY_IAM
需要注意的是 --capabilities CAPABILITY_IAM
会创建一个新的角色,会使用 AWSLambdaBasicExecutionRole
,该策略仅允许该角色访问 Lambda 相关的资源,也可以使用 CAPABILITY_AUTO_EXPAND
或CAPABILITY_NAMED_IAM
使用已有的角色;创建后的角色可以在 AWS IAM 的角色下面管理
aws cloudformation describe-stacks --stack-name spring-boot-serverless-demo
{
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-east-1:266545579784:stack/spring-boot-serverless-demo/1074b6a0-6f97-11e9-835b-0e348661d726",
"StackName": "spring-boot-serverless-demo",
"ChangeSetId": "arn:aws:cloudformation:us-east-1:266545579784:changeSet/awscli-cloudformation-package-deploy-1557103210/fe06fc3b-a353-4d42-814a-4e4f736a2026",
"Description": "AWS Serverless Spring Boot 2 API - io.github.helloworlde::serverless-spring-boot",
"CreationTime": "2019-05-06T00:37:01.857Z",
"LastUpdatedTime": "2019-05-06T00:40:17.502Z",
"RollbackConfiguration": {},
"StackStatus": "CREATE_COMPLETE",
"DisableRollback": false,
"NotificationARNs": [],
"Capabilities": [
"CAPABILITY_IAM"
],
"Outputs": [
{
"OutputKey": "ServerlessSpringBootApi",
"OutputValue": "https://gf2c9fhhql.execute-api.us-east-1.amazonaws.com/Prod/ping",
"Description": "URL for application",
"ExportName": "ServerlessSpringBootApi"
}
],
"Tags": [],
"EnableTerminationProtection": false,
"DriftInformation": {
"StackDriftStatus": "NOT_CHECKED"
}
}
]
}
curl https://gf2c9fhhql.execute-api.us-east-1.amazonaws.com/Prod/ping
{"pong":"Hello, World!"}%
build/
或target/
下;运行语言选择 Java8,处理程序为io.github.helloworlde.StreamLambdaHandler::handleRequest
,点击保存AWS API Gateway AWS Proxy
,修改相关的path和httpMethodhttps://github.com/helloworlde/serverless-spring-boot