ビルドAWSラムダのようなエクスプレスのようなアプリ


ラムハープワールド例のような簡単な簡単な表現⚡ ☁️


私は最近、バックエンドの残りのアプリを構築し、いくつかの簡単なルートを経由してExpress 私のニーズを満たすために.Expressは、JavaScript/ノードの一般的に使用されるバックエンドです.js私は、AWSでこれを走らせたかったですLambda Serverlessなアーキテクチャを持つすべての利点のために.この記事は私が学んだことの結果です.あなたはこの例に従うことができなければならなくてAWS free tier .
私には2つの目標がありました.
  • インフラストラクチャを作成し、管理したい.
  • 私は、バックエンドのような単純な急行を使いたいです.
  • 仕事はいくらですか。


    このHello Worldの例のJavaScript部分は、AWSサービスを作成して配線するためのTorraformであるコードのより多くで単純です.両方について以下に概説する.私も、この例のためにコードのすべてを持ちますmy GitHub .

    必要条件


    あなたが必要Node.js インストールとAWS account .

    ラムダAPI


    lambda-api 提供するシンプルで軽量なソリューションは、誰もが時間を費やしているおなじみに見えるExpress . ソリューションを構築するlambda-api 28 KBで小さい単一の依存性解決を提供します.
    新しいノードプロジェクトを起動するnpm init .
    npm init
    
    インストールlambda-api
    npm install lambda-api
    
    インデックスを作成します.このコンテンツをプロジェクトにJSファイル.
    インデックス.js
    // Require the framework and instantiate it
    const api = require("lambda-api")()
    
    // Define a route
    api.get("/", async (req, res) => {
      console.log("hello world")
      return "hello world"
    })
    
    api.get("/foo", async (req, res) => {
      console.log("/foo hit")
      return "/foo hit"
    })
    
    api.get("/bar", async (req, res) => {
      console.log("/bar hit")
      return "/bar hit"
    })
    
    // Declare your Lambda handler
    exports.handler = async (event, context) => {
      return await api.run(event, context)
    }
    
    lambda-api ルート経由で簡単になりますget() , post() , put() いくつかのオプションを指定します.

    地形


    AWSラムダでこの仕事をするためには、APIゲートウェイを通してラムダを公開する必要があります.私はインフラストラクチャの構築、配備、削除を行うために、terraformを使いたかったです.これは、コード(IAC)としてインフラストラクチャとして知られています.テラフォームを使用すると、いくつかの利点があります.
  • 構成だけでなく、オーケストレーション
  • 不変のインフラストラクチャ
  • 宣言的な手続きコードではない
  • 作成、再作成、変更、またはインフラストラクチャの削除速度.
  • このHello Worldの例を簡単にするために、私はローカルマシンを使用してterraform状態を保存しますがTerraform Cloud 実際のアプリケーションのインフラストラクチャ状態のストレージ.GitHubまたはリポジトリの選択にあなたの状態をアップロードしないでください.
    クリエイトアterraform プロジェクトのフォルダ.そのフォルダでterraform init Torraform設定ファイルを含む作業ディレクトリを初期化する.これは、新しい地形の構成を書いた後、またはバージョン管理から既存のものをクローニングした後に実行されるべき最初のコマンドです.このコマンドを複数回実行することは安全です.

    プロバイダ。TF


    クリエイトアprovider.tf この内容のファイル.
    プロバイダ.TF
    provider "aws" {
      version = "~> 3.0"
      region = var.aws-region
    }
    

    変数。TF


    クリエイトアvariables.tf この内容のファイル.
    変数.TF
    variable "aws-region" {
      description = "AWS region for the infrastructure"
      type = string
      default = "us-east-1"
    }
    

    モジュール


    IAACを組織するためにモジュールを使用するつもりです.インサイドterraform フォルダを作成modules フォルダ.その中にモジュールをたくさん作成します.

    アーカイブファイル


    terraform/modulesフォルダの内部にあるフォルダを作成しましょうarchive .
    フォルダ/モジュール/アーカイブフォルダでmain.tf この内容のファイル.
    メイン.TF
    data "archive_file" "placeholder" {
      type = "zip"
      output_path = "${path.module}/lambda-function-payload.zip"
    
      source {
        content  = "placeholder"
        filename = "placeholder.txt"
      }
    }
    
    output "data-archive-file-placeholder-output-path" {
      value = data.archive_file.placeholder.output_path
    }
    
    使用するarchive_file ファイルの内容、ファイル、ディレクトリからアーカイブを生成します.以下のラムダを作成する際に使用するプレースホルダテキストファイルを保持します.これは、CI/CDパイプラインの展開段階でコードの展開からインフラストラクチャの作成、更新、および削除を分離するために行われます.ああ、きれいなseperation🎉!

    IAM


    私たちはAWS IAM AWSサービスとリソースへのアクセスを安全に管理する.IAMを使用すると、AWSのユーザーとグループを作成し、管理でき、AWSリソースへのアクセスを許可し、拒否するアクセス許可を使用できます.
    terraform/modulesフォルダの内部にあるフォルダを作成しましょうiam .
    フォルダ/モジュール/iamフォルダでmain.tf この内容のファイル.
    メイン.TF
    resource "aws_iam_role" "express-like-lambda-example" {
      name               = "express-like-lambda-example"
      assume_role_policy = <<POLICY
    {
      "Version": "2012-10-17",
      "Statement": {
        "Action": "sts:AssumeRole",
        "Principal": {
          "Service": "lambda.amazonaws.com"
        },
        "Effect": "Allow"
      }
    }
    POLICY
    }
    
    resource "aws_iam_policy" "express-like-lambda-example-logs" {
      name        = "express-like-lambda-example-logs"
      description = "Adds logging access"
    
      policy = <<EOF
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "logs:CreateLogGroup",
            "logs:CreateLogStream",
            "logs:PutLogEvents"
          ],
          "Resource": "arn:aws:logs:*"
        }
      ]
    }
    EOF
    }
    
    resource "aws_iam_role_policy_attachment" "attach-logs" {
      role       = aws_iam_role.express-like-lambda-example.name
      policy_arn = aws_iam_policy.express-like-lambda-example-logs.arn
    }
    
    output "aws-iam-role-express-like-lambda-example-arn" {
      value = aws_iam_role.express-like-lambda-example.arn
    }
    
    The aws_iam_role express-like-lambda-example 使用するラムダの役割を設定します.その後、我々はaws_iam_policy express-like-lambda-example-logs これはラムダへのアクセスをログに追加します.私たちはaws_iam_role_policy_attachment 呼ばれるattach-logs ポリシーをロールにアタッチします.最後に、出力するarn 他のモジュールで使用する役割の少し後に.

    ラメラ


    私たちはAWS Lambda プロビジョニングやサーバの管理なしにコードを実行するには.あなたが消費する計算時間だけを支払う.
    ラムダを使用すると、ほぼすべての種類のアプリケーションまたはバックエンドサービスのコードを実行できます.ラムダは、高可用性でコードを実行し、スケールするために必要なすべての面倒を見ます.
    terraform/modulesフォルダの内部にあるフォルダを作成しましょうlambda .
    モジュール/ラムダを作成するvariables.tf この内容のファイル.
    変数.TF
    variable "aws-iam-role-express-like-lambda-example-arn" {
      description = "IAM role ARN"
      type = string
    }
    
    variable "data-archive-file-placeholder-output-path" {
      description = "Placeholder content for Lambda"
      type = string
    }
    
    最初の変数はarniam role 上から.番目の変数はarchive 上記からのファイル.この例ではラムダを作成する必要があります.
    フォルダ/モジュール/ラムダフォルダでmain.tf この内容のファイル.
    メイン.TF
    resource "aws_lambda_function" "express-like-lambda-example" {
      filename = var.data-archive-file-placeholder-output-path
      function_name = "express-like-lambda-example"
      handler       = "index.handler"
      role          = var.aws-iam-role-express-like-lambda-example-arn
      runtime       = "nodejs12.x"
      memory_size   = 128
      timeout       = 1
    }
    
    resource "aws_lambda_function_event_invoke_config" "express-like-lambda-example-event-invoke-config" {
      function_name = aws_lambda_function.express-like-lambda-example.arn
      maximum_event_age_in_seconds = 60
      maximum_retry_attempts       = 0
    }
    
    resource "aws_lambda_permission" "express-like-lambda-example" {
      statement_id  = "AllowAPIGatewayInvoke"
      action        = "lambda:InvokeFunction"
      function_name = aws_lambda_function.express-like-lambda-example.arn
      principal     = "apigateway.amazonaws.com"
    }
    
    output "aws-lambda-function-express-like-lambda-example-arn" {
      value = aws_lambda_function.express-like-lambda-example.arn
    }
    
    output "aws-lambda-function-express-like-lambda-example-invoke-arn" {
      value = aws_lambda_function.express-like-lambda-example.invoke_arn
    }
    
    The aws_lambda_function express-like-lambda-example ラムダ関数を作成します.The filename 使用するのはarchive terraform/modules/lambda/変数で定義されている変数を使うことで.tf.The aws_lambda_function_event_invoke_config express-like-lambda-example-event-invoke-config 関数を実行できるようにするために秒単位で最大値を定義することができます.The aws_lambda_permission express-like-lambda-example ラムダをAPIゲートウェイ経由で実行することができます.最後に、出力ラムダarn and invoke_arn APIゲートウェイを設定するときに使用します.

    ゲートウェイ


    ホームストレッチ、そこにハングアップ.
    私たちはAWS API Gateway RESTful APIを作成するにはAPIゲートウェイは、トラフィック管理、CORSサポート、認証およびアクセス制御、throttling、監視、およびAPIバージョン管理を含む同時のAPI呼び出しの何十万もの受け入れと処理を行うすべてのタスクを処理します.APIゲートウェイには、最低料金または起動コストがありません.あなたが受け取るAPIコールと転送されたデータの量を支払う.
    terraform/modulesフォルダの内部にあるフォルダを作成しましょうapi-gateway .
    ゲートウェイ/モジュール/APIゲートウェイでvariables.tf この内容のファイル.
    変数.TF
    variable "aws-lambda-function-express-like-lambda-example-arn" {
      description = "express-like-lambda-example Lambda ARN"
      type = string
    }
    
    variable "aws-lambda-function-express-like-lambda-example-invoke-arn" {
      description = "express-like-lambda-example Lambda invoke ARN"
      type = string
    }
    
    最初の変数はarn そして、2番目はラムダを指定するinvoke_arn .
    フォルダ/モジュール/iamゲートウェイフォルダを作成するmain.tf この内容のファイル.
    メイン.TF
    resource "aws_api_gateway_rest_api" "express-like-lambda-example" {
      name = "express-like-lambda-example"
    }
    
    resource "aws_api_gateway_method" "proxy-root" {
      rest_api_id   = aws_api_gateway_rest_api.express-like-lambda-example.id
      resource_id   = aws_api_gateway_rest_api.express-like-lambda-example.root_resource_id
      http_method   = "ANY"
      authorization = "NONE"
    }
    
    resource "aws_api_gateway_integration" "express-like-lambda-example" {
      rest_api_id             = aws_api_gateway_rest_api.express-like-lambda-example.id
      resource_id             = aws_api_gateway_method.proxy-root.resource_id
      http_method             = aws_api_gateway_method.proxy-root.http_method
      integration_http_method = "POST"
      type                    = "AWS_PROXY"
      uri                     = var.aws-lambda-function-express-like-lambda-example-invoke-arn
    }
    
    resource "aws_api_gateway_resource" "proxy" {
      rest_api_id = aws_api_gateway_rest_api.express-like-lambda-example.id
      parent_id   = aws_api_gateway_rest_api.express-like-lambda-example.root_resource_id
      path_part   = "{proxy+}"
    }
    
    resource "aws_api_gateway_method" "proxy" {
      rest_api_id   = aws_api_gateway_rest_api.express-like-lambda-example.id
      resource_id   = aws_api_gateway_resource.proxy.id
      http_method   = "ANY"
      authorization = "NONE"
    }
    
    resource "aws_api_gateway_integration" "lambda" {
      rest_api_id             = aws_api_gateway_rest_api.express-like-lambda-example.id
      resource_id             = aws_api_gateway_method.proxy.resource_id
      http_method             = aws_api_gateway_method.proxy.http_method
      integration_http_method = "POST"
      type                    = "AWS_PROXY"
      uri                     = var.aws-lambda-function-express-like-lambda-example-invoke-arn
    }
    
    resource "aws_api_gateway_deployment" "express-like-lambda-example_v1" {
      depends_on = [
        aws_api_gateway_integration.express-like-lambda-example
      ]
      rest_api_id = aws_api_gateway_rest_api.express-like-lambda-example.id
      stage_name  = "v1"
    }
    
    output "endpoint" {
      value = aws_api_gateway_deployment.express-like-lambda-example_v1.invoke_url
    }
    
    ここでは、APIリクエストの詳細をラムダ関数のイベントパラメータとして渡すことができるAPIゲートウェイのラムダプロキシ統合オプションを設定します.lambda-api 自動的にこの情報を解析し、正規化されたリクエストオブジェクトを作成します.リクエストはlambda-api 'sメソッド.aws_api_gateway_rest_api APIゲートウェイREST APIを提供します.aws_api_gateway_method APIゲートウェイリソースのHTTPメソッドを提供します.aws_api_gateway_integration APIゲートウェイ統合のためのHTTPメソッド統合を提供します.aws_api_gateway_resource APIゲートウェイリソースを提供します.aws_api_gateway_deployment APIゲートウェイレスト展開を提供します.最後に、APIを呼び出すためにURLを出力します.

    メイン.TF


    我々は、我々が一緒に作った地形の全てを結ぶ必要があります.フォルダフォームでmain.tf この内容のファイル.
    module "archive" {
      source = "./modules/archive"
    }
    
    module "iam" {
      source = "./modules/iam"
    }
    
    module "lambda" {
      source = "./modules/lambda"
      data-archive-file-placeholder-output-path = module.archive.data-archive-file-placeholder-output-path
      aws-iam-role-express-like-lambda-example-arn = module.iam.aws-iam-role-express-like-lambda-example-arn
    }
    
    module "api-gateway" {
      source = "./modules/api-gateway"
      aws-lambda-function-express-like-lambda-example-arn = module.lambda.aws-lambda-function-express-like-lambda-example-arn
      aws-lambda-function-express-like-lambda-example-invoke-arn = module.lambda.aws-lambda-function-express-like-lambda-example-invoke-arn
    }
    
    # Set the generated URL as an output. Run `terraform output url` to get this.
    output "endpoint" {
      value = module.api-gateway.endpoint
    }
    
    これは、私たちが書いたモジュールの全てをまとめており、terraformで宣言的なインフラストラクチャを完成させます.

    コードの実行


    インフラストラクチャの配備


    🎉 あなたは、これまでそれを作りました!あなたが作ったコードで遊びましょう!🎉

    インフラストラクチャを展開するためにいくつかの地形コマンドを実行するつもりです.
    terraform plan
    
    terraform planコマンドは実行計画を作成するために使用されます.このコマンドは、変更のセットの実行計画が実際のリソースや状態に変更を加えることなく、期待に一致するかどうかをチェックする便利な方法です.
    あなたがこのterraform計画を適用することの上に動くことができるように、それは問題なしで働かなければなりません.
    terraform apply
    
    The terraform apply コマンドは、構成の望ましい状態に到達するために必要な変更を適用するために使用されるか、またはterraform plan 施工計画.
    これをAで確認する必要がありますyes を返します.あなたが入力する前に何を作成するかを読む時間をくださいyes . それは何が作成されることを示します.
    例えば、
    terraform apply
    
    ...
    
    Do you want to perform these actions?
      Terraform will perform the actions described above.
      Only 'yes' will be accepted to approve.
    
      Enter a value: yes
    
    module.iam.aws_iam_policy.express-like-lambda-example-logs: Creating...
    module.iam.aws_iam_role.express-like-lambda-example: Creating...
    module.api-gateway.aws_api_gateway_rest_api.express-like-lambda-example: Creating...
    module.iam.aws_iam_role.express-like-lambda-example: Creation complete after 0s [id=express-like-lambda-example]
    module.lambda.aws_lambda_function.express-like-lambda-example: Creating...
    module.iam.aws_iam_policy.express-like-lambda-example-logs: Creation complete after 1s [id=arn:aws:iam::REDACTED:policy/express-like-lambda-example-logs]
    module.api-gateway.aws_api_gateway_rest_api.express-like-lambda-example: Creation complete after 1s [id=REDACTED]
    module.iam.aws_iam_role_policy_attachment.attach-logs: Creating...
    module.api-gateway.aws_api_gateway_resource.proxy: Creating...
    module.api-gateway.aws_api_gateway_method.proxy-root: Creating...
    module.api-gateway.aws_api_gateway_method.proxy-root: Creation complete after 0s [id=REDACTED-ANY]
    module.iam.aws_iam_role_policy_attachment.attach-logs: Creation complete after 0s [id=express-like-lambda-example-REDACTED]
    module.api-gateway.aws_api_gateway_resource.proxy: Creation complete after 1s [id=REDACTED]
    module.api-gateway.aws_api_gateway_method.proxy: Creating...
    module.api-gateway.aws_api_gateway_method.proxy: Creation complete after 0s [id=REDACTED-ANY]
    module.lambda.aws_lambda_function.express-like-lambda-example: Still creating... [10s elapsed]
    module.lambda.aws_lambda_function.express-like-lambda-example: Creation complete after 16s [id=express-like-lambda-example]
    module.lambda.aws_lambda_permission.express-like-lambda-example: Creating...
    module.lambda.aws_lambda_function_event_invoke_config.express-like-lambda-example-event-invoke-config: Creating...
    module.api-gateway.aws_api_gateway_integration.lambda: Creating...
    module.api-gateway.aws_api_gateway_integration.express-like-lambda-example: Creating...
    module.lambda.aws_lambda_permission.express-like-lambda-example: Creation complete after 0s [id=AllowAPIGatewayInvoke]
    module.api-gateway.aws_api_gateway_integration.express-like-lambda-example: Creation complete after 0s [id=REDACTED-ANY]
    module.api-gateway.aws_api_gateway_deployment.express-like-lambda-example_v1: Creating...
    module.api-gateway.aws_api_gateway_integration.lambda: Creation complete after 0s [id=REDACTED-ANY]
    module.lambda.aws_lambda_function_event_invoke_config.express-like-lambda-example-event-invoke-config: Creation complete after 0s [id=arn:aws:lambda:us-east-1:REDACTED:function:express-like-lambda-example]
    module.api-gateway.aws_api_gateway_deployment.express-like-lambda-example_v1: Creation complete after 1s [id=REDACTED]
    
    Apply complete! Resources: 13 added, 0 changed, 0 destroyed.
    
    Outputs:
    
    endpoint = https://REDACTED.execute-api.us-east-1.amazonaws.com/v1
    
    ビットの使用のために出力から終点をコピーするか、覚えてください.

    アプリの配備


    開けるpackage.json これを作成npm script .
    "scripts": {
        "build": "npm install --production && rm -rf build && mkdir build && zip -r -q -x='*terraform*' -x='*.md' -x='LICENSE' -x='*build*' -x='*.DS_Store*' -x='*.git*' build/express-like-lambda-example.zip . && du -sh build"
      },
    
    プロジェクトのルートで、ビルドコマンドを実行して、作成したラムダに展開する準備ができました.
    npm run build
    
    例えば、
    npm run build
    
    > [email protected] build /Users/REDACTED/Development/express-like-lambda-example
    > npm install --production && rm -rf build && mkdir build && zip -r -q -x='*media*' -x='*terraform*' -x=*coverage* -x='*.md' -x='LICENSE' -x='*build*' -x='*.DS_Store*' -x='*.git*' build/express-like-lambda-example.zip . && du -sh build
    
    audited 1 package in 0.916s
    found 0 vulnerabilities
    
     28K    build
    
    今、我々はラムダに私たちのzipアプリを展開することができます.そのためにこのコマンドを使います.
    aws lambda update-function-code --function-name=express-like-lambda-example --zip-file=fileb://build/express-like-lambda-example.zip --region=us-east-1 1> /dev/null
    

    APIの呼び出し


    今、我々はAPIを打つことができます🎉
    curl https://REDACTED.execute-api.us-east-1.amazonaws.com/v1
    hello world
    
    使用例
    curl https://REDACTED.execute-api.us-east-1.amazonaws.com/v1/foo
    /foo hit
    
    curl https://REDACTED.execute-api.us-east-1.amazonaws.com/v1/bar
    /bar hit
    
    curl https://REDACTED.execute-api.us-east-1.amazonaws.com/v1/baz
    {"error":"Route not found"}
    
    注意してください、あなたのURLは上記のものと異なります.それぞれの展開はユニークです.あなたのURLの出力から来るterraform apply .

    結論


    私は楽しみました.私は少しの地形とRADノードについて学びました.JSパッケージlambda-api . 私が間違いを犯したならば、私はコメントのそれらを学ぶことが幸せであるということから学ぶことができます.あなたが質問をするならば、自由に尋ねてください.