michimani.net

Docker で Python 用の Lambda レイヤーを作る

2020-05-16

AWS Lambda の関数を Python で実装する際に使用するレイヤーを作ったので、その時のメモです。

目次

概要

Python 3.8 をランタイムとして、 AWS Lambda の関数を実装することを考えます。その際に、 pip でインストールしたモジュールを Lambda レイヤーとして構築します。

AWS Lambda ではラインタイムごとに実行環境 (OS) が異なります。このエントリを書いている時点 (2020/05/16) では、 Python 3.8 の実行環境は Amazon Linux 2 となっています。

今回は Lambda レイヤー用のモジュール群 (zip ファイル) を、 Docker を使って Amazon Linux 2 環境で作成します。

Docker イメージの作成

まずは Python 3.8 をインストールした Amazon Linux 2 の Docker イメージを作成します。Amazon Linux 2 のイメージは Docker Hub で公開されているので、今回はその中から 2.0.20200406..0 を使います。

イメージサイズの削減

とりあえず作成したとき、イメージのサイズが 1.17 GB になってしまいました。さすがにサイズが大きすぎると思ったので、削減方法について調べてみました。

Dockerfile の RUN の挙動

Dockerfile では RUN コマンドを使って Linux コマンドを記述して Python 3.8 をインストールしていきます。この RUN コマンドは実行するたびにレイヤを重ねていく形になります。なので、 Python 3.8 用のインストールパッケージなど不要な一時ファイルを削除する場合は、同じ RUN コマンド内で処理する必要があります。

イメージビルド時のサイズの変遷を調べる

docker build でイメージをビルドしていく過程で、イメージのサイズがどのように変化していったかを確認することができます。

$ docker history IMAGE

実行結果は次のような形で出力されます。

docker history 6a20bde9c6da
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
6a20bde9c6da        10 hours ago        /bin/sh -c pip3.8 -V                            0B
49f06a13c22d        10 hours ago        /bin/sh -c python3.8 -V                         0B
669423823b31        10 hours ago        /bin/sh -c rm -rf Python-3.8.3                  0B
9e98a158748d        10 hours ago        /bin/sh -c rm -rf Python-3.8.3.tgz              0B
2af6cd8cdd37        10 hours ago        /bin/sh -c cd Python-3.8.3 && ./configure --…   360MB
22c8ca1d8b4c        10 hours ago        /bin/sh -c tar xzf Python-3.8.3.tgz             84.1MB
b2acff82a0d2        10 hours ago        /bin/sh -c wget https://www.python.org/ftp/p…   24.1MB
621e604d00a9        10 hours ago        /bin/sh -c yum -y install gcc openssl-devel …   332MB
7f335821efb5        3 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>           3 weeks ago         /bin/sh -c #(nop) ADD file:c64a7d77d1e9f4364…   163MB

イメージ内のディレクトリごとのサイズを調べる

ビルド後のイメージについて、ディレクトリごとのサイズを確認します。

$ docker run -it --rm 6a20bde9c6da du / | sort -n | tail
179120	/var/cache/yum/x86_64/2/amzn2-core
179128	/var/cache/yum/x86_64/2
179132	/var/cache/yum/x86_64
179136	/var/cache/yum
179160	/var/cache
190324	/var
212184	/usr/local/lib
230872	/usr/local
544248	/usr
736976	/

これで不要なディレクトリがあれば削除するようなコマンドを Dockerfile に追記して再ビルド -> 確認 -> 再ビルド … していきます。

最終的に 550 MB くらいになりましたが、これでも大きいですよね。多分。

Docker Hub へ Push

不要ファイルを削除してサイズ削減した Dockerfile は次のようになりました。

FROM amazonlinux:2.0.20200406.0

RUN yum -y install \
    gcc \
    openssl-devel \
    bzip2-devel \
    libffi-devel \
    wget \
    tar \
    gzip \
    make \
    wget https://www.python.org/ftp/python/3.8.3/Python-3.8.3.tgz && \
    tar xzf Python-3.8.3.tgz && \
    cd Python-3.8.3 && \
    ./configure --enable-optimizations && \
    make altinstall && \
    cd ~ && \
    rm -rf /var/cache/yum/* && \
    rm -rf /Python-3.8.3
RUN python3.8 -V
RUN pip3.8 -V

この Dockerfile を使ってイメージを作成します。このとき、 -t で指定するタグ名は {Docker Hub のアカウント ID}/{Docker Hub のリポジトリ名} にしておきます。

$ docker build -t michimani/amzn2py38 . 

イメージが作成できたら、 Docker Hub にログインします。

$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: michimani
Password:
Login Succeeded

ログインできたら、先ほど作成したイメージを Docker Hub に Push します。

$ docker push michimani/amzn2py38:latest

Push されたイメージを Docker Hub で確認します。

レイヤー用 zip ファイルの作成

必要な Python モジュールを requirements.txt に記述しておきます。そして、下記のようなディレクトリ構成を作ります。

.
├── Dockerfile
├── docker-compose.yml
└── requirements.txt

Dockerfile は次のように記述します。

FROM michimani/amzn2py38:latest

RUN yum -y install zip
RUN mkdir /home/dist

docker-compose.yml は次のように記述します。

version: "3"
services:
  app:
    build: .
    volumes:
      - ".:/home"
    command: >
      bash -c "
      mkdir /home/dist &&
      mkdir -p /home/tmp/python &&
      pip3.8 install -r /home/requirements.txt -t /home/tmp/python --upgrade &&
      cd /home/tmp &&
      zip -r /home/dist/layer.zip ./python &&
      cd /home &&
      rm -rf /home/tmp"      

あとは次のコマンドを実行すれば、 dist/layer.zip が生成されます。

$ docker-compose up

Lambda レイヤーの登録

Lambda レイヤーの登録は、下記の AWS CLI コマンドで実行できます。

$ aws lambda publish-layer-version \
--layer-name my_layer \
--zip-file fileb://dist/layer.zip

まとめ

Python 3.8 用の Lambda レイヤーを Docker を使って作成してみた話でした。
Docker まだまだよくわからん状態なので、最初にイメージサイズが 1 GB 超えたときは焦りました。 RUN の挙動についてもちゃんとわかっていなかったので、調べる機会になってよかったです。

Lambda レイヤーについては、外部モジュールを Lambda 自体のデプロイパッケージサイズを小さくすることができ、さらに他の関数でも利用できるので、用途別にレイヤーを作成しておけば Lambda 関数の実装がしやすくなりそうです。


comments powered by Disqus