自己紹介
株式会社マーケライズの開発チームに所属しております、カツシと申します。
現在は自社サービスのマーケライズクラウド(MRC)の開発を担当しております。
MRCにおいてはフロントエンドからバックエンドまで幅広く開発を行わせていただいております。
個人的にバックエンドの開発を行っている時が一番楽しく感じられますので、
プライベートではコンテナ環境やバックエンドサーバーで色々試すことが多いです。
概要
昨今のWEBサービスにおいては、運用や開発にコンテナを使うことが多いと思われます。
コンテナはサービスが稼働する要であり、良きコンテナを作成することは良き開発環境を構築すること、ひいては良きWEBサービスを提供することにつながります。
本記事は、代表的なコンテナ管理アプリケーションにて用いられるDockerfileやDockerイメージの内部的な構成、良いDockerfileの記述方法などについて紹介します。
なお、本記事においてはDockerfileの基本的な記述方法などについては割愛します。
DockerfileとDockerイメージのおさらい
DockerfileとDockerイメージについて、簡単におさらいします。
Dockerイメージに含むファイルシステムや、コンテナ起動後の動作を定義するDockerfileを作成し、
そのDockerfileをbuildすることでDockerイメージの作成を行います。
そして、runによってDockerイメージからDockerコンテナを構築し、サービスが稼働します。
Dockerイメージを直接渡す、またはリモートリポジトリで共有を行うことなどにより、だれでも同じコンテナ環境を構築することができます。
Dockerfileのコマンドについて
Dockerfileに書き込むコマンドは、FROMを除き、以下の2種類に大別できます。
- docker build時に実行されるコマンド
- WORKDIR:ワークディレクトリの指定
- COPY:ホストからコンテナへファイルをコピーする
- RUN:任意のコマンドを実行する
- etc…
- docker run時に実行されるコマンド
- ENV:環境変数の設定
- CMD:コンテナ起動後に実行するコマンドを指定する
- etc…
他にも、ADDやENTRYPOINTなど、Dockerfileには多数のコマンドが用意されています。
コマンドを組み合わせることにより、多種多様なコンテナを直感的に構築することが可能となります。
Dockerイメージの構成について
Dockerイメージは、「1つのファイル」ではなく、「抽象的なイメージレイヤの集合体」です。
Dockerfileに記述された各コマンドがそれぞれイメージレイヤーとなり、それらが順番に積み重なることでDockerイメージが構成されています。
積み重なったイメージレイヤーは、上下間で親子関係を構成します。
Dockerイメージとイメージレイヤについて
それぞれのイメージレイヤーはDockerfileのコマンドによって独立したファイルシステムを持っています。
Dockerイメージを利用する際はこれらのファイルシステムが統合され、Dockerイメージが1つのファイルシステムを持っているように扱うことができます。
良いDockerfileを作るには
最終的なコンテナの動作が同じでも、Dockerfileの書き方を工夫することで、そのコンテナの利便性や開発の快適さが変わってきます。
例として、以下のような工夫が可能です。
- 中間ファイルを削除し、Dockerイメージの容量を小さくする
- イメージレイヤを統合し、イメージレイヤ内のファイルシステムから不要なファイル削除する
- マルチステージビルドにより、必要なファイルのみイメージに残す(今回は割愛します)
- コンテナ構築の際にキャッシュが利用されるようにする
- 頻繫に変更されるファイルを含むコマンドが後に実行されるように記述する
容量が小さいほど、リポジトリからのDockerイメージのpullが早く完了し、素早くコンテナを構築することができます。
また、DockerfileからDockerイメージを作成する際にキャッシュ機能を有効に利用することで、Dockerイメージ作成が劇的に早くなります。
最近はAWS ECSなどのコンテナ管理サービスを用いたシステム運用が主流となっていると思われます。
素早くコンテナを構築できるということは、障害時の復帰時間短縮につながるため、良いDockerfileを作ることはサービスの品質に良い影響を与えると考えられます。
また、Dockerイメージ作成時にキャッシュを活用することで、快適なコンテナ環境の開発を行うことができます。
イメージレイヤを統合してDockerイメージ内の不要なファイルを削除する
Dockerイメージのファイルシステムに、コンテナ稼働に不要なファイルをが含まれている場合、それらを削除することでDockerイメージを軽量化することができます。
各イメージレイヤーは読み取り専用であるため、不要なファイルが作成されるイメージレイヤーと、ファイル削除を行うイメージレイヤーが別々の場合、Dockerイメージ内のファイル削除は行われません。
そのため、Dockerfileにおいてこれらの動作を1つのRUNにまとめることで、Dockerイメージからファイルを削除することができます。
上図の場合、どちらも/listディレクトリが空のコンテナが作られますが、Dockerイメージの容量は大きく異なっています。
右図の場合のみ、Dockerイメージ内のファイルシステムにおいて不要なファイルが削除されているためです。
頻繫に変更されるイメージレイヤを後にしてキャッシュを活用する
同じDockerfileから何度もDockerイメージを作るとき、2回目以降のDockerイメージビルドにおいて、COPY・ADDコマンドによって扱われるファイルに変更があった場合、以降のコマンドはすべてキャッシュが無効になります。
このため、頻繁に変更されるファイルやディレクトリを扱うレイヤを後に記述することで、キャッシュを用いて快適にイメージビルドができるようになります。
コンテナ環境の開発段階においてはキャッシュを意識することで、快適に開発を行うことが可能となります。
最後に
本記事では、Dockerイメージの内部的な構成や、Dockerfileの記述における工夫などを紹介しました。
ここに記載されている以外にも、様々な記述法や、工夫点がいくつも存在します。
コンテナの世界は実に深く広いですが、自分で1から作り上げたコンテナ環境で自分の書いたコードが動作するということは、喜びも一入です。
今回は単一のコンテナを構築するためのDockerイメージについての記事となりましたが、
またの機会には、Docker Composeなど、複数のコンテナ制御に関わる機能についてお話できればと思います。
その際は、さらに深堀りしたお話ができるように僕もレベルアップを図っていこうと思います。
最後までお読みいただき、ありがとうございました!