この記事は レコチョク Advent Calendar 2023 の18日目の記事となります。
はじめに
こんにちは、株式会社レコチョク新卒1年目エンジニアの笹野です。
サーバーサイドエンジニアとして、主にdヒッツというサービスに携わっています。
好きな音楽はK-POPとヴィジュアル系全般で、おすすめアーティストはENHYPENとΛrlequiΩ(アルルカン)です!
好きなAWSのサービスはLambdaで、プライベートで作成したAPIはよくLambdaに置いて使っています。
今回は、そんな大好きなLambdaの開発体験を向上させるべくCI・CD環境を構築してみました。
CI・CDとは?
- CI(Continuous Integration)
- CIとは継続的インテグレーションのことで、ビルドとテストを自動化します。
- CD(Continuous DeliveryもしくはContinuous Deploy)
- CDとは継続的デリバリーのことで、リリース作業を自動化します。
CI・CD環境を構築すると、生産性、開発速度、コード品質の向上といったたくさんの嬉しいことがあります。
今回使用するAWSについて
- AWS Lambda
- Lambdaは関数単位でコードを実行できるサービスであり、サーバレス環境で動きます。ちなみに内部的にはAWS側のサーバに実行用のコンテナを作成→そのコンテナで関数を実行→実行が終了してしばらく経った後に実行用のコンテナを削除という流れでサーバレスを実現しているそうです。
- AWS CodeCommit
- ソース管理サービスです。今回はLambda関数のGitリポジトリとして使います。
- AWS CodeBuild
- ソースコードのコンパイル、テスト、デプロイを自動化するサービスです。
- AWS CodePipeline
- デプロイまでの作業を可視化と自動化するサービスです。CodeCommitとCodeBuildを一連のプロセスとして管理してくれます。
- Amazon Simple Storage Service (Amazon S3)
- ストレージサービスです。今回はビルドしたパッケージファイルを一時保存する場所として使います。
- AWS CloudFormation
- AWSリソースを自動で構築するサービスです。Lambda関数の作成と更新をするために使います。
わかりやすい図
- CodeCommitにPushされたコードの変更をCodePipelineで検知
- CodeBuildでテストとビルド
- CloudFormationでLambdaにデプロイ
という流れになるよう構築していきます。
構築手順
- CodeCommitでリポジトリを作る
- CodePipelineを作成する
- CodeBuildでCI環境を作る
- CloudFormationでCD環境を作る
CodeCommitでリポジトリを作る
リポジトリを作る
まずはAWSコンソールで「CodeCommit」と検索し、リポジトリを作成します。
適宜リポジトリ名を入力し、作成ボタンを押下でリポジトリの作成は完了です!
リポジトリをクローンする
クローンするには、Git認証情報が必要なのでIAMで設定していきます。
AWSコンソールで「IAM」と検索し、現在ログインしているユーザを選択します。
セキュリティ認証情報を開き、認証情報を生成ボタンを押下するとクローンに必要なGit認証情報が作成されます。
表示されたユーザ名とパスワードは忘れずダウンロードするかメモっておきましょう。
そして、接続のステップに表示されているクローンコマンドを実行します。
git clone https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/test-sasano-lambda-cicd-rep
適宜Git認証情報を入力し、リポジトリのクローンは完了です!
CodePipelineを作る
作成したリポジトリを絡めて、CodePipelineを作成していきます。
AWSコンソールで「CodePipeline」と検索し、パイプラインを作成します。
パイプライン名を入力し、パイプラインタイプは一旦V1を選択してください(あとから変更可)。そして、今回は新しいサービスロールを作成します。
次に、ソースプロバイダーはCodeCommitを選択し、リポジトリ名には作成したリポジトリ名を入力してください。
CodeBuildでCI環境を作る
先程の画面で次へを押下するとビルドステージの追加ステップに移ります。
プロバイダーはCodeBuildを選択します。そして、プロジェクトを作成するを押下し、CodeBuildを作っていきます。
CodeBuildを作る
次に環境はLambdaを選択し、作成するLambdaの環境を選択してください。
今回はPython3.11
、x86_64
のLambdaを作成します。ここでも新しいサービスロールを作成します。
buildspecファイルを使用するを選択して、CodeBuildの作成は完了です。
CodePipelineの作成に戻りますが、一旦デプロイの設定はスキップし、CodePipelineを作っちゃいます。
CodePipelineが動きますが、一度もリポジトリにPushしていないのでSourceは失敗でOKです。
Lambda関数を作る
メインとなるソースコードを準備します。
今回は渡された2つの数値の合計を返す関数とそのテストをPythonで簡単に作りました。
def lambda_handler(event, context):
return {
"result": (event["first_num"] + event["second_num"])
}
import pytest
from lambda_function import lambda_handler
def test_lambda_handler():
event = {
"first_num": 3,
"second_num": 2
}
result = lambda_handler(event, {})
assert (result["result"] == 5)
buildspec.ymlを作る
CodeBuildでビルドするために、buildspecを作成します。
version: 0.2
phases:
install:
runtime-versions:
python: 3.11
commands:
- pip install pytest
pre_build:
commands:
- python -m pytest test_lambda.py
build:
commands:
- sam package --template-file template.yml --s3-bucket codepipeline-ap-northeast-1-621537184721 --output-template-file package.yml
artifacts:
files:
- package.yml
install
では使用するライブラリをインストールします。もちろんrequirements.txt
を使ったインストールもできます。
pre_build
ではテストを実行します。テストが通らなかった場合、ちゃんとビルド作業が止まります。
build
ではsam package
コマンドとtemplate.yml
を使ってパッケージングしたものをS3に保存します。指定するS3はCodePipelineの作成と同時に作られたバケットでも良いです。その場合、バケットポリシーで暗号化されていないオブジェクトのアップロードを拒否する権限を削除する必要があることに気をつけてください。
AWSTemplateFormatVersion: "2010-09-09"
Transform: "AWS::Serverless-2016-10-31"
Resources:
Function:
Type: "AWS::Serverless::Function"
Properties:
FunctionName: calcSum
Handler: lambda_function.lambda_handler
Runtime: python3.11
CodeUri: .
このテンプレートは、calcSum
というLambdaを作成(すでに存在していれば更新)するテンプレートになっています。Python3.11をランタイムとし、ルートディレクトリにあるlambda_function
ファイルのlambda_handler
関数のLambdaを作るというものです。
無事にビルドフェーズまで成功しました!
CloudFormationでCD環境を作る
作成したパイプラインを編集してデプロイフェーズを追加していきます。
まず、編集するボタンを押下して編集画面を開きます。
無事にDeployステージが追加されました。
次に、アクショングループを追加するボタンを押下…と行きたいところですが、
その前にCloudFormation用のロールを新たに作成しておきましょう。
IAMからロールを作成します。サービスはCloudFormationを選択します。
取り急ぎ、CloudFormationのFullAccess権限をアタッチします。
それに加え、下記の操作も許可してください。
iam:CreateRole
iam:DetachRolePolicy
iam:DeleteRole
iam:AttachRolePolicy
iam:GetRole
iam:PassRole
lambda:GetFunction
lambda:CreateFunction
lambda:DeleteFunction
lambda:TagResource
lambda:UpdateFunctionCode
lambda:ListTags
s3:GetObject
これらはLambdaのデプロイに必要な操作になるため許可が必要です。
適宜ロール名を入力し、CloudFormation用のポリシーは完成です!
CloudFormation用のポリシーができたところで、アクショングループを追加していきます。
まずは変更セットを作成または更新するアクションを追加します。
アクション名をCreateUpdateChangeSetとし、アクションプロバイダーはCloudFormationを選択、入力アーティファクトはBuildArtifactを選択します。
アクションモードは変更セットを選択または交換するを選択し、適宜スタック名とセット名を入力します。テンプレートはBuildArtifactのpackage.yml
を選択します。そして、このアクションはIAMリソースの作成があり得るのでCAPABILITY_IAMを追加します。ロールは先程作成したCloudFormation用のロールを選択します。
できた変更セットを実行するため、もう一つアクションを追加します。
アクション名はExecuteChangeSetとし、アクションプロバイダーはCloudFormationを選択します。入力アーティファクトはBuildArtifactを選択し、アクションモードは変更セットを実行するを選択します。
これで一連の流れが出来上がりました!パイプラインの変更をリリースするボタンを押下して動きを確かめてみましょう。
Lambdaを見てみると新たにLambda関数が作成されていることが確認できます。
では、試しにPythonのコードを下記に変更してPushしてみましょう。(ステータスコードを追加しています)
def lambda_handler(event, context):
return {
"statusCode": 200,
"result": (event["first_num"] + event["second_num"])
}
Push後、無事にPipelineの流れはすべて成功し、Lambdaのコードが更新されていることまで確認できました!
これでLambdaのCI・CD環境は完成です!
おわりに
本記事ではLambdaのCI・CD環境を構築しました。
サーバレスのCI・CD環境といっても、特別な工程が必要になったりせず、シンプルな構成でCI・CDを実現できました。今後はLambda関数のデプロイを検証環境と本番環境とで分けたり、API Gatewayと絡めたりしてみたいと思いました。これらについてはtemplate.yml
の追記でなんとかなりそうな気がしたので、これから試しつつ作ってみよう思います。
最後まで読んでいただきありがとうございました!
明日のレコチョク Advent Calendar 2023は19日目 「バイナリを読みながらJPEG画像が壊れた原因を探る」となります。お楽しみに!
参考文献
以下の情報を参考にさせていただきました!
- 【aws】CodeCommitにpushするだけでlambda関数をデプロイするパイプラインを作る【CodePipeline】
- CodePipelineでLambdaのデプロイをしようとして躓いた話
- AWS の SAM テンプレートを作成します - AWS CodeDeploy
この記事はレコチョクのエンジニアブログの記事を転載したものとなります。