メインコンテンツにスキップ

SAM Build エラー解決記録 - Go Lambda関数のMakefile設定

タグ: 🏷 AWS ,Lambda ,Go ,SAM ,Makefile ,Build ,Error

はじめに

AWS SAM (Serverless Application Model) を利用してGoで実装された複数のLambda関数を管理していると、sam build実行時に予期せぬビルドエラーに遭遇することがあります。特に、provided.al2023のようなカスタムランタイムを使用する場合、Makefileベースのビルド設定が求められますが、その設定方法が直感的でない場合があります。

本記事では、私が実際に遭遇したMakefile not foundエラーを取り上げ、その原因と具体的な解決策をステップバイステップで解説します。同様の問題に直面している開発者の助けとなれば幸いです。

概要

AWS SAMでGoのLambda関数をビルドする際に発生したMakefile not foundエラーと、その解決方法について記録します。provided.al2023ランタイムを使用する場合の正しい設定方法を具体的に説明します。

発生したエラー

sam build を実行した際に、以下のようなエラーメッセージが出力されました。これは、SAMが指定されたパスにMakefileを見つけられなかったことを示しています。

$ sam build
Error: CustomMakeBuilder:MakeBuild - Makefile not found at /path/to/project/cmd/upload/Makefile

プロジェクト構成

エラーが発生したプロジェクトのディレクトリ構成は以下の通りです。cmdディレクトリ以下に、機能ごとに独立したLambda関数が配置されています。

poc-rag/
├── template.yaml
├── go.mod
├── go.sum
└── cmd/
    ├── upload/
    │   └── main.go
    ├── process/
    │   └── main.go
    ├── query/
    │   └── main.go
    └── status/
        └── main.go

問題の原因

エラーの根本的な原因は、以下の3つの要因が複合的に絡み合っていることでした。

  1. Makefileが存在しない: SAMがビルドターゲットごとにMakefileを探しにいきますが、各Lambda関数のディレクトリ(例: cmd/upload/)にMakefileが配置されていませんでした。
  2. ビルド方法の未指定: template.yamlで、各Lambda関数のビルド方法としてmakefileを使用することを明示的に指定していませんでした。
  3. Goモジュールのパス問題: 各関数ディレクトリで個別にビルドが試みられるため、プロジェクトルートに配置されたgo.modファイルが見つからず、依存関係の解決に失敗していました。

解決方法

これらの問題を解決するために、Makefileの作成とtemplate.yamlの修正を行いました。

1. 各Lambda関数ディレクトリにMakefileを作成

まず、各関数ディレクトリ(cmd/upload/, cmd/process/, cmd/query/, cmd/status/)に、それぞれ対応するMakefileを作成します。

cmd/upload/Makefile

build-DocumentUploadFunction:
	cd ../.. && GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -o cmd/upload/bootstrap ./cmd/upload

.PHONY: build-DocumentUploadFunction

cmd/process/Makefile

build-DocumentProcessingFunction:
	cd ../.. && GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -o cmd/process/bootstrap ./cmd/process

.PHONY: build-DocumentProcessingFunction

cmd/query/Makefile

build-QueryFunction:
	cd ../.. && GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -o cmd/query/bootstrap ./cmd/query

.PHONY: build-QueryFunction

cmd/status/Makefile

build-DocumentStatusFunction:
	cd ../.. && GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -o cmd/status/bootstrap ./cmd/status

.PHONY: build-DocumentStatusFunction

注: cdコマンドのパスを、元の絶対パスからプロジェクトルートを指す相対パス cd ../.. に変更しました。これにより、異なる環境でもビルドが可能になります。

2. template.yamlにBuildMethod追加

次に、template.yaml内の各Lambda関数のMetadataセクションにBuildMethod: makefileを追記し、SAMにMakefileを使ったビルドを指示します。

Resources:
  DocumentUploadFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: cmd/upload/
      Handler: bootstrap
    Metadata:
      BuildMethod: makefile
      
  DocumentProcessingFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: cmd/process/
      Handler: bootstrap
      Timeout: 300
      MemorySize: 1024
    Metadata:
      BuildMethod: makefile
      
  QueryFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: cmd/query/
      Handler: bootstrap
      Timeout: 60
      MemorySize: 512
    Metadata:
      BuildMethod: makefile
      
  DocumentStatusFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: cmd/status/
      Handler: bootstrap
    Metadata:
      BuildMethod: makefile

重要なポイント

この設定を行う上で、いくつか注意すべき重要な点があります。

1. ターゲット名の命名規則

SAMはbuild-{FunctionName}という命名規則に従って、Makefile内のターゲットを自動的に呼び出します。template.yamlで定義したLambda関数のリソース名(例: DocumentUploadFunction)と、Makefileのターゲット名(例: build-DocumentUploadFunction)を正確に一致させる必要があります。

2. プロジェクトルートからのビルド

Makefile内のビルドコマンドは、cd ../..のようにして一度プロジェクトのルートディレクトリに移動してからgo buildを実行しています。これにより、プロジェクトルートにあるgo.modファイルが正しく認識され、依存関係が解決されるようになります。SAMのビルドはコンテナ内で実行されるため、特定のローカルマシンに依存する絶対パス(例: /media/tv_record/poc-rag)は使わず、常にプロジェクト内での相対パスで指定することが不可欠です。

3. ビルドオプション

Goのビルドコマンドで使用している各オプションには以下の意味があります。

  • GOOS=linux: Lambdaの実行環境であるLinux向けにコンパイルします。
  • GOARCH=amd64: x86_64アーキテクチャを指定します。
  • CGO_ENABLED=0: CGOを無効にし、外部ライブラリへの依存がない静的リンクされたバイナリを生成します。
  • -ldflags="-s -w": デバッグ情報(-s)とシンボルテーブル(-w)を除去し、バイナリサイズを削減します。
  • -o cmd/upload/bootstrap: providedランタイムでLambdaが実行するファイル名をbootstrapとして出力します。

結果

上記の手順で修正を行った後、sam buildコマンドが正常に完了するようになりました。

$ sam build

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Validate SAM template: sam validate
[*] Invoke Function: sam local invoke
[*] Test Function in the Cloud: sam sync --stack-name {{stack-name}} --watch
[*] Deploy: sam deploy --guided

まとめ

Go言語、AWS SAM、そしてprovided.al2023ランタイムを組み合わせてサーバーレスアプリケーションを開発する際には、以下の4つのポイントを押さえることが重要です。

  1. 各Lambda関数のCodeUriディレクトリ内にMakefileを配置する。
  2. Makefileのターゲット名はbuild-{FunctionName}という規約に従う。
  3. ビルドコマンドはプロジェクトルートから実行し、go.modを正しく参照させる。
  4. template.yamlMetadataBuildMethod: makefileを明示的に指定する。

これらの設定を正しく行うことで、複数のGo Lambda関数を含むSAMプロジェクトでも、スムーズなビルドプロセスを実現できます。