【初心者向け】Dockerfileとは?書き方をわかりやすく解説(RUN,COPY,ENV,ENTRYPOINTなど)

「【初心者向け】Dockerfileとは?書き方をわかりやすく解説(RUN,COPY,ENV,ENTRYPOINTなど)」のメインビジュアル

この記事は、「Dockerfileってどうやって書くの?」という方に向けて、Dockerfileの書き方をわかりやすく解説する入門記事です。

ポイントを一つずつ確認しながら、Dockerfileの作り方を一緒に見ていきましょう!

単語の意味

本記事で使用する単語のざっくりとした意味を紹介します。

記事の内容をスムーズに理解するための参考にしてください。

  • イメージ:コンテナを作成するための「設計図」のようなものです。
  • コンテナ:イメージをもとに作られる、独立した「アプリケーションの実行環境」のことです。

Dockerfileってなに?

Dockerfileは、アプリケーションを動かすための実行環境を、”コード”で定義した設計図のようなファイルです。

OS・ソフトウェア・設定などを記述しておくことで、誰でも同じ手順でDockerイメージ(実行環境の完成品)を作成できます。

そのイメージからDockerコンテナを起動すれば、どんな環境でも同じ条件でアプリケーションを動かすことができます。

Dockerの仕組み(環境をコードで再現できる)

Dockerについてや、Dockerによる環境構築手順が知りたい方は、本記事の最後に載せている参考記事をご活用ください。

今回説明するサンプルコード

以下「【初心者向け】DockerでRails APIとPostgreSQLの環境構築をする(M1 Mac)」で使用したDockerfileを例に解説していきます。

【Dockerfile】

FROM ruby:3.4.3-alpine

ENV RUNTIME_PACKAGES="bash tzdata postgresql-dev git make" \
    DEV_PACKAGES="build-base yaml-dev" \
    HOME=/${WORKDIR} \
    LANG=C.UTF-8 \
    TZ=UTC

RUN apk update && \
    apk upgrade && \
    apk add --no-cache ${RUNTIME_PACKAGES} && \
    apk add --virtual build-dependencies --no-cache ${DEV_PACKAGES} && \
    apk add nodejs yarn

RUN mkdir /rails-api-docker
ENV APP_ROOT /rails-api-docker
WORKDIR $APP_ROOT

COPY Gemfile $APP_ROOT
COPY Gemfile.lock $APP_ROOT
RUN bundle install

COPY . $APP_ROOT

RUN apk del build-dependencies

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh

ENTRYPOINT ["entrypoint.sh"]

CMD ["bundle", "exec", "rails", "server", "-p", "3000", "-b", "0.0.0.0"]

EXPOSE 3000

【entrypoint.sh】

#!/bin/bash
set -e

# データベースのマイグレーションを実行
echo "Running database migrations..."
bundle exec rails db:migrate

# コンテナのメインプロセス(DockerfileでCMDと設定されているもの)を実行する
exec "$@"

※entrypoint.shは末尾に「.sh」がつくシェルスクリプトと呼ばれるファイルです。シェルスクリプトは複数のコマンドを自動で順番に実行したり、条件分岐やループなどのプログラム的な処理も書けるため、作業の自動化や繰り返し処理に使われます。今回は、デーベースのタマイグレーションの目的で使用します。

Dockerfileとentrypoint.shの処理の流れ

Dockerfile内の処理がどう実行されるか分かりやすくするため、処理の流れを以下に記載します。

  1. Dockerfileが上から順番に実行される
  2. ENTRYPOINTが実行されると、ENTRYPOINTにセットされた「entrypoint.sh」ファイルが実行される
  3. 「entrypoint.sh」内の処理が上から順番に実行される
  4. 「entrypoint.sh」内の「exec "$@"」が実行されると、「entrypont.sh」ファイルから抜けて、Dockerfileの「CMD」が実行される

※「exec "$@"」を実行しない場合、「entrypont.sh」内の「bundle exec rails db:migrate」までが実行された状態で、コンテナが終了します。

Dockerfileの書き方

それではさっそく、記事冒頭でご紹介した、Dockerfileのサンプルコードで定義されている各種命令について解説していきます。

FROM(ベースイメージの指定)

FROMは、Dockerコンテナの元になるイメージを指定します。

Dockerfileは必ずFROMから始めます。

# alpineという種類のRuby3.4.3-alpineイメージを使用する
FROM ruby:3.4.3-alpine

コード例では、Ruby3.4.3-alpineのイメージを使用しています。(Alpineとは、主に開発環境で使用される軽量なDockerイメージのことです)

例えば、Docker公式イメージを使用したい場合は、以下のようになります。

# Docker公式のRuby3.2.0イメージを使用する
FROM ruby:3.2.0

RUN(コマンドの実行)

RUNは、コンテナ内でコマンドを実行します。

# アプリケーション開発に必要なパッケージをコンテナ内にインストールする
RUN apk update && \
    apk upgrade && \
    apk add --no-cache ${RUNTIME_PACKAGES} && \
    apk add --virtual build-dependencies --no-cache ${DEV_PACKAGES} && \
    apk add nodejs yarn

# コンテナ内にディレクトリを作成
RUN mkdir /rails-api-docker

・・・省略・・・

# Gemfileに記述されたGem(ライブラリ)をコンテナ内にインストールする
RUN bundle install

・・・省略・・・

# ビルド用に導入した仮想パッケージを削除する(ビルド後のイメージに含めたくないパッケージを削除)
RUN apk del build-dependencies

・・・省略・・・

# 「/usr/bin/entrypoint.sh」ファイルに実行権を付与する
RUN chmod +x /usr/bin/entrypoint.sh

上記のコードでは、パッケージのインストールやディレクトリの作成などを行っています。

RUN命令の使い分け

RUN命令の書き方には次の2種類があります。

  • シェル形式RUN <コマンド>
  • 実行形式RUN ["実行ファイル", "パラメータ"]

それぞれの形式には特徴があり、使い分けが重要です。

使い分けの考え方

  • シェル形式は、&& やパイプ (|) などのシェル構文を使いたいときに便利です。
    例:RUN apt-get update && apt-get install -y curl
    → 複数コマンドをまとめて実行でき、ビルド効率が良くなります。
  • 実行形式は、特定のシェルを明示的に指定したいときに使います。
    例:RUN ["bash", "-c", "echo $HOME"]
    → 引数(bash)が正確に渡り、環境(コンテナ内のシェル)に依存せず安定して動作します。

使い分けは以下のように覚えておけばOK!

  • 基本は「シェル形式」を使う(読みやすくて効率的)
  • どうしても特定のシェルが必要な場合だけ「実行形式」を使う

この2つを意識しておけば、ほとんどのケースで迷うことはありません。

ENV(環境変数の設定)

ENVは、コンテナ内で使用できる環境変数を設定します。

# 作成したディレクトリのパスを環境変数 APP_ROOT に設定する
ENV APP_ROOT /rails-api-docker

設定した環境変数は、 $環境変数名 または ${環境変数名} の形式で使用できます。

# 設定した環境変数 APP_ROOT を使用する(WORKDIRはDockerfile内で書かれたコマンドを実行するディレクトリを指定)
WORKDIR $APP_ROOT

or

WORKDIR ${APP_ROOT}

WORKDIR(作業ディレクトリの指定)

WORKDIRは、WORKDIR以降のDockerfile内で書かれたコマンドを実行するディレクトリを指定します。

# 作成したディレクトリを作業ディレクトリとして設定する
WORKDIR $APP_ROOT

コード例では、$APP_ROOT (先ほど設定した「/rails-api-docker」)を作業ディレクトリとして設定しています。

これにより、以降の COPYRUN/rails-api-docker 内で実行されます。

例えば、以下のようにDockerfileが書かれている場合、Gemfileは/rails-api-docker 内にコピーされます。

・・・省略・・・

RUN mkdir /rails-api-docker
ENV APP_ROOT /rails-api-docker
WORKDIR $APP_ROOT

COPY Gemfile $APP_ROOT

・・・省略・・・

また他にも、以下のようにDockerfileが書かれている場合を想定してみます。

・・・省略・・・

RUN mkdir /rails-api-docker
ENV APP_ROOT /rails-api-docker
WORKDIR $APP_ROOT

COPY Gemfile ./

・・・省略・・・

この「./」は「WORKDIR $APP_ROOT」から見た時の「./」となるため、「Gemfile」は「/rails-api-docker」にコピーされることになります。

COPY(ファイルのコピー)

COPY は、ホスト(ここではローカル環境)のファイルやディレクトリをコンテナ内のコピー先にコピーします。

<コピー先> は絶対パスか WORKDIR (作業ディレクトリ)からの相対パスです。

# ENV APP_ROOT /rails-api-docker
# WORKDIR $APP_ROOT →「/rails-api-docker」が設定されている。

# ローカルプロジェクトのGemfileをコンテナの $APP_ROOT にコピーする
COPY Gemfile $APP_ROOT/
# ローカルプロジェクトのGemfile.lockをコンテナの $APP_ROOT にコピーする
COPY Gemfile.lock $APP_ROOT/

・・・省略(bundle installなどで依存関係をinstall)・・・

# ローカルプロジェクトのファイル・フォルダをすべてコンテナ内にコピーする
COPY . $APP_ROOT

・・・省略・・・

# ローカルプロジェクトのentrypoint.shをコンテナ内の「/usr/bin/」にコピーする
COPY entrypoint.sh /usr/bin/

コード例では、GemfileやGemfile.lock、entrypoint.shなどのファイルをコンテナ内にコピーしています。

また、COPY . $APP_ROOTを使用し、ローカルプロジェクトの全てのファイルを、WORKDIRで指定された作業ディレクトリ内にコピーしています。

※単純にファイルやフォルダをコンテナ内にコピーしたい場合はCOPY を使用します。

ADDCOPYの違いについて詳しく知りたい方は、「Docker のベストプラクティス: Dockerfile の ADD 命令と COPY 命令の違いを理解する」を参考にしてください。

コンテナ内にファイルやフォルダをコピーしたことを確認する

コンテナ起動後にコンテナにアクセスし、ファイル・フォルダ構造を確認すると(ターミナルでlsコマンドを実行すると)、ローカルプロジェクトと同じ構造になっていることを確認することができます。

以下は、Dockerfileで、COPY . $APP_ROOTCOPY . .COPY . ./それぞれを実行したときの、コンテナ内のフォルダ構造をターミナルで表示したものです。

【Dockerfileで「COPY . $APP_ROOT」を実行した時の、コンテナ内のフォルダ構造】
<コンテナID>:/rails-api-docker# ls

Dockerfile          app                 docker-compose.yml  public              tmp
Gemfile             bin                 docs                script              vendor
Gemfile.lock        config              entrypoint.sh       storage
README.md           config.ru           lib                 terraform
Rakefile            db                  log                 test

【Dockerfileで「COPY . .」を実行した時の、コンテナ内のフォルダ構造】
<コンテナID>:/rails-api-docker# ls

Dockerfile          app                 docker-compose.yml  public              tmp
Gemfile             bin                 docs                script              vendor
Gemfile.lock        config              entrypoint.sh       storage
README.md           config.ru           lib                 terraform
Rakefile            db                  log                 test

【Dockerfileで「COPY . ./」を実行した時の、コンテナ内のフォルダ構造】
<コンテナID>:/rails-api-docker# ls

Dockerfile          app                 docker-compose.yml  public              tmp
Gemfile             bin                 docs                script              vendor
Gemfile.lock        config              entrypoint.sh       storage
README.md           config.ru           lib                 terraform
Rakefile            db                  log                 test

全て同じファイル/フォルダ構造になっていることが確認できました。

コンテナ内にアクセスするコマンド(bashを使用している場合)

  • docker exec -it <コンテナID> bash
  • docker exec -it <コンテナID> /bin/bash

(起動中のコンテナの)コンテナIDを確認するコマンド

  • docker ps
  • docker ps -a

ENTRYPOINT(コンテナ起動時に必ず実行したいコマンド・ファイルを設定)

ENTRYPOINTは、コンテナ起動時に必ず行っておきたい処理(pidの削除、DBの準備等の初期化処理など)を記述したファイルやコマンドを設定/実行することができます。

# コンテナ起動時に entrypoint.sh 実行する
ENTRYPOINT ["entrypoint.sh"]

コード例では、entrypoint.shを実行しています(今回はentrypoint.shファイル内で「#!/bin/bash」を指定しているため、bashで実行されています)。

※Dockerfile には少なくとも1つの CMD または ENTRYPOINT 命令を含む必要があります。

CMD(コンテナ起動時のメイン処理を設定)

CMDは、コンテナを起動した時のメイン処理が書かれたファイルやコマンドを設定することができます。

# コンテナ起動時のメイン処理を設定
CMD ["bundle", "exec", "rails", "server", "-p", "3000", "-b", "0.0.0.0"]

コード例では、Railsサーバーを起動させるコマンドを指定しています。

CMDで指定したコマンドは上書きもできます。

詳しくは「Dockerリファレンス」を参考にしてください。

EXPOSE(公開ポートを明示的に知らせる※ただし実際には公開されない)

EXPOSEは、コンテナが外部と通信する際に使用するポート番号を、明示的に書いておくための命令です。

# コンテナが3000番ポートで通信を受け付けることを宣言
EXPOSE 3000

例えば、「このコンテナは3000番ポートで動くんだな」と一目で分かるようになるため、EXPOSE を書いておくと、開発者同士の連携やコンテナ間の設定がしやすくなります。

ただし、EXPOSE を書いただけではポートは実際には公開されません。

実際に外部(ホスト側)へアクセスできるようにするには、docker run -p コマンドや docker-compose.ymlports 設定でポートを公開する必要があります。

以上で各種コマンドの説明は終わりです!

おわりに

この記事では、Dockerfileの基本構造と各命令の役割を解説しました。

FROMでベースイメージを指定し、RUNでコマンドを実行、COPYでファイルをコピーし、ENVで環境変数を設定。

そして、ENTRYPOINTCMD でコンテナ起動時の動作を決めます。

この流れを理解すれば、Dockerfileの基本はしっかり押さえられます。

より詳しい仕様や最新構文を知りたい方は、以下をチェックしてみてください👇

本記事が、Dockerfileを理解する第一歩になれば幸いです!

Docker・Docker Composeとは何かを知りたい方はこちら

「【入門】Dockerとは|仕組みとメリットをわかりやすく解説」のメインビジュアル【初心者向け】Dockerとは|仕組みとメリットをわかりやすく解説 「【初心者向け】Docker Composeとは|仕組みとメリットをわかりやすく解説」のメインビジュアル【初心者向け】Docker Composeとは|仕組みとメリットをわかりやすく解説

Docker・Docker Composeで環境構築をしたい方はこちら

【初心者向け】DockerでReactの環境構築をする(M1 Mac) 【初心者向け】DockerでRails APIとPostgreSQLの環境構築をする(M1 Mac) 【初心者向け】DockerでRails APIとMySQLの環境構築をする(M1 Mac)

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA