Lambda | さゆフィクション http://it.kensan.net aws wordpress などなどゆるーく書いてます Sat, 21 Dec 2024 01:56:12 +0000 ja hourly 1 https://wordpress.org/?v=6.7.1 https://it.kensan.net/wp-content/uploads/2023/03/cropped-icon-32x32.png Lambda | さゆフィクション http://it.kensan.net 32 32 Laravel 11をAWS Lambdaで動くようにして簡単なAPIを作ってみる https://it.kensan.net/laravel-11-aws-lambda.html Sat, 21 Dec 2024 01:56:12 +0000 http://43.207.185.157/it/?p=2180 Laravel 11をAWS Lambdaで動くようにして簡単なAPIを作ってみます。

ちなみに私は割とPHP初心者です。

初心者ですが、これからAPI作成するならPHPが良いと思っています。

PHPが良い理由は、実装できる人が多いと思っていて、いざとなれば他の人(他の会社)に協力を仰ぎやすそうだからです。インターネット上に情報もいっぱいあるというアドバンテージもあると思います。

フレームワークは、PHPの中で一番ポピュラーという理由からLaravel!と考えています。

まずは、作成する環境について記載していきます。

 

 

作成する環境の構成

以下の構成で動かしてみます。

  • プログラム
    • Laravel
  • AWSリソース
    • API Gateway
    • Lambda

実際に、Laravel 11をAWS Lambdaで動かしてみます!!

Laravel 11をAWS Lambdaで動かしてみる

まずはLaravelプロジェクトを作成します。

Laravelプロジェクトの作成

以下のコマンドでLaravelプロジェクトを作成します。

composer create-project laravel/laravel lambda_test

プロジェクト作成ができたら、Laravelバージョンを確認します

cd lambda_test/
php artisan -V
Laravel Framework 11.36.1

上記のようにLaravelバージョンが確認できればOKです。

AWSにデプロイする準備

以下のコマンドでAWSのアクセスキーとシークレットキーを設定します

aws configure

アクセスキーとシークレットキーを設定後は、以下のような簡単なコマンドを使用して(以下はバケットの一覧を表示する例)、設定に成功しているか確認できます。

aws s3 ls

次にserverless frameworkをインストールします。

npm install -g serverless

次にbrefをインストールします。

composer require bref/bref bref/laravel-bridge --update-with-dependencies

次に serverless.yamlを生成します。

php artisan vendor:publish --tag=serverless-config

serverless.yamlのregionを以下の通り変更しておきます。(これで、デプロイ先を東京リージョンに変更しています。)

region: ap-northeast-1

serverless.yamlのruntimeも以下の通り変更しておきます。

    web:
        runtime: php-84-fpm
    artisan:
        runtime: php-84-console

 

修正後のserverless.yamlの全体は以下です。


service: laravel

provider:
    name: aws
    # The AWS region in which to deploy (us-east-1 is the default)
    region: ap-northeast-1
    # Environment variables
    environment:
        APP_ENV: production # Or use ${sls:stage} if you want the environment to match the stage
        SESSION_DRIVER: cookie # Change to database if you have set up a database

package:
    # Files and directories to exclude from deployment
    patterns:
        - '!node_modules/**'
        - '!public/storage'
        - '!resources/assets/**'
        - '!storage/**'
        - '!tests/**'
        - '!database/*.sqlite'

functions:

    # This function runs the Laravel website/API
    web:
        handler: public/index.php
        runtime: php-84-fpm
        timeout: 28 # in seconds (API Gateway has a timeout of 29 seconds)
        events:
            - httpApi: '*'

    # This function lets us run artisan commands in Lambda
    artisan:
        handler: artisan
        runtime: php-84-console
        timeout: 720 # in seconds
        # Uncomment to also run the scheduler every minute
        #events:
        #    - schedule:
        #          rate: rate(1 minute)
        #          input: '"schedule:run"'

plugins:
    # We need to include the Bref plugin
    - ./vendor/bref/bref

デプロイしてみる

以下のコマンドでデプロイできます

serverless deploy

以下が出てきた場合はLogin/Registerを選択するとWEBブラウザが開くと思うので、ログイン(アカウントを持っていない場合作成)します。


Please login/register or enter your license key: … 
❯ Login/Register
  Get A License
  Enter A License Key
  Explain Licensing Basics

デプロイができると以下のようにエンドポイントが表示されます。

endpoint: ANY - ****************

動作確認

エンドポイントにアクセスし、以下のように画面表示されればOKです。

簡単なAPIを作ってみる

以下のコマンドでコントローラを作成

php artisan make:controller ApiProductController

コントローラを以下のとおり実装


namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ApiProductController extends Controller
{
    public function index()
    {
        $products = [];
        $products['id'] = 1;
        $products['name'] = '商品名';
        return response()->json($products, 200);
    }
}

ルーティング(routes/web.php)ファイルに以下を追加

Route::get('/api', 'App\Http\Controllers\ApiProductController@index');

以下のコマンドでデプロイできます

serverless deploy

「エンドポイント/api」にアクセスして、以下のレスポンスが返って来れば成功です。


{
  "id": 1,
  "name": "商品名"
}

Laravelで作ったAPIをLambdaで動かす際に使うと良さそうAWSサービスは何か

API Gatewayは不要?

まず、Lambda関数URLがあるので、API GatewayはなくてもAPIの公開は可能で、この方がAPI Gateway分のコスト削減が可能とは思います。

しかし、API Gatewayを使用した方が、リクエストの認証(IAM、Cognito、APIキーなど)などが使える点で有利かと思います。

DBは?

RDSなら、RDS Proxy × Aurora Serverless v2という選択肢があると思います。

RDS Proxyを使うことで、データベースへの接続をプロキシすることで、データベースへの負荷を軽減できますが、8ACU分の料金がかかります。

費用を抑えたい場合、かつ同時接続数が少ない場合は、RDS ProxyなしでもOKかな。Aurora Serverless v2はゼロキャパシティのスケーリングをサポートしています。

Amazon Aurora Serverless v2 がゼロキャパシティへのスケーリングをサポート - AWS
AWS の新機能についてさらに詳しく知るには、 Amazon Aurora Serverless v2 がゼロキャパシティへのスケーリングをサポート

ユースケースによっては、Aurora Serverless v2とElastiCacheという組み合わせで、ElastiCacheでRDSの情報をキャッシュしてRDSの負荷を抑えるというのもありかと。

もしくは、DynamoDBという選択肢もあると思いますが、設計の難易度が高い…ですね。

まとめ

DBの選択肢など迷うことも多くありますが、プログラムとしてはLaravel(PHP)がいいと思っています!!

]]>
API Gateway不要!? Lambda関数URLでのAPI構築について考える https://it.kensan.net/lambdafunctionurl-api-gateway.html Sat, 25 May 2024 04:03:57 +0000 http://35.78.183.95/it/?p=1944

Lambda関数URLで、HTTPリクエストを介して直接Lambda関数を呼び出すことが可能になりました。

これまで、Lambda関数をAPI経由で実行するためにはAPI Gatewayを使用する必要がありました。しかし、Lambda関数URLの登場により、API Gatewayを使わずにLambda関数を直接実行できるようになりました。

Lambda関数URLを使うと、API Gatewayのセットアップや管理の手間を省けます。また、API Gatewayが引き起こす遅延やコストも削減できます。

ただし、API Gatewayには多くの利点もあります。

この記事では、Lambda関数URLの利用方法とその利点、注意点について詳しく解説します。Lambda関数とAPI Gatewayのどちらを選ぶか迷っている方は、ぜひ参考にしてください。

Lambda関数URLとは

Lambda関数URLは、Lambdaで提供される機能の一つで、直接HTTPリクエストを受け取ることができるエンドポイントURLをLambda関数に対して自動的に生成するものです。

この機能により、API Gatewayなどの中間サービスを使用せずに、簡単にLambda関数をウェブリクエストに対応させることができます。

主な特徴

  1. 直接アクセス可能: Lambda関数に対して直接HTTPリクエストを送信可能なURLが生成されます。
  2. 簡易な設定: API Gatewayを設定する必要がなく、簡単にHTTPエンドポイントを作成できます。
  3. セキュリティ設定: IAM(Identity and Access Management)ポリシーを使用して、アクセス制御を行うことができます。

Lambda関数URLの利点

Lambda関数URLを使用することによる利点は次のとおりです。

  1. 簡単なセットアップ: API Gatewayを使用せずにLambda関数を呼び出すため、API Gatewayのセットアップと管理の手間が省けます。Lambda関数URLは、Lambda関数を公開するための単一のエンドポイントです。
  1. 遅延の低減: API Gatewayを経由せずにLambda関数を直接呼び出すため、API Gatewayによる遅延がなくなります。これにより、リクエストとレスポンスの処理が高速化されます。
  1. コストの削減: Lambda関数URLを使用することでAPI Gatewayのコストを削減することができます。

次はAPI Gatewayについて記載していきます。

API Gatewayとは

API Gatewayは、APIの作成、デプロイ、管理を簡単に行えるツールです。API Gatewayを利用することで、RESTful APIやWebSocket APIを構築し、バックエンドサービスにアクセスするためのエンドポイントを提供することができます。

主な特徴

  1. API作成とデプロイ: APIのエンドポイントを簡単に作成し、デプロイできます。これにより、フロントエンドとバックエンドの間でデータをやり取りするためのインターフェースを構築できます。
  2. トラフィック管理: スロットリング(リクエスト制限)、キャッシング、モニタリングなどのトラフィック管理機能を提供します。
  3. セキュリティ: IAMポリシー、Cognito、APIキー、OAuth 2.0などを使った認証・認可をサポートし、APIのセキュリティを強化します。
  4. 統合: Lambda関数、EC2、DynamoDB、S3などのAWSサービス、さらにはオンプレミスのシステムとも統合できます。
  5. スケーラビリティと可用性: 自動スケーリングと高可用性が保証されており、大規模なトラフィックにも対応可能です。

API Gatewayの利点

API Gatewayの利点は次のとおりです。

  1. セキュリティ: API Gatewayは、APIの認証や承認を行うためのセキュリティ機能を提供します。APIキー、IAMロール、カスタム認証などを使用して、APIのアクセスを制御することができます。
    1. アクセス制御: API Gatewayは、APIのエンドポイントへのアクセス制御を行うための機能を提供します。IP制限、ユーザー認証、アクセスポリシーなどを使用して、APIの使用を管理することができます。これにより、APIの不正利用や過度の負荷を防ぐことができます。

    それでは、次にLambda関数とAPI GatewayでどのようにAPIを構築すれば良いか記載していきます。

      どのようにLambdaのAPIを構築すればいいか

      LambdaのAPIは

      • Lambda関数URLを使用
      • API Gatewayを使用してLambdaを実行

      などいろいろな構築方法があります。実際に構築方法を5つ紹介し、それぞれ特徴やメリットとデメリットについて記載していきます。

      方式 使用するAWSサービス 概要
      API Gateway×Lambda API Gateway

      Lambda

      最もオーソドックスな方式

      API Gatewayの利点を活かせる

      API Gatewayのタイムアウトの上限は29 秒(上限緩和申請可能)なので、29秒以上の処理はエラーとなる

      API Gateway×Step Functions×Lambda API Gateway

      Step Functions

      Lambda

      API Gatewayの利点を活かせる

      API Gatewayのタイムアウトの上限の29 秒(上限緩和申請可能)を超えた処理を実行できる

      Lambda関数URL Lambda Lambda関数URLのエンドポイントURLにアクセスすることで簡単にLambdaを実行できる

      簡単にLambda実行できるので、セキュリティ面が懸念

      タイムアウトはLambdaのタイムアウトの15分

      CloudFront×Lambda関数URL CloudFront

      Lambda

      CloudfrontのOAC を利用して Lambda関数URLを実行する

      Lambda関数URLの実行をCloudFront経由のみに絞れる(CloudFront経由以外はエラーにできる)

      CloudFrontのWAF設定等を活かせる

      タイムアウトはCloudFrontのタイムアウトの60秒(上限緩和申請可能)

      Lambda関数URL(署名付きURL化) Lambda S3の署名付きURLと同じことを、Lambdaの関数URLでやるイメージ

      署名付きURLの場合のみLambdaを実行可能

      タイムアウトはLambdaのタイムアウトの15分

      5つのAPI実行方式の詳細

      API Gateway×Lambda

      使用するAWSサービス

      API Gateway

      Lambda

      概要

      最もオーソドックスな方式

      API Gatewayの利点を活かせる

      API Gatewayのタイムアウトの上限は29 秒(上限緩和申請可能)なので、29秒以上の処理はエラーとなる

      2024.6.4に、タイムアウトの上限は、引き上げ可能となりました。詳細は以下の記事をご参照ください。

      https://it.kensan.net/it/amazon-api-gateway-limit-29.html

      メリット

      最もオーソドックスな方式のため、実装難易度が低い

      API Gatewayの利点を活かせる

      デメリット

      API Gatewayのタイムアウトの上限は29 秒(上限緩和申請可能)なので、29秒以上の処理はエラーとなる

      API Gatewayのコストがかかる

      API Gateway×Step Functions×Lambda

      使用するAWSサービス

      API Gateway

      Step Functions

      Lambda

      概要

      API Gatewayの利点を活かせる

      API Gatewayのタイムアウトの上限の29 秒(上限緩和申請可能)を超えた処理を実行できる

      メリット

      API Gatewayの利点を活かせる

      API Gatewayのタイムアウトの上限の29 秒(上限緩和申請可能)を超えた処理を実行できる。基本的にLambdaのタイムアウトの上限15分までは実行可能。

      Step Functionsの機能を活かせる。Lambdaを複数呼び出したり

      デメリット

      API Gateway/Step Funcstionsのコストがかかる

      Step Funcstionsが入る分、実装難易度が高い

      Lambda関数URL

      使用するAWSサービス

      Lambda

      概要

      Lambda関数URLのエンドポイントURLにアクセスすることで簡単にLambdaを実行できる

      簡単にLambda実行できるので、セキュリティ面が懸念

      タイムアウトはLambdaのタイムアウトの15分

      メリット

      実装難易度が低い

      API Gatewayのタイムアウトの上限の29 秒(上限緩和申請可能)を超えた処理を実行できる。Lambdaのタイムアウトの上限15分までは実行可能。

      デメリット

      セキュリティ面が懸念。独自に対策する必要がある

      CloudFront×Lambda関数URL

      使用するAWSサービス

      CloudFront

      Lambda

      概要

      CloudfrontのOAC を利用して Lambda関数URLを実行する想定

      Lambda関数URLの実行をCloudFront経由のみに絞れる(CloudFront経由以外はエラーにできる)

      CloudFrontのWAF設定等を活かせる

      タイムアウトはCloudFrontのタイムアウトの60秒(上限緩和申請可能)

      メリット

      実装難易度が低い

      CloudFrontのWAF設定等を活かせる

      デメリット

      タイムアウトはCloudFrontのタイムアウトの60秒(上限緩和申請可能)

      構築方法参照URL

      CloudfrontのOAC を利用した Lambdaの 関数URL実行を試してみた | DevelopersIO
      Cloudfront OAC が Lambda関数URLをサポート。 オリジンのLambda関数URLが第三者により直接実行される事を簡単に回避できるようになりました。

      Lambda関数URL(署名付きURL化)

      使用するAWSサービス

      Lambda

      概要

      S3の署名付きURLと同じことをLambdaの関数URLでやるイメージ

      署名付きURLの場合のみLambdaを実行可能

      タイムアウトはLambdaのタイムアウトの15分

      メリット

      実装難易度が低い

      比較的に安全にLambdaを実行できる。(署名付きURLを持っている場合のみLambdaを実行できる)

      タイムアウトはLambdaのタイムアウトの15分

      デメリット

      署名付きURLを発行する仕組みが別途必要

      構築方法参照URL

      実はLambdaの関数URLは署名付きURL化できるよ、29秒の壁も越えられるよ、という話 - Qiita
      この記事を2行でS3の署名付きURLと同じことが、Lambdaの関数URLにもできますLambdaの署名付きURLを使えば、APIGatewayの29秒タイムアウトを超えてLambdaを実行でき…

      どのサービスを使えばよいか

      29秒以内に処理が終わる場合

      以下のいずれかを選択するのが良いかと思われます。

      • API Gateway×Lambda
      • CloudFront×Lambda関数URL

      API GateWayの機能を活かしたい場合は「API Gateway×Lambda」を選択しましょ

      30秒〜60秒以内に処理が終わる場合

      実装難易度とセキュリティ面から「CloudFront×Lambda関数URL」が良いと思われます。

      1分〜15分処理に時間がかかる場合

      以下のいずれかを選択するのが良いかと思われます。

      • API Gateway×Step Functions×Lambda
      • Lambda関数URL(署名付きURL化)

      API GateWayの機能を活かしたいか、Step Functionsの実装コストをどう捉えるかを検討して、上記の中から選択するのが良いと思います。

      テスト用にとにかく簡単に動かしたい

      実装難易度の面から「Lambda関数URL」が良いと思われます。

      ]]>
      Lambdaのタイムアウト検知の仕方とタイムアウト時の対処法・調査方法 https://it.kensan.net/lambda-timedout.html Mon, 08 Jan 2024 07:10:12 +0000 http://3.113.9.194/it/?p=717 Lambdaのタイムアウトについて書いていきまーす!

      具体的には、以下の内容について、記載します。

      本記事でやること
      • Lambdaのタイムアウトの検知
        • ログからタイムアウトの有無を確認する
        • CloudWatchのメトリクスフィルタを使用してタイムアウトをメトリクス化(可視化)する方法
      • タイムアウト時の対処法

      まずタイムアウトの検知方法からみていきます。

      タイムアウトの検知方法

      ログからタイムアウトを検知可能です。

      ログを検索してタイムアウトの有無を確認する

      Lambdaタイムアウト時には「Task timed out」というフレーズがログに吐かれますので、CloudWatchLogsのログインサイトを使ってログを検索することで、タイムアウトの有無を確認できます。

      対象のロググループを指定してログインサイト画面へ行き、以下のクエリを実行することで、タイムアウトのログを検索できます。

      fields @timestamp, @message, @logStream, @log
      | filter @message like 'Task timed out'
      | sort @timestamp desc
      | limit 20

      次にメトリクスフィルタを使用して、タイムアウトをメトリクス化します!

      メトリクスフィルタを使用してタイムアウトをメトリクス化する

      メトリクスフィルタでタイムアウトをメトリクス化し、メトリクスのグラフ表示でタイムアウトを確認する方法です。

      これにより、タイムアウトの回数や頻度を可視化できます。

      メトリクスフィルターとは?という方は以下の記事をご参照ください。

      https://it.kensan.net/it/cloudwatchlogs_metrics_filter.html
      1. AWSマネージメントコンソール画面からCloudWatchダッシュボードを表示
      2. ロググループリンクを押下する
      3. メトリクスフィルタータブを開く
      4. 「メトリクスフィルターを作成」ボタンを押下
      メモリクスフィルターを作成

      メモリクスフィルターを作成

      以下の画面が表示されるので、「フィルターパターン」を入力します。

      フィルターパターンに「Task timed out」と入力します。

      フィルタパターンを設定して、Nextボタンを押下すると

      メトリクスの割り当て画面が表示されます。以下の設定をしてNextボタンを押下します。

      • フィルター名:適切なもの
      • メトリクス空間:CloudWatchのメトリクスの名前空間を指定します。メトリクスフィルターであることが分かりやすいものを入力すると良いです
      • メトリクス名:どのようなメトリクスなのか分かりやすい名前をつけると良いです。
      • メトリクス値:「1」を指定します

      Review and confirm画面が表示されるので、「メトリクスフィルターを作成」ボタンを押下します。

      AWSマネージメントコンソール画面からCloudWatchダッシュボードを表示して、すべてのメトリクスを押下して作成したメトリクスを選択するとグラフを確認できます。

      また、作成したメトリクスでアラームを設定し、エラーが起きた時にメールやSlackなどに通知をすることも可能です。

      タイムアウト時の対処法

      公式記事にトラブルシューティングの方法が記載されています。

      Lambda 関数呼び出しタイムアウトエラーのトラブルシューティング
      コードの変更をデプロイしていなくても、AWS Lambda 関数が断続的にタイムアウトします。Lambda 関数呼び出しタイムアウトの問題をトラブルシューティングおよび防止するにはどうすればよいですか?

      タイムアウト時の対処法は大きく3つと読み取れます

      3つの対処法

      AWS SDK の再試行とタイムアウト設定の見直し
      タイムアウト時間を延ばす
      メモリを増やす

      まずは、AWS SDK の再試行とタイムアウト設定の見直しについて書いていきます!

      AWS SDK の再試行とタイムアウト設定の見直し

      以下にAWS SDK の再試行のデフォルト設定が記載されています。

      AWS SDK を使用するときの Lambda 関数の再試行とタイムアウトに関する問題のトラブルシューティング
      AWS SDK を使用して AWS Lambda 関数を呼び出す際に、関数がタイムアウトするか、API リクエストが応答を停止するか、API アクションが重複します。これらの問題の解決方法を教えてください。

      これを現在のLambdaタイムアウト値と見比べ調整する感じです。

      特に、GoとJavaScript/Node.jsは、接続タイムアウトの設定がないため、注意が必要です。

      次は、タイムアウト時間を延ばす対策です。

      タイムアウト時間を延ばす

      こちらは、一旦タイムアウト値を伸ばして様子をみてみようという作戦です。

      タイムアウト時間を延ばすことで、アプリケーションログが出力され、時間を要している箇所の特定ができるといったことが期待できます。

      次はメモリを増やす対策です。

      メモリを増やす

      メモリを増やすことで

      • 呼び出し時間のレイテンシーを短縮
      • コンピューティング性能を向上

      が期待できるようです。簡単にいうと早くなると言うことですね。

      メモリを増やせば必ず解決する訳ではないので、メモリを増やしたうえで経過観察が必要となります。

      それでも解決しないとき

      Lambda Insights を有効化して、リソースの状態を確認してみるのが良いです。

      Lambda Insightsの有効化方法は以下をご参照ください

      https://it.kensan.net/it/lambda-insights-start-end.html

      X-Ray でも時間を要している箇所(ボトルネック)の確認が可能です。

      https://it.kensan.net/it/aws-lambda-x-ray-start-end.html

      Lambda InsightsとX-Rayのどちらを使えば良いかで迷ったら、以下の基準で判断します。

      • サーバリソースの状況を確認したい場合:Lambda Insights
      • 間を要している箇所(ボトルネック)を確認したい場合:X-Ray
      ]]>
      AWS Lambdaのメモリ使用率と処理速度〜メモリ使用率が100%になるとエラーになったり速度劣化したりする?〜 https://it.kensan.net/lambda-memory-over.html Tue, 02 Jan 2024 23:28:17 +0000 http://3.113.9.194/it/?p=1738 AWS Lambdaのメモリ使用率と処理速度について調べてみました。

      具体的には、以下についてLambdaを動かして試しました。

      • メモリ使用率が100%になるとエラーが起きたりするか?
      • メモリ使用率が高いと速度劣化するか?

      まず、先に調査結果を書いちゃいます。以下の通りです。

      調査結果

      ・メモリ使用率が100%になるとエラーが起きたりするか?
      →エラーにはならない!
      ・メモリ使用率が高いと速度劣化するか?
      →速度劣化する

      では早速、調査していきます!

      メモリ使用率が100%になるとエラーが起きたりするか?について調査

      調査方法

      まず調査方法についてです。

      以下の条件で調査しました。

      前提条件

      本記事では以下の条件で、調査しました。

      言語:Python 3.12

      Lambdaのタイムアウト設定:29

      Lambdaのメモリ:128MB

      調査で使用したソースコード

      以下のソースを使用して調査しました。

      import sys
      import time
      def lambda_handler(event, context):
          mem_test = []
          start = time.perf_counter()
          for i in range(5000000):
              mem_test.append(str(i))
              if i % 50000 == 0:
                  print ('num:'+str(i))
                  end = time.perf_counter()
                  print(f"{end - start:.3f} s.")
                  start = time.perf_counter()

      ざっくり、以下のような処理のプログラムです。

      •  500万ループの中で、変数:mem_testに要素を追加
        • 要素を追加してメモリ使用量をあげる
        • 5万件おきにログ出力して、実行速度をログ出力

      実行してみる

      以下の実行結果になりました。

      Task timed out after 29.55 seconds
      Duration: 29548.53 ms Billed Duration: 29000 ms Memory Size: 128 MB Max Memory Used: 128 MB Init Duration: 79.60 ms

      調査結果

      処理がLambdaのタイムアウト時間以内に終わらずにエラーとなっていますが、メモリ全てを使ってもエラーにはならないことがわかりました。

      以下の通り、ループを200万に減らして実行すると、タイムアウトせず、正常に処理終了しました。

      <ソースコード>

      import sys
      import time
      def lambda_handler(event, context):
          mem_test = []
          start = time.perf_counter()
          for i in range(2000000):
              mem_test.append(str(i))
              if i % 50000 == 0:
                  print ('num:'+str(i))
                  end = time.perf_counter()
                  print(f"{end - start:.3f} s.")
                  start = time.perf_counter()

      <実行結果>

      Duration: 9003.65 ms Billed Duration: 9004 ms Memory Size: 128 MB Max Memory Used: 128 MB Init Duration: 116.19 ms

       

      次は、メモリ使用率が高いと速度劣化するか?についてみていきます。

      メモリ使用率が高いと速度劣化するか?

      上記の「メモリ使用率が100%になるとエラーが起きたりするか?」と同様の前提条件で、確認していきます。

      調査①メモリ解放せずに500万ループする

      以下のソースで確認していきます。

      import sys
      import time
      def lambda_handler(event, context):
          mem_test = []
          start = time.perf_counter()
          for i in range(5000000):
              mem_test.append(str(i))
              if i % 50000 == 0:
                  print ('num:'+str(i))
                  end = time.perf_counter()
                  print(f"{end - start:.3f} s.")
                  start = time.perf_counter()

      上記の「メモリ使用率が100%になるとエラーが起きたりするか?」と同様ですが、ざっくり、以下のような処理のプログラムです。

      •  500万ループの中で、変数:mem_testに要素を追加
        • 要素を追加してメモリ使用量をあげる
        • 5万件おきにログ出力して、実行速度をログ出力

      調査①実行結果

      途中でタイムアウトしたが、5万件毎の処理速度は0.220 s程度。メモリは限界まで(128MBまで)使っている。

      Task timed out after 31.03 seconds
      Duration: 31029.20 ms Billed Duration: 29000 ms Memory Size: 128 MB Max Memory Used: 128 MB Init Duration: 81.84 ms

      調査②メモリ解放しつつに500万ループする

      以下のソースコードで調査します。

      調査①との違いは、最後の行で「mem_test = []」を使い、5万件毎に初期化をして、メモリ解放している点です。

      import sys
      import time
      def lambda_handler(event, context):
          mem_test = []
          start = time.perf_counter()
          for i in range(5000000):
              mem_test.append(str(i))
              if i % 50000 == 0:
                  print ('num:'+str(i))
                  end = time.perf_counter()
                  print(f"{end - start:.3f} s.")
                  start = time.perf_counter()
                  mem_test = []

      調査②実行結果

      途中でタイムアウトせず、5万件毎の処理速度は0.161s程度。メモリは36MBまで使っている。

      Duration: 18596.18 ms Billed Duration: 18597 ms Memory Size: 128 MB Max Memory Used: 36 MB Init Duration: 83.56 ms

       

      調査結果

      適切にメモリ解放する方が、メモリ解放処理の時間を差し引いても早いことがわかりました。

      まとめ

      以下の結果となりました〜

      調査結果まとめ

      ・メモリ使用率が100%になるとエラーが起きたりするか?
      →エラーにはならない!
      ・メモリ使用率が高いと速度劣化するか?
      →速度劣化する

      ]]>
      API Gateway(REST API)とLambda(Python)でBOM付きUTF8なCSVをレスポンスする方法 https://it.kensan.net/api-gateway-lambdapython-bom-utf8-csv.html Thu, 19 Oct 2023 18:51:22 +0000 http://3.113.9.194/it/?p=1741 API Gateway(REST API)とLambda(Python)の構成で、LambdaでBOM付きUTF8のCSVを作成して、API Gateway経由でCSVをレスポンスする方法を記載します。

      BOM付きUTF8とすることで外字が文字化けしないCSVをAPI Gateway経由でダウンロードできます。

      やること

      AWSの構成はAPI GatewayとLambda
      使用する言語はPython
      外字が文字化けしないCSVをダンロードできるようにする
      →BOM付きUTF8とすることで文字化け対策ができます。

      Lambdaの作成

      以下のソースコードでLambdaを作成します。

      
      
      def lambda_handler(event, context):
          body_str = """column1,column2
      "㐂","昱"
      """
          return {
              'statusCode': 200,
              'isBase64Encoded': True,
              'headers': {
                  'Access-Control-Allow-Origin': '*',
                  'Access-Control-Allow-Credentials': True,
                  'Content-Type': "application/vnd.ms-excel",
                  'Content-disposition': "attachment; filename=hoge.csv" # CSVファイル名を指定
              },
              'body': f'\ufeff{body_str}' # BOMを設定
          }
      
      

      「body_str」がレスポンスするCSVの中身になりますので適宜調整します。

      次はAPI Gatewayです

      API Gatewayの設定

      REST APIを新規作成します。特に気にする設定項目はないので、とにかくREST APIを作成です。

      次にメソッドを追加します。このとき、以下の画面のように「Lambda プロキシ統合の使用」にチェックを入れておきます。

      API-Gatewayメソッド追加

      API-Gatewayメソッド追加

      Lambda プロキシ統合の使用」にチェックを入れないと、Lambdaで返却したJSONがそのままレスポンスボディとして返却され、CSVではなく、ヘッダ情報を含んだJSONがレスポンスされてしまう現象にハマります。

      最後にAPIをデプロイします。

      動作確認

      API GatewayのステージからURLを確認し、ブラウザでアクセスします。

      この時、メソッドやパスは、作成したAPI Gatewayに沿ったものを設定する必要があります。

      URLにアクセスするとCSVダウンロードができ、文字化けもしていないかと思います。

      まとめ

      API Gateway(REST API)とLambda(Python)の構成で、CSVダウンロードできました!

      Lambdaにはリクエストとレスポンスの上限サイズとして、それぞれ 6 MBが上限という制限があります。6MB以上のCSVはLambdaではレスポンスできないということです。

      大きいCSVファイルを扱うには、LambdaでCSVを作成して、S3に保存して、ユーザーにはS3のデータをダウンロードしてもらった方が良さそうですね。

      ]]>
      AWS Lambdaは1秒間にいくつまで数えられるか-メモリ毎の性能調査 https://it.kensan.net/aws-lambda%e3%81%af1%e7%a7%92%e9%96%93%e3%81%ab%e3%81%84%e3%81%8f%e3%81%a4%e3%81%be%e3%81%a7%e6%95%b0%e3%81%88%e3%82%89%e3%82%8c%e3%82%8b%e3%81%8b.html Sat, 19 Aug 2023 02:57:18 +0000 http://3.113.9.194/it/?p=1718 AWS Lambdaは1秒間にいくつまで数えられるか調べてみました。

      前提として、メモリと処理性能と料金は比例しています。

      メモリを倍にすれば、CPUの処理性能と料金は倍になるということです。

      本記事では、上記の前提が本当なの?を、AWS Lambdaは1秒間にいくつまで数えられるか確認するプログラムを作成して、メモリ量を変更してLambda実行して検証していきます。

      まずは、前提としている「メモリと処理性能と料金は比例しています」についてどこからきているか記載していきます。

      前提知識

      まずは、前提知識です。以下の点についてみていきます。

      • 料金に関する前提知識
      • 設定・性能に関する前提知識

      料金に関する前提知識

      メモリ (MB) 1 ミリ秒あたりの料金
      128 0.0000000021USD
      512 0.0000000083USD
      1,024 0.0000000167USD
      料金 - AWS Lambda |AWS
      AWS Lambda では、使用した分の料金が発生します。料金は、関数に対するリクエストの数とコードの実行時間に応じて請求されます。
      料金に関する前提知識

      メモリと料金は比例している
      →メモリを倍にすれば、1 ミリ秒あたりの料金も倍になる。

       

      設定・性能に関する前提知識

      シンプルな Lambda 関数の場合にのみ 128 MB を選択するのがベストプラクティスです。

      メモリの量によって、関数で使用できる仮想 CPU の量も決まります。メモリを追加すると、それに比例して CPU の量が増え、それによって使用可能な計算能力も増加します。

      メモリと計算能力は AWS Lambda のコストにどのように影響しますか?
      メモリと計算能力が AWS Lambda のコストにどのように影響するかを把握したいと考えています。
      設定・性能に関する前提知識

      シンプルな Lambda 関数の場合はメモリを128 MBに設定することがベストプラクティス
      メモリの量に応じて、CPUの計算能力も増える

      では検証していきます。

      検証条件

      まずは検証条件から記載していきます。

      CPU

      CPUアーキテクチャは「x86_64」で検証しました。

      言語と検証用プログラム

      次に言語と検証用プログラムについてです。

      言語

      言語は「Python 3.11」を使用しました。

      検証用プログラム

      検証用に使用したプログラムは以下のものとなります。

      import os
      
      def lambda_handler(event, context):
          os.system('cat /proc/cpuinfo')   # CPUの確認
          cnt = 0
          while True:   # タイムアウトするまで、ひたすらカウントアップ
              if cnt % 1000 == 0:
                  print(cnt)
              cnt += 1

      タイムアウト設定

      タイムアウトは1秒に設定

      検証結果

      検証結果は以下の通りです。

      • CPUは全ての結果において「Intel(R) Xeon(R) Processor @ 2.50GHz」が使用されている
      • 多少ばらつきはあるが、メモリを増やすと、処理できる件数(CPU性能)もアップする
        • メモリを倍にすれば、処理速度も上がるので、料金が倍になることはなさそう
          • プログラム自体の作りによっても結果は変わってくると思うので断言できず…
      メモリ カウントアップできた数 CPU 128MBをベースとして、メモリは何倍か 128MBをベースとして、何倍処理できたか
      128MB 638000 Intel(R) Xeon(R) Processor @ 2.50GHz
      512MB 2625000 Intel(R) Xeon(R) Processor @ 2.50GHz 4.00倍 4.15倍
      1GB 4192000 Intel(R) Xeon(R) Processor @ 2.50GHz 7.81倍 6.57倍
      1.5GB 5738000 Intel(R) Xeon(R) Processor @ 2.50GHz 11.71倍 8.99倍
      2GB 7612000 Intel(R) Xeon(R) Processor @ 2.50GHz 15.63倍 11.93倍
      2.5GB 10157000 Intel(R) Xeon(R) Processor @ 2.50GHz 19.53倍 15.92倍

      まとめ

      「メモリと処理性能と料金は比例しています」が本当?を調べました。

      結果は、本当でした。

      まとめ

      多少ばらつきはあるが、メモリを増やすと、処理できる件数(CPU性能)もアップする(メモリを倍にすれば、処理速度も上がるので、料金が倍になることはなさそう)
      →プログラム自体の作りによっても結果は変わってくると思うので断言できず…

      CPUはメモリ量に関係なく「Intel(R) Xeon(R) Processor @ 2.50GHz」が使用されている

      ]]>
      Lambda関数URLにCloudFrontとAWS WAFを使ってIP制限をかける方法 https://it.kensan.net/lambda-function-url-ip-limit.html Wed, 29 Mar 2023 21:43:40 +0000 http://3.113.9.194/it/?p=633 Lambda関数URLにIP制限をかけて公開する方法を記載します。

      • Lambda関数URLへのアクセスはCloudFront経由のみに絞る
      • AWS WAFを使用してCloudFrontへのアクセスにIP制限をかける

      こんな感じのことをやっていきます。

      まず、「Lambda関数URLへのアクセスはCloudFront経由のみに絞る」については以下の記事を参考に、Lambda関数URLへのアクセスをCloudFrontのみに絞ることができます。

      https://it.kensan.net/it/lambda-function-url-safety.html

      本記事では、上記記事に記載の「Lambda関数URLへのアクセスはCloudFront経由のみに絞る」を作成済みの状態である前提で記載していきます。

      Lambda関数URLにIP制限をかける

      AWS WAFの作成

      1. AWSマネジメントコンソールからWAF & Shieldを選択します。
      2. 「Create web ACL」を選択します。
      3.  以下の設定を行い「Next」で次へ進みます。
        • Web ACL details
          • Resource type :「CloudFront distributions」を指定
          • Name:適切に設定
        • Associated AWS resources
          • 作成したCloudFrontを追加
      4. 以下の設定でAdd rules and rule groupsの設定を行います。
        • Rules:指定なし
        • Default action:「Block」を選択
      5. 「Next」で次へ進み、ACLを作成します

        WebACL でIP 制限ルールをかける

        IP set を作成する

        1. 「Create IP set」を押します。
        2. 以下、IP set detailsの設定を行います
          • IP set name:適切に設定
          • IP addresses:許可するIPアドレスを指定(例: 10.0.0.0/32)
        3. 「Create IP set」を押下し作成します。

          ルールを設定する

          1. 作成したWebACLを選択、Rulesタブを選択します。
          2. 「Add rules」の「Add my own rules and rule groups」を選択します。
          3. 以下、Ruleの設定を行います。
            • Rule type:「Rule builder」を選択
            • Rule Name:適切に設定
            • If a request:「matches the statement」を選択
            • Statement を設定する。
              • Inspect:「Original from an IP address in」
              • IP set:先ほど作成したIP setを選択
            • IP address to use as the originating address:「Source IP address」を選択
            • ActionのAction:「Allow」を選択
          4. 「Add Rule」でルールを追加する

          動作確認

          以下の2点について動作確認できればOKです。

          •  許可したIPアドレスでのアクセス
            • 「”Hello from Lambda!”」が表示されればOK
          • 許可していないIPアドレすでのアクセス
            • 403エラーが返ってくればOK

          まとめ

          Lambda関数URLにIP制限をかけて、安全にLambda関数URLを使う方法を記載しました。

          IP制限は以下のようなケースで使用できそうですね。

          • 開発環境でLambda関数URLを一般公開していない場合
          • 本番環境で特定のIPから攻撃を受けた場合

          ↓Lambda関数URLの公式記事です↓

          Lambda 関数 URL - AWS Lambda
          関数 URL を使用して、Lambda 関数に専用の HTTP エンドポイントを追加します。
          ]]>
          Lambda関数URLの使い方-CloudFrontを使って安全にLambda関数URLを使いましょ! https://it.kensan.net/lambda-function-url-safety.html Wed, 29 Mar 2023 15:21:28 +0000 http://3.113.9.194/it/?p=551 Lambda関数URLという便利機能が公開されましたが、

          • Lamdba関数URLって使う上でのリスクないの?
          • Lambda関数URLを安全に使うには?

          など、リスクや安全な使い方に関する疑問がありますが、関連する記事はあまり見かけませんので、この記事で記載します。

          結論としては、CloudFrontを使ってLambda関数URLを安全に使いましょ!ということです。実装方法が早く知りたい場合は「CloudFront経由でLambda関数URLを使用する」をご参照くださいー。

          まず、Lambda関数URLとは?について記載していきます

          Lambda関数URLとは

          Lambda関数をHTTPS経由で実行するには、Lambdaの前段にAPI Gatewayを設置する必要がありました。 

          Lambda関数URLというものが公開され、この機能をONにするだけでLambda関数をHTTPS公開できるようになりました。

          API Gatewayを使わずにLambda単体でAPI(HTTPS)公開できるようになったということです。

          ↓公式記事ですー↓

          Lambda 関数 URL - AWS Lambda
          関数 URL を使用して、Lambda 関数に専用の HTTP エンドポイントを追加します。

          Lambda関数の使い道・どんな用途で使えるか

          • WebAPIとしての利用
            • API GateWayを使用せずに直接Lambdaを呼び出す
              • Lambda関数URLの認証タイプでAWS_IAMを使い、CloudFront経由にアクセスを絞る
          • 非同期処理としての利用(15分以内に終わる処理限定)
            • Lambdaに非同期処理を実装しておく
              • ECS/EC2などのWEBサーバで発生した非同期処理はLambdaで実行するようにしておく
                • Lambda関数URLの認証タイプでAWS_IAMにしておき、WEBサーバ以外のリクエストは受ける気ないようにしておく

          Lambda関数URLを使うメリット

          WebAPIとして使う場合のメリットを記載します。

          まずタイムアウト面についてです。

          API Gateway + Lambdaの構成の場合、タイムアウトの上限が29秒という制限がありました。

          これがLambda関数URLではLambda側のタイムアウト上限の15分になります。

          次に手軽さです。

          API Gateway + Lambdaの構成の場合、API Gatewayの知識・ノウハウが必要でしたが、Lambda関数URLではボタン1つで機能をHTTPSで公開できます。

          Lambda関数URLの使い方・設定について

          Lambda関数URLの危険な使い方と安全な使い方について記載します。

          安全な使い方については、CloudFront経由でLambda関数URLにアクセスできるようにしておき、攻撃された場合にはWAFを設定しようといった内容になります。

          まず、Lambda関数URLの危険な使い方についてみてみます。

          Lambda関数URLの危険な使い方について

          Lambda関数URL設定画面で認証タイプをNONEに設定することです。

          Lambda関数URL 認証タイプNONE

          Lambda関数URL 認証タイプNONE

          認証タイプNONEで公開すると攻撃を受けた時にもアクセスを遮断することができず、常にフルオープンとなってしまい危険です。

          ちなみに、画面上に以下の文言が表示されます。(上のキャプチャは少し古いので文言違いますが、最新ですと以下の文言となります)

          すべてのユーザーに公開されます」がポイントですね。しかもWAFも設定できないので、この使い方は危険と考えています。

          認証タイプの NONE を選択すると、Lambda によって以下のリソースベースのポリシーが自動的に作成され、関数にアタッチされます。このポリシーにより、関数が関数 URL を持つすべてのユーザーに公開されます。後でポリシーを編集できます。認証された IAM ユーザーとロールへのアクセスを制限するには、認証タイプの AWS_IAM を選択します。

          Lambda関数URLの安全な使い方について

          Lambda関数URL設定画面で認証タイプをAWS_IAMに設定することです。

          Lambda関数URL 認証タイプAWS_IAM

          Lambda関数URL 認証タイプAWS_IAM

          AWS_IAMに設定すると直接関数URLにアクセスできなくなります。

          関数URLにアクセスできないままでは使えないので、CloudFront経由でアクセスできるようにします。

          CloudFront経由にしただけではIAMの権限がついていないので、Lambda@Edgeを使ってLambda の IAM ロールで関数URLにアクセスするようにします。

          以下のような流れでアクセスするイメージです。

          CloudFront→Lambda@Edge(IAMロールを付与)→Lambda関数URL

          これでCloudFront経由でしかアクセスできなくなります。

          CloudFrontにはWAFを設定できるため、攻撃を受けた際も通信を遮断することが可能です。

          危険な使い方をしてはいけないのか

          危険な使い方をしても良いと思いますが、リスクを知った上で認証タイプを設定しましょ!

          次にCloudFrontを使って関数URLを使用する実際の手順を記載します

          CloudFront経由でLambda関数URLを使用する

          まずはLambda関数URLを使用できるようにします

          関数の作成ボタンから以下の設定で関数を作成します。

          • 関数名:適切なもの
          • ランタイム:Node.js 18.x
          • 詳細設定->関数URLの有効化にチェックを入れる
            • 認証タイプはAWS_IAMにチェックを入れる

          ソースコードは以下のコードを設定し、デプロイします。

          (40秒スリープしてからレスポンスを返すプログラムです)

          
          const sleep = (m) => {
            return new Promise((resolve) => setTimeout(resolve, m));
          };
          export const handler = async(event) => {
              // TODO implement
              await sleep(40000);
              const response = {
                  statusCode: 200,
                  body: JSON.stringify('Hello from Lambda!'),
              };
              return response;
          };
          
          
          

          作成した関数のタイムアウト値は1分に変更しておきます

          次にCloudFrontを作成します

          ディストリビューションを作成ボタンを押下して以下の設定で作成します。

          • オリジンドメイン:Lambda関数URLを貼り付ける
          • 追加設定の「レスポンスタイムアウト」と「キープアライブタイムアウト」を60に設定
          • 上記以外はデフォルトのままとします

          次にLambda@edgeを作成します

          関数の作成ボタンから以下の設定で関数を作成します。

          • 関数名:適切なもの
          • ランタイム:Python 3.9
          • 上記以外はデフォルトのままとします

          ソースコードは以下の通りです。

          Sigv4を使って署名してあげます。

          
          
          import boto3
          from botocore.auth import SigV4Auth
          from botocore.awsrequest import AWSRequest
          import urllib
          import base64
          
          session = boto3.Session()
          CREDENTIALS = session.get_credentials()
          creds = CREDENTIALS.get_frozen_credentials()
          
          def lambda_handler(event, context):
              request = event['Records'][0]['cf']['request']
              headers = request['headers']
              url = f"https://{headers['host'][0]['value']}{request['uri']}"
              method = request['method']
              data = base64.b64decode(request['body']['data'])
              headers = {v[0]['key']:v[0]['value'] for k,v in headers.items()}
              headers.pop('X-Forwarded-For')
              url_segments = urllib.parse.urlparse(url).netloc.split('.')
              region = url_segments[2]
              req = AWSRequest(method=method, url=url, params=None, headers=headers, data=data)
              SigV4Auth(creds, 'lambda', region).add_auth(req)
              signed_headers=dict(req.headers.items())
              request['headers'] = { k.lower():[{'key':k,'value':v}] for k,v in signed_headers.items() }
          
              return request
          
          

          Sigv4(AWS 署名バージョン 4)とは

          Sigv4(AWS 署名バージョン 4)は、HTTPリクエストをAWSサービスに行う際の認証情報です。HTTPのホスト名やアクセスパス、クエリ、ヘッダーなどの情報とIAMのアクセスキーを元に署名を作成し、それを認証情報としてリクエストに付加して送信します。

          以下、公式ページと公式ページの引用です。

          AWS API リクエストの署名 - AWS Identity and Access Management
          AWS API リクエストに署名する方法はこちら。

          署名バージョン 4 は、AWS 署名プロトコルです。AWS は、マルチリージョン API リクエストの署名をサポートする署名バージョン 4A という拡張機能もサポートしています。詳細については、GitHub で「sigv4a-signing-example」プロジェクトを参照してください。

          次にLambdaのロールの信頼関係を以下のように編集します。

          
          {
              "Version": "2012-10-17",
              "Statement": [
                  {
                      "Effect": "Allow",
                      "Principal": {
                          "Service": [
                              "lambda.amazonaws.com",
                              "edgelambda.amazonaws.com"
                          ]
                      },
                      "Action": "sts:AssumeRole"
                  }
              ]
          }
          

          次に以下の設定でトリガーを追加します。

          CloudFrontトリガー設定

          CloudFrontトリガー設定

          次に関数URLを有効にしたLambda関数にアクセス権限を追加します

          アクセス権限を追加ボタンを押下し、以下の設定でアクセス権限を追加します。

          • ステートメント ID:適切なもの
          • プリンシパル:Lambda@EdgeのロールのARN
          Lambda関数アクセス権追加

          Lambda関数アクセス権追加

           動作確認

          ディストリビューションドメイン名でアクセスして、

          “Hello from Lambda!”

          が表示されればOKです。

          まとめ

          「CloudFront経由でLambda関数URLにアクセスできるようにする」方法で、Lambda関数URLの安全な使い方について紹介させていただきました。

          初回の設定は少し面倒ですが、Lambda@Edgeは使い回しできますので、関数を複数公開する場合は徐々に楽になっていくかと思います。

          CloudFrontへのWAFの設定は以下の記事をご参照ください

          https://it.kensan.net/it/lambda-function-url-ip-limit.html
          ]]>
          Lambda関数URLでAWS WAFは使えないの?と疑問に思ったので考えてみた https://it.kensan.net/lambda_aws_waf.html Wed, 29 Mar 2023 09:27:05 +0000 http://3.113.9.194/it/?p=1071 Lambda関数URLという便利機能が公開されましたが、

          • Lamdba関数URLって使う上でのリスクないの?
          • Lambda関数URLを安全に使うには?

          など、使う際のリスクや安全な使い方に関する疑問が浮かんできましたが、関連する記事を見つけられなかったので、考えてみることにしました。

          以下のように考えました。私の頭の中です。(私の頭の中には、おじさんとお姉さんがいます)

          攻撃された時の対処法がないから不安なの?

          うん!攻撃された時、何もできないけど大丈夫?

          大丈夫じゃな気がするけど

          じゃ、AWS WAFを使えれば安心かな?

          そうだね。でもWAFは使えないようですよ〜

          確かに…

          じゃあ、CloudFront経由でLambda関数URLにアクセスするよう設定すれば良いよ!CloudFrontにWAF設定する感じ!

          そうだね。でもLambda関数URLに直接アクセスされたらどうするの?

          認証タイプ:AWS_IAMにすればいいよ!

          CloudFront経由の場合は、Lambda@Edge使ってSigv4署名すれば、

          CloudFront経由のみLambda関数URL実行できるじゃない?

          なるほど、やってみますか

          要するに、以下のようにLambda関数URLを呼び出すイメージとなります

          CloudFront→Lambda@Edge(Sigv4)→Lambda関数URL

          これでCloudFront経由でしかLambda関数URLにアクセスできなくなります。

          CloudFrontにはWAFを設定できるため、攻撃を受けた際も通信を遮断することなどが可能!という考えです。

           

          本題に入る前に、Lambda関数URLとは?について記載していきます。

          Lambda関数URLとは

          Lambda関数をHTTPS経由で実行するには、Lambdaの前段にAPI Gatewayを設置する必要がありました。 

          Lambda関数URLというものが公開され、この機能をONにするだけでLambda関数をHTTPS公開できるようになりました。

          API Gatewayを使わずにLambda単体でAPI(HTTPS)公開できるようになったということです。

          ↓公式記事ですー↓

          Lambda 関数 URL - AWS Lambda
          関数 URL を使用して、Lambda 関数に専用の HTTP エンドポイントを追加します。

          Lambda関数URLの使い道・どんな用途で使えるか

          • WebAPIとしての利用
            • API GateWayを使用せずに直接Lambdaを呼び出す
              • Lambda関数URLの認証タイプでAWS_IAMを使い、CloudFront経由にアクセスを絞る
                • CloudFrontではWAFを使えるので、Lambda関数URLを安全に使うことができる
                • 本記事ではこのケースの実装方法を記載しています。
          • 非同期処理としての利用(15分以内に終わる処理限定)
            • Lambdaに非同期処理を実装しておく
              • ECS/EC2などのWEBサーバで発生した非同期処理はLambdaで実行するようにしておく
                • Lambda関数URLの認証タイプでAWS_IAMにしておき、WEBサーバ以外のリクエストは受ける気ないようにしておく

          CloudFront経由でのみLambda関数URLにアクセスできるように実装する

          以下の流れで、Lambda関数URLにアクセスするよう実装していきます。

          CloudFront→Lambda@Edge(Sigv4)→Lambda関数URLの実装

          実装イメージは以下の通りです。

          実装イメージ

          ①Lambda関数URL設定画面で認証タイプをAWS_IAMに設定します。AWS_IAMに設定すると直接関数URLにアクセスできなくなります。
          ②CloudFrontを作成し、オリジンをLambda関数URLに設定します。
          ③CloudFront経由にしただけではIAMの権限がついていないので、Lambda@Edgeを使ってSigv4署名してアクセスできるようにします

          これで以下の構成の実装ができるということです!

          CloudFront→Lambda@Edge(Sigv4)→Lambda関数URL

          まずはLambda関数URLを使用できるようにします

          関数の作成ボタンから以下の設定で関数を作成します。

          • 関数名:適切なもの
          • ランタイム:Node.js 18.x
          • 詳細設定->関数URLの有効化にチェックを入れる
            • 認証タイプはAWS_IAMにチェックを入れる
          Lambda関数URL 認証タイプAWS_IAM

          Lambda関数URL 認証タイプAWS_IAM

          ソースコードは以下のコードを設定し、デプロイします。

          (40秒スリープしてからレスポンスを返すプログラムです)

          
          const sleep = (m) => {
            return new Promise((resolve) => setTimeout(resolve, m));
          };
          export const handler = async(event) => {
              // TODO implement
              await sleep(40000);
              const response = {
                  statusCode: 200,
                  body: JSON.stringify('Hello from Lambda!'),
              };
              return response;
          };
          
          
          

          作成した関数のタイムアウト値は1分に変更しておきます

          次にCloudFrontを作成します

          ディストリビューションを作成ボタンを押下して以下の設定で作成します。

          • オリジンドメイン:Lambda関数URLを貼り付ける
          • 追加設定の「レスポンスタイムアウト」と「キープアライブタイムアウト」を60に設定
          • 上記以外はデフォルトのままとします

          次にLambda@edgeを作成します

          関数の作成ボタンから以下の設定で関数を作成します。

          • 関数名:適切なもの
          • ランタイム:Python 3.9
          • 上記以外はデフォルトのままとします

          ソースコードは以下の通りです。

          Sigv4を使って署名してあげます。

          
          
          import boto3
          from botocore.auth import SigV4Auth
          from botocore.awsrequest import AWSRequest
          import urllib
          import base64
          
          session = boto3.Session()
          CREDENTIALS = session.get_credentials()
          creds = CREDENTIALS.get_frozen_credentials()
          
          def lambda_handler(event, context):
              request = event['Records'][0]['cf']['request']
              headers = request['headers']
              url = f"https://{headers['host'][0]['value']}{request['uri']}"
              method = request['method']
              data = base64.b64decode(request['body']['data'])
              headers = {v[0]['key']:v[0]['value'] for k,v in headers.items()}
              headers.pop('X-Forwarded-For')
              url_segments = urllib.parse.urlparse(url).netloc.split('.')
              region = url_segments[2]
              req = AWSRequest(method=method, url=url, params=None, headers=headers, data=data)
              SigV4Auth(creds, 'lambda', region).add_auth(req)
              signed_headers=dict(req.headers.items())
              request['headers'] = { k.lower():[{'key':k,'value':v}] for k,v in signed_headers.items() }
          
              return request
          
          

          Sigv4(AWS 署名バージョン 4)とは

          Sigv4(AWS 署名バージョン 4)は、HTTPリクエストをAWSサービスに行う際の認証情報です。HTTPのホスト名やアクセスパス、クエリ、ヘッダーなどの情報とIAMのアクセスキーを元に署名を作成し、それを認証情報としてリクエストに付加して送信します。

          以下、公式ページと公式ページの引用です。

          AWS API リクエストの署名 - AWS Identity and Access Management
          AWS API リクエストに署名する方法はこちら。

          署名バージョン 4 は、AWS 署名プロトコルです。AWS は、マルチリージョン API リクエストの署名をサポートする署名バージョン 4A という拡張機能もサポートしています。詳細については、GitHub で「sigv4a-signing-example」プロジェクトを参照してください。

          次にLambdaのロールの信頼関係を以下のように編集します。

          
          {
              "Version": "2012-10-17",
              "Statement": [
                  {
                      "Effect": "Allow",
                      "Principal": {
                          "Service": [
                              "lambda.amazonaws.com",
                              "edgelambda.amazonaws.com"
                          ]
                      },
                      "Action": "sts:AssumeRole"
                  }
              ]
          }
          

          次に以下の設定でトリガーを追加します。

          CloudFrontトリガー設定

          CloudFrontトリガー設定

          次に関数URLを有効にしたLambda関数にアクセス権限を追加します

          アクセス権限を追加ボタンを押下し、以下の設定でアクセス権限を追加します。

          • ステートメント ID:適切なもの
          • プリンシパル:Lambda@EdgeのロールのARN
          Lambda関数アクセス権追加

          Lambda関数アクセス権追加

           動作確認

          ディストリビューションドメイン名でブラウザからアクセスして、

          “Hello from Lambda!”

          が表示されればOKです。

          ついでに、Lambda関数URLへ直接アクセスできないことを確認しましょ!

          Lambda関数URLにブラウザでアクセスし、

          “Hello from Lambda!”

          が表示されなければOKです。

          これで、以下の実装ができました!

          CloudFront→Lambda@Edge(Sigv4)→Lambda関数URL

          次にWAFでIP制限をかけてみます。

          WAFの設定(Lambda関数URLにIP制限をかける)

          AWS WAFの作成

          1. AWSマネジメントコンソールからWAF & Shieldを選択します。
          2. 「Create web ACL」を選択します。
          3.  以下の設定を行い「Next」で次へ進みます。
            • Web ACL details
              • Resource type :「CloudFront distributions」を指定
              • Name:適切に設定
            • Associated AWS resources
              • 作成したCloudFrontを追加
          4. 以下の設定でAdd rules and rule groupsの設定を行います。
            • Rules:指定なし
            • Default action:「Block」を選択
          5. 「Next」で次へ進み、ACLを作成します

            WebACL でIP 制限ルールをかける

            IP set を作成する

            1. 「Create IP set」を押します。
            2. 以下、IP set detailsの設定を行います
              • IP set name:適切に設定
              • IP addresses:許可するIPアドレスを指定(例: 10.0.0.0/32)
            3. 「Create IP set」を押下し作成します。

              ルールを設定する

              1. 作成したWebACLを選択、Rulesタブを選択します。
              2. 「Add rules」の「Add my own rules and rule groups」を選択します。
              3. 以下、Ruleの設定を行います。
                • Rule type:「Rule builder」を選択
                • Rule Name:適切に設定
                • If a request:「matches the statement」を選択
                • Statement を設定する。
                  • Inspect:「Original from an IP address in」
                  • IP set:先ほど作成したIP setを選択
                • IP address to use as the originating address:「Source IP address」を選択
                • ActionのAction:「Allow」を選択
              4. 「Add Rule」でルールを追加する

              動作確認

              以下の2点について動作確認できればOKです。

              •  許可したIPアドレスでのアクセス
                • 「”Hello from Lambda!”」が表示されればOK
              • 許可していないIPアドレすでのアクセス
                • 403エラーが返ってくればOK

               

              まとめ

              「CloudFront経由でLambda関数URLにアクセスできるようにする」方法について紹介させていただきました。

              このやり方の最大の弱点は、簡単に導入できるはずのLambda関数URL導入が、やや難しいものになってしまう点です。

              ]]>
              AWSのLambdaの同時実行数の計算方法 https://it.kensan.net/aws-lambda-concurrent-executions.html Thu, 23 Feb 2023 08:25:37 +0000 http://3.113.9.194/it/?p=532 Lambdaの同時実行数の計算方法計算で必要となるデータをログインサイトから求める方法について記載します。

              まず、なぜ同時実行数の計算が必要になるかについてですが、Lambdaには同時実行数の上限があるためです。

              同時実行数のデフォルトは1000となっています。

              これを超える場合には上限引き上げをAWSに依頼する必要があります。

              引き上げが必要になる少なくとも 2 週間前に制限の引き上げをリクエストするのがベストプラクティスです。

              参照:https://aws.amazon.com/jp/premiumsupport/knowledge-center/lambda-concurrency-limit-increase/

              同時実行数の上限引き上げは、事前にリクエストしておく必要があります。

              また、同時実行数を超えるとスロットリングエラーというエラーが発生してしまいますので、事前に同時実行数を見積もり、同時実行数が1000を超える場合は、上限引き上げをAWSに依頼する必要があります。

              スロットリングエラーが発生しないように正しく同時実行数の計算方法を理解していきましょー

              それでは、同時実行数の計算方法について記載していきます。

              同時実行数の計算方法

              以下の計算式で求めることができます。

              関数の平均実行時間 × 関数の平均リクエスト数 = 同時実行数
              

              次に運用中の関数の同時実行数について、計算要素をログから求め、最終的に同時実行数を算出する方法について記載します。

              計算要素

              関数の平均実行時間
              関数の平均リクエスト数

              関数の平均実行時間の求め方

              CloudWatchのログインサイトを使用します。

              CloudWatchのログインサイトで対象関数のログを検索をします。

              検索時に使用するクエリは以下の通りです。

              
              fields @timestamp, @message, @logStream, @log
              | filter @message like 'REPORT RequestId'
              | parse @message 'REPORT RequestId: *Duration:*ms*' as reqid, time, after
              | stats avg(time)
              

              上記クエリの結果が「関数の平均実行時間(ms)」になります。

              関数の平均リクエスト数の求め方

              こちらもCloudWatchのログインサイトを使用します。

              CloudWatchのログインサイトで対象関数のログを選択して、以下のクエリを実行します。

              fields @timestamp, @message, @logStream, @log
              | filter @message like 'REPORT RequestId'
              | stats count(@message)

              上記クエリの結果を1秒に割り戻した数が「関数の平均リクエスト数」になります。

              クエリの結果が36000でログ検索対象の時間が1時間の場合、以下の計算式のように「関数の平均リクエスト数」を算出します。

              36000(クエリの結果)÷60(1分あたりに割り戻す)÷60(1秒あたりに割り戻す) = 10

              同時実行数を算出してみる

              上記で求めた「関数の平均実行時間(ms)」と「関数の平均リクエスト数」がそれぞれ以下の通りとします。

              • 関数の平均実行時間:100(ms)
              • 関数の平均リクエスト数:10

              上記の場合、以下の計算で同時実行数を算出できます。

              100(関数の平均実行時間(ms))÷1000(関数の平均実行時間を秒に換算)×10(関数の平均リクエスト数)=1(同時実行数)

              まとめ

              Lambdaの同時実行数の計算方法計算に用いる値の調べ方についてみてきました。

              Lambdaの同時実行数の計算は漠然と難しそうでしたが、改めて整理すると思ったより簡単に算出できたと思います!

              同時実行数のスロットリングエラーではなく、タイムアウトエラーも知りたいよ、という場合は以下の記事もご参照ください

              https://it.kensan.net/it/lambda-timedout.html
              ]]>