AWS는 서버리스 환경에서의 애플리케이션 구현 모델인 AWS SAM을 개발하고 있다. 여기에는 SAM 사양과 SAM 템플릿을 AWS CloudFormation으로 변환하는 코드, 프로그래밍 예제 등을 포함하고 있다. SAM Local은 SAM의 구현체다.
서비리스 응용 프로그램을 만들려면, 람다함수에 대한 사양을 저장하는 JSON이나 YAML 형식의 SAM 템플릿을 만든다. 그리고 SAM 구현체 중 하나인 SAM Local의 CLI를 이용해서 응용 프로그램을 테스트 하고 업로드 및 배포를 한다. SAM은 응용 프로그램의 사양을 클라우드포메이션 구문으로 변환하면서, 지정되지 않은 속성들을 기본 값으로 채우고 호출 권한등을 결정해서 람다를 배포 한다.
$ go get -u github.com/cpliakas/aws-sam-golang-example
셈플 패키지 디렉토리를 보면 run.sh가 있다. 내용을 보자.
GOOS=linux go build -o main
sam local start-api
main 이름으로 빌드하고 sam local start-api를 실행한다.
# sam local start-api
2018-06-14 13:54:14 Mounting ExampleAPI at http://127.0.0.1:3000/hello [GET]
2018-06-14 13:54:14 Mounting ExampleAPI at http://127.0.0.1:3000/goodbye [GET]
2018-06-14 13:54:14 Mounting ExampleAPI at http://127.0.0.1:3000/ [GET]
2018-06-14 13:54:14 You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2018-06-14 13:54:14 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
2018-06-14 13:54:27 Invoking main (go1.x)
2018-06-14 13:54:27 Found credentials in shared credentials file: ~/.aws/credentials
Fetching lambci/lambda:go1.x Docker container image..............
2018-06-14 13:55:04 Mounting /home/yundream/golang/src/github.com/cpliakas/aws-sam-golang-example as /var/task:ro inside runtime container
START RequestId: 857bf496-779d-18c1-a151-8d00a7c4db3f Version: $LATEST
END RequestId: 857bf496-779d-18c1-a151-8d00a7c4db3f
REPORT RequestId: 857bf496-779d-18c1-a151-8d00a7c4db3f Duration: 1.12 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 6 MB
sam local 은 docker를 기반으로 하는 개발환경을 제공한다. 처음 실행 할 경우 lambci/lambda:go1.x 도커이미지를 설치한다. 이 후에는 현재 디렉토리를 컨테이너 볼륨으로 해서 컨테이너를 실행하고 main 파일을 실행한다.
테스트를 해보자.
DynamoDB는 로컬에 설치 해서 사용 할 수 있으며, 도커 이미지도 제공한다. DynamoDB 도커 버전을 이용해서 테스트하기로 했다.
$ docker run --name dynamo -p 8000:8000 dwmkerr/dynamodb -sharedDb
Initializing DynamoDB Local with the following configuration:
Port: 8000
InMemory: false
DbPath: null
SharedDb: true
shouldDelayTransientStatuses: false
CorsParams: *
Contents
AWS Serverless Application Model (AWS SAM)
SAM LOCAL 설치
- 우분투 리눅스 17.10 
- Docker version : 17.12.0-ce
- Python 2.7.14 : 2.7 혹은 3.6이어야 한다.
- go1.9.2
pip로 설치했다(보통 nodejs 기반인데, javascript가 주력이 아니라서 그나마? 익숙한 python 기반으로).Sample 테스트
$ curl http://127.0.0.1:3000/hello {"message":"Hello, world!"}테스트 API
package main import ( "context" "encoding/json" "fmt" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) type Info struct { Name string `json:"name"` Age int `json:"age"` Email string `json:"email"` } func Handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { fmt.Println(request.Path) body, _ := json.Marshal(Info{"yundream", 5, "yundream@gmail.com"}) return events.APIGatewayProxyResponse{ Body: string(body), StatusCode: 200, }, nil } func main() { lambda.Start(Handler) }AWSTemplateFormatVersion : '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: 유저 정보를 반환한다. Resources: ExampleAPI: Type: AWS::Serverless::Function Properties: Runtime: go1.x Handler: main Events: RootHandler: Type: Api Properties: Path: '/' Method: get HelloHandler: Type: Api Properties: Path: '/hello' Method: get GoodbyeHandler: Type: Api Properties: Path: '/goodbye' Method: get$ curl localhost:3000/hello -i HTTP/1.0 200 OK Content-Type: application/json Content-Length: 52 Server: Werkzeug/0.11.15 Python/2.7.14 Date: Thu, 21 Jun 2018 06:35:13 GMT {"name":"yundream","age":5,"email":"yundream@gmail.com"}쿼리 파라메터
func Handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { name := request.PathParameters["name"] body, _ := json.Marshal(Info{name, 5, "yundream@gmail.com"}) return events.APIGatewayProxyResponse{ Body: string(body), StatusCode: 200, }, nil }AWSTemplateFormatVersion : '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: 유저 정보를 반환한다. Resources: ExampleAPI: Type: AWS::Serverless::Function Properties: Runtime: go1.x Handler: main Events: RootHandler: Type: Api Properties: Path: '/' Method: get HelloHandler: Type: Api Properties: Path: '/hello/{name}' Method: get GoodbyeHandler: Type: Api Properties: Path: '/goodbye/{name}' Method: get$ curl localhost:3000/hello/john -i HTTP/1.0 200 OK Content-Type: application/json Content-Length: 52 Server: Werkzeug/0.11.15 Python/2.7.14 Date: Thu, 21 Jun 2018 06:35:13 GMT {"name":"john","age":5,"email":"yundream@gmail.com"}DynamoDB 연동
DynamoDB Local 준비
$ aws dynamodb create-table \ --table-name User \ --attribute-definitions \ AttributeName=Email,AttributeType=S \ --key-schema AttributeName=Email,KeyType=HASH \ --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 \ --endpoint-url http://localhost:8000 $ aws dynamodb list-tables --endpoint-url http://localhost:8000 { "TableNames": [ "User" ] }$ aws dynamodb put-item \ --table-name User \ --item '{ "Name": {"S": "yundream"}, "Email": {"S": "yundream@gmail.com"} , "Age": {"N": "45"} }' \ --return-consumed-capacity TOTAL \ --endpoint-url http://localhost:8000 $ aws dynamodb put-item \ --table-name User \ --item '{ "Name": {"S": "sangbae.yun"}, "Email": {"S": "sangbae.yungjoinc.co.kr"} , "Age": {"N": "40"} }' \ --return-consumed-capacity TOTAL \ --endpoint-url http://localhost:8000$ aws dynamodb get-item --table-name User --key '{"Email":{"S": "yundream@gmail.com"}}' --endpoint-url http://localhost:8000 { "Item": { "Age": { "N": "45" }, "Name": { "S": "yundream" }, "Email": { "S": "yundream@gmail.com" } } }API 개발
awsConfig := &aws.Config{ Region: aws.String("ap-northeast-2"), Endpoint: aws.String("http://172.17.0.2:8000"), } sess, err := session.NewSession(awsConfig)sess, err := session.NewSession(awsConfig) if err != nil { fmt.Println("ERROR ", err.Error()) return }result, err := dbSvc.GetItem(&dynamodb.GetItemInput{ TableName: aws.String("User"), Key: map[string]*dynamodb.AttributeValue{ "Email": {S: aws.String(name)}, }, }) item := Item{} err = dynamodbattribute.UnmarshalMap(result.Item, &item) if err != nil { return events.APIGatewayProxyResponse{ StatusCode: 500, }, nil }package main import ( "context" "encoding/json" "fmt" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/dynamodb" "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute" ) var ( dbSvc *dynamodb.DynamoDB ) type Item struct { Name string `json:"name"` Age int `json:"age"` Email string `json:"email"` } // Handler는 람다함수함수를 호출 할 때 실행되는 코드다. // AWS API Gateway의 요청과 응답 처리 매커니즘은 aws-lambda-go/envents 패키지가 제공한다. func Handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { name := request.PathParameters["name"] fmt.Println("Find User ", name) result, err := dbSvc.GetItem(&dynamodb.GetItemInput{ TableName: aws.String("User"), Key: map[string]*dynamodb.AttributeValue{ "Email": {S: aws.String(name)}, }, }) item := Item{} err = dynamodbattribute.UnmarshalMap(result.Item, &item) if err != nil { return events.APIGatewayProxyResponse{ StatusCode: 500, }, nil } body, _ := json.Marshal(item) return events.APIGatewayProxyResponse{ Body: string(body), StatusCode: 200, }, nil } func main() { awsConfig := &aws.Config{ Region: aws.String("ap-northeast-2"), Endpoint: aws.String("http://172.17.0.2:8000"), } sess, err := session.NewSession(awsConfig) if err != nil { fmt.Println("ERROR ", err.Error()) return } dbSvc = dynamodb.New(sess) fmt.Println("Server Start ........") lambda.Start(Handler) }$ curl localhost:3000/hello/yundream@gmail.com {"name":"yundream","age":45,"email":"yundream@gmail.com"}테스트와 배포
Recent Posts
Archive Posts
Tags