serverless frameworkでコンテナLambdaを使ってみる
記事の内容
serverless frameworkでコンテナLambdaをAWSへデプロイする手順をまとめてみました。
Lambdaは特別なことしなければ、zipでアップロードするのが基本です。
ですが、osに依存するパッケージが必要な処理の場合、Lambdaのデプロイは大変になります。
Lambdaレイヤーを使うこともできますが、1レイヤー250MBの制限があります。
大きなパッケージだと250MBは簡単に超えてしまいます。
そこで、コンテナLambdaにすることで、パッケージのos依存と容量の問題をいい感じに解決できます。
serverless frameworkがコンテナLambdaのデプロイをサポートしているので試してみました。
関係する技術・ツール
- serverless framework 3系
- typescript
- docker
作業の流れ
- serverless.ts作成
- dockerfile作成
- デプロイ
ディレクトリ構成
想定するプロジェクトディレクトリは下記のような構成です。
├ docker
| └ nodejs
| └ prod
| └ dockerfile
├ src
| └ functions
| └ s3trigger
| └ index.ts
├ package.json
├ tsconfig.json
└ serverless.ts
1. serverless.ts作成
こちらを参考にserverless.tsを作成します。tsなのでtypescriptで作成します。
import type { AWS } from "@serverless/typescript";
const serverlessConfiguration: AWS = {
service: "function-in-docker",
useDotenv: true,
frameworkVersion: "3",
provider: {
name: "aws",
runtime: "nodejs16.x",
region: "ap-northeast-1",
stage: "${opt:stage, 'dev'}",
ecr: {
images: {
s3triger: {
file: "./docker/nodejs/prod/dockerfile",
path: "./",
},
},
},
environment: {
AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1",
NODE_OPTIONS: "--enable-source-maps --stack-trace-limit=1000",
BUCKET_NAME: "BUCKET_NAME",
},
},
functions: {
create_thumbnail: {
image: {
name: "s3triger",
},
architecture: "x86_64",
},
},
};
module.exports = serverlessConfiguration;
docker buildに関する設定はprovider.ecr
内で定義します。
上記の場合、
-
file
: 相対パスでdocker/nodejs/prod/dockerfile
をビルド -
path
: 相対パスで./
をビルドコンテクストに指定します。
結果、docker/nodejs/prod/dockerfileを元に、プロジェクトのルート(serverless.tsがあるディレクトリ)をビルド場所にしています。
2. dockerfile作成
docker/nodejs/prod
にdockerfileを作成します。
FROM public.ecr.aws/lambda/nodejs:16 as builder
WORKDIR /usr/app
COPY package.json tsconfig.json src/functions/s3trigger/index.ts ./
RUN npm install -g yarn && \
yarn install
FROM public.ecr.aws/lambda/nodejs:16
RUN yum update -y && \
yum install -y \
amazon-linux-extras \
ghostscript-9.25-5.amzn2.0.2 \
python \
python3 \
build-essential \
libjpeg-dev \
libpng-dev \
libcurl4-openssl-dev \
mupdf-tools \
libfreetype6-dev \
qpdf && \
amazon-linux-extras enable libreoffice && \
yum install -y \
libreoffice-core \
libreoffice-calc \
libreoffice-opensymbol-fonts \
libreoffice-ure \
libreoffice-writer \
libreoffice-pyuno \
libreoffice-impress \
libreoffice-langpack-ja \
ipa-gothic-fonts \
ipa-mincho-fonts \
ipa-pgothic-fonts \
ipa-pmincho-fonts
WORKDIR ${LAMBDA_TASK_ROOT}
COPY /usr/app/dist ./
COPY /usr/app/node_modules ./node_modules/
CMD ["index.handler"]
マルチステージビルドを活用しています。簡単に解説すると、
1つ目のFROMではtypescriptで作成したLambda関数をコンパイルしています。
この環境はコンパイルが目的で、実際にデプロイするコンテナではありません。
ですので、最終的には破棄される環境です。
2つ目のFROMから実際にデプロイされるコンテナです。
amazonlinux上にyumで様々なパッケージをインストールしています。これらが今回osに依存するパッケージ群です。
またCOPY --from=builder /usr/app/dist .
のようにファイルをコピーしています。
この--from=builderは1つ目の環境を指しています。つまり、1つ目の環境でコンパイルしたソースをコピーしていることになります。
このように、マルチステージビルドにより、ビルドした環境から必要最低限のソースを厳選してコンテナイメージを作成できます。
3. デプロイ
ここまでできたら、デプロイしてみます。事前にdockerを起動しておきます。
プロジェクトのルートで
sls deploy
を実行します。
すると、Lambdaのパッケージングとともに、docker buildも実行されます。
これによりビルドしたイメージがECRにpushされます。
リポジトリ名はserverless-<service>-<stage>
のようになります。
また、こちらに記載ありますが、過去のイメージは自動で削除されないようです。
関数をzipでデプロイする際はs3に配置されることになりますが、コンテナLambaにしたことによってLambda関数はdockerイメージに含まれることになります。したがってdockerイメージを残しておかないとLambdaのバージョン管理ができなくなってしまうわけですね。
デプロイ方法は変更できない。
ちなみに、Lambdaのデプロイ方法は変更することができません。したがってzip↔︎dockerの双方向どちらからも変更できません。さらに、cfnで作成した場合も同様です。たとえcfnで作成した該当のLambda関数を手動で削除してもスタック履歴的に変更できないようです。
変更しようとすると下記のようなエラーとなりました。
Resource handler returned message: "Invalid request provided: Updating PackageType is not supported"
この場合cfnスタックを削除するか、関数名を変えるなどの対応が必要になるので注意する必要があります。
以上になります。
これで無事、serverless frameworkでコンテナLambdaをデプロイすることができました。
NCDC株式会社( ncdc.co.jp/ )のエンジニアチームです。 募集中のエンジニアのポジションや、採用している技術スタックの紹介などはこちら( github.com/ncdcdev/recruitment )をご覧ください! ※エンジニア以外も記事を投稿することがあります
Discussion