はじめに
- AWS ECS+ECRで動いているpythonのサービスがあった
- ただし、ECRのスキャン機能でものすごい数の脆弱性が出ていた...
- 1つずつ解消していくのは現実的ではないため、distrolessコンテナを導入することになった
distrolessとは
- Googleが提供しているコンテナイメージ
- アプリケーションとそのランタイム依存関係のみ含まれる(=必要最小限)
- パッケージマネージャー、シェルなどは含まれない
- 導入メリットとして、①軽量であること、②セキュアであることが挙げられる
やったことざっくりと
- 以前は「python:3.9.13-slim-buster」のイメージを使用してビルドしていた
- これを1段階目「python:3.9.13-slim-buster」→2段階目「gcr.io/distroless/python3」のマルチステージビルドにする
- 1段階目
- パッケージをインストールし、コピー用ディレクトリに置く
- コードをコピー用ディレクトリに展開する
- 2段階目
- PYTHONPATHにパッケージのパスを追加する
- 1段階目のコピー用ディレクトリからコピーする
辿り着いたDockerfile(一部省略)
- ARGの内容は、実行時に
--build-arg
で引数として指定する運用 -
RUNNER_IMAGE_NAME
にdistrolessコンテナイメージを設定している- dev環境の場合は
gcr.io/distroless/python3:debug
-
:debug
のイメージにはshellが含まれているため
-
- stg, prod環境の場合は
gcr.io/distroless/python3
- dev環境の場合は
-
APP_NAME
= コードが格納されたディレクトリ
ARG RUNNER_IMAGE_NAME
// ここから1段階目
FROM python:3.9.13-slim-buster AS builder
ARG APP_NAME
ARG DC_TZ
ARG ...
COPY requirements.txt /tmp/
RUN set -x \
&& \
: "timezones" \
&& cp /usr/share/zoneinfo/${DC_TZ} /etc/localtime \
&& cp -a --parents /etc/localtime /opt \
&& \
: "lib install from pip" \
&& mv /tmp/requirements.txt ./ \
&& pip3 install --upgrade pip \
&& pip3 install --no-cache-dir -r requirements.txt \
&& cp -a --parents /usr/local/lib/python3.9/site-packages /opt
COPY ${APP_NAME} /opt/opt/${APP_NAME}
// ここから2段階目
FROM ${RUNNER_IMAGE_NAME}
ARG APP_NAME
ARG DC_LANG
ARG ...
ENV LANG ${DC_LANG}
ENV ...
ENV PYTHONPATH=/usr/local/lib/python3.9/site-packages:/opt/${APP_NAME}/libs:/opt/${APP_NAME}
COPY --from=builder /opt /
WORKDIR /opt/${APP_NAME}
CMD ["***.py"]
ポイント
パッケージのインストール場所とコピー場所
&& pip3 install --upgrade pip \
&& pip3 install --no-cache-dir -r requirements.txt \
&& cp -a --parents /usr/local/lib/python3.9/site-packages /opt
- (1段階目)pipインストールしたパッケージが入っているディレクトリをコピー用ディレクトリ(/opt)にコピー
- 私はこれを忘れていてしばらく詰まりました
COPY ${APP_NAME} /opt/opt/${APP_NAME}
- (1段階目)コードをコピー用ディレクトリにコピー
- 運用上、コードを
opt/${APP_NAME}
というディレクトリ構造で格納したいため、/opt(コピー用ディレクトリ)/opt(格納用ディレクトリ)
という指定に...(コピー用ディレクトリの名前をtempとかに変えればよかった)
ENV PYTHONPATH=/usr/local/lib/python3.9/site-packages:/opt/${APP_NAME}/libs:/opt/${APP_NAME}
- (2段階目)パッケージが入っているディレクトリをPYTHONPATHに追加
COPY --from=builder /opt /
- (2段階目)1段階目のコピー用ディレクトリ(/opt)の中身をコピー
- これでインストールしたパッケージもコードも使えるようになる
CMDの変更
- distroless
gcr.io/distroless/python3
の entrypoint はpython
に設定されていることに注意 - 以前はCMDに
python3
を入れていたが削除する必要があった
// 変更前
CMD ["python3", "***.py"]
// 変更後
CMD ["***.py"]
成果
- あんなにあった脆弱性がなくなった!
- サイズも削減された!
今後に向けての課題
- python3のdistrolessは本番運用向きではないらしい...
- https://github.com/GoogleContainerTools/distroless
The following images are also published on gcr.io, but are considered experimental and not recommended for production usage: ...
- 今回は内部向けのサービスだったため踏み切ったが、外部向けに展開するサービスには向いていないかも
- 今回のサービスはパッケージも少なめだったが、複雑なサービスになったときに動作がどこまで保証されるか未検証
参考記事
実装時、大変お世話になりました!