Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Getting Started with Websocket from API Gateway

08a41b7722fa80a2b02d009fad5cabd8?s=47 AnD00
March 13, 2022

Getting Started with Websocket from API Gateway

08a41b7722fa80a2b02d009fad5cabd8?s=128

AnD00

March 13, 2022
Tweet

More Decks by AnD00

Other Decks in Technology

Transcript

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

  2. はじめに

  3. 自己紹介 • 安藤 尚之 • enza部 ディレクター/プロダクトオーナー • GitHub: AnD00

    • Tech: Ruby, Go, AWS, etc • 2020年1月、ドリコムに中途入社。 enza部に所属して、コード書いたりスクラムマスターやったりし てます。 多摩美大入学から10年近く演劇に人生を捧げてきたのに突然 エンジニアになってみた系エンジニア。
  4. enzaとは 人気作品の新作タイトルが遊べるスマートフォン向けブラウザゲー ムプラットフォーム「enza」大好評配信中! 気の合う仲間同士が輪になってワイワイ楽しめる場所、それがenza (エンザ)です。 現在配信中タイトルの「アイドルマスター シャイニーカラーズ」など、 ぜひお楽しみください! [引用 元]https://www.bandainamcoid.com/portal/serviceDetail?sv=110000&backto=%2Fportal%2FserviceDetail%3Fs

    v%3D1203
  5. アジェンダ

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

    • まとめ
  7. ターゲットとゴール

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

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

  10. Websocket APIについて

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

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

  13. API Gatewayとは • APIの管理や実行を簡単にしてくれる仕組み • API Gateway自体はアプリケーションではなく、 ◦ クライアントからリクエストを受け取ってそれをバックエンドに渡す ◦

    バックエンドからレスポンスを受け取ってクライアントに返す というプロキシのような働きをするもの • Lambdaと組み合わせるのが鉄板 ◦ サーバーレスでスケールを意識しなくて済むし、コストも使った分だけ!
  14. つくるもの

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

  16. 技術スタック • AWS Serverless Application Model(SAM) • AWS Lambda •

    Amazon API Gateway • Amazon CloudWatch • Amazon DynamoDB • Golang
  17. システム構成

  18. つくりかた

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

  20. フォルダ構成 ├── 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
  21. フォルダ構成 ├── 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の設定やテンプレート
  22. AWS Lambda • チャットルームへの入室 • チャットメッセージの送信 • チャットルームから退出

  23. 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 }
  24. 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に 保存する
  25. 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 }
  26. 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から 削除する
  27. 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 }
  28. 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から一括で取得する 取得したコネクションすべてを対象に チャットメッセージを通知する
  29. Amazon DynamoDB • 接続情報の保存 • 同じチャットルームの 接続情報一覧の参照 • 接続情報の削除

  30. 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 }
  31. 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のクライアントを作成する
  32. 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 }
  33. 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を指定して、 レコードを削除する
  34. 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:
  35. 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を接続先に指定する
  36. dynamodb-admin

  37. dynamodb-admin

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

  39. 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} ...
  40. 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関数の ログを出力
  41. 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}/
  42. 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の テーブルを作成する スタックのプロパティに 含める値を指定する
  43. 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
  44. 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
  45. 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]:
  46. 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
  47. simple-websockets-chat-app

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

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

  50. 接続情報の保存

  51. ログの出力

  52. システム構成 完成

  53. お金の話 • 各サービスの料金 • 後片付け

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

  55. 各サービスの料金(Lambda)

  56. 各サービスの料金(DynamoDB)

  57. 各サービスの料金(CloudWatch)

  58. 後片付け(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
  59. まとめ

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

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