LoginSignup
2
2

More than 1 year has passed since last update.

【AWS SAM】デプロイとローカル確認用のコンテナイメージを一つのDockerfileで使い分ける

Last updated at Posted at 2021-08-27

初めに

LambdaアプリケーションをSAMで開発するにあたりコンテナでデプロイを初めて取り扱うにあたり色々試行していたのですが、
せっかくコンテナで環境をそのまま持ってけるんだからローカル環境も同じコンテナで処理したい!
でもDockerfileの多重管理はしたくない!となっていい方法がないかと模索していたところ
割と良さげな方法がありましたので備忘録がてらまとめます。

AWSのサポートに問い合わせてみたら記事執筆時点(2021/08/27)では
まだ公式のドキュメントが追いついてなく記載がなく追記予定らしいです。

こんなことしなくてももっと簡単にできるよっていうのがあったらご教示いただければと思います。

何がしたかったか

今回はAWS公式で提供されているpython3.8のイメージをベースとします。

大元のDockerfileはドキュメントに記載の通り以下で
ENTRYPOINT ["/lambda-entrypoint.sh"] が実装されており、これに対して
CMD で引数を渡すような作りをすることで対象のスクリプトを実行できます。
https://github.com/aws/aws-lambda-base-images/blob/python3.8/Dockerfile.python3.8

ただしこのコンテナをローカル開発で利用するにあたり以下の点が不満でした

  • sam経由で実行する場合sam buildsam local invoke が必要
    • せっかくのスクリプト言語なのにいちいちビルドが必要で確認がめんどくさい
    • build問題はsamを経由せずに  ボリュームをマウントdockerコマンド経由で実行してやれば解決はするが次項の問題がある
  • コンテナは起動するとスクリプトの実行が終わるのでコンテナを立ち上げっぱなしにできない
    • ちょっと確認のためにコンテナ内で作業したいとかがやりづらい

なのでローカル用にはENTRYPOINT ["tail", "-f"]を実装したDockerfileを利用し、
通常時は内部でpythonコマンド経由で実行しある程度整理できたところで、
sam経由で実行のようなことを考えていました。

ただしこのためにDockerfileを複数作って管理というのは共通部分を多重に管理しないといけないため回避したい問題でした。

解決方法

Dockerのマルチステージビルドを使うことで解決できました。
今回調べるまで知らなかったのですがdocker build--targetオプションで指定したステージを最終成果物としてできることを知りました。

ただdocker build --target=xxxと同じ挙動をするものがSAMに見当たらないという点でつまづきました。
sam buildのドキュメントにもなくSAMのアプリケーションビルドのページにもなかったのでAWSに問い合わせしてみたのですが、
ドキュメントが追いついていないだけでSAM CLI 1.12.0で実装されていたようです。
(SAM側のテンプレートファイルのDockerBuildTargetで利用するステージの指定が可能)
https://github.com/aws/aws-sam-cli/issues/2578

各種実装

ファイルの配置

testフォルダとか一部省略してます。
docker-compose.ymlが増えている以外はsam initで作られるのと同じです。

.
├── src
│   ├── Dockerfile
│   ├── docker-compose.yml
│   ├── __init__.py
│   ├── requirements.txt
│   └── app.py
└── template.yaml

Dockerfileの実装

最初にデプロイ用のprodイメージを作り、
その後にそこからローカル開発用に一部を書き換えたdevイメージを作成します。

Dockerfile
FROM public.ecr.aws/lambda/python:3.8 as prod

#ソースコード類をコピー
COPY ./* ./
RUN python3.8 -m pip install -r requirements.txt
CMD ["app.lambda_handler"]

FROM prod AS dev
ENTRYPOINT [ "tail", "-f"]

docker-compose.yml

個人的にはdocker-compose経由の起動が楽なのでdocker-compose.ymlも作っておきます。
このdocker-compose.ymlはローカルの開発専用でSAMでデプロイする場合は後述のSAMテンプレート側が読まれます。
target: devを指定することでdocker-compose経由で起動する場合はdev側のイメージを利用します。

docker-compose.yml
version: "3"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: dev
    volumes:
      - .:/var/task

template.yml

SAM側のコンテナの設定系はMetadataで設定します(詳しくはSAMのアプリケーションビルドを参照)
このMetadataにDockerBuildTargetで対象のステージを指定することができるようです。
(2021/08/27時点で記載がなかったのはこのパラメータ)

なおこのパラメータをなしで実行すると最終のdevまで実行されたものが利用されました。

template.ymlは以下のような形で実装します(一部省略)

template.yml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
...
Resources:
  CreateMntFolder:
    Type: AWS::Serverless::Function
    Properties:
      PackageType: Image
...
    Metadata:
      Dockerfile: Dockerfile
      DockerContext: ./src
      DockerTag: python3.8-v1
      DockerBuildTarget: prod

こうすることでDockerfileを多重管理する必要もなくなり、
ちょっと確認したい時はdocker-compose up -d経由でコンテナを立ち上げ、
docker-compose exec app bashで内部に入って実行もできますし、
sam buildからのsam local invokesam local start-api等の各種ローカル実行系のコマンドでの確認もできます。

終わりに

マルチステージビルドはDocker公式チュートリアルちょっと読む限りは中間成果物があるような環境のために
使い捨ての作業イメージが作れるくらいのイメージでした。

実際はそうではなくSAMに限らずこれまで環境ごとに複数Dockerfileを作っていたのが、
一つのファイルで共通の部分の中間イメージを作りそこから個別実装ということができたり、
今回みたいに用途によって途中のステージのものを利用することもできたりと
少し視野を広く持っていればいろいろなことができそうです。

一つのDockerfileにアプリとDBのイメージを両方記載してdocker-compose.ymltargetパラメータをうまく指定することで
Dockerfileを一つにまとめられそうなんですがどうなんでしょうか。
同じこと考えている人はいそうなのでこの辺りはまたチュートリアルとかフォーラムとか色々みてみようかと思います。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2