Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8945

俺流AWS Lambdaデプロイ+IaCのベストプラクティスを考える

$
0
0
概要 Lambdaをコード管理したいのですが、Lambdaのベストプラクティス で良い感じのものがないと思い困り調べてみました。 以下のデプロイ方法がありますが、どれも個人的にはいまいちなため悩んでいました。 AWS CLIでのデプロイする 独自のデプロイツール(Lambdaroll)を使う https://tech.toreta.in/entry/2020/12/05/000000 ServerlessFrameworkを使う AWS CDKでデプロイする https://zenn.dev/faycute/articles/be5599fc093511 課題感 サーバレス関連のツールはどうしても構成管理ツールと紐づいてしまっているのでどうしたものかなぁと思っていました。 例えば、AWS CDKでもServerlessFrameworkでもバックエンドではCloudFormationで動いてしまっています。このようなツールのメリットは1リポジトリでインフラの構成管理とコード管理が完結することがメリットだと思っています。ただ、既存インフラ環境のIaCにTerraformを使っている場合、TerraformとCloudFormationのダブルスタンダードになってしまい個人的には気持ち悪いなと思っています。 それを解消する方法をこの記事では考えていきたいです。 Lambdaリソースの基本設計 設計方針 Lambdaのデプロイ方法はソースコードをzipで固めてS3に置くか、ECRにDocker Imageをプッシュするかを現在は選ぶことができます。 保守性が高く、どの環境でもちゃんとアプリケーションのデバッグができるという点ではDocker Imageの方がいいので、LambdaコードはECRにプッシュすることを選びます。 ちなみにサーバレスアプリケーションを作るときは、アプリケーションごとにリポジトリを作る方が良いとAWSの中の人に聞いたことがありますが、今回の用途としてはヘッダー書替え用やIP制限等のLambdaを扱いたいため1リポジトリでディレクトリを分けてデプロイします。 Lambdaリソースの管理方針 インフラリソースはTerraformで管理 Lambdaへのデプロイはソースコード側で行う CI/CDはGitHub Actionsで行う インフラリソースをTerraform管理にする ここではLambda Functionの管理とIAM、ECRの管理をします。 # IAM resource "aws_iam_role" "lambda_basic" { name = "lambda-basic-role" path = "/service-role/" assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json } data "aws_iam_policy_document" "lambda_assume_role" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["lambda.amazonaws.com", "edgelambda.amazonaws.com"] } } } resource "aws_iam_role_policy_attachment" "lambda_basic" { role = aws_iam_role.lambda_basic.name policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" } # ECR resource "aws_ecr_repository" "lambda1" { name = "lambda-function1" image_tag_mutability = "IMMUTABLE" image_scanning_configuration { scan_on_push = true } } # Lambda Function resource "aws_lambda_function" "lambda1" { function_name = "lambda-function1" role = aws_iam_role.lambda_basic.arn package_type = "Image" image_uri = "${aws_ecr_repository.lambda1.repository_url}:latest" timeout = 60 lifecycle { ignore_changes = [image_uri] } } # CloudWatch Logs ## ref https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/monitoring-cloudwatchlogs.html resource "aws_cloudwatch_log_group" "lambda1" { name = "/aws/lambda/${aws_lambda_function.lambda1.function_name}" retention_in_days = 30 } Lambdaコードを作成する 以下のようなディレクトリにコードを配置します。 言語はnode.jsにします。 $ tree . └── lambda-function1 ├── Dockerfile ├── index.js ├── package-lock.json └── package.json 各ファイルの中身は以下の通りです。 { "name": "lambda-container-image-example", "license": "MIT" } npm install して package-lock.json は作っておいてください。 index.js のコード内容は以下の通り。 exports.handler = (event, context, callback) => { const response = event.Records[0].cf.response; const headers = response.headers; const headerNameSrc = 'X-Amz-Meta-Last-Modified'; const headerNameDst = 'Last-Modified'; if (headers[headerNameSrc.toLowerCase()]) { headers[headerNameDst.toLowerCase()] = [ headers[headerNameSrc.toLowerCase()][0], ]; console.log(`Response header "${headerNameDst}" was set to ` + `"${headers[headerNameDst.toLowerCase()][0].value}"`); } callback(null, response); }; Dockerfileはこの通り # AWS ベースイメージを使用 FROM public.ecr.aws/lambda/nodejs:14 # ソースコードを関数のルートディレクトリにコピーします。 # 関数のルートディレクトリは `LAMBDA_TASK_ROOT` 環境変数を上書きすることで変更することができます ( デフォルトは `/var/task` ) 。 COPY index.js package.json package-lock.json /var/task/ RUN npm install # CMD にハンドラを設定します。 # Node.js の場合は `{ファイル名(拡張子なし)}.{関数名}` のように指定します。 # 今回は `index.js` の `handler` 関数をハンドラとして用意しているので以下のようになります。 CMD ["index.handler"] GitHub Actionsのworkflowを作成する Lambdaのソースコードおいてあるリポジトリでこちらのコードを設定してください。 GitHub Actionsで特定のディレクトリで操作された時にデプロイされるように設定してます。 name: "Deploy" on: push: branches: - main paths: - "lambda-function1/**" jobs: lambda: name: "Deploy Lambda" runs-on: ubuntu-latest defaults: run: shell: bash working-directory: ./lambda-function1/ steps: - name: Checkout uses: actions/checkout@v2 with: fetch-depth: 1 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v1 - name: docker build & docker push & lambda update run: | docker build -t XXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-function1:${{ github.sha }} . docker push XXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-function1:${{ github.sha }} aws lambda update-function-code --function-name lambda-function1 --image-uri XXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-function1:${{ github.sha }} Tips GitHub Actionsのマーケットプレイスにあるものは使えない GitHubActionsの公式にLambdaデプロイ用のコードがありますが、これはimage対応していないのでエラーになります。 参考資料: lambda-action 大元はこちらのコードですが、ファイルのみの対応になってしまっています。 image_uri だけ指定してデプロイを試みても400エラーになります。 参考資料: drone-lambda 結論 ServerlessFrameworkのようにそのフレームワークだけ使えばインフラのことを管理しなくても良いのですが、元々のインフラ設計がサーバーレスを中心とした設計でない場合、余計なインフラリソースが勝手に作られて運用が辛い経験がありました。 この方法を使えば、TerraformとLambdaのデプロイを分離できますし、ServerlessFrameworkでできるような余計なインフラリソースが増えてしまう心配がありませんし、不要になれば、Terraformから削除すればAWS環境も綺麗な状態を保てるのでお勧めです。

Viewing all articles
Browse latest Browse all 8945

Trending Articles