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

AWS SDKの裏側を見てみよう〜AWS SDK for Go (v1) を例にとって〜

AWS SDKの裏側を見てみよう〜AWS SDK for Go (v1) を例にとって〜

nakanoshima.dev #28での発表資料です。

elecho1_t

July 28, 2022
Tweet

Other Decks in Technology

Transcript

  1. NAKANOSHIMA.DEV #28 – AWS SDK FOR JAVA © 2022, Amazon

    Web Services, Inc. or its affiliates. © 2022, Amazon Web Services, Inc. or its affiliates. AWS SDKの裏側を見てみよう! 王 力捷 N A K A N O S H I M A . D E V # 2 8 Amazon Web Services Japan Solutions Architect 〜 AWS SDK for Go (v1) を例にとって 〜
  2. © 2022, Amazon Web Services, Inc. or its Affiliates. ⾃⼰紹介

    王 ⼒捷(おう りきしょう) • Solutions Architect 好きなAWSサービス • Amazon SageMaker • Amazon DevOps Guru
  3. © 2022, Amazon Web Services, Inc. or its Affiliates. アジェンダ

    o AWS SDK for Go v1 に含まれるコンポーネント紹介 o SDK の基本設定の裏側(Session, Service client) o API リクエストを送るまでの裏側(Request) o AWS SDK を利⽤する際の Tips 3 AWS SDKの中でどのような処理をしているのか考えたことなかったけれど 興味があるという方向けに、ざっくりとして説明していきます!
  4. © 2022, Amazon Web Services, Inc. or its Affiliates. AWS

    SDK の実際の処理 5 (サービス、リージョンごと に異なる)
  5. © 2022, Amazon Web Services, Inc. or its Affiliates. 今回扱うAWS

    SDKのバージョン o AWS SDK for Go v1 (v.1.42.35) o AWS SDK for Go v1 を例として使う理由︓ o 処理が AWS SDK として完結しており、構造を把握しやすい 6
  6. © 2022, Amazon Web Services, Inc. or its Affiliates. package

    main import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/dynamodb" ) func main() { sess := session.NewSession() svc := dynamodb.New(sess) result, err := svc.GetItem(&dynamodb.GetItemInput{ TableName: aws.String(tableName), Key: map[string]*dynamodb.AttributeValue{ "Year": { N: aws.String(movieYear), }, "Title": { S: aws.String(movieName), }, }, }) } AWS SDK for Go v1のコード例 7
  7. © 2022, Amazon Web Services, Inc. or its Affiliates. package

    main import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/dynamodb" ) func main() { sess := session.NewSession() svc := dynamodb.New(sess) result, err := svc.GetItem(&dynamodb.GetItemInput{ TableName: aws.String(tableName), Key: map[string]*dynamodb.AttributeValue{ "Year": { N: aws.String(movieYear), }, "Title": { S: aws.String(movieName), }, }, }) } AWS SDK for Go v1のコード例 8 Session Service client Request
  8. © 2022, Amazon Web Services, Inc. or its Affiliates. AWS

    SDK for Go v1 の内部処理 o 設定読み込み o HTTPクライアントを 設定 9 Session Service client Request o AWS APIエンドポイン トの設定 o AWS API リクエスト送 信に必要な情報の収集 o HTTPリクエストボディ ・ヘッダー作成 o AWS認証情報の取得 o SigV4署名の追加 o HTTPリクエストの送信 o リトライ判定 基本設定 AWSサービス専⽤の クライアント AWS APIリクエストの管理
  9. © 2022, Amazon Web Services, Inc. or its Affiliates. Session

    を作成する処理の全体像 11 └── session.NewSession() // Session作成を呼び出す関数 └── session.NewSessionWithOptions() │ // Option付きでSession作成を呼び出す関数 ├── session.loadSharedEnvConfig() │ // 環境変数から設定(envCfg)を読み取る関数 └── session.newSession() // Session作成を行う関数 ├── defaults.Config() // デフォルト設定(cfg)を生成する変数 ├── session.loadSharedConfig() │ // 共有設定(sharedCfg)を読み込む関数 ├── session.mergeConfigSrcs() // 設定の統合を行う関数 └── defaults.Handlers() // デフォルトハンドラーを生成する変数
  10. © 2022, Amazon Web Services, Inc. or its Affiliates. Sessionに反映される4種類の設定(cfg)

    o cfg: デフォルト設定(⼀部のみ) o userCfg : session.NewSession() 関数を呼んだ際に引数で渡される設定 o envCfg : AWS_REGION 等、環境変数から読み込んだ設定 o sharedCfg : ~/.aws/config や ~/.aws/credentials などの設定ファイル から読み込んだ設定 設定が重複した際の、基本的な優先順位 userCfg > (cfg) > envCfg > sharedCfg これらの設定は session.mergeConfigSrcs() 関数によって統合される 12
  11. © 2022, Amazon Web Services, Inc. or its Affiliates. HTTP

    クライアントは Session で作成される defaults.Config() 関数内で HTTP クライアントが作成される https://github.com/aws/aws-sdk-go/blob/v1.42.35/aws/defaults/defaults.go#L48:L64 13 func Config() *aws.Config { return aws.NewConfig(). WithCredentials(credentials.AnonymousCredentials). WithRegion(os.Getenv("AWS_REGION")). WithHTTPClient(http.DefaultClient). WithMaxRetries(aws.UseServiceDefaultRetries). WithLogger(aws.NewDefaultLogger()). WithLogLevel(aws.LogOff). WithEndpointResolver(endpoints.DefaultResolver()) } HTTP クライアントを共有することで、コネクションの再利用による効率化が 可能に
  12. © 2022, Amazon Web Services, Inc. or its Affiliates. Service

    client によるサービス毎のクライアント 作成
  13. © 2022, Amazon Web Services, Inc. or its Affiliates. Service

    client の作成 o Service client は、AWSサービスごとに作成される。 o AWSサービスごとにクライアントを分ける理由 o AWSサービスごとにAPIエンドポイントが異なるため o AWSサービスごとにメソッドを分けられるため 15 svc := dynamodb.New(sess) サービス毎のライブラリ Session(基本設定)
  14. © 2022, Amazon Web Services, Inc. or its Affiliates. Service

    client を作成する処理の全体像 16 └── dynamodb.New() // DynamoDB 用のService Client作成を呼び出す関数 ├── Session.ClientConfig() // Sessionの設定を取得する関数 └── dynamodb.newClient() // Client作成を行う関数
  15. © 2022, Amazon Web Services, Inc. or its Affiliates. 作成される

    Service client https://github.com/aws/aws-sdk-go/blob/v1.42.35/service/dynamodb/service.go#L61:L98 17 func newClient(cfg aws.Config, handlers request.Handlers, partitionID, endpoint, signingRegion, signingName, resolvedRegion string) *DynamoDB { svc := &DynamoDB{ Client: client.New( cfg, metadata.ClientInfo{ ServiceName: ServiceName, ServiceID: ServiceID, SigningName: signingName, SigningRegion: signingRegion, PartitionID: partitionID, Endpoint: endpoint, APIVersion: "2012-08-10", ResolvedRegion: resolvedRegion, JSONVersion: "1.0", TargetPrefix: "DynamoDB_20120810", }, handlers, ), } (〜省略〜)
  16. © 2022, Amazon Web Services, Inc. or its Affiliates. Request

    による AWS APIリクエスト送信
  17. © 2022, Amazon Web Services, Inc. or its Affiliates. Request

    の利⽤例 19 result, err := svc.GetItem(&dynamodb.GetItemInput{ TableName: aws.String(tableName), Key: map[string]*dynamodb.AttributeValue{ "Year": { N: aws.String(movieYear), }, "Title": { S: aws.String(movieName), }, }, }) API リクエストを送るためのメソッド 内部で Request というコンポーネントが作成され、 API リクエストが作成・送信される API リクエスト の内容
  18. © 2022, Amazon Web Services, Inc. or its Affiliates. Request

    による AWS APIリクエスト送信の流れ 20 URL, メソッド ヘッダー ボディ URL, メソッド ① Request 作成 各種情報 ② HTTP リクエスト ヘッダー, ボディ作成 URL, メソッド ヘッダー ボディ ③ SigV4 署名付与 URL, メソッド ヘッダー ボディ ④ HTTP リクエスト 送信 (含リトライ処理)
  19. © 2022, Amazon Web Services, Inc. or its Affiliates. API

    リクエスト⽣成のイメージ 21 <材料> operation(リクエストの種類) op := &request.Operation{ Name: opGetItem, // 中身は “GetItem” という文字列 HTTPMethod: "POST", HTTPPath: "/", } params(リクエストの内容) &dynamodb.GetItemInput{ TableName: aws.String(tableName), Key: map[string]*dynamodb.AttributeValue{ "Year": { N: aws.String(movieYear), }, "Title": { S: aws.String(movieName), }, }, }
  20. © 2022, Amazon Web Services, Inc. or its Affiliates. API

    リクエスト⽣成のイメージ 22 <生成したい API リクエスト> POST / HTTP/1.1 Host: dynamodb.<region>.<domain>; Accept-Encoding: identity Content-Length: <PayloadSizeBytes> User-Agent: <UserAgentString> Content-Type: application/x-amz-json-1.0 Authorization: AWS4-HMAC-SHA256 Credential=<Credential>, SignedHeaders=<Headers>, Signature=<Signature> X-Amz-Date: <Date> X-Amz-Target: DynamoDB_20120810.GetItem { "TableName": "Movies", "Key": { "Year": { "N": "2015" }, "Title": { "S": "The Big New Movie" } } } ヘッダー等 ボディ
  21. © 2022, Amazon Web Services, Inc. or its Affiliates. API

    リクエストを送信するまでの処理リスト = Handlers 23 type Handlers struct { Validate HandlerList Build HandlerList BuildStream HandlerList Sign HandlerList Send HandlerList ValidateResponse HandlerList Unmarshal HandlerList UnmarshalStream HandlerList UnmarshalMeta HandlerList UnmarshalError HandlerList Retry HandlerList AfterRetry HandlerList CompleteAttempt HandlerList Complete HandlerList }
  22. © 2022, Amazon Web Services, Inc. or its Affiliates. API

    リクエストを送信するまでの処理の全体像 24 DynamoDB.GetItem() // GetItem リクエストの作成・送信・結果取得を行うメソッド ├── DynamoDB.GetItemRequest() // GetItem 用の Request 作成を行うメソッド │ └── DynamoDB.newRequest() // Client.NewRequest のラッパーメソッド │ └── Client.NewRequest() // 新規 Request を作成 └── Request.Send() // HTTP リクエスト作成・リクエスト送信を行うメソッド ├── Request.Sign() // HTTP リクエスト作成・署名を行うメソッド │ ├── Request.Build() // HTTP リクエストの検証・作成を行うメソッド │ │ └── Request.Handlers.Build.Run() │ │ │ // HTTP リクエスト作成を行う HandlerList の実行 │ │ └── jsonrpc.Build() // HTTP リクエストボディ・ヘッダの作成を行う関数 │ └── (★) Request.Handlers.Sign.Run() │ │ // HTTP リクエストへの署名付与を行う HandlerList の実行 │ └── Signer.signWithBody() // 署名に利用する AWS 認証情報を取得するメソッド │ └── signingCtx.build() // SigV4 署名を生成・付与するメソッド ├── Request.sendRequest() // HTTP リクエストの送信・レスポンスの受信を行うメソッド └── (★) Request.Handlers.AfterRetry.Run(r) // リトライ判定、リトライ間隔設定の実行 └── corehandlers.AfterRetryHandler // リトライ判定、リトライ間隔設定を行う Handler
  23. © 2022, Amazon Web Services, Inc. or its Affiliates. Request

    による AWS APIリクエスト送信の流れ 25 URL, メソッド ヘッダー ボディ URL, メソッド ① Request 作成 各種情報 ② HTTP リクエスト ヘッダー, ボディ作成 URL, メソッド ヘッダー ボディ ③ SigV4 署名付与 URL, メソッド ヘッダー ボディ ④ HTTP リクエスト 送信 (含リトライ処理)
  24. © 2022, Amazon Web Services, Inc. or its Affiliates. Request

    SigV4 署名付与の流れ 1. HTTP リクエストの内容 (リクエスト先、ヘッダー、ボディなど) を整形し、 SHA256 などのハッシュアルゴリズムでハッシュ値を計算 2. 1 で計算したハッシュ値とハッシュアルゴリズム名などのメタ情報を合わせ て署名⽂字列を⽣成 3. HMAC アルゴリズムを利⽤し、AWS認証情報やリージョン名などの情報を ハッシュキーとして署名⽂字列からSigV4署名を⽣成 4. HTTP リクエストヘッダもしくはクエリ⽂字列としてSigV4署名を付与 26
  25. © 2022, Amazon Web Services, Inc. or its Affiliates. SigV4署名ヘッダの例

    27 Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east- 1/iam/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5 d7
  26. © 2022, Amazon Web Services, Inc. or its Affiliates. AWS

    認証情報の取得① AWS 認証情報の取得は、SigV4の署名作成時に⾏われる https://github.com/aws/aws-sdk-go/blob/v1.42.35/aws/signer/v4/v4.go#L315:L374 28 var err error ctx.credValues, err = v4.Credentials.GetWithContext(requestContext(r)) if err != nil { return http.Header{}, err } ctx.sanitizeHostForHeader() ctx.assignAmzQueryValues() if err := ctx.build(v4.DisableHeaderHoisting); err != nil { return nil, err }
  27. © 2022, Amazon Web Services, Inc. or its Affiliates. AWS

    認証情報の取得② 29 func (c *Credentials) GetWithContext(ctx Context) (Value, error) { // Check if credentials are cached, and not expired. select { case curCreds, ok := <-c.asyncIsExpired(): if ok { return curCreds, nil } case <-ctx.Done(): return Value{}, awserr.New("RequestCanceled", "request context canceled", ctx.Err()) } resCh := c.sf.DoChan("", func() (interface{}, error) { return c.singleRetrieve(&suppressedContext{ctx}) }) select { case res := <-resCh: return res.Val.(Value), res.Err case <-ctx.Done(): return Value{}, awserr.New("RequestCanceled", "request context canceled", ctx.Err()) } } IUUQTHJUIVCDPNBXTBXTTELHPCMPCWBXTDSFEFOUJBMTDSFEFOUJBMTHP--
  28. © 2022, Amazon Web Services, Inc. or its Affiliates. API

    リクエスト送信のリトライ処理 o エクスポネンシャルバックオフ が採⽤されている o 1 回⽬の再試⾏の前に 50 ミリ秒、2 回⽬の前に 100 ミリ秒、3 回⽬の前 に 200 ミリ秒、というように指数関数的に待機時間を増やす 30
  29. © 2022, Amazon Web Services, Inc. or its Affiliates. エクスポネンシャルバックオフの実装

    (defaultRetryer) 31 // Logic to cap the retry count based on the minDelay provided actualRetryCount := int(math.Log2(float64(minDelay))) + 1 if actualRetryCount < 63-retryCount { delay = time.Duration(1<<uint64(retryCount)) * getJitterDelay(minDelay) // エクスポネンシャルバックオフ待機時間の計算 if delay > maxDelay { delay = getJitterDelay(maxDelay / 2) } } else { delay = getJitterDelay(maxDelay / 2) } return delay + initialDelay
  30. © 2020, Amazon Web Services, Inc. or its Affiliates. All

    rights reserved. AWS SDK for Go (v1) を利⽤する際の Tips
  31. © 2022, Amazon Web Services, Inc. or its Affiliates. AWS

    SDK for Go (v1) を利⽤する際の Tips 33 o Session, Service client は使い回すことが推奨される o Session を使い回すことで、HTTP Client, AWS認証情報を再利⽤できる o Service client を使い回すことで、必要なhandlersのセットを共⽤できる o EC2などで利⽤する際認証情報は⾃動で更新されるので、開発者側で気にする 必要はないです︕
  32. © 2022, Amazon Web Services, Inc. or its Affiliates. (参考資料)

    このセッションは、以下のブログ記事をもとにお話ししました。 o AWS SDK の裏側を⾒てみよう ! ~AWS SDK for Go (v1) のコードとともに (前編) - builders.flash o AWS SDK の裏側を⾒てみよう ! ~AWS SDK for Go (v1) のソースコードと 共に (後編) - builders.flash 34
  33. © 2020, Amazon Web Services, Inc. or its Affiliates. All

    rights reserved. © 2020, Amazon Web Services, Inc. or its Affiliates. All rights reserved. Thank you!