在使用Terraform进行配置管理的同时部署AWS Lambda

我是yuua,属于DMM数据基础设施部门。
在使用terraform创建并部署lambda函数时,如果将lambda函数包含在terraform中,可能会导致变更和操作变得复杂。
因此,我想记录下那些导致困扰的情况和相应的操作步骤。

这次的lambda不是lambda容器。

第一步操作

1. 使用Terraform创建Lambda函数的基本前置信息(如部署位置等)。
2. 将Lambda函数压缩为zip格式并放置在S3存储桶中。

持之以恒的部署步骤

我通常使用Github Actions等CI/CD工具。

1. 使用Github Actions将Lambda函数/ Lambda层部署到存储桶中。
2. 使用Github Actions执行函数的更新操作。

做好准备

使用 Terraform 创建用于分配 Lambda 的 S3 key 等。

// bucket作成
resource "aws_s3_bucket" "lambda_bucket" {
  bucket = "lambda-bucket"
}

// lambda関数配置するようのkeyを作成
resource "aws_s3_bucket_object" "lambda_function" {
  key    = "functions/nodejs/"
  bucket = aws_s3_bucket.lambda_bucket.id
}

// layerを使う場合はlayer用も
resource "aws_s3_bucket_object" "lambda_layer" {
  key    = "layers/nodejs/hoge/"
  bucket = aws_s3_bucket.lambda_bucket.id
}

现在暂时执行 terraform apply

部署lambda函数

默认的 node.js lambda

exports.handler = async (event) => {
    // TODO implement
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

将上述内容打包为 zip 文件,并将其放置在之前创建的 s3 key 中。
放置的方式可以使用 cli、控制台或 ci,任何方式都可以。

lambda资源定义

在将函数部署到s3后,使用terraform定义lambda函数。

data "aws_iam_policy_document" "role" {
  statement {
    actions = ["sts:AssumeRole"]
    effect  = "Allow"
    principals {
      identifiers = ["lambda.amazonaws.com"]
      type        = "Service"
    }
  }
}

resource "aws_iam_role" "role" {
  name               = "${var.function_name}-lambda"
  assume_role_policy = data.aws_iam_policy_document.role.json
}

// logginなど必要であれば、適宜roleにattachなどしてください

// 関数定義
resource "aws_lambda_function" "function" {
  function_name                  = var.function_name
  handler                        = var.handler
  role                           = aws_iam_role.role.arn
  runtime                        = var.runtime
  s3_bucket                      = bucketを指定
  s3_key                         = 配置したkeyを指定
  layers                         = [layerを使うのであれば指定]
}

实施 terraform apply。

上述的lambda函数已经创建完成,发布等选项可以根据需要进行相应调整。

为了进行持续部署

我认为基本上一旦创建就结束的情况并不多,所以我会创建定义来实现持续的部署。

在这里定义 GitHub Actions,并在从Pull Request到各个分支被关闭/合并的时候进行部署,以便能够进行部署。
在从外部进行部署时,创建一个具有所需权限的用户,并使用该用户的密钥。

大致上可能需要的权限

s3:PutObject
s3:GetObject
s3:DeleteObject
s3:ListBucket
lambda:GetFunction
lambda:UpdateFunctionCode
lambda:UpdateFunctionConfiguration
// layerがいるなら
lambda:GetLayerVersion
lambda:PublishLayerVersion
lambda:ListLayerVersions
lambda:AddLayerVersionPermission

GitHub Actions是一種定義行為的工具。

匿名函数 (ní shù)

以下是关于自助主机设置的定义,但通常情况下使用GitHub托管或自助主机都没有问题。

name: deploy-function

on:
  pull_request:
    branches:
      - ブランチ名
    types: [closed]

jobs:
  update-functions:
    name: lambda deploy
    runs-on: [self-hosted, linux]
    if: github.event.pull_request.merged == true // mergeされたら処理継続
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: code build
        uses: actions/setup-node@v2
        with:
          node-version: "14"
          cache: "npm"
      - run: npm install // 必要であれば
      - run: npx webpack // buildなどあれば

      // aws credentialを設定
      - 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: リージョン

      // zipファイルを作成
      - name: compression
        working-directory: ./dist
        run: zip function.zip main.js

      - name: lambda update function codes
        working-directory: ./dist
        run: |
          aws lambda update-function-code --function-name function名 --zip-file fileb://function.zip
      // 必要であればpublish

将代码压缩成zip文件,并执行aws cli中的update-function-code命令。
这样,在PR关闭/合并时将执行部署操作。

Lambda层

如果 `layer` 是一个节点,那么需要将 `nodejs/node_modules` 进行压缩。在这里,我们还在lambda函数之外定义了一个供 `layer` 使用的目录,并在其中放置了 `package.json`。

name: lambda-layer-deploy

on:
  pull_request:
    branches:
      - develop
    types: [closed]
    paths:
      - "layerのpath/**"

jobs:
  update-functions:
    name: lambda layer deploy
    runs-on: [self-hosted, linux]
    if: github.event.pull_request.merged == true
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: code build
        uses: actions/setup-node@v2
        with:
          node-version: "14"
          cache: "npm"

      - name: lambda layer
        working-directory: ./layer
        run: |
          mkdir nodejs
          cp package*.json nodejs/
          npm install --prefix ./nodejs --production
          zip -r package.zip nodejs/node_modules

      - 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: リージョン

      - name: lambda layer deploy
        working-directory: ./layer
        run: aws lambda publish-layer-version --layer-name layer名 --zip-file fileb://package.zip --compatible-runtimes nodejs14.x

      // 現在のlatestを取得
      - name: lambda layer latest version
        id: step1
        run: |
          layer=$(aws lambda list-layer-versions --layer-name layer名 --query 'LayerVersions[0].LayerVersionArn')
          echo "::set-output name=layer_ver::$layer"

      - name: lambda update configure
        run: |
          aws lambda update-function-configuration --function-name function名 --layers ${{ steps.step1.outputs.layer_ver }} //複数layerがある場合は ${{ hogehoge }} ${{ foobar }} このような形で続けてかけます 

总结

我把创建Lambda资源的Terraform环境以备忘录的形式总结了下来,并使用CI/CD进行持续部署。
由于IaC和Lambda函数的代码被分离了,也不必考虑太多事情,所以如果不使用Lambda容器的话,目前我认为这是最方便的方式。

在数据基础设施部门,我们负责AWS上的大数据基础设施运维,并进行利用各种资源开展产品开发工作。
我们也进行中途招聘等活动,所以如果您有兴趣,请通过我们公司的网站等渠道进行非正式面谈等,请务必提出申请。

bannerAds