slsコマンドが環境に依存してしまうのがイヤで、slsコマンドをコンテナ化してみました。
サクッと作れるかと思いきや、意外と苦戦しました。。。
serverless-python-requirements プラグインを利用すると、dockerを利用してパッケージをビルドするのですが、こいつが曲者で、slsコマンド自体をコンテナ化しようとすると、コンテナ内からDockerコマンドが実行できてかつ、別コンテナでビルドした資材を参照できなくてはなりません。
いろいろ試行錯誤したので、まとめておこうかと思います。
一応、今回のソースはGitHubにpushしていますので、ソースの確認はこちらからどうぞ。
ディレクトリ構成
sls-sample/
| app/ // lambdaにデプロイするソースコード (今回はfastapiのwebアプリ)
| | __init__.py
| | main.py
| bin/
| | build.sh* // イメージをビルドするスクリプト
| | sls.sh* // コンテナを起動するコマンド
| docker/
| | sls/
| | | Dockerfile // slsコンテナのDockerfile
| .dockerignore
| package-lock.json
| package.json
| requirements.txt // lambdaの依存パッケージ定義(layerにする)
| serverless.yml
イメージ作成
コンテナ内でDockerコマンドを実行する方法は、コンテナ内でDockerデーモンを起動する「Docker in Docker(DinD)」と、docker.sockをマウントしてホスト側のDockerを操作する「Docker outside of Docker(DooD)」があります。DinDはさすがにオーバーヘッドが大きすぎるので、今回はDooDで作っていきます。
とはいえ、コンテナ内でDockerを利用する以上、Dockerのインストールは必須ですので、FROMイメージは公式のDinDのイメージ( docker:20.10.12-dind
)を利用します。
このイメージは alpine:3.15
が元になっていますので apk
コマンドで必要なパッケージをインストールしていけば良さそうです。
※ DinD, DooDはこちらの記事が非常に参考になりました。
コンテナからコンテナを操作する | 二畳半堂
Dockerコンテナ内からDockerを使うことについて
FROM docker:20.10.12-dind
RUN printenv
RUN apk update && apk add --no-cache nodejs npm
RUN npm install -g serverless@3.7.9
RUN mkdir /opt/sls
WORKDIR /opt/sls
COPY . .
RUN npm install
ビルドスクリプトを作ります。
※ 私はいつもオプションを忘れてしまうので、このくらいでもスクリプト化してしまいます。。。
#!/bin/bash
PROJECT_ROOT=$(cd $(dirname $0)/..; pwd)
cd $PROJECT_ROOT
docker build --rm -f docker/sls/Dockerfile -t sample-sls:latest .
コンテナの起動
コンテナの起動は少しポイントがあります。
基本は sls
コマンドでコンテナを起動させればいいのですが、マウントするディレクトリが結構重要です。
-
-v ${HOME}/.aws:/root/.aws:ro
ホスト側のAWSのcredentialsを参照できるようにします。 -
-v /root/.cache:/root/.cache
serverless-python-requirementsによって別コンテナでビルドされた資材はホストの${HOME}/.cache/serverless-python-requirements
に配置されますので、slsコンテナからビルド資材を参照できるように/root/.cache
をマウントします。
※ .serverlessディレクトリの中を見ると~/.cache/serverless-python-requirements/...
へのシンボリックリンクがあります。 -
-v /var/run/docker.sock:/var/run/docker.sock
Dockerコマンドがホスト側のDocker環境で実行されるように、docker.sock
をマウントします。 -
-v ${PROJECT_ROOT}/app:/opt/sls/app
lambdaにデプロイするディレクトリをマウントします。(複数ある場合はすべて) -
-v ${PROJECT_ROOT}/requirements.txt:/opt/sls/requirements.txt
pythonの依存パッケージ定義ファイルをマウントします。 -
-v ${PROJECT_ROOT}/serverless.yml:/opt/sls/serverless.yml
ServerlessFrameworkの設定ファイルをマウントします。
#!/bin/bash
PROJECT_ROOT=$(cd $(dirname $0)/..; pwd)
docker run -ti --rm \
-v ${HOME}/.aws:/root/.aws:ro \
-v /root/.cache:/root/.cache \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ${PROJECT_ROOT}/app:/opt/sls/app \
-v ${PROJECT_ROOT}/requirements.txt:/opt/sls/requirements.txt \
-v ${PROJECT_ROOT}/serverless.yml:/opt/sls/serverless.yml \
sample-sls:latest sls $*
ビルド & デプロイ手順
chmod 755 ./bin/*.sh
# イメージのビルド
./bin/build.sh
# デプロイ
./bin/sls.sh deploy
# ブラウザでAPIにアクセス
# https://xxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/api/hello
参考