🐳

distroless なイメージでも envsubst が使いたい

2024/11/08に公開

本番環境向けのコンテナイメージを作成する際、イメージサイズやセキュリティを考慮して distroless イメージを使うことがあります。

https://blog.inductor.me/entry/alpine-not-recommended
https://sysdig.jp/blog/dockerfile-best-practices/
https://zenn.dev/ymd65536/articles/improve_by_distroless_image

またコンテナイメージを作成する際、docker-entrypoint.sh を作成してコンテナの起動時に envsubst を実行し、環境変数から各種設定ファイル等を生成するパターンも定番です。

https://qiita.com/minamijoyo/items/63ae57b99d4a4c5d7987
https://reearth.engineering/posts/ngnix-docker-envsubst-ja/

しかし distroless イメージでこれをやろうとすると、次のような問題からうまくいきません。

envsubst がない

distroless イメージは必要最小限のパッケージのみを含めることで省サイズと高いセキュリティを実現しています。そこには /bin/sh すら含まれておらず、当然ながら envsubst も残念ながら含まれていません。

ただし、以下のようにすることで distroless ベースのイメージに envsubst を追加することは可能です。

Dockerfile
FROM debian:bookworm AS envsubst

RUN apt-get update && apt-get install -y gettext-base

FROM gcr.io/distroless/base-debian12:nonroot AS app

COPY --from=envsubst /usr/bin/envsubst /usr/bin/envsubst

しかし例え envsubst を追加しても、次の問題が残り、うまくいきません。

/bin/sh がない

前述の通り distroless イメージには /bin/bash はおろか /bin/sh すら含まれていません。そのため、仮に docker-entrypoint.sh などを作成し ENTRYPOINT に指定したとしても当然ながらそれが動くことはありません……。

envsubst と同じように COPY 命令で /bin/sh をいずれかのイメージから持ってくることはできるかもしれませんが、それでは distroless イメージのメリットが半減してしまいます。

解決策

そもそも envsubstコンテナの起動時に実行したい理由は、コンテナを起動するタイミングで任意の環境変数を渡し、それを反映した状態でコンテナを実行したいからです。

しかし例えば HTTP サーバーにおけるドメイン名やポート番号、パフォーマンスチューニングのための設定値など、本番環境においてはあらかじめ決めておくことが可能な値であることが多いのではないでしょうか。

もしそうであれば、実行時ではなくイメージのビルド時に envsubst を実行し、対象の設定ファイルを生成することで、任意の設定値が埋め込まれたイメージを生成すれば事足ります。

Dockerfile
FROM nginxinc/nginx-unprivileged:1.27-bookworm

ARG APP_DOMAIN_NAME="example.com"

RUN echo "$APP_DOMAIN_NAME" && \
    envsubst '$APP_DOMAIN_NAME' < /tmp/app.conf.template > /opt/etc/nginx/conf.d/app.conf

条件付きではあるものの、多くのケースではこれで課題は解決できるんじゃないでしょうか。

Discussion