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

実例から学ぶ! AWSを活用したシステム開発の勘所

実例から学ぶ! AWSを活用したシステム開発の勘所

「Developers.IO 2022 〜技術で心を揺さぶる3日間〜」 の発表で利用した資料です

TomoyaIwata

July 21, 2022
Tweet

More Decks by TomoyaIwata

Other Decks in Technology

Transcript

  1. アジェンダ • 案件概要 • API仕様書の肥⼤化 • デプロイ速度の問題 • RDS Proxyのピン留め問題

    • X-Rayの導⼊ • ENIのDNSスロットリング問題 • ログ出⼒の改善 • まとめ
  2. 単⼀ファイルによる管理の限界 • YAMLファイルが1万⾏超 • メンテナンスの負荷が⾼い openapi: "3.0.0" info: version: 1.0.0

    title: Swagger Petstore license: name: MIT servers: - url: http://petstore.swagger.io/v1 paths: /pets: get: summary: List all pets operationId: listPets tags: - pets parameters: - name: limit in: query description: How many items to return at one time (max 100) required: false schema: type: integer format: int32 responses: '200': description: A paged array of pets headers: x-next: description: A link to the next page of responses schema: type: string content: application/json: schema: $ref: "#/components/schemas/Pets" default: description: unexpected error
  3. openapi: "3.0.0" info: version: 1.0.0 title: Swagger Petstore paths: /pets:

    $ref: ./paths/pets.yaml#/paths/~1pets /pets/{pet_id}: $ref: ./paths/pets.yaml#/paths/~1pets~1{petId} ファイル分割のアプローチ $refで参照 openapi: "3.0.0" paths: /pets: get: summary: List all pets operationId: listPets tags: - pets parameters: - name: limit
  4. ディレクトリ構成の⼀例 ├ api.yaml…⼦ファイルを読み込むメインのファイル ├ paths …パスごとにAPIをグルーピングして定義 │ ├ pets.yaml │

    └ users.yaml └ components ├ schemas.yaml…共通のスキーマを定義 └ responses.yaml…共通のレスポンスを定義
  5. Swagger UI等の環境を構築する場合 • swagger-mergerで定義ファイルのマージが可能 • ただしv1.5.4 時点では{}を含む参照がマージできない ☓ swagger-mergerでマージ不可 ◦

    swagger-mergerでマージ可 paths: /pets/{petId}: paths: /pets-petId: /pets/{pet_id}: $ref: ./paths/pets.yaml#/paths/~1pets~1{petId} /pets/{pet_id}: $ref: ./paths/pets.yaml#/paths/~1pets-petId
  6. Lambda Layers導⼊前後の⽐較 導入前 導入後 全Lambda Functionのビルド時間 100秒超 約40秒 Assetサイズ (圧縮前のファイルサイズ合計)

    約1.5G 約60M Assetサイズ (ZIPファイル1つあたりの平均) 約4.2M 約100K ビルド対象のLambda Function数:110で⽐較
  7. Lambda Layers導⼊のデメリット Layerを使わない⽅がバンドルファイルのサイズを最適化できる import { S3Client } from '@aws-sdk/client-s3’ export

    const handler = async (…略 import { SQSClient } from '@aws-sdk/client-sqs’ export const handler = async (…略
  8. Lambda Layers導⼊のデメリット Layerを使うと未使⽤のライブラリまでデプロイされる Lambda 1でのみ利⽤する 全Lambdaで利⽤ Lambda 2と3でのみ利⽤ コールドスタートに悪影響 "dependencies":

    { “@aws-sdk/client-s3”: “^3.53.1”, "@aws-sdk/client-secrets-manager": "^3.53.0", "@aws-sdk/client-sqs": "^3.53.0", "@aws-sdk/s3-request-presigner": "^3.52.0",
  9. [ 'mkdir -p /asset-output/nodejs/’, 'npm install -g [email protected] [email protected]’, …略

    'npm ci –prefix=/asset-output/nodejs/’, 'cd /asset-output/nodejs/’, 'find node_modules -type l | xargs rm -f’, 'find node_modules -type f -name *.d.ts | xargs rm -f’, 'modclean’, 'node-prune', ].join(' && '), node_modulesのサイズを最適化 READMEや型定義ファイルを削除
  10. CDKでLambda Layersを利⽤する際のポイント package-lock.jsonやyarn.lockに変更が無い限り ビルド&デプロイしない const lockFileBuffer = readFileSync(path.join(pjRootDir, 'yarn.lock')) const

    lockFileHash = createHash('sha256') lockFileHash.update(lockFileBuffer) const lockFileDigest = lockFileHash.digest('hex’) const nodeModuleLayer = new lambda.LayerVersion(this, `NodeModuleLayer${suffix}`, { code: lambda.Code.fromAsset(pjRootDir, { assetHash: lockFileDigest, assetHashType: AssetHashType.CUSTOM,
  11. キャプチャ処理はhandler内で実⾏する import {capturePromise} from 'aws-xray-sdk-core' capturePromise() export const handler =

    async ( event: APIGatewayProxyEventBase<APIGatewayEventDefaultAuthorizerContext> ): Promise<APIGatewayProxyResult> => { // ...略 } Error: Missing AWS Lambda trace data for X-Ray. Ensure Active Tracing is enabled and no subsegments are created outside the function handler.
  12. ラッパークラスにキャプチャ処理を実装 export class ExternalApiClient implements I ExternalApiClient { #captured =

    false async requestWith<Type>(options: RequestWithOptions): Promise<Type> { … if (!this.#captured) { captureHTTPsGlobal(http, true) captureHTTPsGlobal(https, true) capturePromise() this.#captured = true }
  13. テスト中にLambdaのエラーが 2022-02-18T01:27:18.747Z b9dfaf70-f7d8-465c-8a26-e2a736b4c97a ERROR Error: getaddrinfo EMFILE secretsmanager.ap-northeast-1.amazonaws.com at GetAddrInfoReqWrap.onlookup

    [as oncomplete] (dns.js:71:26) { errno: -24, code: 'EMFILE', syscall: 'getaddrinfo', hostname: 'secretsmanager.ap- northeast-1.amazonaws.com', '$metadata': { attempts: 1, totalRetryDelay: 0 } } ENIあたり1024パケット/sの上限に抵触 https://aws.amazon.com/jp/premiumsupport/knowledge-center/vpc-find-cause-of-failed-dns-queries/
  14. SecretsManagerへの過剰なアクセスが原因 外部API呼び出しの都度SecretsManagerにアクセスしていた const output = await axios .request<Type>({ headers: {‘X-Hoge-Token’:

    await this.#driver.fetchToken()}…略 async fetchToken(): Promise<string> { return (await this.#fetchSecret(process.env.HOGE_TOKEN!)) .SecretString! } async #fetchSecret(secretId: string): Promise<GetSecretValueCommandOutput> { return this.#secretsManagerClient.send( new GetSecretValueCommand({SecretId: secretId,}) )}
  15. シークレットをキャッシュして対応 static変数を使い取得済みのシークレットをキャッシュ async #fetchSecret(secretId: string): Promise<GetSecretValueCommandOutput> { if (secretId in

    SecretsStorageDriver.#cachedSecrets) { return SecretsStorageDriver.#cachedSecrets[secretId] } const secret = await this.#secretsManagerClient.send( new GetSecretValueCommand({ SecretId: secretId, }) ) SecretsStorageDriver.#cachedSecrets[secretId] = secret return secret
  16. ログのフォーマットをJSONに CloudWatch Logs Insights等のツールで容易に検索可能 { "petId": 1, "userId": 1, "message":

    "ペットの登録が完了しました", "level": "info", "timestamp": "2022-06-09T12:41:14.236+09:00", "xRayTraceId": "1-62b3cc5b-5b3366656062344734c1c0e3", "requestId": "51f3d508-2067-4417-9b34-25f40e77c186" }
  17. 構造化ログの出⼒処理 const logging = (loggingFn: (...data: any[]) => void, options:

    LoggingOptions) => { const logObj = { xRayTraceId: process.env._X_AMZN_TRACE_ID, lambdaFunction: { name: process.env.AWS_LAMBDA_FUNCTION_NAME, …略 }, ...options, } loggingFn(JSON.stringify(logObj)) } export const debug = (options: LogOptions) => { logging(console.debug, { level: 'debug', ...options }) }