Docker 開発のベストプラクティス

Docker を使ってアプリケーション開発を行う方々にとって、以下に示す開発パターンが有効であることが明らかになっています。 何かを見つけている場合は お知らせください

どうやってイメージを小さく保つか

イメージが小さければネットワークからの取得が速くなります。 またコンテナーやサービスの起動時に、メモリへのロードも速くなります。 イメージを小さく保つ経験則をいくつか示します。

  • 適切なイメージをベースとして始めます。 たとえば JDK を必要とするのであれば、公式イメージ openjdk をベースとしたイメージ作りとします。 逆に、汎用的な ubuntu イメージから始めて、Dockerfile 内に openjdk をインストールするような方法は取らないようにします。

  • マルチステージビルド を利用します。 たとえば maven イメージを使うと Java アプリケーションを構築することができます。 これを tomcat イメージとして作り直して、Java アプリのコード類を適切な場所に配置してデプロイできるようにします。 これをすべて同一の Dockerfile 内で行います。 これはつまり最終的なイメージがビルドされたら、そのイメージ内には、元々のイメージ取得時に存在していたライブラリや依存パッケージがすべて含まれるわけではなく、実行時に必要なモジュールや環境のみが含まれるということを意味します。

    • マルチステージビルドの機能を持たない Docker バージョンを使う必要があるときには、イメージ内に作られるレイヤー数を減らすようにしてください。 これは Dockerfile 内での RUN コマンドの実行が、できるだけ分断されないように、その実行数を最小化します。 これを実現するには、複数の RUN コマンドはできるだけ 1 つの RUN コマンドとなるように、シェルの機能を使って互いに連結させます。 たとえば以下のような 2 つのコマンド実行例があったとします。 1 つめのコマンドは、イメージ内に 2 つのレイヤーを生成しますが、2 つめのコマンドはレイヤーが 1 つで済みます。

      RUN apt-get -y update
      RUN apt-get install -y python
      
      RUN apt-get -y update && apt-get install -y python
      
  • 共有するイメージがたくさんある場合は、ベースイメージ を作ることを考えてみてください。 これを用いてコンポーネントを共有し、これをベースとした独自のイメージを作っていくことができます。 Docker は共通するレイヤーであれば 1 度しかロードする必要がなく、ロードした内容はキャッシュされます。 つまりベースイメージから派生させたイメージは、Docker ホスト上でのメモリ利用が効率よく行われ、ロードもすばやく行われることになります。

  • 本番環境向けのイメージはスリムにしたいものの、デバッグは可能にしたいといった場合は、デバッグ環境向けとして、本番環境イメージをベースイメージとすることを考えてみてください。 さらにテストやデバッグツールを加えたい場合でも、本番環境イメージの上に追加ができます。

  • イメージをビルドする場合には、常にわかりやすいタグをつけるようにします。 このタグを用いて、バージョン情報をコード化したり、目的とする用途(たとえば prodtest など)や安定性など、いろいろな情報を付与したりします。 こうしておけば、アプリケーションをさまざまな環境にデプロイする際にわかりやすくなります。 自動的に生成される latest タグには頼らないようにします。

アプリケーションデータはどこにどう保存するか

  • ストレージドライバー によって、コンテナーの書き込み可能レイヤーへデータ保存を行うことができますが、アプリケーションデータの保存を行うことは避けます。 これを行ってしまうと、コンテナーのサイズが増えることになり、I/O 観点で言えば、ボリュームやバインドマウントを用いることに比べて非効率なものになります。
  • そのかわりに、データ保存は ボリューム を利用します。
  • バインドマウント を用いるのが適当な例として、開発時での利用が考えられます。 開発時には、ソースディレクトリや生成したばかりのバイナリを、コンテナー内にマウントしたくなります。 本番環境ではボリュームを利用しますが、本番環境がマウントする同じ場所を、開発環境時はバインドマウントによりマウントします。
  • 本番環境において、サービスが機密情報を利用している場合、その保存には secrets を利用します。 そして機密情報ではない設定ファイルなどの情報は configs を利用します。 今利用しているコンテナーがスタンドアロンである場合は、1 つのレプリカからなるサービスコンテナーに移行することを考えてみてください。 これを行うと、サービスコンテナーのみに提供される機能を活用することができます。

テストやデプロイ時の CI/CD 利用

  • ソース管理上の変更を確認したりプルリクエストを生成したりする場合には、Docker Hub やこれに似た別の CI/CD を利用し、自動的なイメージビルド、タグづけ、テストを行うようにしてください。

  • 開発、テスト、セキュリティチームによる イメージ認証 といった必要に応じて、上のことをより一層進め、 本番環境へのデプロイを行ってください。 こうする場合に、イメージを本番環境にデプロイするのであれば、たとえば開発、品質管理、セキュリティの各チームにおいて十分にテストされ承認されていなければなりません。

開発環境と本番環境の違い

開発環境 本番環境
バインドマウントを用いて、コンテナーからソースコードへアクセスできるようにします。 ボリュームを利用してコンテナーデータを保存します。
Docker Desktop for Mac または Docker Desktop for Windows を利用します。 可能であれば Docker EE を利用してください。userns mapping を利用すると、ホストプロセスからの Docker プロセスの独立性をさらに高めることになります。
時間のずれは、気にする必要はありません。 Docker ホスト上、あるいは各コンテナープロセス内においては NTP クライアントを常時稼動させてください。そして同一の NTP サーバーによって同期をとるようにしてください。スウォームサービスを用いる場合、各 Docker ノードは、コンテナーとして同一時刻となるように同期をとるようにしてください。
application, development