AWS CodePipelineを用いてLambda関数を更新するCI/CDの実装

## はじめに
本記事は[PERSOL PROCESS & TECHNOLOGY Advent Calendar 2023](https://qiita.com/advent-calendar/2023/ppt)の記事です。
前記事([公式LINEアカウントへのログイン処理をAWS Lambda経由で実装してみた](https://qiita.com/inacyc_k/items/8337489e3e148db1f9e5))を実装する開発環境にて、自動でLambda関数を更新するCI/CD環境を作成してみました。開発フェーズを素早く実施するための環境整備のお話になります。とっても簡単便利なのでぜひ。

## 構成図

<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/98a2e7e7-1116-1c67-2fc5-d7f0a0f6710f.png">

各AWSサービスの役割は以下になります。

・Amazon ECR
　　→DockerImageレポジトリ
・AWS Lambda
　　→今回のデプロイ先
・CodeCommit
　　→コードGitリポジトリ
・CodeBuild
　　→ビルド・lambda更新コマンド実行
・CodePipeline
　　→CI/CDのパイプライン

## 実装

### ローカル環境

Dockerにてイメージ作成できる環境、およびCodeBuildで実行するbuildコマンドを記述したbuildspecファイルを作成します。

ファイル構成例は以下です。今回の実装で利用しないファイルは削除しているため、とても単純な設計となっています。
<img width="40%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/3e1f65ab-1572-9577-7436-70ea90a7b02e.png">

docker フォルダ
　　∟Dockerfile … Dockerのコンテナイメージを作成するコマンドファイル
　　∟requirements.txt … 必要なライブラリを記載、Dockerfileの参照先
domain フォルダ
　　∟app.py … Lambda関数コードを記載するファイル
buildspec.yml … CodeBuildで実行するbuildコマンドファイル

---
始めにDockerfileです。ここでのポイントはベースイメージの選択です。
今回はLambda関数実行環境であること、そしてLambda関数はPythonで記述すること、そしてECRからイメージを取得して作成されるということから、AWSベースイメージを使用しています。

```Dockerfile:Dockerfile
# ベースイメージの指定
FROM public.ecr.aws/lambda/python:3.11

# requirements.txtをコピーして依存関係をインストール
COPY ./docker/requirements.txt ./
RUN pip install --upgrade pip && \
    pip install -r requirements.txt && \
    rm requirements.txt

# アプリケーションのコードをコピー
COPY ./domain/  ./domain/

# ポートの公開
EXPOSE 80
CMD ["domain.app.lambda_handler"]
```

次にYMLファイルを作成します。これはこの後作成されるCodeBuildで実行されるコマンドを記述しているものとなります。
"＄"から始まる変数は環境変数値です。（詳細は実際に環境変数値を設定する箇所で説明）
envはイメージ作成時のタグ名とバージョン管理用の変数になります。こちらはご自身が管理しやすいよう変更してください。

各フェーズの説明です。
1.pre_build
→AWSCLIを利用してECRへ認証を行います。
2.build
→DockerFileを元にイメージを作成します。作成したイメージへタグつけを行います。
3.post_build
→ECRへイメージをプッシュします。プッシュしたイメージを元にLambda関数を更新します。

```yml:buildspec.yml
version: 0.2
env:
  variables:
    tag_name: codebuild
    version: 1.0.0
phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws ecr get-login-password --region $region | docker login --username AWS --password-stdin $account
  build:
    commands:
      - echo Building the Docker image...
      - docker build -t $tag_name:$version -f ./docker/Dockerfile .
      - docker tag $tag_name:$version $account/$ecr_repo:$version
  post_build:
    commands:
      - echo Pushing the Docker image...
      - docker push $account/$ecr_repo:$version
      - aws lambda update-function-code --function-name $lambda_func --image-uri $account/$ecr_repo:$version
```

### Amazon ECR

コンテナイメージを格納するレポジトリを作成します。
<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/a94157c5-bda7-ab91-e348-9552963039ee.png">

今回ECRへのプッシュはCodeBuildより実行するのでデフォルトは空でも大丈夫です。

### AWS Lambda

更新デプロイ先であるLambda関数を作成します。
<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/a94cff95-1a4d-4689-7f95-50517a5310d3.png">

今回はコンテナイメージより関数の作成をします。
"すでにコンテナイメージから作成されたLambda関数を更新する"CI/CDのため、中身はなんでも大丈夫です。
【注意！】ここでのアーキテクチャはCodeBuildの実行環境と同じにします。

（本CI/CDを利用して開発したLambda関数詳細はこちら→[公式LINEアカウントへのログイン処理をAWS Lambda経由で実装してみた](https://qiita.com/inacyc_k/items/8337489e3e148db1f9e5)）

### CodeCommit

コードを格納するレポジトリを作成します。
<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/2988255a-761c-019f-aa5f-6501b933a473.png">

作成したレポジトリのクローンをローカル環境へ作成します。今回はHTTPでの接続を表示します。
<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/06fbc480-c7ff-9538-3ec3-af353110137d.png">

ステップ２：Git認証　では、作業しているIAMユーザーに対してGit認証を作成する必要があります。IAMより該当ユーザー内からセキュリティ認証情報を選択、AWS　CodeCommitのHTTPS Git認証情報より認証情報を生成してください。ステップ3：リポジトリのクローンを作成するにおいて、ローカル環境からクローンを作成する際に入力が必要となる情報です。
<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/4c9547d4-8f8d-89be-3327-e96459da66e0.png">

### CodePipeline と CodeBuild

早速作成してきた上記AWSサービスをパイプラインで繋げていきます。
CodeBuildはCodePipeline内で新規作成します。

Step1 パイプライン設定を行う。
パイプラインタイプは機能や課金形態が異なるため、実際の環境に合わせて選択ください。
<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/0c8fe6f6-bb2f-2246-0c17-5e1b1496e7be.png">

Step2　ソースコードを選択する。
ソースプロバイダーにCodeCommitを選択し、先ほど作成したCodeCommitのレポジトリを選択します。
またレポジトリのブランチまで選択できるので、開発用と本番用で分けることが可能です。
<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/bfa31d87-bd6c-697a-bc14-46d5407ff22e.png">

Step3　CodeBuildの作成、設定を行う。
プロバイダーでCodeBuildを選択し、プロジェクトの作成を行います。
<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/68fc4fe7-a1de-965e-659e-ce529d8cddcf.png">

別タブで以下のように新規でCodeBuildを作成していきます。
<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/5a893b72-e3c2-0d99-52ab-a0b5da49bf78.png">

【注意！】ビルド実行環境について、イメージは先ほど作成したLambda関数のアーキテクチャと同じにします。
<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/1c018c40-9c18-abbb-0d99-0e28595a2795.png">

また今回のビルドコマンドでDockerを利用するため特権付与にチェック✅を忘れずに！
<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/de278412-5c8f-d83e-dab1-bf53740de37d.png">

設定完了したら、CodePipelineへ戻り、次へ進みます。

Step4　デプロイステージは今回スキップします。
"導入段階をスキップ"で次へ進みます。
<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/505a8b1d-d276-e89b-5132-75f0f9545345.png">

最後のページで設定内容を確認し、パイプラインの完成です。
完成するとすぐに初めのパイプラインが実行されます。ビルドで失敗してしまうので、次のステップで修正します。

### CodeBuild

先ほどCodePipeline内で作成したCodeBuildの設定を少し変更します。
CodePipelineから、またはCodeBuildの一覧からプロジェクト詳細を確認します。
<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/945783df-08f2-3d99-4c2b-6af2231c2b3e.png">

該当プロジェクトを選択し、ビルドの詳細を表示します。
ここで変更する点は環境変数値とIAMロールです。

1.環境変数
環境の編集を行い、環境変数を追加します。
<img width="90%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/3be51782-7737-4f66-510b-d5b8240b1b9f.png">

環境変数値は以下となります。ご自身の作成環境値に合わせ設定ください。
| 環境変数 | 説明 | 例 |
|:-----------|------------|------------|
| region       | 環境を作成したリージョン | ap-northeast-1　|
| account     | 自身のECRURI | 60000000.dkr.ecr.ap-northeast-1.amazonaws.com
| ecr_repo       | 作成したECR名  | test_ecr |
| lambda_func         | 作成したLambda関数名      | test_lambda |


2.IAM
同じく環境にある、サービスロールを押下し、CodeBuildに紐付くIAMロールを修正します。
このIAMロールはCodeBuild作成時に新規作成しましたが、LambdaとECRへのアクセス権限が足らないので追加します。

今回はAWSより提供されているポリシーをアタッチしています。（IAMは必要に応じ最小限の権限へ変更をお願いします。）
<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/5986d6b3-19bb-63e5-ab98-4abe6bdb361d.png">

これで問題なく動くはず。再度CodePipelineへ戻り、ステージの再試行を実施。
このようにオールグリーンになれば完成です。
<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1827273/d5d3d21d-7eab-0e71-9335-8e521d144c8b.png">


## まとめ
今回はAWS CodePipelineを用いてGit更新をトリガーに、Lambda関数へ自動デプロイ更新されるCI/CD環境を実装しました。ここまで自動でできていると、ローカル環境でGitPushするだけで数分後には自動で更新が反映されているのです。めちゃ便利〜。開発前に自動環境が揃っていると以降の作業負担が減るので、最初は大変でも環境整備しておいてよかったなと思います。
今後はpytestを利用したテストコードの実行まで自化できればいいな。。と実装検討中です。引き続き便利なCI/CD環境目指して精進します。