俺たち流! Lambda 関数開発:Codespaces × Terraform × GitHub Actions の活用術
今回のテーマ:Lambda 関数
こんにちは、TOPPANデジタルで AWS Top Engineers を目指している奈良です。今回のテーマは 「Lambda 関数」です。AWS サービスの中でも頻繁に利用される Lambda 関数は、アプリケーション開発だけでなく、自動化や監視など多用途に活用できる非常に優秀なサービスですね。
課題:汎用性が高いゆえの悩み
しかし、Lambda 関数はその汎用性の高さから、開発・管理・デプロイの方法が多岐にわたります。様々な案件や記事、公式ドキュメントを少し調べただけでも、多くの選択肢が見つかります。そこで、私たちが実案件を通して試行錯誤した3つのパターンからから、開発・管理・デプロイに特に効果的だったパターンをご紹介したいと思います!
検証:(俺たち流)開発手法
私たちは「通常のアプリケーションと同様の方法」で開発を行うことにいたしました。具体的な方法に入る前に、このアプローチの背景について説明します。Lambda は AWS サービスでありながら、アプリケーションとしても機能します。そのため、これらを無理に一つにまとめるのではなく、AWS サービスとアプリケーションを分離して管理する方法を選びました。
アプリケーション部分は通常の開発・管理
AWS サービスは Terraform で構築・管理・デプロイ
処理の流れイメージ
それでは具体的な方法の解説をしていきたいと思います。まずは、全体の流れですが、以下の3つのステップです。流れ自体には特別なステップや複雑なロジックなどは、含めていません。
Lambda 関数のソースコードを pullrequest
pullrequest が branch に merge されると Githubactions が起動
Terraform にて AWS へ apply
構成イメージ
上記の処理の流れを実現するために、さらに詳細にディレクトリ構成などをご紹介します。まず、Lambda 関数をデプロイする Terraform と Lambda 関数のソースコードを同一リポジトリで管理しています。
以下に、ディレクトリ構成、Lambda 関数の設定とポリシー、GitHub Actions の yml ファイル構成をご紹介します。doc、src、test コードについては、皆さまのご想像通りかと思いますので割愛いたします。
ポイントは、各 Lambda 関数ごとに doc、src、test、terraform のコードを1つのセットにしています。
ディレクトリ構成
app
├ .github # GitHub Actions用のディレクトリ
├ template # Lambda関数ディレクトリ内に配置する設定ファイルのテンプレート
│ └ terraform
├ terraform # Terraformのコード
└ lambda_func_A # Lambda関数のディレクトリ(Lambda関数名と同一のディレクトリ名)
├ doc # Lambda関数のREADMEを格納
├ src # Lambda関数にアップロードするコードを格納
├ test # テストコードを格納
└ terraform # Terraform用設定ファイルを格納(環境ごとに準備)
├ dev.lambda.config.json # Lambda関数の設定ファイル
├ dev.policy_statement.json # Lambda関数で使用するIAMロールのポリシー記載
└ requirements.txt # Lambda関数にレイヤーを設定
├ lambda_func_B
├ lambda_func_C
… (以下略)
lambda.config.json.tmpl ( Lambda 関数の設定を記述するファイル)
{
"source_dir": "../../../../{edit here}/src/",
"function_name": "ai-proof-dev-textapi-force-shutdownv2",
"handler": "lambda_function.lambda_handler",
"runtime": "python3.10",
"environment": [
{"name": "ENV_VAL1", "value": "value1"},
{"name": "ENV_VAL2", "value": "value2"}
],
"ephemeral_storage_size": 512,
"role_name": "${var.role_name}",
"file_system_arn": "",
"local_mount_path": "",
"subnet_ids": [],
"security_group_ids": [],
"timeout": 20,
"memory_size": 128,
"policy_statement_path": "../../../../{edit here}/terraform/policy_statement.json",
"managed_policy_arns": [
"arn:aws:iam::aws:policy/AmazonEC2FullAccess",
"arn:aws:iam::aws:policy/CloudWatchFullAccessV2"
]
}
policy_statement.json.tmpl ( Lambda 関数で使用するIAMロールのポリシー記載)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "logs:CreateLogGroup",
"Resource": "arn:aws:logs:ap-northeast-1:${account_id}:*"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"${cloudwatch_logs_arn}:*"
]
}
]
}
GitHub Actions の yml ファイル(※SECRET は GitHub の SECRET に値をセットし、デプロイ時に取得しています)
name: Develop Deploy
on:
push:
branches:
- develop
env:
TF_VERSION: 1.6.2
AWS_ACCESS_KEY_ID: ${{ secrets.DEV_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.DEV_SECRET_ACCESS_KEY }}
DEV_TFSTATE_BACKEND: ${{ secrets.DEV_TFSTATE_BACKEND }}
DEV_TFSTATE_KEY: ${{ secrets.DEV_TFSTATE_KEY }}
DEV_ROLE_ARN: ${{ secrets.DEV_ROLE_ARN }}
AWS_REGION: ${{ secrets.DEFAULT_REGION }}
TFSTATE_ENCRYPT: ${{ secrets.TFSTATE_ENCRYPT }}
TF_VAR_role_arn: ${{ secrets.DEV_TERRAFORM_ARN }}
jobs:
terraform_apply:
name: Terraform apply
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
pull-requests: write
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: ${{ secrets.DEV_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: Check Branch
run: |
if ["${{ github.event_name }}" == "push"] && [ "${{ github.ref }}" != "refs/heads/develop" ]; then
return
fi
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Execute create paths file
working-directory: terraform/src/environment/dev
run: |
chmod 755 ./create_paths_file.sh
./create_paths_file.sh
ls -l
- name: Execute create layer file
working-directory: terraform/src/environment/dev
run: |
chmod 755 ./create_layer_file.sh
./create_layer_file.sh
- name: Create backend config
working-directory: terraform/src/environment/dev
run: envsubst < dev.tfbackend.tmpl > dev.tfbackend
- name: init
working-directory: terraform/src/environment/dev
run: |
ls -l
cat ./dev.tfbackend
terraform init -backend-config=dev.tfbackend
- name: apply
working-directory: terraform/src/environment/dev
run: terraform apply -auto-approve
所感:実際に導入してみた効果
Lambda 関数の自由度と柔軟性を活かして、以下のような課題を解決し、効果的な開発・管理・デプロイのパターンを確立できました。
・チームメンバーが使い慣れた環境(VSCode や Codespaces、GitHub)での開発・コード管理が可能
・ AWS へデプロイする前にユニットテストが可能
・AWS コンソール上での開発時に発生しがちなセキュリティ事故や他リソースへの影響を防ぎ、コストを抑えることができる
〆:さいごに
最後までお読みいただき、ありがとうございました。今回ご紹介した方法は一例に過ぎません。他にも、AWS SAM を利用した方法などが存在します。今後も試行錯誤を重ね、より良い方法を皆さまにお届けできるよう努めます。
それでは、次回の記事もお楽しみに!
参考
本記事の内容を以下のLT会でも紹介し登壇資料もアップしております。なお、登壇時間の都合上、LT会ではご紹介できなかったコードも記載しております!