🚤

Github Actions | Terraform を活用して、AWS Lambda への自動デプロイを構築する

に公開

はじめに

最近、私自身Github上でソース管理することが増えてきました。
その上で、Github Actionsを積極的に活用することで、CI/CDを実現することが可能になります。
優秀な生成AIモデルも増えてきましたし、CI/CDを実現するためのハードルが徐々に低くなってきたと感じています。
そんな今だからこそ、今一度自分の中で整理してみようと思い立ち、記事を書くことにしました。
誰かの助けになるかどうかはわかりませんが、業務フロー等の見直しの良いキッカケになることを願います✋

全体構成図

構成図

今回作成していく構成図になります。
構成図

Githubへのpushをトリガーにし、Terraforom Applyを実行してAWS上にLambda関数を作成および更新をしていきます。
これによって、Github Actionsを使った自動デプロイを実行してみようといった感じです。

この構成を実現するメリットは以下のように考えます。

  • 手動実行する手間がかからない
  • Terraformを利用することで複数プラットフォームに対応できる
  • トリガーの工夫次第でステージング、プロダクション環境等への適用が容易
    • 障害発生時のリカバリーが容易かつ迅速

ディレクトリ構成

最終的に作るディレクトリ構成を先に出しておきます。
ディレクトリ構成

.github/workflows/deploy.ymlにはGituhub Actionsから実行するフローをステップごとに分け、それぞれで認証情報の取得、コマンド実行等を担います。
terraform下には、Lambda関数をデプロイするためのHCLがつらつらと書かれます。

TerraformでLambdaデプロイコードを記述

terraformディレクトリを作成し、main.tfvariables.tfを作成し、Lambda関数を作成するためのコードを記述していきます。

terraform/main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}

data "archive_file" "lambda_zip" {
  type        = "zip"
  source_file = "${path.module}/../lambda_function.py"
  output_path = "${path.module}/lambda_function.zip"
}

data "aws_iam_role" "lambda_role" {
  name = "hello-lambda-role"
}

## 新規作成時のみ不要
# import {
#   to = aws_lambda_function.hello_function
#   id = "hello-function"
# }

resource "aws_lambda_function" "hello_function" {
  filename         = data.archive_file.lambda_zip.output_path
  function_name    = "hello-function"
  role             = data.aws_iam_role.lambda_role.arn
  handler          = "lambda_function.lambda_handler"
  source_code_hash = data.archive_file.lambda_zip.output_base64sha256
  runtime          = "python3.11"
  timeout          = 30

  tags = {
    Name = "hello-lambda"
  }
}
terraform/variables.tf
variable "aws_region" {
  description = "AWS region"
  type        = string
  default     = "ap-northeast-1"
}

コードを読んで気がついた方がいらっしゃるかもですが、hello-lambda-roleというカスタムロールを使用していて、IAMにてこのロールをあらかじめ作っています。

hello-lambda-roleには、以下のようなポリシーが含まれていればOKです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
    ]
}

このIAMロールを使用した上で、Lambda関数の作成および更新を行います。

Github Actionsのセットアップ

IAMユーザーの作成

まずは、Github ActionsからTerraform Apply等のコマンドを実行するためのIAMユーザーを作成します。IAMユーザーの作成手順に従い、ユーザーを作成します。

IAM
IAM、Lambdaへのアクセスが必要になってくるので、ポリシーを設置しています。
IAMポリシーのベストプラクティスに則ってポリシーを定義すべきでしょうが、一旦はフルアクセスとしました。

アクセスキー、シークレットアクセスキーの発行

作成が終わったら、IAMユーザーのアクセスキー、シークレットアクセスキーを発行しておきます。
これは後にActionsのシークレット、変数に定義しておく必要があるためです。

IAM

Actionsシークレットに、アクセスキーを追加

GithubリポジトリのSettings>Secrets and variables>Actionsに移動し、
「New repository secret」ボタンから、追加していきます。
キーにはそれぞれ以下のように指定し、バリューには先ほど発行したアクセスキー、シークレットアクセスキーを追加します。

  • AWS_ACCESS_KEY_ID:{アクセスキー}
  • AWS_SECRET_ACCESS_KEY:{シークレットアクセスキー}

Actionsシークレット

以下のようにセットされていればOKです。
Actionsシークレット

ActionsのYAMLを作成する

最後に、Github ActionsからTerraformコマンド等を実行するための手順書をYAMLにて記述していきます。

.github/workflows/deploy.yml
name: Deploy Lambda Function

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  terraform:
    name: 'Terraform'
    runs-on: ubuntu-latest
    environment: production

    defaults:
      run:
        shell: bash

    steps:
    - name: Checkout
      uses: actions/checkout@v4

    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v3
      with:
        terraform_version: 1.5.0

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v4
      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: Terraform Init
      run: terraform init
      working-directory: terraform

    - name: Terraform Format
      run: terraform fmt -check
      working-directory: terraform

    - name: Terraform Plan
      run: terraform plan -input=false
      working-directory: terraform

    - name: Terraform Apply
      if: github.ref == 'refs/heads/main' && github.event_name == 'push'
      run: terraform apply -auto-approve -input=false
      working-directory: terraform

今回は、ubuntu-latestのランナーを使用しています。
AWS認証情報の取得後、各種Terraformコマンドの実行をステップで分けています。

自動デプロイを実行してみる

ここまでできたら、冒頭に記載したディレクトリ構成になっているかと思います。
Lambdaは非常にシンプルなものでOKだと思いますので、以下のようなコードでいきます。
これをGithub上にプッシュしていくと・・

lambda_function.py
import json

def lambda_handler(event, context):
    name = event.get('name', 'World')
    
    message = f"Hello {name}さん"
    
    return {
        'statusCode': 200,
        'body': json.dumps({
            'message': message
        }, ensure_ascii=False)
    }

Actionsが回り始めました。
Actions

Actionsが回りきりました。これでTerraform Applyまで完了しています!
Actions

無事にLambda関数が作成ができるところまで完了しました!
継続デプロイ時はmain.tfのコメントアウトされている箇所を復活すれば問題なしです。

おわりに

今回はLambdaのみのデプロイでしたが、他にもプラットフォーム問わず色々なサービスをデプロイすることができます。
CI/CDを構築すると、素早いサービス展開、障害の防止、発生時のリカバリーが容易になると考えています。新しいサービスを作るとなった時のためにも、常に知見をアップデートしておきたいところですね。

では!

Discussion