Slide 1

Slide 1 text

Amazon API Gateway から WebSocket へ御入門 - enza部 AnD00 -

Slide 2

Slide 2 text

はじめに

Slide 3

Slide 3 text

自己紹介 ● 安藤 尚之 ● enza部 ディレクター/プロダクトオーナー ● GitHub: AnD00 ● Tech: Ruby, Go, AWS, etc ● 2020年1月、ドリコムに中途入社。 enza部に所属して、コード書いたりスクラムマスターやったりし てます。 多摩美大入学から10年近く演劇に人生を捧げてきたのに突然 エンジニアになってみた系エンジニア。

Slide 4

Slide 4 text

enzaとは 人気作品の新作タイトルが遊べるスマートフォン向けブラウザゲー ムプラットフォーム「enza」大好評配信中! 気の合う仲間同士が輪になってワイワイ楽しめる場所、それがenza (エンザ)です。 現在配信中タイトルの「アイドルマスター シャイニーカラーズ」など、 ぜひお楽しみください! [引用 元]https://www.bandainamcoid.com/portal/serviceDetail?sv=110000&backto=%2Fportal%2FserviceDetail%3Fs v%3D1203

Slide 5

Slide 5 text

アジェンダ

Slide 6

Slide 6 text

アジェンダ ● ターゲットとゴール ● Websocket APIについて ● つくるもの ● つくりかた ● まとめ

Slide 7

Slide 7 text

ターゲットとゴール

Slide 8

Slide 8 text

ターゲット ● Websocketについて知りたい ● サーバーレスについて知りたい ● AWSに興味がある

Slide 9

Slide 9 text

ゴール ● Websocketアプリケーションの概要を理解する ● API Gatewayを使って、Websocket APIが作れる(ような気がする)

Slide 10

Slide 10 text

Websocket APIについて

Slide 11

Slide 11 text

Websocketとは ● WebサーバーとWebブラウザの間で、低コストで双方向通信できるようにするため の仕組み ● HTTPでは通常、「クライアントからのリクエストにサーバーがレスポンスを返す」とい う形でしか通信できない(ロングポーリング等やりようはある) ● WebSocketでは一度コネクションを確立すれば、クライアント・サーバーどちらから でも能動的にメッセージを送ることができる

Slide 12

Slide 12 text

APIとは ● Application Programming Interfaceの略称 ● ソフトウェアやプログラム、Webサービスの間で情報をやりとりするために使うイン ターフェースの仕様

Slide 13

Slide 13 text

API Gatewayとは ● APIの管理や実行を簡単にしてくれる仕組み ● API Gateway自体はアプリケーションではなく、 ○ クライアントからリクエストを受け取ってそれをバックエンドに渡す ○ バックエンドからレスポンスを受け取ってクライアントに返す というプロキシのような働きをするもの ● Lambdaと組み合わせるのが鉄板 ○ サーバーレスでスケールを意識しなくて済むし、コストも使った分だけ!

Slide 14

Slide 14 text

つくるもの

Slide 15

Slide 15 text

チャット機能 1. ユーザーは任意のルームに入室できる 2. ユーザーは入室したルーム内でメッセージが送受信できる 3. ユーザーは任意のルームから退出できる

Slide 16

Slide 16 text

技術スタック ● AWS Serverless Application Model(SAM) ● AWS Lambda ● Amazon API Gateway ● Amazon CloudWatch ● Amazon DynamoDB ● Golang

Slide 17

Slide 17 text

システム構成

Slide 18

Slide 18 text

つくりかた

Slide 19

Slide 19 text

ソースコード ● https://github.com/AnD00/simple-websockets-chat-app

Slide 20

Slide 20 text

フォルダ構成 ├── Makefile ├── README.md ├── connect │ ├── Makefile │ └── connect.go ├── disconnect │ ├── Makefile │ └── disconnect.go ├── docker-compose.yml ├── go.mod ├── go.sum ├── lib │ ├── apigw │ └── dynamodb ├── publish │ ├── Makefile │ └── publish.go ├── samconfig.toml ├── template.yaml └── testdata ├── event_connection.json └── event_publish.json

Slide 21

Slide 21 text

フォルダ構成 ├── Makefile ├── README.md ├── connect │ ├── Makefile │ └── connect.go ├── disconnect │ ├── Makefile │ └── disconnect.go ├── docker-compose.yml ├── go.mod ├── go.sum ├── lib │ ├── apigw │ └── dynamodb ├── publish │ ├── Makefile │ └── publish.go ├── samconfig.toml ├── template.yaml └── testdata ├── event_connection.json └── event_publish.json チャットルーム入室の関数 チャットルーム退室の関数 外部サービスとやりとりする ためのライブラリ チャットメッセージ送信の関数 SAMの設定やテンプレート

Slide 22

Slide 22 text

AWS Lambda ● チャットルームへの入室 ● チャットメッセージの送信 ● チャットルームから退出

Slide 23

Slide 23 text

connect.go package main import ( "context" "fmt" "simple-websockets-chat-app/lib/apigw" "simple-websockets-chat-app/lib/dynamodb" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func main() { lambda.Start(handler) } func handler(_ context.Context, req *events.APIGatewayWebsocketProxyRequest) (apigw.Response, error) { fmt.Println("websocket connect") err := dynamodb.PutConnection(req.RequestContext.ConnectionID, req.QueryStringParameters["room"]) if err != nil { fmt.Println(err) return apigw.InternalServerErrorResponse(), err } fmt.Println("websocket connection cached") return apigw.OkResponse(), nil }

Slide 24

Slide 24 text

connect.go package main import ( "context" "fmt" "simple-websockets-chat-app/lib/apigw" "simple-websockets-chat-app/lib/dynamodb" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func main() { lambda.Start(handler) } func handler(_ context.Context, req *events.APIGatewayWebsocketProxyRequest) (apigw.Response, error) { fmt.Println("websocket connect") err := dynamodb.PutConnection(req.RequestContext.ConnectionID, req.QueryStringParameters["room"]) if err != nil { fmt.Println(err) return apigw.InternalServerErrorResponse(), err } fmt.Println("websocket connection cached") return apigw.OkResponse(), nil } WebsocketのコネクションIDと チャットルーム情報を DynamoDBに 保存する

Slide 25

Slide 25 text

disconnect.go package main import ( "context" "fmt" "simple-websockets-chat-app/lib/apigw" "simple-websockets-chat-app/lib/dynamodb" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func main() { lambda.Start(handler) } func handler(_ context.Context, req *events.APIGatewayWebsocketProxyRequest) (apigw.Response, error) { fmt.Println("websocket disconnect") err := dynamodb.DeleteConnection(req.RequestContext.ConnectionID) if err != nil { fmt.Println(err) return apigw.InternalServerErrorResponse(), err } fmt.Println("websocket connection deleted from cache") return apigw.OkResponse(), nil }

Slide 26

Slide 26 text

disconnect.go package main import ( "context" "fmt" "simple-websockets-chat-app/lib/apigw" "simple-websockets-chat-app/lib/dynamodb" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func main() { lambda.Start(handler) } func handler(_ context.Context, req *events.APIGatewayWebsocketProxyRequest) (apigw.Response, error) { fmt.Println("websocket disconnect") err := dynamodb.DeleteConnection(req.RequestContext.ConnectionID) if err != nil { fmt.Println(err) return apigw.InternalServerErrorResponse(), err } fmt.Println("websocket connection deleted from cache") return apigw.OkResponse(), nil } WebsocketのコネクションIDと チャットルーム情報を DynamoDBから 削除する

Slide 27

Slide 27 text

publish.go ... func handler(ctx context.Context, req *events.APIGatewayWebsocketProxyRequest) (apigw.Response, error) { apiClient := apigw.NewAPIGatewayManagementClient(req.RequestContext.DomainName, req.RequestContext.Stage) input, err := new(ws.InputEnvelop).Decode([]byte(req.Body)) if err != nil { return apigw.BadRequestResponse(), err } output := &ws.OutputEnvelop{ Data: input.Data, Received: time.Now().Unix(), } data, err := output.Encode() if err != nil { return apigw.InternalServerErrorResponse(), err } conns, err := dynamodb.GetAllConnections(input.Room) if err != nil { return apigw.InternalServerErrorResponse(), err } sender := req.RequestContext.ConnectionID for _, conn := range conns { id := conn.ConnectionID if id == sender { continue } ws.Publish(apiClient, ctx, id, data) } return apigw.OkResponse(), nil }

Slide 28

Slide 28 text

publish.go ... func handler(ctx context.Context, req *events.APIGatewayWebsocketProxyRequest) (apigw.Response, error) { apiClient := apigw.NewAPIGatewayManagementClient(req.RequestContext.DomainName, req.RequestContext.Stage) input, err := new(ws.InputEnvelop).Decode([]byte(req.Body)) if err != nil { return apigw.BadRequestResponse(), err } output := &ws.OutputEnvelop{ Data: input.Data, Received: time.Now().Unix(), } data, err := output.Encode() if err != nil { return apigw.InternalServerErrorResponse(), err } conns, err := dynamodb.GetAllConnections(input.Room) if err != nil { return apigw.InternalServerErrorResponse(), err } sender := req.RequestContext.ConnectionID for _, conn := range conns { id := conn.ConnectionID if id == sender { continue } ws.Publish(apiClient, ctx, id, data) } return apigw.OkResponse(), nil } リクエストボディをデコードする 同じチャットルームのコネクションを DynamoDBから一括で取得する 取得したコネクションすべてを対象に チャットメッセージを通知する

Slide 29

Slide 29 text

Amazon DynamoDB ● 接続情報の保存 ● 同じチャットルームの 接続情報一覧の参照 ● 接続情報の削除

Slide 30

Slide 30 text

dynamodb/dynamodb.go package dynamodb import ( "os" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/guregu/dynamo" "github.com/pkg/errors" ) func getTable(db *dynamo.DB, tableName string) dynamo.Table { return db.Table(tableName) } func connect() (*dynamo.DB, error) { config := aws.Config{ Endpoint: aws.String(os.Getenv("DYNAMO_ENDPOINT")), } dynamoSession, err := session.NewSession(&config) if err != nil { return nil, errors.WithStack(err) } return dynamo.New(dynamoSession), nil }

Slide 31

Slide 31 text

dynamodb/dynamodb.go package dynamodb import ( "os" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/guregu/dynamo" "github.com/pkg/errors" ) func getTable(db *dynamo.DB, tableName string) dynamo.Table { return db.Table(tableName) } func connect() (*dynamo.DB, error) { config := aws.Config{ Endpoint: aws.String(os.Getenv("DYNAMO_ENDPOINT")), } dynamoSession, err := session.NewSession(&config) if err != nil { return nil, errors.WithStack(err) } return dynamo.New(dynamoSession), nil } 指定されたテーブルのハンドラを返す DynamoDBのクライアントを作成する

Slide 32

Slide 32 text

dynamodb/connection.go package dynamodb import ( "os" "github.com/pkg/errors" ) const connectionsTableNameTemplate = "simple-websockets-chat-app-connections" type Connection struct { ConnectionID string `dynamo:"connectionId,hash"` Room string `dynamo:"room" index:"room-index,hash"` } func getConnectionsTableName() string { return connectionsTableNameTemplate + "-" + os.Getenv("STAGE_NAME") } func GetAllConnections(room string) ([]Connection, error) { db, err := connect() if err != nil { return nil, errors.WithStack(err) } tableName := getConnectionsTableName() table := getTable(db, tableName) var results []Connection err = table.Get("room", room).Index("room-index").All(&results) if err != nil { return nil, errors.WithStack(err) } return results, nil } ... func PutConnection(connectionId string, room string) error { db, err := connect() if err != nil { return errors.WithStack(err) } tableName := getConnectionsTableName() table := getTable(db, tableName) putModel := Connection{ ConnectionID: connectionId, Room: room, } err = table.Put(putModel).Run() if err != nil { return errors.WithStack(err) } return nil } func DeleteConnection(connectionId string) error { db, err := connect() if err != nil { return errors.WithStack(err) } tableName := getConnectionsTableName() table := getTable(db, tableName) err = table.Delete("connectionId", connectionId).Run() if err != nil { return errors.WithStack(err) } return nil }

Slide 33

Slide 33 text

dynamodb/connection.go package dynamodb import ( "os" "github.com/pkg/errors" ) const connectionsTableNameTemplate = "simple-websockets-chat-app-connections" type Connection struct { ConnectionID string `dynamo:"connectionId,hash"` Room string `dynamo:"room" index:"room-index,hash"` } func getConnectionsTableName() string { return connectionsTableNameTemplate + "-" + os.Getenv("STAGE_NAME") } func GetAllConnections(room string) ([]Connection, error) { db, err := connect() if err != nil { return nil, errors.WithStack(err) } tableName := getConnectionsTableName() table := getTable(db, tableName) var results []Connection err = table.Get("room", room).Index("room-index").All(&results) if err != nil { return nil, errors.WithStack(err) } return results, nil } ... func PutConnection(connectionId string, room string) error { db, err := connect() if err != nil { return errors.WithStack(err) } tableName := getConnectionsTableName() table := getTable(db, tableName) putModel := Connection{ ConnectionID: connectionId, Room: room, } err = table.Put(putModel).Run() if err != nil { return errors.WithStack(err) } return nil } func DeleteConnection(connectionId string) error { db, err := connect() if err != nil { return errors.WithStack(err) } tableName := getConnectionsTableName() table := getTable(db, tableName) err = table.Delete("connectionId", connectionId).Run() if err != nil { return errors.WithStack(err) } return nil } Global secondary indexを使い、 同じroomの値を持つレコードを 一括取得する Key/Valueを指定して、 レコードを作成する Primary Keyを指定して、 レコードを削除する

Slide 34

Slide 34 text

docker-compose.yml version: '3' services: dynamodb-local: container_name: dynamodb-local image: amazon/dynamodb-local:latest user: root command: -jar DynamoDBLocal.jar -sharedDb -dbPath /data volumes: - dynamodb-local-data:/data ports: - 8000:8000 dynamodb-admin: container_name: dynamodb-admin image: aaronshaf/dynamodb-admin:latest environment: - DYNAMO_ENDPOINT=http://host.docker.internal:8000 ports: - 8001:8001 depends_on: - dynamodb-local volumes: dynamodb-local-data:

Slide 35

Slide 35 text

docker-compose.yml version: '3' services: dynamodb-local: container_name: dynamodb-local image: amazon/dynamodb-local:latest user: root command: -jar DynamoDBLocal.jar -sharedDb -dbPath /data volumes: - dynamodb-local-data:/data ports: - 8000:8000 dynamodb-admin: container_name: dynamodb-admin image: aaronshaf/dynamodb-admin:latest environment: - DYNAMO_ENDPOINT=http://host.docker.internal:8000 ports: - 8001:8001 depends_on: - dynamodb-local volumes: dynamodb-local-data: dbPathのオプションを指定して、 コンテナ終了時にデータが消えないようにする dynamodb-localを接続先に指定する

Slide 36

Slide 36 text

dynamodb-admin

Slide 37

Slide 37 text

dynamodb-admin

Slide 38

Slide 38 text

AWS Serverless Application Model ● API GatewayやLambdaなど、 アプリケーションに必要な リソースを構築する

Slide 39

Slide 39 text

AWSTemplateFormatVersion: "2010-09-09" Transform: AWS::Serverless-2016-10-31 ... Resources: WebSocket: Type: AWS::ApiGatewayV2::Api Properties: Name: !Ref ApplicationName ProtocolType: WEBSOCKET RouteSelectionExpression: "$request.body.message" ConnectFunction: Type: AWS::Serverless::Function Metadata: BuildMethod: makefile Properties: Policies: - DynamoDBCrudPolicy: TableName: !Ref ConnectionsTable ConnectRoute: Type: AWS::ApiGatewayV2::Route Properties: RouteKey: $connect ApiId: !Ref WebSocket AuthorizationType: NONE OperationName: ConnectRoute Target: !Join - "/" - - "integrations" - !Ref ConnectIntegration ... template.yml ConnectIntegration: Type: AWS::ApiGatewayV2::Integration Properties: ApiId: !Ref WebSocket IntegrationType: AWS_PROXY IntegrationUri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ConnectFunction.Arn }/invocations ConnectFunctionPermission: Type: AWS::Lambda::Permission DependsOn: - WebSocket Properties: Action: lambda:InvokeFunction Principal: apigateway.amazonaws.com FunctionName: !Ref ConnectFunction ConnectFunctionLogGroup: Type: AWS::Logs::LogGroup DependsOn: - ConnectFunction Properties: RetentionInDays: 30 LogGroupName: !Sub /aws/lambda/${ConnectFunction} ...

Slide 40

Slide 40 text

AWSTemplateFormatVersion: "2010-09-09" Transform: AWS::Serverless-2016-10-31 ... Resources: WebSocket: Type: AWS::ApiGatewayV2::Api Properties: Name: !Ref ApplicationName ProtocolType: WEBSOCKET RouteSelectionExpression: "$request.body.message" ConnectFunction: Type: AWS::Serverless::Function Metadata: BuildMethod: makefile Properties: Policies: - DynamoDBCrudPolicy: TableName: !Ref ConnectionsTable ConnectRoute: Type: AWS::ApiGatewayV2::Route Properties: RouteKey: $connect ApiId: !Ref WebSocket AuthorizationType: NONE OperationName: ConnectRoute Target: !Join - "/" - - "integrations" - !Ref ConnectIntegration ... template.yml ConnectIntegration: Type: AWS::ApiGatewayV2::Integration Properties: ApiId: !Ref WebSocket IntegrationType: AWS_PROXY IntegrationUri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ConnectFunction.Arn }/invocations ConnectFunctionPermission: Type: AWS::Lambda::Permission DependsOn: - WebSocket Properties: Action: lambda:InvokeFunction Principal: apigateway.amazonaws.com FunctionName: !Ref ConnectFunction ConnectFunctionLogGroup: Type: AWS::Logs::LogGroup DependsOn: - ConnectFunction Properties: RetentionInDays: 30 LogGroupName: !Sub /aws/lambda/${ConnectFunction} ... APIを作成する Lambda関数を 作成する APIのルートを 作成する APIにLambda関数 を呼び出す権限を 付与する APIの統合リクエ ストを作成する Lambda関数の ログを出力

Slide 41

Slide 41 text

template.yml Deployment: Type: AWS::ApiGatewayV2::Deployment DependsOn: - ConnectRoute ... Properties: ApiId: !Ref WebSocket Stage: Type: AWS::ApiGatewayV2::Stage Properties: StageName: !Ref StageName ApiId: !Ref WebSocket DeploymentId: !Ref Deployment DefaultRouteSettings: LoggingLevel: INFO DataTraceEnabled: true DetailedMetricsEnabled: true ... ConnectionsTable: Type: AWS::DynamoDB::Table Properties: AttributeDefinitions: - AttributeName: connectionId AttributeType: S - AttributeName: room AttributeType: S KeySchema: - AttributeName: connectionId KeyType: HASH ProvisionedThroughput: ReadCapacityUnits: 5 WriteCapacityUnits: 5 GlobalSecondaryIndexes: - IndexName: room-index KeySchema: - AttributeName: room KeyType: HASH ProvisionedThroughput: ReadCapacityUnits: 5 WriteCapacityUnits: 5 Projection: ProjectionType: ALL SSESpecification: SSEEnabled: False TableName: !Sub ${ApplicationName}-connections-${StageName} Outputs: WebSocketEndpoint: Value: !Sub wss://${WebSocket}.execute-api.${AWS::Region}.amazonaws.com/${StageName}/

Slide 42

Slide 42 text

template.yml Deployment: Type: AWS::ApiGatewayV2::Deployment DependsOn: - ConnectRoute ... Properties: ApiId: !Ref WebSocket Stage: Type: AWS::ApiGatewayV2::Stage Properties: StageName: !Ref StageName ApiId: !Ref WebSocket DeploymentId: !Ref Deployment DefaultRouteSettings: LoggingLevel: INFO DataTraceEnabled: true DetailedMetricsEnabled: true ... ConnectionsTable: Type: AWS::DynamoDB::Table Properties: AttributeDefinitions: - AttributeName: connectionId AttributeType: S - AttributeName: room AttributeType: S KeySchema: - AttributeName: connectionId KeyType: HASH ProvisionedThroughput: ReadCapacityUnits: 5 WriteCapacityUnits: 5 GlobalSecondaryIndexes: - IndexName: room-index KeySchema: - AttributeName: room KeyType: HASH ProvisionedThroughput: ReadCapacityUnits: 5 WriteCapacityUnits: 5 Projection: ProjectionType: ALL SSESpecification: SSEEnabled: False TableName: !Sub ${ApplicationName}-connections-${StageName} Outputs: WebSocketEndpoint: Value: !Sub wss://${WebSocket}.execute-api.${AWS::Region}.amazonaws.com/${StageName}/ APIのステージ (環境)を作成する APIのデプロイメント を作成する DynamoDBの テーブルを作成する スタックのプロパティに 含める値を指定する

Slide 43

Slide 43 text

sam build ~/g/g/A/simple-websockets-chat-app ❯❯❯ make clean build /Library/Developer/CommandLineTools/usr/bin/make -C connect clean rm -rfv bin /Library/Developer/CommandLineTools/usr/bin/make -C disconnect clean rm -rfv bin /Library/Developer/CommandLineTools/usr/bin/make -C publish clean rm -rfv bin building handlers for aws lambda sam build Building codeuri: /Users/naoyukiando/ghq/github.com/AnD00/simple-websockets-chat-app runtime: go1.x metadata: {'BuildMethod': 'makefile'} architecture: x86_64 functions: ['ConnectFunction'] Running CustomMakeBuilder:CopySource Running CustomMakeBuilder:MakeBuild Current Artifacts Directory : /Users/naoyukiando/ghq/github.com/AnD00/simple-websockets-chat-app/.aws-sam/build/ConnectFunction Building codeuri: /Users/naoyukiando/ghq/github.com/AnD00/simple-websockets-chat-app runtime: go1.x metadata: {'BuildMethod': 'makefile'} architecture: x86_64 functions: ['DisconnectFunction'] Running CustomMakeBuilder:CopySource Running CustomMakeBuilder:MakeBuild Current Artifacts Directory : /Users/naoyukiando/ghq/github.com/AnD00/simple-websockets-chat-app/.aws-sam/build/DisconnectFunction Building codeuri: /Users/naoyukiando/ghq/github.com/AnD00/simple-websockets-chat-app runtime: go1.x metadata: {'BuildMethod': 'makefile'} architecture: x86_64 functions: ['PublishFunction'] Running CustomMakeBuilder:CopySource Running CustomMakeBuilder:MakeBuild Current Artifacts Directory : /Users/naoyukiando/ghq/github.com/AnD00/simple-websockets-chat-app/.aws-sam/build/PublishFunction Build Succeeded Built Artifacts : .aws-sam/build Built Template : .aws-sam/build/template.yaml Commands you can use next ========================= [*] Invoke Function: sam local invoke [*] Test Function in the Cloud: sam sync --stack-name {stack-name} --watch [*] Deploy: sam deploy --guided

Slide 44

Slide 44 text

sam local invoke ~/g/g/A/simple-websockets-chat-app ❯❯❯ sam local invoke ConnectFunction -e testdata/event_connection.json Invoking bootstrap (go1.x) Skip pulling image and use local one: public.ecr.aws/sam/emulation-go1.x:rapid-1.37.0-x86_64. Mounting /Users/naoyukiando/ghq/github.com/AnD00/simple-websockets-chat-app/.aws-sam/build/ConnectFunction as /var/task:ro,delegated inside runtime container START RequestId: d3edc6df-0d32-4844-bcca-d44a1aad4e4f Version: $LATEST websocket connect websocket connection cached {"statusCode":200,"headers":null,"multiValueHeaders":null,"body":""}END RequestId: d3edc6df-0d32-4844-bcca-d44a1aad4e4f REPORT RequestId: d3edc6df-0d32-4844-bcca-d44a1aad4e4f Init Duration: 0.33 ms Duration: 181.49 ms Billed Duration: 182 ms Memory Size: 512 MB Max Memory Used: 512 MB

Slide 45

Slide 45 text

sam deploy ~/g/g/A/simple-websockets-chat-app ❯❯❯ sam deploy Uploading to simple-websockets-chat-app/4c81dd4bb11c6d37226e889c7bd580e7 4054042 / 4054042 (100.00%) Uploading to simple-websockets-chat-app/c81a285b0602438de0e3bf25a9f5b597 4032349 / 4032349 (100.00%) Uploading to simple-websockets-chat-app/c5bc02bd98696c8959e5cebe9ca8b9e8 4145071 / 4145071 (100.00%) Deploying with following values =============================== Stack name : simple-websockets-chat-app Region : ap-northeast-1 Confirm changeset : True Disable rollback : False Deployment s3 bucket : aws-sam-cli-managed-default-samclisourcebucket-1xj859y6o8i0b Capabilities : ["CAPABILITY_IAM"] Parameter overrides : {"ApplicationName": "simple-websockets-chat-app", "DynamoEndpoint": "", "StageName": "develop"} Signing Profiles : {} Initiating deployment ===================== Uploading to simple-websockets-chat-app/eb99c7fac22b19b31309262a174e7c72.template 7491 / 7491 (100.00%) Waiting for changeset to be created.. CloudFormation stack changeset -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------- Operation LogicalResourceId ResourceType Replacement -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------- + Add ConnectFunctionLogGroup AWS::Logs::LogGroup N/A ... -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------- Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:317496045611:changeSet/samcli-deploy1647007398/ae55b142-9151-4d60-82d6-3b6ab18ca217 Previewing CloudFormation changeset before deployment ====================================================== Deploy this changeset? [y/N]:

Slide 46

Slide 46 text

sam deploy 2022-03-11 23:04:59 - Waiting for stack create/update to complete CloudFormation events from stack operations -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------- ResourceStatus ResourceType LogicalResourceId ResourceStatusReason -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------- CREATE_IN_PROGRESS AWS::DynamoDB::Table ConnectionsTable - CREATE_IN_PROGRESS AWS::ApiGatewayV2::Api WebSocket - CREATE_IN_PROGRESS AWS::DynamoDB::Table ConnectionsTable Resource creation ... CREATE_COMPLETE AWS::CloudFormation::Stack simple-websockets-chat-app - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------- CloudFormation outputs from deployed stack -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------- Outputs -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------- Key WebSocketEndpoint Description - Value wss://r8mgbs7a31.execute-api.ap-northeast-1.amazonaws.com/develop/ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------- Successfully created/updated stack - simple-websockets-chat-app in ap-northeast-1

Slide 47

Slide 47 text

simple-websockets-chat-app

Slide 48

Slide 48 text

実際に 動かしてみる ● チャットメッセージの送信 ● 接続情報の保存 ● ログの出力

Slide 49

Slide 49 text

チャットメッセージの送信

Slide 50

Slide 50 text

接続情報の保存

Slide 51

Slide 51 text

ログの出力

Slide 52

Slide 52 text

システム構成 完成

Slide 53

Slide 53 text

お金の話 ● 各サービスの料金 ● 後片付け

Slide 54

Slide 54 text

各サービスの料金(API Gateway)

Slide 55

Slide 55 text

各サービスの料金(Lambda)

Slide 56

Slide 56 text

各サービスの料金(DynamoDB)

Slide 57

Slide 57 text

各サービスの料金(CloudWatch)

Slide 58

Slide 58 text

後片付け(sam delete) ~/g/g/A/simple-websockets-chat-app ❯❯❯ sam delete --config-file samconfig.toml Are you sure you want to delete the stack simple-websockets-chat-app in the region ap-northeast-1 ? [y/N]: y Are you sure you want to delete the folder simple-websockets-chat-app in S3 which contains the artifacts? [y/N]: y - Deleting S3 object with key simple-websockets-chat-app/4c81dd4bb11c6d37226e889c7bd580e7 - Deleting S3 object with key simple-websockets-chat-app/c81a285b0602438de0e3bf25a9f5b597 - Deleting S3 object with key simple-websockets-chat-app/c5bc02bd98696c8959e5cebe9ca8b9e8 - Deleting S3 object with key simple-websockets-chat-app/9dc03003f3c1f0b4378ec79252c43630 - Deleting S3 object with key simple-websockets-chat-app/a0739d16f65ef707e530bd11d4f7a97b - Deleting S3 object with key simple-websockets-chat-app/b5edd04a5d3d134c63bf3799fbefab1e.template - Deleting S3 object with key simple-websockets-chat-app/c8879faaa0a1bbe9d15e2b33fe23a2e2 - Deleting S3 object with key simple-websockets-chat-app/eb99c7fac22b19b31309262a174e7c72.template - Deleting Cloudformation stack simple-websockets-chat-app Deleted successfully

Slide 59

Slide 59 text

まとめ

Slide 60

Slide 60 text

まとめ ● API Gatewayを使うと、簡単にWebsocket APIが作れる 興味あれば是非つくってみて、 そして動かしてみてください 🔧🔨

Slide 61

Slide 61 text

ご静聴 🎉 ありがとうございました 🎉