この記事は レコチョク 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関数の作成と更新をするために使います。
わかりやすい図
1. CodeCommitにPushされたコードの変更をCodePipelineで検知
2. CodeBuildでテストとビルド
3. 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を作るというものです。
これらファイルたちをPushしてみましょう。
無事にビルドフェーズまで成功しました!
CloudFormationでCD環境を作る
作成したパイプラインを編集してデプロイフェーズを追加していきます。
まず、編集するボタンを押下して編集画面を開きます。
次にBuildの下のステージを追加するボタンを押下します。
ステージ名はDeployにしましょう。
無事に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画像が壊れた原因を探る」となります。お楽しみに!
参考文献
以下の情報を参考にさせていただきました!