Dockerfileを書いて?みました。
はじめに
ハローワールド!マイクです。
Dockerはオープンなプラットフォームです。Dockerオブジェクトと呼ばれるイメージ、コンテナ、ネットワーク、ボリュームなどで構成され、これらを操作する事でアプリケーションを開発、配布、実行することが出来ます。
今回はDockerfileの書き方です。
今回の環境です
Ubuntu 22.04 Jammy Jellyfish
VirtualBox 7.0
Ubuntu 22.04.3 LTS(VirtualBoxのゲストOSです。)
Docker Engine - Community 24.0.7(VirtualBoxのゲストOSの中で実行しました。)
まず、Dockerfileとはなんでしょう?
流行りのChatGPTに聞いてみました。(笑)
DockerfileはImageを作成するための指示書です。Dockerfileの先頭でベースにするImage(OSだったり、アプリケーションだったり)を指定したあと、必要に応じてパッケージの追加やアプリケーションのインストールの指示を書いていきます。他にユーザの追加、ファイルのコピー、開放するポートの指定などImageをコンテナで実行する為に必要な事を書いていくのですが、中にはDockerfileに記述できないものもあるので、それらはImageの実行時に指定します。例えば、ホストポート番号とコンテナポート番号のルーティングです。
Dockerfileは上から順番に実行されます。Dockerfileの一行はレイヤと呼ばれ、一度ビルドされるとローカルにキャッシュされます。次回Imageを作成するときに、レイヤに変更が加わって無ければキャッシュを使います。変更されていれば、そのレイヤを含む以降のレイアはすべて再度ビルドされます。つまり、頻繁に変更が加わるレイアをDockerfileの下の方で記述しておけば、効率的にImageの作成が行えるという事になります。
Imageを作るときは必ずDockerfileから作るようにしましょう。確実に同じImageを誰でも作れるようにしておくことは、安定した運用と保守を考えるときに大変重要だと思います。
次に、DockerfileでNginx UnitのImageを作成してみます。
最初は自分でゼロから書こうかと思ったのですが、よく考えたらDockerHubのOfficial Imageには必ずと言って良いほどDockerfileが公開されています。ですので、Nginx UnitのOfficial ImageのDockerfileを見てみましょう。
私はPythonが好きなのでPythonアプリケーションを実行するためのNginx UnitのImageを(たぶん)作成するDockerfileを見てみます。
FROM python:3.11-bullseye
LABEL org.opencontainers.image.title="Unit (python3.11)"
LABEL org.opencontainers.image.description="Official build of Unit for Docker."
LABEL org.opencontainers.image.url="https://unit.nginx.org"
LABEL org.opencontainers.image.source="https://github.com/nginx/unit"
LABEL org.opencontainers.image.documentation="https://unit.nginx.org/installation/#docker-images"
LABEL org.opencontainers.image.vendor="NGINX Docker Maintainers <docker-maint@nginx.com>"
LABEL org.opencontainers.image.version="1.31.1"
RUN set -ex \
&& savedAptMark="$(apt-mark showmanual)" \
&& apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev curl pkg-config \
&& mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \
&& mkdir -p /usr/src/unit \
&& cd /usr/src/unit \
&& hg clone -u 1.31.1-1 https://hg.nginx.org/unit \
&& cd unit \
&& NCPU="$(getconf _NPROCESSORS_ONLN)" \
&& DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \
&& CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \
&& LD_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_LDFLAGS_MAINT_APPEND="-Wl,--as-needed -pie" dpkg-buildflags --get LDFLAGS)" \
&& CONFIGURE_ARGS_MODULES="--prefix=/usr \
--statedir=/var/lib/unit \
--control=unix:/var/run/control.unit.sock \
--runstatedir=/var/run \
--pid=/var/run/unit.pid \
--logdir=/var/log \
--log=/var/log/unit.log \
--tmpdir=/var/tmp \
--user=unit \
--group=unit \
--openssl \
--libdir=/usr/lib/$DEB_HOST_MULTIARCH" \
&& CONFIGURE_ARGS="$CONFIGURE_ARGS_MODULES \
--njs" \
&& make -j $NCPU -C pkg/contrib .njs \
&& export PKG_CONFIG_PATH=$(pwd)/pkg/contrib/njs/build \
&& ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modulesdir=/usr/lib/unit/debug-modules --debug \
&& make -j $NCPU unitd \
&& install -pm755 build/sbin/unitd /usr/sbin/unitd-debug \
&& make clean \
&& ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modulesdir=/usr/lib/unit/modules \
&& make -j $NCPU unitd \
&& install -pm755 build/sbin/unitd /usr/sbin/unitd \
&& make clean \
&& /bin/true \
&& ./configure $CONFIGURE_ARGS_MODULES --cc-opt="$CC_OPT" --modulesdir=/usr/lib/unit/debug-modules --debug \
&& ./configure python --config=/usr/local/bin/python3-config \
&& make -j $NCPU python3-install \
&& make clean \
&& ./configure $CONFIGURE_ARGS_MODULES --cc-opt="$CC_OPT" --modulesdir=/usr/lib/unit/modules \
&& ./configure python --config=/usr/local/bin/python3-config \
&& make -j $NCPU python3-install \
&& cd \
&& rm -rf /usr/src/unit \
&& for f in /usr/sbin/unitd /usr/lib/unit/modules/*.unit.so; do \
ldd $f | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq >> /requirements.apt; \
done \
&& apt-mark showmanual | xargs apt-mark auto > /dev/null \
&& { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
&& /bin/true \
&& mkdir -p /var/lib/unit/ \
&& mkdir -p /docker-entrypoint.d/ \
&& groupadd --gid 999 unit \
&& useradd \
--uid 999 \
--gid unit \
--no-create-home \
--home /nonexistent \
--comment "unit user" \
--shell /bin/false \
unit \
&& apt-get update \
&& apt-get --no-install-recommends --no-install-suggests -y install curl $(cat /requirements.apt) \
&& apt-get purge -y --auto-remove build-essential \
&& rm -rf /var/lib/apt/lists/* \
&& rm -f /requirements.apt \
&& ln -sf /dev/stdout /var/log/unit.log
COPY docker-entrypoint.sh /usr/local/bin/
COPY welcome.* /usr/share/unit/welcome/
STOPSIGNAL SIGTERM
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
EXPOSE 80
CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
”FROM”は必ず書かなければなりません。ベースイメージを指定します。
”LABEL”はImageにメタデータを追加します。docker image inspectでImageの情報を表示させると、このメタデータを読む事ができます。
”RUN”ではパッケージのアップデート、ディレクトリの作成、ユーザの追加などしています。Nginx Unitをmakeでビルドしているみたいです。とても長いですが、これで一つのレイヤです。
”COPY”はカレントディレクトリからImageのディレクトリにファイルをコピーします。
”STOPSIGNAL”ではコンテナを終了させるためのシステムコールを指定します。ここではデフォルトの値が指定されているみたいです。
"ENTRYPOINT"、Imageからコンテナを作成するときに実行する事を指定します。(シェルファイルの中には何が書いてあるんだろう?)
"EXPOSE"はコンテナを作成・実行したときにアプリケーションが開いているポート番号を指定します。
"CMD"、これも必須らしいです。コンテナを作成・実行したときに実行するコマンドを指定します。カッコ[]がついているのでunitdという実行ファイルにパラメータを3つ渡して実行を指定しています。CMDをDockerfileに複数記述した場合は最後の記述のみが有効とのことです。
他に、”VOLUME”を記述する事でコンテナ外にファイルをおくことができます。VOLUMEはコンテナが削除されても、一緒に削除される事はありません。docker image inspectでコンテナ内のディレクトリとホストのディレクトリがマウントされている場所を確認する事ができます。
DockerfileのリファレンスへのURLを最後に書いておきましたのでよかったら見てみてください。CMDやCOPYなどのInstructionはそんなに沢山はないです。
最後に
また、ChatGPTにお願いしてみました。プロンプトには”以下のDockerfileを作成してください。 Nginx Unitのイメージを作成します。 Python 3.11のウェブアプリケーションを実行します。”
そしたら、こんな感じのものが出力されてきました。
# ベースイメージとしてNginx Unitを使用
FROM nginx/unit:1.26.0-python3.11
# アプリケーションのコードをコピー
COPY . /app
# アプリケーションの依存関係をインストール
RUN apt-get update && \
apt-get install -y python3.11 && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# アプリケーションの設定をNginx Unitにコピー
RUN cp /app/unit_config.json /docker-entrypoint.d/
# アプリケーションのコードのディレクトリに移動
WORKDIR /app
# ユニットによって管理されるPythonアプリケーションの実行コマンドを指定
CMD ["unitd", "--no-daemon", "--control", "0.0.0.0:8080"]
更に”フレームワークはFlaskを使用します。”を追加で指定したから、こんな感じのものが出力されてきました。
# ベースイメージ
FROM python:3.11-slim
# Nginx Unitのインストール
RUN apt-get update && \
apt-get install -y curl && \
curl -O https://unit.nginx.org/download/unit-1.26.0.tar.gz && \
tar xzf unit-1.26.0.tar.gz && \
cd unit-1.26.0 && \
./configure --prefix=/usr/local/nginx-unit && \
make && \
make install
# Nginx Unitの設定ファイルをコピー
COPY unit_config.json /usr/local/nginx-unit/conf.json
# Flaskアプリケーションのコピー
COPY app /app
# アプリケーションディレクトリに移動
WORKDIR /app
# Flaskの依存関係のインストール
RUN pip install --no-cache-dir -r requirements.txt
# Nginx UnitとFlaskアプリケーションのスタートアップコマンド
CMD ["/usr/local/nginx-unit/sbin/unitd", "--no-daemon", "--control", "unix:/usr/local/nginx-unit/control.sock"]
数行の指示でここまで書いてくれるAIって面白いですね。これってちゃんと動くのかしら?後で試してみよう。
今回はImageを作成するためのDockerfileについて書いてみました。最後までお読み頂きありがとうございます。
参考
履歴
2023年12月30日:投稿
この記事が気に入ったらサポートをしてみませんか?