医療職からデータサイエンティストへ

統計学、機械学習に関する記事をまとめています。

CodepipeLineとgithubを使って、lambdaの更新を自動化

以前にFastAPI+lambdaで機械学習推論APIを作成しました。

www.medi-08-data-06.work

ここでは、中身の修正があると、毎回手作業でECRへのpush、およびlambda関数の更新を行う必要があり手間でした。

今回は、AWSのcodepiplineとgithubを使用し、最小限のCI/CD環境を構築したので、その方法をまとめておきます。

今回のコードレポジトリ GitHub - koy0208/lambda-cicd

役に立つ人

  • lambda関数をECRにあるdocker-imageから作成しており(以下docker-lambdaとする)、CI/CDパイプラインがない方

最終形態

最終的な構成とリポジトリ構造は以下のようになります。

codepipeline
codepipeline

├── Dockerfile
├── README.md
├── app
│   └── app.py
├── buildspec.yml
└── requirements.txt

下準備

今回は、すでに作成ずみのdocker-lambdaを自動更新することを想定しているため、下記作業の詳細は割愛します。

githubレポジトリ作成

lambda関数およびdockerファイルなどを置いておく。

ECRレポジトリの作成とイメージのpush

dockerイメージを格納するためのレポジトリを作成する。作成後に表示されるプッシュコマンドを参考に、イメージをプッシュする。今回はlambda-cicdという名前で作成

docker-lambda作成

ECRのdockerイメージからlambdaを作成する。testを実行し、正常に動作するか確認する。今回はcicd-testという名前でシンプルなlambda関数を使用する。

import json
def lambda_handler(event, context):
    return {"statusCode": 200, "body": json.dumps("Hello from Lambda!!")}

テストの実行結果は以下のとおり

codepipeline
codepipeline

buildspec.ymlファイルの設定

ここからcodepipelineの設定に入っていきます。

buildspec.ymlとは、codepipeline実行時の動作仕様を決めるものです。ここでは、ECRへのdockerイメージpush、およびlambdaの更新を行うように記述しています。

version: 0.2

phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - IMAGE_TAG=$(date "+%Y-%m%d-%H%M%S")
      - echo IMAGE_TAG is $IMAGE_TAG
      - docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
      - docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker image...
      - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
      - echo Remove latest Tag from Current Docker image...
      - aws ecr batch-delete-image --repository-name $IMAGE_REPO_NAME --image-ids imageTag=latest
      - echo Set latest Tag To New Docker image...
      - MANIFEST=$(aws ecr batch-get-image --repository-name $IMAGE_REPO_NAME --image-ids imageTag=$IMAGE_TAG --query images[].imageManifest --output text)
      - aws ecr put-image --repository-name $IMAGE_REPO_NAME --image-tag latest --image-manifest "$MANIFEST"
      - aws lambda update-function-code --function-name $LAMBDA_FUNC_NAME --image-uri $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG

CodePipelineの作成

AWSのコンソール画面から、CodePipelineを作成していきましょう。今回は、lambda-cicdという名前にします。

codepipeline
codepipeline

次にソースの選択です。今回はgithub(バージョン2)を選択し、対象のレポジトリとブランチ名を選択ます。初期は、Githubとの接続設定がいるので、画面の指示にしたがって進めてください。

ちなみに、、
組織で利用しているGithubリポジトリを使う場合は、オーナー権限が必要になることがあります。うまくいかない場合は、管理者に問い合わせてみてください。

codepipeline
codepipeline

codepipeline
codepipeline

最後はビルドステージの設定です。ここで、新たなビルドプロジェクトを作成していきます。

codepipeline
codepipeline

環境は事前に作成してあるECRイメージを選択します。 また、特権付与にはチェックをいれてください。これがないと、dockerを使うことができなくなります。ビルドプロジェクトを作成したら、codepipeline作成に戻ります。

codepipeline
codepipeline

codepipeline
codepipeline

設定の最後にbuildspec.yml内で使用する環境変数を指定します。これでcodepipeline設定は完了です。

codepipeline
codepipeline

IAMの設定

デフォルトでは、権限が不足しているため、ECRの更新とlambdaの更新が行えるように権限を追加します。 作成されたロールのpolicyを直接編集すると、エラーがでてしまうため、インラインポリシーで権限付与しています。

codepipeline

policyの中身は以下のとおりです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecr:BatchGetImage",
                "ecr:GetAuthorizationToken",
                "ecr:BatchCheckLayerAvailability",
                "ecr:GetDownloadUrlForLayer",
                "ecr:InitiateLayerUpload",
                "ecr:UploadLayerPart",
                "ecr:CompleteLayerUpload",
                "ecr:PutImage",
                "ecr:GetRepositoryPolicy",
                "ecr:DescribeRepositories",
                "ecr:ListImages",
                "ecr:DescribeImages",
                "ecr:BatchDeleteImage"
            ],
            "Resource": "*"
        }
    ]
}
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Action": [
                "lambda:UpdateFunctionCode"
            ],
            "Resource": "*"
        }
    ]
}

パイプラインを実行

さて、これで準備が整いました。lambda関数の中身を以下のように修正し、コードをpushしてみます。

import json
def lambda_handler(event, context):
    return {"statusCode": 200, "body": json.dumps("Hello from Update Lambda!!")}

すると、githubの変更を検知し、codepipelineが動き出します。(わくわく)

codepipeline
codepipeline

パイプラインが完了したら、もう一度lambdaのテストしてみます。

codepipeline
codepipeline

メッセージが、Hello from Lambda!から、Hello from Update Lambda!!に変わってますね!うまくlambdaが更新されたようです。

まとめ

今回は最小構造で、lambda更新のCI/CD環境を作ってみました。さらなる展望として、lambdaやECRの作成にCloudformationを使うと、保守運用しやすいのですが、それは今後の課題

codepipelineを使うと、CI/CD環境を簡単につくれるので、活用していきたいですね。

※本記事は筆者が個人的に学んだこと感じたことをまとめた記事になります。所属する組織の意見・見解とは無関係です。

参考

CodePipelineのソースにGitHubのリポジトリを利用する方法@CloudFormation

GitHub に push したら AWS Lambda が更新されるようにする

チュートリアル:Amazon ECS による標準デプロイ CodePipeline - AWS CodePipeline