web-dev-qa-db-ja.com

DockerFileの "VOLUME"命令を理解する

以下は私の "Dockerfile"の内容です。

FROM node:boron

# Create app directory
RUN mkdir -p /usr/src/app

# change working dir to /usr/src/app
WORKDIR /usr/src/app

VOLUME . /usr/src/app

RUN npm install

EXPOSE 8080

CMD ["node" , "server" ]

このファイルでは、現在の作業ディレクトリの内容をホストの/ usr/src/appフォルダにマウントするように指示する「VOLUME。/ usr/src/app」命令を期待しています。

これが正しい方法かどうかを教えてください。

63
refactor

公式dockerチュートリアルにはこう書かれています:

データボリュームは、Unionファイルシステムをバイパスする1つ以上のコンテナー内の特別に指定されたディレクトリーです。データボリュームは、永続データまたは共有データに役立つ機能をいくつか提供します。

  • ボリュームは、コンテナーが作成されたときに初期化されます。指定したマウントポイントにコンテナのベースイメージにデータが含まれている場合は、
    既存のデータがボリューム上に新しいボリュームにコピーされている
    初期化(これはホストをマウントするときには適用されません。
    ディレクトリ。)
  • データボリュームはコンテナ間で共有および再利用できます。

  • データボリュームへの変更は直接行われます。

  • 画像を更新しても、データボリュームへの変更は含まれません。

  • コンテナー自体が削除されてもデータ量は持続します。

Dockerfileでは、ボリュームのコピー先insideコンテナのみを指定できます。例えば/usr/src/app

コンテナを動かすときdocker run --volume=/opt:/usr/src/app my_image you mayですが、ホストマシン上のマウントポイント(/ opt)を指定する必要はありません。 --volume引数を指定しないと、マウントポイントが自動的に選択されます。

36
Bukharov Sergey

一言で言えば、いいえ、あなたのVOLUME命令は正しくありません。

DockerfileのVOLUMEは、コンテナー側のパスを指定して1つ以上のボリュームを指定します。しかし、それはイメージ作成者がホストパスを指定することを許可しません。ホスト側では、ボリュームはDockerルート内に非常に長いIDのような名前で作成されます。私のマシンではこれは/var/lib/docker/volumesです。

注:自動生成された名前は非常に長く、人間の観点からは意味をなさないので、これらのボリュームはしばしば「名前なし」または「匿名」と呼ばれます。

'。'を使ったあなたの例ドットを最初の引数にしても2番目の引数にしても、characterは私のマシンでは動作しません。私はこのエラーメッセージを得ます:

docker:デーモンからのエラー応答:ociランタイムエラー:container_linux.go:265:コンテナプロセスの開始により "process_linux.go:368:コンテナinitが発生しました\" open/dev/ptmx:そのようなファイルまたはディレクトリはありません\ "".

VOLUME-vを理解しようとしている人には、これまで述べたことはおそらくあまり価値がないことを知っていますし、それは確かにあなたが達成しようとしていることに対する解決策を提供しません。それで、うまくいけば、以下の例はこれらの問題にもう少し光を当てるでしょう。

ミニチュートリアル:ボリュームの指定

このDockerfileを考えます。

FROM openjdk:8u131-jdk-Alpine
VOLUME vol1 vol2

(このミニチュートリアルの結果として、vol1 vol2または/vol1 /vol2を指定しても違いはありません。理由を聞かないでください)

それを構築します。

docker build -t my-openjdk

実行します。

docker run --rm -it my-openjdk

コンテナ内で、コマンドラインでlsを実行すると、2つのディレクトリが存在することがわかります。 /vol1/vol2

コンテナを実行すると、ホスト側に2つのディレクトリ、つまり「ボリューム」も作成されます。

コンテナの実行中に、Host machinedocker volume lsを実行すると、以下のように表示されます(名前の真ん中を3つのドットに置き換えています) :

DRIVER    VOLUME NAME
local     c984...e4fc
local     f670...49f0

containerに戻り、touch /vol1/weird-ass-fileを実行します(上記の場所に空のファイルを作成します)。

このファイルは、ホストマシンの名前のないボリュームlolの1つに格納されています。最初にリストされたボリュームを最初に試したので2回試行しましたが、最終的にはホストマシンで次のコマンドを使用して2番目にリストされたボリュームでファイルを見つけました。

Sudo ls /var/lib/docker/volumes/f670...49f0/_data

同様に、あなたはホスト上でこのファイルを削除しようとすることができ、それは同様にコンテナ内でも削除されます。

注:_dataフォルダは「マウントポイント」とも呼ばれます。

コンテナーから出て、ホスト上のボリュームをリストします。彼らは去った。コンテナーを実行するときに--rmフラグを使用しました。このオプションは、終了時にコンテナーだけでなくボリュームも効果的に消去します。

新しいコンテナーを実行しますが、-vを使用してボリュームを指定します。

docker run --rm -it -v /vol3 my-openjdk

このは3番目のボリュームにを追加し、システム全体は3つの名前なしボリュームを持つことになります。 -v vol3だけを指定した場合、コマンドはクラッシュしました。引数はabsolutepathコンテナ内である必要があります。ホスト側では、新しい3番目のボリュームは匿名で、/var/lib/docker/volumes/内の他の2つのボリュームと共に存在します。

実行時にホストからコンテナにファイルを持ち込もうとすると、Dockerfileがホストパスにマップできないため、問題が発生することがあります。別の-v構文がこの問題を解決します。

プロジェクトディレクトリ./srcに、コンテナ内の/srcに同期したいサブフォルダがあるとします。このコマンドはトリックを行います:

docker run -it -v $(pwd)/src:/src my-openjdk

:文字の両側には絶対パスが必要です。左側はホストマシン上の絶対パス、右側はコンテナ内の絶対パスです。 pwdは、「現在の/作業ディレクトリを表示する」というコマンドです。コマンドを$()に入れると、コマンドは括弧で囲まれてサブシェルで実行され、プロジェクトディレクトリへの絶対パスが返されます。

まとめると、ホストマシンのプロジェクトフォルダに次の内容の./src/Hello.Javaがあるとします。

public class Hello {
    public static void main(String... ignored) {
        System.out.println("Hello, World!");
    }
}

このDockerfileを構築します。

FROM openjdk:8u131-jdk-Alpine
WORKDIR /src
ENTRYPOINT javac Hello.Java && Java Hello

このコマンドを実行します。

docker run -v $(pwd)/src:/src my-openjdk

これは「こんにちは、世界!」と印刷します。

最良の部分は、2回目の実行で別の出力のために新しいメッセージで.Javaファイルを完全に自由に変更できることです - image =を再構築する必要はありません)

最後に

私はDockerにはまったく慣れていません。前述の「チュートリアル」は、3日間のコマンドラインハッカソンから集めた情報を反映しています。私は自分の陳述を裏付ける明確な英語のような文書へのリンクを提供することができなかったことをほとんど恥ずかしいと思うが、これは文書の欠如によるもので個人的な努力によるものではないと正直に思う。 "Windows 10 - > Vagrant 2.0.0 - > Docker 17.09.0-ce"という現在の設定で宣伝されている通りに動作することがわかります。

このチュートリアルでは、「Dockerfileでコンテナーのパスを指定し、runコマンドでホストのパスのみを指定する方法」という問題は解決されていません。方法があるかもしれません、私はちょうどそれを見つけていません。

最後に、DockerfileでVOLUMEを指定するのは珍しいことではないと思いますが、おそらくVOLUMEを絶対に使用しないことをお勧めします。二つの理由があります。私たちがすでに識別した最初の理由は:ホストパスを指定することはできません - Dockerfilesはホストマシンの詳細にはあまり関係がないため、これは良いことです。しかし2番目の理由は、コンテナを実行するときに--rmオプションを使用するのを忘れてしまう可能性があることです。コンテナを削除するのを忘れないで、ボリュームを削除するのを忘れないかもしれません。さらに、最高のヒューマンメモリを使用しても、すべての匿名ボリュームのうちどれを削除しても安全であるかを判断するのは困難な作業です。

159

DockerfileでVOLUME行を指定すると、イメージに少しメタデータが設定されますが、そのメタデータの使用方法は重要です。

最初に、この2行は何をしましたか?

WORKDIR /usr/src/app
VOLUME . /usr/src/app

そこにあるWORKDIR行は、それが存在しない場合はディレクトリを作成し、RUNのようなコマンドの現在のディレクトリがその場所にあるように、すべての相対パスを指定するためにイメージメタデータを更新します。そこのVOLUMEは2つのボリュームを指定し、1つは相対パス.、もう1つは/usr/src/appです。どちらも偶然同じディレクトリです。ほとんどの場合、VOLUME行に含まれるディレクトリは1つだけですが、これまでと同じように複数のディレクトリを含めることも、json形式の配列にすることもできます。

Dockerfileでボリュームソースを指定することはできません:Dockerfileでボリュームを指定するときの混乱の一般的な原因は、ソースの実行時構文と一致することです。イメージ作成時の保存先これは機能しません。 Dockerfileはボリュームのコピー先のみを指定できます。誰かがDocker Hub上の共通イメージを更新してルートディレクトリをコンテナにマウントし、その後エントリポイントの一部としてコンテナ内でバックグラウンドプロセスを起動する可能性があるため、誰かがボリュームのソースを定義できれば簡単なセキュリティ攻撃になります。/etc/passwdにログインを追加するか、次回の再起動時にbitcoinマイナーを起動するようにsystemdを設定するか、クレジットカード、SSN、およびプライベートキーを探してリモートサイトに送信するようにファイルシステムを検索します。

VOLUME行は何をしますか?前述のように、イメージ内のディレクトリをボリュームと言うようにイメージメタデータを設定します。このメタデータはどのように使用されていますか?このイメージからコンテナを作成するたびに、dockerはそのディレクトリを強制的にボリュームにします。実行コマンドでボリュームを指定しない場合、またはファイルを作成する場合、dockerの唯一のオプションは匿名ボリュームを作成することです。これは、名前の一意のIDが長いローカルの名前付きボリュームで、作成された理由や含まれているデータを示すものではありません(匿名ボリュームとはデータが失われることを意味します)。名前付きボリュームまたはホストボリュームを指定してボリュームを上書きすると、データは代わりにそこに移動します。

VOLUMEが機能しません。Dockerfileで一度定義したボリュームを無効にすることはできません。さらに重要なことに、dockerのRUNコマンドは一時的なコンテナを使って実装されています。それらの一時的なコンテナは一時的な匿名ボリュームを取得します。その無名ボリュームはあなたのイメージの内容で初期化されます。 RUNコマンドからコンテナー内への書き込みはすべてそのボリュームに対して行われます。 RUNコマンドが終了すると、イメージへの変更は保存され、匿名ボリュームへの変更は破棄されますこのため、Dockerfile内でVOLUMEを定義しないことを強くお勧めします。その結果、ボリューム位置の初期データでイメージを拡張しようとしているイメージのダウンストリームユーザーにとって予期しない動作が発生します。

ボリュームの指定方法は?イメージのどこにボリュームを含めるかを指定するには、docker-compose.ymlを指定します。ユーザーはこれを変更してボリュームの場所を自分のローカル環境に合わせることができます。また、発行ポートやネットワークなどの他のランタイム設定も取得できます。

誰かがこれを文書化すべきです!彼らは持っています。 Dockerは、 Dockerfileのドキュメント にVOLUMEの使用法に関する警告を、実行時にソースを指定するためのアドバイスとともに含めています。

  • Dockerfile内からボリュームを変更する:宣言後にボリューム内のデータが変更された場合、それらの変更は破棄されます。

...

  • Hostディレクトリはコンテナの実行時に宣言されます。Hostディレクトリ(マウントポイント)は、その性質上、ホストに依存します。特定のホストディレクトリがすべてのホストで利用できるとは限らないため、これはイメージの移植性を保つためです。このため、Dockerfile内からHostディレクトリをマウントすることはできません。 VOLUME命令は、Host-dirパラメーターの指定をサポートしません。コンテナーを作成または実行するときにマウントポイントを指定する必要があります。
8
BMitch

VOLUMEname__内のDockerfilename__コマンドは非常に合法的で、まったく慣習的なものであり、使用することは絶対に問題ありません。いずれにしても非推奨ではありません。理解するだけでいいのです。

コンテナ内のアプリケーションが書き込むディレクトリを指すために使用します。設定ファイルのようにHostとcontainerの間で共有したいという理由だけでVOLUMEname__を使用しません。

コマンドは単に1つのパラメータを必要とします。コンテナー内からのフォルダーへのパス(設定されている場合はWORKDIRname__からの相対パス)。その後dockerはそのグラフ(/ var/lib/docker)にボリュームを作成し、それをコンテナ内のフォルダにマウントします。今度はコンテナーはどこかに高性能で書く必要があります。 VOLUMEname__コマンドがないと、指定されたフォルダーへの書き込み速度は非常に遅くなります。これは、コンテナーがコンテナー自体でcopy on writeストラテジーを使用しているためです。 copy on write戦略がボリュームが存在する主な理由です。

VOLUMEname__コマンドで指定されたフォルダーにマウントすると、VOLUMEname__はコンテナーの開始時にのみ実行されるため、ENVname__のようにコマンドは実行されません。

基本的にVOLUMEname__コマンドを使用すると、外部からボリュームをマウントしなくてもパフォーマンスが得られます。データは、外部マウントなしでコンテナの実行全体にわたって保存されます。それから準備ができたら、単にその上に何かをマウントします。

いくつかの良い使用例
- ログ
- 一時フォルダー

いくつかの悪い使用例:
- 静的ファイル
- 設定
- コード

7
mr haven

Dockerfileのvolume命令をよりよく理解するために、mysql公式dockerファイル実装での典型的なボリューム使用法を学びましょう。

VOLUME /var/lib/mysql

参照: https://github.com/docker-library/mysql/blob/3362baccb4352bcf0022014f67c1ec7e6808b8c5/8.0/Dockerfile

/var/lib/mysqlは、データファイルを保存するMySQLのデフォルトの場所です。

テスト目的でのみテストコンテナを実行する場合、マウントポイントを指定することはできません。

docker run mysql:8

mysqlコンテナインスタンスは、dockerfileのvolume命令で指定されたデフォルトのマウントパスを使用します。ボリュームはDockerルート内に非常に長いIDのような名前で作成されます。これは「名前なし」または「匿名」ボリュームと呼ばれます。基盤となるホストシステムのフォルダー/ var/lib/docker/volumes。

/var/lib/docker/volumes/320752e0e70d1590e905b02d484c22689e69adcbd764a69e39b17bc330b984e4

これは、マウントポイントを指定することなく迅速なテストを行う場合に非常に便利ですが、コンテナレイヤーではなくデータストアにボリュームを使用することで最高のパフォーマンスを得ることができます。

正式に使用するには、名前付きボリュームを使用するようにマウントポイントをオーバーライドしてマウントパスを指定する必要があります。

docker run  -v /my/own/datadir:/var/lib/mysql mysql:8

このコマンドは、コンテナ内の/ var/lib/mysqlとして、基礎となるホストシステムの/ my/own/datadirディレクトリをマウントします。データディレクトリ/ my/own/datadirは、コンテナが削除されても自動的に削除されません。

Mysql公式イメージの使用法:リファレンス: https://hub.docker.com/_/mysql/

4
Li-Tian