こんにちは。Energy Data Dev1チームのマネージャーの宮尾です。
社内のLambdaの運用基盤を「Serverless Framework」から「AWS SAM(Serverless Application Model)」へ移行する作業を担当しました。 Lambdaではメール送信や電力使用量ファイルの変換処理を行っています。 本記事では、その移行過程とAIエージェント「Cursor」を利用した際の所感を記載します。
移行理由
これまでServerless Framework v3を利用していましたが、v4から有料プランが必須となるため、無償で利用できるAWS公式のSAMへ移行することにしました。 v3を使い続ける選択肢もありましたが、アップデートがないことによるセキュリティ面での不安やコスト面の理由が主です。
移行作業の流れ
1. 既存Serverless Frameworkプロジェクトの確認
まず、既存のserverless.ymlをCursorに読み込ませ、SAMテンプレートへの変換方針を検討しました。主要なリソース(Lambda、SQS、DynamoDBなど)はCursorの支援で抽出し、SAM用のtemplate.yamlの雛形を作成しました。
2. Python依存パッケージの管理
AWS SAMではpoetryを直接利用できないため、poetry exportコマンドでrequirements.txtを生成し、そのファイルをLayerとしてデプロイに利用しました。これにより、Serverless Framework時代のpoetryベースの開発体験を維持しつつ、SAMのLayer機能で依存パッケージを管理できました。
AIエージェントとのやり取りはだいたい10往復くらいで動くものが完成しました。
プロンプトとしては、「poetryを使っているのですが、それを使うようにすることはできますか?」とか、「このエラーはなんですか?」等のだいぶざっくりした質問でした。
エラーをコピペして尋ねることが大多数だったかと思います。
deploy: mkdir -p layer/ poetry export -f requirements.txt --output layer/requirements.txt --without-hashes
このようにして、poetryの利便性とSAMの運用を両立しています。
3. デプロイ・テストの自動化
デプロイやテストの自動化には、GitHub ActionsではなくMakefileを利用しました。CursorにMakefileの書き方を相談しながら、下記のような内容で運用しています。 AWS SAMではcacheファイルやディレクトリを除外してデプロイする機能はないため、Makefileではそれらを削除するようにしています。 こちらはそんなに大層なことをやっていなかったのと、ビルド方法はもう提示してくれていたので、数回のやりとりで完成させられました。
STAGE ?= test-stg deploy: mkdir -p layer/ poetry export -f requirements.txt --output layer/requirements.txt --without-hashes rm -rf app/__pycache__ .aws-sam poetry run sam build poetry run sam deploy --parameter-overrides Stage=$(STAGE) test: PYTHONPATH=app poetry run pytest tests/test_dr_mail_by_ses.py
4. 環境変数の管理方法の違い
各クライアントごとにLambdaを新規で作成する際、環境変数の管理方法に苦労しました。Serverless Frameworkでは、XXX-stg.yamlやXXX-prod.yamlで各ステージやクライアントごとにYAMLファイルを分けて環境変数を読み込むことができ、柔軟な管理が可能でした。
一方、AWS SAMではデプロイ時に外部ファイルから環境変数を直接読み込む仕組みがなく、Serverless Frameworkのようなファイル分割運用ができません。そのため、samconfig.tomlで各ステージごとにparameter_overridesを設定し、パラメータを切り替える運用にしました。
こちらもやりとりが多く、大体20往復くらいは会話しました。
ひたすらプロンプトにエラーを貼り付ける作業を繰り返しておりました。
[test-stg.deploy.parameters] stack_name = "ses-sendmail-test-stg" s3_bucket = "aws-sam-assets" s3_prefix = "ses-sendmail-test-stg" parameter_overrides = "Stage=test-stg" confirm_changeset = false
このように、samconfig.tomlでステージごとにパラメータを切り替えることで、環境ごとの設定を管理しています。Serverless Frameworkのような柔軟さはありませんが、SAMの標準的な運用方法として落ち着きました。
注意点・所感
- Serverless Framework特有のカスタムリソースやプラグインは自動変換が難しく、人手による確認・修正が必要でした。
- Cursorの提案内容は必ず公式ドキュメントと突き合わせて検証する必要があります。間違いを平気で提案してくるので、最終的に頼りになるのはやはり公式ドキュメントです。
- 雛形作成や調査の効率は向上しましたが、最終的な品質担保は人間が行う必要があります。
- プロンプトの質が悪いと、AIエージェントの提案が間違っていることが多かったかなという印象でした。具体的な指示を出すことの大切さを感じました。
まとめ
AIエージェント(Cursor)を活用することで、調査や雛形作成の効率は上がりましたが、細かい調整や最終的な判断は人間が担う必要があると感じました。コスト面の理由で移行を検討している場合、SAMは有力な選択肢となります。
参考:最終的にできたtemplate.yaml(一例)
以下は、クライアント「test-stg」用に整理した最終的なtemplate.yamlの例です。
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: DR Send Mail Lambda Functions (test-stg) Globals: Function: Timeout: 15 MemorySize: 256 Runtime: python3.13 Architectures: - arm64 Parameters: Stage: Type: String Default: test-stg Description: Deployment stage AllowedValues: - test-stg SESIdentityResource: Type: String Default: "arn:aws:ses:ap-northeast-1:123456789012:identity/test.example.com" Description: SES identity resource ARN Resources: PythonRequirementsLambdaLayer: Type: AWS::Serverless::LayerVersion Properties: LayerName: !Sub "dr-ses-sendmail-libs-${Stage}" Description: "Python libs: boto3, jinja, sendgrid and more" ContentUri: layer/ CompatibleRuntimes: - python3.13 CompatibleArchitectures: - arm64 RetentionPolicy: Retain Metadata: BuildMethod: python3.13 BuildArchitecture: arm64 SendDrMailBySES: Type: AWS::Serverless::Function Properties: FunctionName: !Sub "dr-ses-sendmail-${Stage}" CodeUri: app/ Handler: send_dr_mail_by_ses.send_mail Description: !Sub "send DR mail by SES (${Stage})" Layers: - !Ref PythonRequirementsLambdaLayer Environment: Variables: STAGE: !Ref Stage COMPANY: test_company ENVIRONMENT: staging DYNAMO_DB_TABLE: test_stg_dr_ses_sendmail SES_CONFIGURATION_SET: test-stg-configuration-set SES_NO_TRACK: yes SES_RETRY_COUNT: 5 FROM_ADDRESS: test@example.com Events: SQSEvent: Type: SQS Properties: Queue: "arn:aws:sqs:ap-northeast-1:123456789012:test_stg_dr_ses_sendmail.fifo" BatchSize: 1 Policies: - Version: "2012-10-17" Statement: - Effect: Allow Action: - ses:SendEmail - ses:SendRawEmail Resource: !Ref SESIdentityResource - Effect: Allow Action: - dynamodb:GetItem - dynamodb:Query - dynamodb:DeleteItem - dynamodb:PutItem Resource: "arn:aws:dynamodb:ap-northeast-1:123456789012:table/test_stg_dr_ses_sendmail" Tags: Service: test Mappings: StageConfig: test-stg: Company: test_company Environment: staging DynamoDBTable: test_stg_dr_ses_sendmail DynamoDBTableArn: "arn:aws:dynamodb:ap-northeast-1:123456789012:table/test_stg_dr_ses_sendmail" SESConfigurationSet: test-stg-configuration-set SESNoTrack: yes SESRetryCount: 5 FromAddress: test@example.com SESQueueArn: "arn:aws:sqs:ap-northeast-1:123456789012:test_stg_dr_ses_sendmail.fifo" Outputs: PythonRequirementsLambdaLayer: Description: Python requirements Lambda layer Value: !Ref PythonRequirementsLambdaLayer
参考:最終的にできたsamconfig.toml(一例)
以下は、クライアント「test-stg」用に整理したsamconfig.tomlの例です。
version = 0.1 [test-stg.deploy.parameters] stack_name = "ses-sendmail-test-stg" s3_bucket = "aws-sam-assets" s3_prefix = "ses-sendmail-test-stg" parameter_overrides = "Stage=test-stg" confirm_changeset = false