🐹

Dockerfileのフロントエンドとバックエンドの構築、命令コマンドの使い方、レイヤーキャッシュについて

に公開

Dockerfileは、アプリケーションの実行環境を作るための「レシピ」のようなものです。この記事では、私がフロントエンドとバックエンド用に作成したDockerfileをもとに、その内容や命令コマンドの学習内容を記録します。

フロントエンドのDockerfileを解説

Vue.jsを使用したフロントエンドをDockerで動かすDockerfileのコードを下のようにします。

Dockerfile
FROM node:18.17.1

RUN apt-get update && yarn global add @vue/cli

RUN mkdir -p /frontend
WORKDIR /frontend

COPY package*.json ./
RUN yarn install
COPY . .

ポイント

  • 環境構築
    • FROMでNode.jsの公式イメージを指定し、node:18.17.1という特定のバージョンとします。
  • パッケージのインストール
    • RUN命令では、以下の2つの操作を実行します。
      1. apt-get update:Debian系Linuxのapt-getコマンドで、利用可能なパッケージリストを最新に更新します。
      2. yarn global add @vue/cli:Yarnを使ってVue.js CLIをグローバルにインストールします。
        → これにより、Vue.js CLIをDockerコンテナ内で使用でき、CLIは、Vue.jsプロジェクトの作成や管理を簡単に行うようにします。
  • ディレクトリの作成
    • RUNコマンドを使って、コンテナ内に/frontendというディレクトリを作成します。なお、-pオプションにて、親ディレクトリが存在しない場合でもエラーを出さずにディレクトリを作成できるようにします。
  • 作業ディレクトリの設定
    • WORKDIRで作業する場所を指定し、コンテナ内の/frontendフォルダを作業場所とします。
  • 依存関係のインストール
    以下の1~3の順序で記述することで、Dockerのキャッシュを有効活用し、依存関係の再インストールを避けるようになり、その結果イメージのビルドが高速化します。
    1. COPYコマンドを使用して、ホスト側のpackage*.jsonファイル(package.jsonおよびpackage-lock.jsonまたはyarn.lockなど)をコンテナ内の作業ディレクトリ(./)にコピーします。
    2. yarn installを実行して、package*.jsonに基づく依存関係をインストールします。
    3. COPY . .の形で、フロントエンドのコード全体をコンテナ内にコピーします。

バックエンドのDockerfileを解説

Railsを使用したバックエンドをDockerで動かすDockerfileのコードを下のようにします。

Dockerfile
FROM ruby:3.2.2

RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs

ENV APP_PATH /myapp

RUN mkdir $APP_PATH
WORKDIR $APP_PATH

COPY Gemfile $APP_PATH/Gemfile
COPY Gemfile.lock $APP_PATH/Gemfile.lock
RUN bundle install

COPY . $APP_PATH

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

EXPOSE 3000

ポイント

  • 環境構築
    • FROMコマンドでRubyの公式イメージを指定し、ruby:3.2.2という特定のバージョンとします。
  • パッケージのインストール
    • RUNコマンドでは、以下の2つの操作を実行します。
      1. apt-get update -qq:Linuxのパッケージリストを最新に更新し、-qqオプションでログ出力を抑え、ビルド時の出力を見やすくします。
      2. apt-get install -y build-essential libpq-dev nodejs
        • build-essential:Ruby用の外部ライブラリ(Gem)の中で、C/C++を含むものをビルドするために必要なツールで、pgnokogiriなどネイティブコード(特定のプラットフォーム向けに最適化されたコード)を含むGemのビルドに必要となります。
        • libpq-dev:PostgreSQLとRailsを連携させるために必要なライブラリのヘッダーファイル(※1)となります。(MySQLを使用する場合はlibmysqlclient-dev)。
        • nodejs:JavaScriptランタイムで、Railsのフロントエンドアセットの処理に必要となります。

※1.ヘッダーファイルとは?
ヘッダーファイルは、CやC++で書かれたプログラムが外部ライブラリを利用する際のインターフェース情報を提供するファイルとなります。

  • 環境変数の設定
    • ENV APP_PATH /myapp
      環境変数APP_PATHを設定することで、バックエンドの作業ディレクトリのパスを簡単に変更できるようにします。
    • RUN mkdir $APP_PATH
      環境変数APP_PATHで指定したパス(/myapp)にディレクトリを作成します。
    • WORKDIR $APP_PATH
      環境変数APP_PATHを利用して、コンテナ内の作業ディレクトリを/myappに指定します。これ以降の命令(例:COPYRUN)はすべてこのディレクトリ内で実行します。
  • コンテナにセットアップするための必要な処理
    1. 依存関係のインストール:
      • COPY Gemfile $APP_PATH/Gemfile
        Gemfileをコンテナ内の作業ディレクトリ($APP_PATH)にコピーします。
      • COPY Gemfile.lock $APP_PATH/Gemfile.lock
        Gemfile.lockをコピーし、依存関係のバージョンを固定します。
      • RUN bundle install
        GemfileGemfile.lockから必要なGemをインストールします。
    2. アプリケーションコードのコピー:
      • COPY . $APP_PATH
        バックエンドのコード全体を作業ディレクトリ($APP_PATH)にコピーします。
    3. エントリポイントスクリプトの設定:
      • COPY entrypoint.sh /usr/bin/
        アプリケーションのentrypoint.shをコンテナの/usr/bin/にコピーします。
      • RUN chmod +x /usr/bin/entrypoint.sh
        entrypoint.shに実行権限を与え、スクリプトを動作するようにします。
      • ENTRYPOINT ["entrypoint.sh"]
        コンテナ起動時にentrypoint.shを実行します。
        entrypoint.shの内容
        • rm -f /myapp/tmp/pids/server.pid:RailsアプリケーションのPIDファイル(server.pid)が残っていると、再起動時「サーバーがすでに起動している」と誤認識され、サーバーの起動に失敗するため、不要なPIDファイルの削除をします。
  • アプリケーションのポートの公開
    • EXPOSE 3000
      Railsのデフォルトポートである3000番を公開することで、外部からのアクセスを可能にします。

Dockerfileに登場する命令コマンドの解説

Dockerfileを構成する命令コマンドの役割を、簡単にまとめてみました。
使い方や注意点も参考にしてください。

  • FROM:ベースとなるDockerイメージを指定します。
    • 一番最初に書く命令です。
    • 軽量なAlpineなどを選ぶとイメージサイズを減らします。
  • RUN:イメージをビルドするときに実行するコマンドを記載します。
    • コマンドをまとめるとイメージサイズを減らします。
  • ENV:環境変数を設定します。
    • =を付ける記法が推奨されています(例: ENV APP_PATH=/myapp)。
    • 環境変数は後続の命令(例:WORKDIRやRUN)で利用します。
  • WORKDIR:作業ディレクトリを指定します。
    • cdコマンドのように、ディレクトリを移動するイメージで使用します。
    • 以降の命令は、このディレクトリを基準として実行します。
  • COPY:ホストマシンのファイルやフォルダを、コンテナ内にコピーします。
    • COPY . .の形で、カレントディレクトリ全体をコピーするパターンをよく使用します。
  • ENTRYPOINT:コンテナ起動時に実行したいコマンドを指定します。
    • CMDと組み合わせることで、柔軟なコマンド実行が可能にします。
  • EXPOSE:コンテナが外部に公開するポートを指定します。
    • 実際の公開は-Pオプションなどで行いますが、ここで指定することで明示的にします。
    • EXPOSEは、開発者やツールに対して「どのポートを使うのか」を明示するために使用します。。

Dockerのレイヤーキャッシュ

Dockerのレイヤーキャッシュは、Dockerイメージ(仮想化)をビルドする際に過去のビルド結果を再利用する仕組みです。これにより、同じ作業を何度も繰り返す必要がなく、ビルド時間を短縮し、リソースを効率的に利用します。

レイヤーとキャッシュの説明

  • レイヤーとは?
    Dockerfileの各命令(例: RUN, COPYなど)ごとに積み重ねられる層のことで、Dockerイメージ全体は下のレイヤーの集合体で構成します。
    • 仕組み
      • FROMRUNCOPYなどの命令を1つずつ実行するたびに新しいレイヤーを作成します。
      • 各レイヤーは差分だけを保持しており、効率的なストレージ管理を可能とします。
    • Dockerfile
      FROM ruby:3.2.2          # レイヤー1
      RUN apt-get update       # レイヤー2
      ENV APP_PATH /myapp      # レイヤー3
      
    • イメージの構造
      レイヤーは「積み木」のように積み重なり、最終的なDockerイメージを構成します。
  • キャッシュとは?
    ビルド時に過去のレイヤーを再利用する仕組みです。
    • 仕組み
      • 各命令の実行結果をキャッシュとして保存します。
      • 同じ命令を再実行する場合、過去に生成されたレイヤーを再利用することで効率的なビルドを可能にします。
    • キャッシュが有効になる条件
      1. Dockerfile内の命令が完全に同じである
      • 例:RUN apt-get install -y curlRUN apt-get install -y wgetに変更された場合、キャッシュは無効
      1. 参照するファイルやデータが変更されていない
      • 例:COPY Gemfile /app/Gemfileの場合、Gemfileが変更されるとキャッシュは無効
      1. ビルドコンテキストが同じ
      • ビルド時に指定したディレクトリ(例:docker build .で指定する.)の内容が変わるとキャッシュが無効
  • キャッシュ無効化が与える影響
    Dockerでは、キャッシュが1つの命令で無効化されると、それ以降の全ての命令のキャッシュも無効化されます。
    • 変更なしの場合:全ての命令がキャッシュされ、ビルドを高速化します。
    • 下のGemfileが変更された場合、COPY Gemfile以降の命令(RUN bundle installCOPY .)が全て再実行されます。
      Dockerfile
      COPY Gemfile /app/Gemfile       # 1. キャッシュが無効
      RUN bundle install              # 2. キャッシュが無効
      COPY . /app                     # 3. キャッシュが無効
      
    • 順序が重要な理由
      頻繁に変更される命令を後ろに配置することで、それ以前の命令のキャッシュが最大限再利用されます。
      上の例だと、COPY Gemfileを先に配置し、COPY .を後にすることで、Gemfileが変更されない限りbundle installのキャッシュが有効なままになります。
  • レイヤーキャッシュを活用するポイント
    1. 頻繁に変更される命令を後に配置:
    • 変更が少ない部分を先に記述し、変更頻度の高い命令を後ろに配置します。
    • 例: インストールは最初に行うことで、依存関係ファイルが変更されなければbundle installがキャッシュされます。
      Dockerfile
      COPY Gemfile Gemfile.lock /app/  # 依存関係ファイルだけコピー
      RUN bundle install               # 依存関係をインストール
      COPY . /app                      # アプリケーションコード全体をコピー
      
    1. 複数の命令を1つにまとめる:
      不要なキャッシュ無効化を防ぐために、下のrunのように関連する処理をまとめるようにします。
      Dockerfile
      RUN apt-get update && apt-get install -y build-essential libpq-dev
      
    2. 特定ファイルだけを先にコピー:
      依存関係のインストールや設定ファイルのコピーをコードのコピーよりも優先します。
    • 効率的なキャッシュ
      Dockerfile
      COPY Gemfile Gemfile.lock /app/
      RUN bundle install
      COPY . /app
      
    • キャッシュが効かない
      Dockerfile
      COPY . /app
      RUN bundle install
      

Dockerfileのフロントエンドとバックエンドの構築、命令コマンドの使い方、そしてレイヤーキャッシュの活用法を理解することで、効率的で再利用可能な環境を構築できます。この記事があなたの開発に役立てば幸いです!

docker-compose:「docker-composeの環境構築、命令コマンドの使い方について」も参考にして下さい!

Discussion