Tech Hotoke Blog

IT観音とは私のことです。

AWS Lambda 入門

これは何?

AWS Lamdaを触ることになった。とりあえず、なんとなくの雰囲気で実装してみたところ、かなり初歩の段階でエラーが発生した。 この機会なので、学習を兼ねて、まずはちゃんとドキュメントを読んでからエラーの解消にあたって行こうと思い書いてます。

前提

  • Rubyで実装しているので、Rubyベースで。
  • コードは実務で使っているものになってしまうので、内容を改変・抜粋して掲載しています。(コピペでは動かないと思ってください)
  • 公式リファレンスを網羅しているわけではありません。
  • AWS SESでLambdaを使用することを前提にしています

AWS Lambdaって何?

  • AWS Lambda はサーバーのプロビジョニングや管理をする必要がなく、コードを実行できるコンピューティングサービスです。
    • いわゆるサーバーレスアーキテクチャというやつですかね。Lambdaの他に、グーグルの「Google Cloud Functions」、IBMの「OpenWhisk」、マイクロソフトの「Azure Functions」などが挙げられますが、定義がそれぞれ異なるため一概にサーバーレスという言葉でくくることができないのが注意。
    • AWSにおけるサーバーレスは「インスタンスベースの仮想サーバー(EC2など)を使わずにアプリケーションを開発するアーキテクチャ」と個人的には解釈しています(間違っていたらご指摘ください)
    • 必要な場合にのみ関数を実行する
    • 自動的にスケーリング
    • 従量課金
    • フルマネージドサービス(コードを実行するメモリのバランス、CPU、ネットワーク、その他のリソースを提供するコンピューティングフリート)
    • コンピューティングインスタンスにログインしたり、提供されたランタイムのオペレーティングシステムをカスタマイズしたりすることはできない → コンピュートリソースを管理する必要がある場合は向かない

Lambdaの概念

  • 関数:Lambda でコードを実行するために呼び出すことができるリソース -トリガー:Lambda 関数を呼び出すリソースまたは設定
  • イベント:処理する Lambda 関数のデータを含む JSON 形式のドキュメント。ランタイムによりオブジェクトに変換された上で、関数のコードに渡される。
  • ランタイム環境:関数の実行に必要なプロセスとリソースが、実行環境により管理される。
  • 命令セットアーキテクチャ:Lambda が関数の実行に使用するコンピュータプロセッサタイプ。2024/02/27時点では、arm64かx86_64が選択できる。
  • レイヤー:追加のコードまたはデータを含むことができる .zip ファイルアーカイブ。レイヤーには、ライブラリ、 カスタムランタイム 、データ、または設定ファイルを含めることができる。

    • 各関数につき 最大 5 つ のレイヤーを含めることができる
    • 関数にレイヤーを含むと、実行環境においてコンテンツが /opt ディレクトリに抽出される。
    • デフォルトでは、作成したレイヤーは AWS アカウントに対してプライベートになる。
    • コンテナイメージとしてデプロイされた関数はレイヤーを使用しない。代わりに、コンテナイメージをビルドする際、必要なランタイム、ライブラリ、およびその他の依存関係を、そのイメージ内にパッケージ化する。
  • 拡張機能

    • 関数を拡張できる。
      • 例) 任意のモニタリングツール、オブザーバビリティツール、セキュリティツール、およびガバナンスツールに関数を統合できます。
    • AWSか提供するツールセットが存在する。カスタムで作成も可能。
    • 拡張機能は内部モードと外部モード、2つのタイプに分かれるらしい。
    • 内部拡張機能は、ランタイムプロセスで実行され、ランタイムと同じライフサイクルを共有する。
    • 外部拡張機能は、実行環境で別のプロセスとして実行される。外部拡張機能は、関数が呼び出される前に初期化される。また、関数のランタイムと並行して実行され、関数の呼び出しが完了した後も引き続き実行される。
  • 同時実行

    • ある時点で関数が処理しているリクエストの数。
    • リクエストの処理中に関数が再度呼び出されると、別のインスタンスがプロビジョンされるため、関数の同時実行数が増加する。
  • エイリアス

    • 関数を呼び出したり表示したりするときに、バージョンまたはエイリアスを指定するための修飾子を含めることができる。
    • バージョンは、数値修飾子を持つ関数のコードと設定の変更不可能なスナップショット。たとえば、my-function:1 と指定できる。
  • 送信先

    • Lambda が非同期呼び出しからイベントを送信できる AWS リソース。
    • 処理に失敗したイベントの送信先を設定できます。一部のサービスでは、正常に処理されたイベントの宛先もサポートしている。

今回発生したエラーと対応

{
    "errorMessage": "cannot load such file -- lambda_function",
    "errorType": "Init<LoadError>",
    "stackTrace": [
        "<internal:/var/lang/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:86:in `require'",
        "<internal:/var/lang/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:86:in `require'"
    ]
}

lambda_functionが存在しないとのことでしたが、Handler設定でデフォルトがlambda_function として設定されていました。 私のコードではルートディレクトリにhandler.rbファイルを作成し、notifyメソッドを定義していたため、添付画像のような形式になるのが正でした。

AWS SESからどんなデータが渡ってくるのか?

{
  "Records": [
    {
      "eventVersion": "1.0",
      "ses": {
        "mail": {
          "commonHeaders": {
            "from": [
              "Jane Doe <janedoe@example.com>"
            ],
            "to": [
              "johndoe@example.com"
            ],
            "returnPath": "janedoe@example.com",
            "messageId": "<0123456789example.com>",
            "date": "Wed, 7 Oct 2015 12:34:56 -0700",
            "subject": "Test Subject"
          },
          "source": "janedoe@example.com",
          "timestamp": "1970-01-01T00:00:00.000Z",
          "destination": [
            "johndoe@example.com"
          ],
          "headers": [
            {
              "name": "Return-Path",
              "value": "<janedoe@example.com>"
            },
            {
              "name": "Received",
              "value": "from mailer.example.com (mailer.example.com [203.0.113.1]) by inbound-smtp.us-west-2.amazonaws.com with SMTP id o3vrnil0e2ic for johndoe@example.com; Wed, 07 Oct 2015 12:34:56 +0000 (UTC)"
            },
            {
              "name": "DKIM-Signature",
              "value": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=example; h=mime-version:from:date:message-id:subject:to:content-type; bh=jX3F0bCAI7sIbkHyy3mLYO28ieDQz2R0P8HwQkklFj4=; b=sQwJ+LMe9RjkesGu+vqU56asvMhrLRRYrWCbV"
            },
            {
              "name": "MIME-Version",
              "value": "1.0"
            },
            {
              "name": "From",
              "value": "Jane Doe <janedoe@example.com>"
            },
            {
              "name": "Date",
              "value": "Wed, 7 Oct 2015 12:34:56 -0700"
            },
            {
              "name": "Message-ID",
              "value": "<0123456789example.com>"
            },
            {
              "name": "Subject",
              "value": "Test Subject"
            },
            {
              "name": "To",
              "value": "johndoe@example.com"
            },
            {
              "name": "Content-Type",
              "value": "text/plain; charset=UTF-8"
            }
          ],
          "headersTruncated": false,
          "messageId": "o3vrnil0e2ic28tr"
        },
        "receipt": {
          "recipients": [
            "johndoe@example.com"
          ],
          "timestamp": "1970-01-01T00:00:00.000Z",
          "spamVerdict": {
            "status": "PASS"
          },
          "dkimVerdict": {
            "status": "PASS"
          },
          "processingTimeMillis": 574,
          "action": {
            "type": "Lambda",
            "invocationType": "Event",
            "functionArn": "arn:aws:lambda:us-west-2:111122223333:function:Example"
          },
          "spfVerdict": {
            "status": "PASS"
          },
          "virusVerdict": {
            "status": "PASS"
          }
        }
      },
      "eventSource": "aws:ses"
    }
  ]
}

参考: Amazon SES で AWS Lambda を使用する - AWS Lambda

AWS Lambdaにレイヤーを作成する

coming soon...