Dockerの開発環境構築その2-Dockerfileの解説

この投稿は、弊社が提供するWESEEK TECH通信の一環です。
WESEEK TECH通信とは、WESEEKのエンジニアがキャッチアップした技術に関する情報を、techブログを通じて定期的に発信していくものです。

はじめに

どうも、システムエンジニアの蛸井です。
前回の記事では DockerCompose について解説しました。今回はその続きで、Dockerfile について解説していこうと思います。

Dockerfile とは何か

Dockerfile とは、イメージを作成する際に実行したいコマンドを書き込むことができるテキストファイルです。
一般的には、元となるイメージを Docker Hub から pull した後に、そのイメージを元としてプロジェクトや開発スタイルに合わせてカスタマイズした新しいイメージを作成して使用します。

Dockerfile を扱うメリット

まず Dockerfile を用いない場合のコンテナの作成について話します。
Docker Hub からコンテナの元となるイメージを探し、pull します。

docker pull ruby:3.0.2

そして pull したイメージからコンテナを作成します。

docker run -dit --name my-container ruby:3.0.2

docker run コマンドは docker create と docker start が1つにまとまったコマンドで、コンテナの作成と実行ができます。
(-d オプションはバックグラウンドでの実行(デタッチドモード)、-i と -t オプションでコマンドを受け付ける状態にしている)

これで my-container という名前の Ruby コンテナが無事立ち上がりました。こだわりがなければ今作成したコンテナを使うのでも特に問題はありません。

Dockerfile を使うことのメリットとは、イメージを作り上げる時に実行したい命令を全てファイル内に書きこむことができるという点です。
命令を書き込むことにより、DockerHub から pull したイメージをプロジェクトや開発スタイルに合うようにカスタマイズして使用することができます。
例えば Ruby のコンテナを立ち上げる場合 Dockerfile に COPY や CMD を記述することによって、プロジェクト内の Gemfile, Gemfile.lock をコンテナ内にコピーし、bundle install を実行させることができます。

上手く Dockerfile を記述することで、コンテナ立ち上げ時に初回起動時の処理を全て行いアプリケーションを起動させる、なんてことも出来ます。

Dockerfile の各種命令

今回はこのような Dockerfile を用意しました。

FROM ruby:3.0.2

COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock

WORKDIR /app

RUN gem install bundler -v "2.2.25"
RUN bundle install

ENTRYPOINT ["bash", "-c", "bundle exec rails db:create db:migrate db:seed && bundle exec rails server"]

FROM

Dockerfile では、まず始めに FROM を使用します。
FROM では以降の命令で使うベースイメージを設定します。
Dockerfile を実行した時に、まず始めに実行した環境内からイメージを探し、実行した環境でそのイメージが見つからなければ、今度は Docker Hub からイメージを探ししてダウンロードします。

それ以降の命令は、FROM で指定したイメージに対して順次実行されるという仕組みになっています。

FROM の後ろに AS 名前 を追加することで構築ステージに名前を付けることができます。
名前をつけることで以降の FROM や COPY などで名前を参照することが可能になり、Dockerfile に複数の FROM を定義して管理できるようになります。

COPY

ファイルやフォルダをコピーして追加します。

Dockerfile を置いているディレクトリ内のファイル、ディレクトリしかコピーできないため、注意が必要です。
要するに、COPY ../ファイル名 のように、上の階層のファイルを指定することはできません。

ADD

ファイルやフォルダを追加します。

こちらは COPY とは異なり、対象が圧縮ファイルであった場合は自動的に展開されます。
さらにファイル名の代わりに URL を指定することでリモートのファイルをダウンロードして追加することができます。

WORKDIR

RUN, CMD, ENTRYPOINT, COPY, ADD を実行するディレクトリを指定します。
もし WORKDIR で指定したディレクトリが存在しなければ新しく作成されます。

RUN

イメージのビルド時に指定したコマンドが実行されます。
ここにはパッケージのインストールやファイルのコピーなど、イメージの時点で実行しておきたいコマンドを書きます。

CMD

こちらは RUN とは異なり、コンテナの起動時に指定したコマンドが実行されます。
CMD の主な目的はコンテナ実行時の初期設定を指定することで、Dockerfile 内で1度しか使うことができません。複数の CMD が存在していた場合は一番最後の CMD が有効になります。

ENTRYPOINT

こちらも CMD と同様にコンテナの起動時に指定したコマンドが実行されますが、コマンドの実行を強要するというのが CMD との違いです。
どういうことかというと、CMD の場合は docker run 時に後ろにコマンドを指定すると CMD が上書きされて実行されます。

docker run ruby-image ls

例えば上記のコマンドでコンテナの立ち上げを行なった場合、Dockerfile 内の CMD は上書きされて ls コマンドが実行されます。
CMD ではなく ENTRYPOINT を使用していた場合は上書きがされず、そのまま ENTRYPOINT に指定されたコマンドが実行されます。
つまり ENTRYPOINT で指定されたコマンドは必ず実行されます。

ちなみに ENTRYPOINT と CMD を併用した場合は両方が合わさったようなコマンドが実行されます。

ENTRYPOINT ["ls"]
CMD ["-a"]

例えば Dockerfile にこのような記述をした場合、コンテナの起動時に ls -a が実行されます。
CMD の部分は上書きが可能なため -a を別のオプションに変えて実行させるなんてこともできますが、使う機会は無いかと思われます。

USER

RUN や CMD などのコマンドを実行するユーザーを指定します。
USER の記述がなかった場合は自動的に root ユーザーで実行されます。

LABEL

イメージにメタデータを追加します。
具体的には名前やバージョン、製作者の情報を記述できます。

ENV

環境変数を設定します。
設定した環境変数は後から上書き可能です。

終わりに

前回用意した docker-compose.yml と今回の Dockerfile を用意して

docker-compose up

を実行することで、無事 Rails の開発環境を構築できました。

今回は主に Dockerfile の各種命令について解説しましたが、RUN, CMD, ENTRYPOINT の違いや COPY と ADD の違いは全く知らなかったので、記事の執筆を通じてとても勉強になりました。

この記事は公式ドキュメントの Dockerfile リファレンスを参考に書かせていただきました。
docs.docker.jp