
はじめに
こんにちは、トモニテで開発を担当している吉田です。 AWS を活用したサービス運営において、IaC(Infrastructure as Code)ツールの選択は長期的な運用効率に影響することがあります。
本記事では、実際に私たちが経験した Serverless Framework v3 から lambroll と Terraform への移行事例をもとに、 移行の背景から具体的な手順、そして移行を通じて得られた知見についてまとめています。
なお、移行先の候補検討や各ツールの比較については、前回の検証編記事で詳しく解説していますので、併せてご参照ください。
背景:Serverless Framework を取り巻く環境変化
適切な IaC ツールの選択は大切な判断の一つです。代表的なツールとしては、Serverless Framework、AWS SAM、Terraform などが挙げられます。これらのツールは、それぞれ異なる特徴と利点を持ち、プロジェクトの要件に応じて選択することが多いです。
中でも、Serverless Framework は多くのプロジェクトで採用されている有力な選択肢の一つです。しかし、2024 年の v4 リリースに伴い、ライセンス形態の大幅な変更が発生しました。(参考)
こうした背景から、私たちは代替手段の検討を開始しました。ここからは、実際に私たちが行った移行プロジェクトの記録と、そこから得られた教訓について紹介します。
移行の背景
Serverless Framework v4 の変更点
2024 年、Serverless Framework v4 がリリースされ、従来のオープンソースモデルから舵を切りました。
- ライセンス形態の変更: 一定以上の収益を上げる組織では有料サブスクリプションが必須
- v3 のサポート終了: 2024 年末までの延長サポートのみで、機能改善・バグ修正は終了
- ランタイム対応の遅れ: 最新の AWS Lambda ランタイム更新への追随が停止
課題の整理
これらの変更により、私たちは以下の課題を感じました。
- 継続的なライセンス費用の発生
- 将来的なランタイムサポート切れのリスク
- v3→v4 移行に伴う運用フロー変更の負荷
移行先の採用
検証編記事で複数の移行先候補(Pulumi、AWS CDK、AWS SAM、lambroll + Terraform、Terraform + AWS CLI)を比較検討した結果、学習コストや社内での運用実績を考慮し、「lambroll + Terraform」の組み合わせを採用することにしました。
この選択により、以下のメリットを期待しました。
- ライセンス費用の回避
- IaC ツールの統一による運用効率向上
- AWS のネイティブ機能への完全対応
- より細かい粒度でのリソース管理
移行アプローチ
今回移行したのは、CodeBuild の実行結果を Slack に通知する Lambda 関数とその周辺リソース(EventBridge、IAM ロール)です。
注意:この通知機能は一時的に停止しても業務に重大な影響がないため、移行中のサービス停止を許容する前提で手順を設計しています。本番環境の重要なサービスを移行する場合は、無停止での移行手順を検討することをお勧めします。
移行戦略
安全性を重視し、以下の段階的アプローチを採用しました。
- 移行前の準備と調査
- lambroll による Lambda 関数の先行移行
- Terraform による周辺リソースの段階的移行
- 統合テストと動作確認
- Serverless Framework スタックの削除
実践的な移行手順
Step 1: 移行前の準備と調査
移行を安全に進めるため、まずは現状把握とバックアップ取得から始めます。
# CloudFormation スタックの詳細取得 aws cloudformation describe-stacks \ --stack-name your-service-stack # Lambda 関数のバックアップ aws lambda get-function \ --function-name your-function-name \ > function-backup.json # CloudFormation のバックアップ aws cloudformation get-template \ --stack-name your-service-stack \ > backup-cfn.json
Step 2: lambroll による Lambda 関数の移行
lambroll の初期化
# 既存の Lambda 関数から lambroll 設定を生成 lambroll init --download \ --function-name your-function-name
このコマンドにより、以下のファイルが自動生成されます。
function.json: Lambda 関数の設定.lambdaignore: デプロイ時に除外するファイルの定義function.zip: 現在の関数コード
function.json の調整
生成された function.json を環境変数やタイムアウト設定に合わせて調整。
{ "FunctionName": "your-function-name", "Runtime": "nodejs22.x", "Timeout": 30, "MemorySize": 128, "Role": "arn:aws:iam::{{ env `AWS_ACCOUNT_ID` }}:role/<IAM ロール名>", "Environment": { "Variables": { "SLACK_WEBHOOK_URL": "{{ env `SLACK_WEBHOOK_URL` }}" } } }
環境変数ファイルの作成
# development.env SLACK_WEBHOOK_URL=https://hooks.slack.com/services/YOUR/WEBHOOK/URL AWS_ACCOUNT_ID=123456789012
デプロイとテスト
# 事前確認 lambroll deploy --dry-run --envfile=<デプロイ先の環境>.env # 実際のデプロイ lambroll deploy --envfile=<デプロイ先の環境>.env # 動作確認 lambroll invoke \ --payload='{"detail":{"build-status":"FAILED"}}' \ --envfile=<デプロイ先の環境>.env
Step 3: Terraform による周辺リソースの移行
既存リソースの詳細調査
Terraform import を進めるために、既存リソースの正確な識別子を収集。
# EventBridge ルールの詳細 aws events list-rules --name-prefix "your-service" # Lambda パーミッションの確認 aws lambda get-policy --function-name your-function-name # EventBridge ターゲットの確認 aws events list-targets-by-rule --rule your-rule-name # IAM ロールの詳細 aws iam get-role --role-name your-lambda-role
Terraform 設定の作成
# Lambda ログ権限ポリシー data "aws_iam_policy_document" "lambda_logging" { statement { effect = "Allow" actions = [ "logs:CreateLogStream", "logs:CreateLogGroup", "logs:TagResource" ] resources = [ "<LambdaのロググループARN>" ] } statement { effect = "Allow" actions = [ "logs:PutLogEvents" ] resources = [ "<LambdaログストリームARN>" ] } } # Lambda ログポリシー作成 module "lambda_logging" { source = "terraform-aws-modules/iam/aws//modules/iam-policy" version = "5.59.0" name = "lambda_logging_policy" policy = data.aws_iam_policy_document.lambda_logging.json } # Lambda 実行ロール作成 module "lambda_logging_role" { source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role" version = "5.59.0" create_role = true role_name = "lambda_logging_role" role_requires_mfa = false trusted_role_services = [ "lambda.amazonaws.com" ] custom_role_policy_arns = [ module.lambda_logging.arn ] } # EventBridge ルール resource "aws_cloudwatch_event_rule" "codebuild_notification" { name = "codebuild-notification-rule" description = "CodeBuild status change notification" event_pattern = jsonencode({ source = ["aws.codebuild"] detail-type = ["CodeBuild Build State Change"] detail = { build-status = ["FAILED", "SUCCEEDED"] } }) } # EventBridge ターゲット resource "aws_cloudwatch_event_target" "lambda" { rule = aws_cloudwatch_event_rule.codebuild_notification.name target_id = "notificationLambdaTarget" arn = data.aws_lambda_function.notification.arn } # Lambda パーミッション resource "aws_lambda_permission" "allow_eventbridge" { statement_id = "AllowExecutionFromEventBridge" action = "lambda:InvokeFunction" function_name = data.aws_lambda_function.notification.function_name principal = "events.amazonaws.com" source_arn = aws_cloudwatch_event_rule.codebuild_notification.arn }
既存リソースの import
# EventBridge ルール terraform import aws_cloudwatch_event_rule.codebuild_notification \ <EventBridgeルール名> # EventBridge ターゲット terraform import aws_cloudwatch_event_target.lambda \ <EventBridgeルールの実際の名前>/<ターゲットID> # Lambda パーミッション terraform import aws_lambda_permission.allow_eventbridge \ <Lambda関数名>/<LambdaパーミッションのStatementId> # IAM ロール terraform import module.lambda_logging_role.aws_iam_role.this \ <IAM ロール名>
ポイント: CloudFormation で生成されたリソース名は予測しにくいため、AWS CLI で正確な名前を確認してから import する必要があります。また、import 完了後は必ず terraform plan でドリフトがないことを確認しましょう。
段階的な適用
# IAM ロールのみ先行適用 terraform apply --target=aws_iam_role.lambda_role # Lambda 関数の再デプロイ(新しい IAM ロールを使用) lambroll deploy --envfile=<デプロイ先の環境>.env # 残りのリソースを適用 terraform apply
IAM ロールを先行適用する理由は、lambroll が Lambda 関数をデプロイする際に実行ロールが必要だからです。ロールが存在しない状態で lambroll deploy を実行するとエラーになってしまうため、まず Terraform で IAM ロールを作成してから Lambda 関数のデプロイを行います。
Step 4: Serverless Framework からの分離
serverless.yml からの Lambda 関数削除
# functions セクションをコメントアウト # functions: # notification: # handler: handler.notifier # events: # - cloudwatchEvent: # event: # source: # - aws.codebuild
影響範囲の確認と適用
# 変更の影響を確認 sls package # Lambda 関数と関連リソースを Serverless 管理から削除 sls deploy # Serverless Framework による削除でリソースが消えるため、 # Terraform と lambroll で再作成 terraform apply lambroll deploy --envfile=<デプロイ先の環境>.env
Step 5: 統合テストと最終確認
エンドツーエンドテスト
# EventBridge 経由でのテスト aws events put-events --entries file://test-event.json # ログの確認 aws logs get-log-events \ --log-group-name /aws/lambda/your-function-name
Serverless スタックの削除
# 残存リソースの確認 aws cloudformation describe-stack-resources \ --stack-name your-service-stack # スタックの削除 sls remove
lambroll vs Serverless Framework: 運用面での違い
移行を通じて実感した大きな違いは、デプロイの仕組みと運用特性でした。
| 項目 | Serverless Framework | lambroll |
|---|---|---|
| デプロイ方式 | CloudFormation スタック更新 | Lambda API 直接呼び出し |
| 対象範囲 | Lambda + 全関連リソース | Lambda 関数のみ |
| デプロイ速度 | CloudFormation の処理時間に依存 | Lambda API の応答時間 |
lambroll は Lambda API を直接呼び出すため、コードの変更を素早く反映できます。開発時の反復サイクルが改善されました。
得られた知見とベストプラクティス
1. 段階的移行について
一度にすべてを移行するのではなく、Lambda 関数を先行移行することで、リスクを抑えることができました。これにより、問題が発生した場合の影響範囲を限定しやすくなります。
2. CloudFormation で生成されたリソース名の把握
Serverless Framework が自動生成するリソース名は予測しにくいです。移行前に AWS CLI を使って正確な名前を調査することで、import 作業がスムーズに進みやすくなります。
3. 環境変数管理の統一
lambroll と Terraform で環境変数の管理方法を統一することで、設定ミスを防ぎやすくなります。.env ファイルを活用した統一的な管理をお勧めします。
4. バックアップとロールバック戦略
移行作業では、各段階でのバックアップと、問題発生時のロールバック手順を事前に定義しておくことが大切です。
5. サービス停止の許容範囲の事前確認
今回の移行では通知機能という性質上、一時的なサービス停止を許容できました。しかし、本番環境の重要なサービスでは無停止移行が必要な場合があります。移行対象の重要度とサービス停止の影響範囲を事前に評価し、適切な移行戦略を選択することが重要です。
まとめ
今回の移行プロジェクトを通じて、ツール選択における長期的な視点の大切さを改めて認識しました。 また、移行作業の中で段階的なアプローチが、リスクを抑えながら着実に成果を得る鍵であることも実感しました。
検証編記事での事前調査から実際の移行作業まで、一連の取り組みを通じて、事前調査で想定していたメリット(デプロイ速度の改善、IaC ツールの統一、ライセンス費用の回避)を実現できています。
今後は、この移行で得られた知見を活かし、他のプロジェクトでも同様のアプローチを適用していく予定です。 IaC ツールの選択は一度決めれば終わりではなく、技術環境の変化に応じて継続的に見直しを行うことが重要です。今後も最適なツール選択と運用効率の向上に取り組んでいきます。
同様の移行を検討されている方の参考になれば幸いです。まずは小さなプロジェクトから段階的に試してみることをお勧めします。