👾

Go言語とSAMを使ってAWS Lambdaを実装し、GitHub Actionsで環境別デプロイを行う方法

2023/05/30に公開

はじめに

今回業務でSAMでLambda(go)を構築することになり、ブランチ毎にデプロイ先を変更する必要がありました。
GitHub Actionsで環境別にデプロイする方法を調べたので簡単にまとめてみようと思います😁

Go言語とSAMの利点

Go言語はコンパイル言語でありながら、開発速度が速く、バイナリサイズが小さく、高いパフォーマンスを実現できます。そのため、AWS Lambdaなどのサーバーレス環境での利用に適しています。また、Go言語は並行処理が得意であり、Lambdaのようなイベント駆動型のアーキテクチャにマッチしています。

一方、AWS Serverless Application Model(SAM)は、AWS LambdaやAPI Gatewayなどのサーバーレスアプリケーションを簡単に構築、デプロイ、管理できるフレームワークです。SAMを使うことで、AWSリソースの管理やデプロイプロセスを簡素化でき、開発者はアプリケーションのコードに集中することができます。

目的

本記事では、Go言語とSAMを組み合わせて、AWS Lambdaを実装し、GitHub Actionsを用いてdev、stg、prodの環境毎にデプロイする方法を解説します。これにより、開発者は環境ごとのデプロイを効率的に行うことができます。

環境準備

まず、プロジェクトを開始する前に、いくつかの必要なツールをインストールする必要があります。
概要だけ説明します。

必要なツールのインストール

Go言語
Go言語の公式サイト(https://golang.org/dl/ )から、環境に合わせて最新のバージョンをダウンロードしてインストールします。インストールが完了したら、go versionコマンドでバージョンを確認できます。

AWS CLI
AWS CLIは、AWSのリソースをコマンドラインから操作できるツールです。公式ドキュメント(https://aws.amazon.com/cli/ )に従って、インストールを行います。インストール後、aws --versionコマンドでバージョンを確認できます。

SAM CLI
SAM CLIは、SAMアプリケーションの開発やデプロイに必要なツールです。公式ドキュメント(https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html )に従って、インストールを行います。インストールが完了したら、sam --versionコマンドでバージョンを確認できます。

AWS認証情報の設定
AWS CLIやSAM CLIを使用するためには、AWSの認証情報が必要です。
ユーザーのアクセスキーとシークレットアクセスキーをメモし、aws configureコマンドなどで認証情報を設定してください。

Go言語でのLambda関数実装

プロジェクト構成

まず、プロジェクトのディレクトリ構成を作成します。以下のような構成になります。

my-lambda-app/
├── main.go
├── go.mod
├── go.sum
├── handler
│   ├── handler.go
│   └── handler_test.go
└── template.yaml

ハンドラ関数の実装

handlerディレクトリ内にhandler.goファイルを作成し、以下のようなコードを記述します。このコードは、環境変数によって振る舞いが変わる簡単なLambda関数です。

package handler

import (
	"context"
	"fmt"
	"os"

	"github.com/aws/aws-lambda-go/events"
)

type Response events.APIGatewayProxyResponse

func HandleRequest(ctx context.Context) (Response, error) {
	env := os.Getenv("APP_ENV")

	message := "Hello, this is the default environment."
	if env != "" {
		message = fmt.Sprintf("Hello, this is the %s environment.", env)
	}

	return Response{
		StatusCode: 200,
		Body:       message,
	}, nil
}

この関数では、APP_ENVという環境変数によって返すメッセージが変わります。この環境変数は、後ほどSAMテンプレートで設定します。

テストの作成

次に、handler_test.goファイルを作成し、ハンドラ関数のテストを記述します。

package handler

import (
	"context"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestHandleRequest(t *testing.T) {
	ctx := context.Background()

	// Test for the default environment
	resp, err := HandleRequest(ctx)
	assert.NoError(t, err)
	assert.Equal(t, 200, resp.StatusCode)
	assert.Equal(t, "Hello, this is the default environment.", resp.Body)

	// Test for the dev environment
	os.Setenv("APP_ENV", "dev")
	resp, err = HandleRequest(ctx)
	assert.NoError(t, err)
	assert.Equal(t, 200, resp.StatusCode)
	assert.Equal(t, "Hello, this is the dev environment.", resp.Body)

	// Test for the stg environment
	os.Setenv("APP_ENV", "stg")
	resp, err = HandleRequest(ctx)
	assert.NoError(t, err)
	assert.Equal(t, 200, resp.StatusCode)
	assert.Equal(t, "Hello, this is the stg environment.", resp.Body)

	// Test for the prod environment
	os.Setenv("APP_ENV", "prod")
	resp, err = HandleRequest(ctx)
	assert.NoError(t, err)
	assert.Equal(t, 200, resp.StatusCode)
	assert.Equal(t, "Hello, this is the prod environment.", resp.Body)
}

次にhandler.goのHandleRequestを呼び出すmain.goです。

package main

import (
	"my-lambda-app/handler"

	"github.com/aws/aws-lambda-go/lambda"
)

func main() {
	lambda.Start(handler.HandleRequest)
}

ここでgo modコマンドでモジュールをダウンロードしてからテストを実行してみましょう。

$ go mod init my-lambda-app
$ go mod tidy
$ go test ./...
?       my-lambda-app   [no test files]
ok      my-lambda-app/handler   0.369s

これで、Go言語でのLambda関数実装が完了しました。次に、SAMを使ってこの関数をビルドし、デプロイする方法を解説します。

SAMを使ったLambdaのビルドとデプロイ

SAMテンプレートの作成

プロジェクトのルートディレクトリに、template.yamlファイルを作成し、以下のような内容を記述します。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Parameters:
  Environment:
    Type: String
    Description: The environment (dev, stg, or prod) for this deployment
    AllowedValues:
      - dev
      - stg
      - prod

Resources:
  MyLambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub my-lambda-function-${Environment}
      Runtime: go1.x
      Handler: main
      CodeUri: ./
      Environment:
        Variables:
          APP_ENV: !Ref Environment
      Events:
        MyAPI:
          Type: Api
          Properties:
            Path: /
            Method: GET

Outputs:
  ApiURL:
    Description: URL of the API
    Value: !Sub https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/

このテンプレートでは、Environmentパラメータを使用して、環境変数APP_ENVの値を設定しています。これにより、デプロイ時に環境ごとに適切な値が設定されます。

ビルドとデプロイのコマンド

まず、sam buildコマンドを使用して、Lambda関数をビルドします。

$ sam build

sam buildコマンドは、Go言語のビルドに必要な環境変数やバイナリ名などを自動的に設定し、プロジェクトをビルドします。

最後に、デプロイします。このとき、--parameter-overridesオプションを使って、デプロイ環境(dev、stg、prod)を指定します。

$ sam deploy \
    --stack-name my-lambda-stack \
    --resolve-s3 \
    --s3-prefix dev-my-lambda-app \
    --capabilities CAPABILITY_IAM \
    --parameter-overrides Environment=dev

コマンドが正常に実行されるとmy-lambda-function-devがAWSにデプロイされます。

コンソール上でテストを実行してみます。

実行できました。今回のtemplate.yamlではApiGatewayも指定している為こちらもコンソールで確認してみます。

こちらもテストを実行してみます。

これでGo言語でSAMを構築してAWSにデプロイできました。

今後はGitHub Actionsからデプロイするので今作った構成をコンソールから消しておきます。(関数名が被るので)
コンソールのCloudFormation->スタックを表示しmy-lambda-stackをチェックして削除しておきます。

GitHub Actionsを用いた環境別デプロイの設定

GitHubリポジトリの準備

まず、Go言語で実装されたLambda関数のコードをGitHubリポジトリにプッシュします。リポジトリを作成し、以下のコマンドでリモートリポジトリにプッシュします。

$ git init
$ git add .
$ git commit -m "Initial commit"
$ git branch -M main
$ git remote add origin https://github.com/your-username/your-repo-name.git
$ git push -u origin main

GitHub Actionsのワークフロー構成

次に、GitHubリポジトリの.github/workflowsディレクトリに、deploy.yamlという名前のワークフローファイルを作成します。

name: Deploy to AWS Lambda

on:
  push:
    branches:
      - dev
      - stg
      - prod

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Set up Go
        uses: actions/setup-go@v4
        with:
          go-version: '1.20'

      - name: Set up AWS CLI
        uses: aws-actions/configure-aws-credentials@v2
        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: Install SAM CLI
        run: |
          pip install aws-sam-cli

      - name: Build and deploy
        run: |
          ENV=$(echo ${GITHUB_REF#refs/heads/})
          sam build
          sam deploy --stack-name my-lambda-stack-${ENV} --resolve-s3 --s3-prefix ${ENV}-my-lambda-app --capabilities CAPABILITY_IAM --parameter-overrides Environment=${ENV}

環境変数の設定

GitHubリポジトリのSettingsタブからSecretsを選択し、AWSのアクセスキーIDとシークレットアクセスキーを設定します。これらはワークフロー内で環境変数として利用されます。

AWS_ACCESS_KEY_ID: あなたのAWSアクセスキーID
AWS_SECRET_ACCESS_KEY: あなたのAWSシークレットアクセスキー

デプロイスクリプトの作成

上記のワークフローファイルでは、sam buildとsam deployコマンドを実行しています。これにより、Go言語で実装されたLambda関数がビルドされ、指定された環境にデプロイされます。

ワークフローのトリガー設定

以下のdeploy.yamlファイルは、dev, stg, prodブランチにプッシュされるたびに、それぞれの環境に対応したLambda関数がデプロイされるように設定されています。

実際のデプロイフローの動作確認

先ほど設定したGitHub Actionsのデプロイフローが正常に動作するかどうかを確認します。

コード変更のプッシュ

まず、コードに変更を加えて、対象となるブランチにプッシュします。この例では、devブランチにプッシュしてみます。

$ git checkout -b dev
$ git add .
$ git commit -m "Update Lambda function"
$ git push origin dev

GitHub Actionsの実行ログ確認

コードがプッシュされると、GitHub Actionsのワークフローがトリガーされます。リポジトリページの "Actions" タブをクリックし、実行中のワークフローを選択してログを確認します。

正常に終了すれば、ビルドとデプロイが成功していることがわかります。

AWSコンソールでデプロイ状況の確認

最後に、AWSコンソールにログインし、Lambdaサービスページにアクセスして、デプロイされた関数が正しく表示されているか確認します。関数名が my-lambda-function-dev のように、ブランチ名を含む形式で表示されていれば、環境別デプロイが正常に行われています。

このようにして、実際のデプロイフローが正常に動作することを確認できます。同様の手順で、stgおよびprodブランチにもプッシュして、それぞれの環境に対応したLambda関数がデプロイされることを確認してください。これにより、各環境でのデプロイフローが正しく機能していることが確認できます。

まとめ

本記事では、Go言語で実装されたAWS Lambda関数を、AWS SAMとGitHub Actionsを使って環境別(dev, stg, prod)に自動デプロイする方法を紹介しました。以下に、この方法の利点と今後の改善点や拡張可能性についてまとめます。

本記事で紹介した方法の利点

  1. 環境別デプロイの自動化: GitHub Actionsを利用することで、各環境へのデプロイを自動化し、手動でのデプロイ作業が不要になります。

  2. 一貫性のあるデプロイフロー: 各環境で同じデプロイフローを使用することで、環境間の差異による問題を防ぎます。

  3. シームレスな開発体験: リポジトリにプッシュするだけでデプロイが実行されるため、開発者はデプロイプロセスに関する心配をせずにコードに集中できます。

今後の改善点や拡張可能性

  1. deploy.yamlでAWS IAM取得にアクセスキーとシークレットキーを指定していますが、OpenID Connect (OIDC) の仕組みを使って一時的な認証情報を取得することが可能です。
    アマゾン ウェブ サービスでの OpenID Connect の構成

  2. テストの自動化: デプロイ前に自動的にテストが実行されるようにすることで、品質を確保し、デプロイの安全性を向上させることができます。

  3. カスタムドメインの設定: 環境ごとに異なるドメインやサブドメインを割り当てることで、より本番に近いテスト環境を構築することができます。

  4. 環境ごとのリソース制限: 各環境に応じたリソース制限(Lambdaのメモリ、タイムアウトなど)を設定することで、コストやパフォーマンスの最適化が可能になります。

  5. deploy.yamlのAWS IAMをブランチ毎に切り替えることで別々のAWS環境にデプロイできるようになります。
    これによってAWSプロジェクト単位で環境を切り替えている場合にも対応できるようになります。

  6. template.yamlでCloudFormationのMappingsとFindInMapを使うことで環境別に環境変数を定義できます。

ざっと箇条書きにしてしまいましたが、template.yamlとdeploy.yamlをいろいろ工夫することでかなり自由度の高いCI/CDパイプラインが組めて管理も簡単になるのでぜひ一度お試しください💪

レスキューナウテックブログ

Discussion