AWS新機能「AWS Lambda Container Image Support」利用時のCI/CDパイプライン構築
はじめに
2020年11月30日から開催されているAWSのグローバルカンファレンスAWS re:Invent 2020 にて、「AWS Lambda Container Image Support」 という新サービスが発表されました。
AWS Lambda Container Image Supportは、AWSのサーバーレスコンピューティング「AWS Lambda」にて、仮想コンテナ技術を利用できるようになる画期的なサービスです。
本検証では、AWS Lambda Container Image Supportを使い、任意のDockerイメージをデプロイするまでのContinuous Integration(CI) / Continuous Delivery(CD)の実装をご紹介いたします。
検証の背景と目的
MMMでは運用しているLambdaワークロードで wget
および awscli
を利用しているケースがあります。
しかし、Lambda実行環境には wget
がインストールされておらす、これまではビルドした wget
バイナリを追加して利用するという手法を取っていました。ビルドの実行環境によって生成されるバイナリは異なるため、今後のLambda環境の変更により、バイナリが動作しなくなるというリスクが内包していました。
また、AWSコマンドスクリプトを作成してプロジェクトに追加することによって、Lambda内で awscli
を利用し aws s3 sync
コマンドを実行しているケースもあります。
今回のAWS Lambda Container Image Supportの活用により、wget
そして awscli
をDockerビルドプロセスの中でインストールし、プロジェクト構成の簡略化及びワークロードの安定性を高めることを目的としています。
検証の流れ
python:3.9-alpine3.12
をベースイメージとしawscli
をpip
wget
をapk add
でそれぞれインストール- Amazon ECRにリポジトリを作成
- ビルドしたコンテナイメージをAmazon ECRにプッシュ
- プッシュしたコンテナイメージからLambda関数を作成
- 上記1-4を自動化するCI/CDパイプラインを実装
対象ワークロードの概念は以下のとおりです。
1. python:3.9-alpine3.12
ベースのDockerイメージビルド
今回はpython:3.9-alpine3.12
をベースイメージとして使用し、このイメージに対して wget
とawscli
をインストールしていきます。
プロジェクトにDockerを利用する前後で、下記ディレクトリ構成に変更しています。
Docker利用前
.
├── aws
├── bin
│ └── wget
├── main.py
└── rename.sh
Docker利用後
.
├── .circleci
│ └── config.yml
├── Dockerfile
├── app
│ ├── main.py
│ └── rename.sh
└── scripts
├── circleci_lambda.sh
├── create_envfile.sh
└── docker-build.sh
本検証では下記Dockerfileを作成します。
# Define global args
ARG FUNCTION_DIR="/home/app/"
# Stage 1 - bundle base image + runtime
# Grab a fresh copy of the image and install GCC
FROM python:3.9-alpine3.12 AS python-alpine
# Install GCC (Alpine uses musl but we compile and link dependencies with GCC)
RUN apk add --no-cache \
libstdc++
# Stage 2 - build function and dependencies
# Install aws-lambda-cpp build dependencies
RUN apk add --no-cache \
build-base \
libtool \
autoconf \
automake \
libexecinfo-dev \
make \
cmake \
libcurl \
wget \
bash \
which \
groff
# Install AWS CLI
RUN pip install awscli
FROM python-alpine AS build-image
# Authenticating with AWS CLI
ARG AWS_ACCESS_KEY_ID
ARG AWS_SECRET_ACCESS_KEY
ENV AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
ENV AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
# Include global args in this stage of the build
# Create function directory
RUN mkdir -p /home/app/
# Copy handler function
COPY app/* /home/app/
RUN ls -la /home/app/
RUN chmod 755 /home/app/rename.sh
RUN ls -la /home/app/
# Install the function's dependencies
RUN aws s3 cp s3://aws-lambda-runtime-clients/python/awslambdaruntimeclient-0.0.1.tar.gz awslambdaruntimeclient.tar.gz && \
python3.9 -m pip install \
awslambdaruntimeclient.tar.gz \
--target /home/app/
# Stage 3 - final runtime image
# Grab a fresh copy of the Python image
FROM python-alpine
# Set working directory to function root directory
WORKDIR /home/app/
# Copy in the built dependencies
COPY --from=build-image /home/app/ /home/app/
RUN ls -la /home/app/
RUN ls -la /usr/local/bin
RUN ls -la /usr/bin
ENTRYPOINT [ "/usr/local/bin/python", "-m", "awslambdaruntimeclient" ]
CMD [ "main.lambda_handler" ]
Dockerfile作成後、イメージビルドを実施します。
$ docker build . \
-f Dockerfile \
-t lambda/python:3.9-alpine3.12 \
--build-arg AWS_ACCESS_KEY_ID="$(aws configure get mmm-test-env.aws_access_key_id)" \
--build-arg AWS_SECRET_ACCESS_KEY="$(aws configure get mmm-test-env.aws_secret_access_key)"
ビルドが成功し、wget
やawscli
がインストールされていることが確認できます。
2. Amazon ECRにリポジトリを作成
コンテナイメージが用意できたら、次はイメージをプッシュするAmazon Elastic Container Registry(ECR)を用意します。
今回はdocker-lambda
という名前でリポジトリを用意しました。
3. ビルドしたコンテナイメージをAmazon ECRにプッシュする
ビルドしたコンテナイメージにタグ付けを行い、先ほど作成したECRリポジトリにプッシュします。
$ docker tag lambda/python:3.9-alpine3.12 000000000000.dkr.ecr.sa-east-1.amazonaws.com/docker-lambda:v1.0.0
$ export AWS_ACCESS_KEY_ID=<YOUR_AWS_ACCESS_KEY_ID>
$ export AWS_SECRET_ACCESS_KEY=<YOUR_AWS_SECRET_ACCESS_KEY>
$(aws ecr get-login --region sa-east-1 --no-include-email)
$ docker push 000000000000.dkr.ecr.sa-east-1.amazonaws.com/docker-lambda:v1.0.0
AWSコンソールでECRを開くと、先程プッシュしたイメージがリポジトリに追加されていることが確認できます。
4. プッシュしたコンテナイメージからLambda関数を作成する
イメージプッシュが完了できたら、早速Lambda関数を作成していきましょう。
下記コマンドのように--code ImageUri=<YOUR_IMAGE_URI>
で先程プッシュしたイメージのURIを指定してあげます。
$ aws lambda --region sa-east-1 create-function \
--function-name docker-lambda-function --package-type Image \
--code ImageUri=<YOUR_IMAGE_URI> \
--role <YOUR_IAM_ROLE_ARN>
AWSコンソールからLambda関数が作成できていることが確認できます。(作成完了までに多少時間がかかることがあります)
関数を作成できたら環境変数をセットします。今回はAmazon S3に静的ファイルをアップロードするというワークロードのため、S3BUCKET
というキーに対して任意のバケット名を設定します。
また、Lambdaではデフォルトでタイムアウト3秒に設定されているため、60秒程度に延長しておきます。
次に関数をテストしていきます。
今回は下記のようなテストイベントを作成しました。
{
"url": "https://mmmcorp.co.jp/"
}
S3バケットを確認すると、静的ファイルがアップロードされており、DockerイメージがLambda上で正常に動作していることが確認できます。
5. CI/CDパイプラインの実装
次に、イメージビルド、ECRへのイメージプッシュ、Lambda関数のアップデートの一連の流れを、masterブランチへのPushをトリガーに、CI/CDパイプラインとして実装してきます。
CI/CDにはCircleCIを利用し、実装イメージは下記となります。
masterブランチへのPush・マージを起点として、Dockerコンテナ内でイメージビルドを実行。ビルドしたコンテナイメージをECRにプッシュし、awscliでLambda関数をアップデートするという流れです。
早速、.circleci/config.yml
と、ymlファイル内で実行するスクリプトファイルを準備します。
defaults: &defaults
working_directory: ~/app
machine:
image: ubuntu-1604:201903-01
docker_layer_caching: true
version: 2.1
jobs:
build:
<<: *defaults
steps:
- checkout
- run:
name: ls
command: ls
- run:
name: create .env file
command: ./scripts/create_envfile.sh
- persist_to_workspace:
root: ~/app
paths:
- ./*
deploy:
working_directory: ~/app
docker:
- image: docker:19-git
steps:
- attach_workspace:
at: ~/app
- setup_remote_docker
- run:
name: install python, pip, awscli
command: |
apk add --update python2 \
jq \
python2-dev \
py-pip \
build-base \
gcc \
abuild \
binutils \
binutils-doc \
gcc-doc \
bash \
&& pip install --no-cache-dir awscli
- run:
name: ls
command: ls
- run:
name: docker build
command: ./scripts/docker-build.sh
- run:
name: ECR login
command: $(aws ecr get-login --region sa-east-1 --no-include-email)
- run:
name: ECR deploy
no_output_timeout: 2400
command: ./scripts/circleci_lambda.sh
workflows:
version: 2
build_and_deploy:
jobs:
- build
- deploy:
requires:
- build
filters:
branches:
only:
- master
今回、CircleCIのジョブの中で3種類のスクリプトファイルを呼び出して実行しています。
#!/bin/bash
env | sort | grep AWS_ > .env
cat .env
exit 0
#!/bin/bash
docker build . -f Dockerfile --tag <YOUR_ECR_REPOSITORY_NAME>:<YOUR_TAG> --build-arg AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID}" --build-arg AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY}"
#!/bin/bash
echo "Push Image to ECR..."
docker push <YOUR_ECR_REPOSITORY_NAME>:<YOUR_TAG>
echo "Download the model update from Amazon S3 to enable the AWS Lambda private beta feature..."
aws s3 cp s3://aws-lambda-image-beta-tooling/cli/lambda-2015-03-31.normal.json .
echo "Install the model..."
aws configure add-model --service-model file://lambda-2015-03-31.normal.json --service-name lambda
echo "build done and running lambda update..."
aws lambda update-function-code --region sa-east-1 --function-name <YOUR_LAMBDA_FUNCTION_NAME> --image-uri <YOUR_IMAGE_URI>
RETURNCD=$?
if [ ${RETURNCD} -ne 0 ]; then
echo
echo "Lambda Update FAILED"
echo
exit ${RETURNCD}
fi
exit 0
検証の振り返り
今回の検証ではAWS Lambda Container Image Supportを活用して、既存ワークロードの改善を図っています。プロジェクトをコンテナでパッケージ化することで、Lambda基盤に依存しない形でのワークロード実行が可能になり、ワークロードの安定性を向上させることができました。
AWS Lambdaのビジネス活用をサポート
株式会社MMMは 、AWS Lambdaのビジネス活用に豊富な実績を持っており、AWS LambdaのAWSサービスデリバリーパートナーにも認定されています。
MMMは、多種多様なAWSマネージドサービスをクラウドのベストプラクティスに準拠した形で活用することで、クラウドの真の旨味である柔軟性や俊敏性をお客様のビジネスに組み込むことを得意としています。
今回リリースされたAWS Lambda Container Image Supportも含め、 サーバーレスアーキテクチャ(AWS Lambda) の導入や無料相談に関しては次のページよりお気軽にご相談ください。