DockerFileの「VOLUME」命令について


136

以下は私の「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" ]

このファイルでは、「VOLUME。/ usr / src / app」命令を実行して、ホストの現在の作業ディレクトリの内容をコンテナの/ usr / src / appフォルダにマウントする必要があります。

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

回答:


88

公式のドッカーのチュートリアルは言う:

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

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

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

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

  • コンテナー自体が削除されても、データボリュームは保持されます。

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

あなたは、コンテナを実行すると、例えばdocker run --volume=/opt:/usr/src/app my_image、あなたはありますが、そのマウントポイント(指定する必要はありませんの/ optをホストマシン上で)。--volume引数を指定しない場合、マウントポイントは通常、で自動的に選択されます/var/lib/docker/volumes/


275

つまり、いいえ、あなたのVOLUME指示は正しくありません。

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

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

「。」を使用する例 ドットを最初の引数にするか2番目の引数にするかに関係なく、文字は私のマシンでも実行されません。私はこのエラーメッセージを受け取ります:

docker:デーモンからのエラー応答:ociランタイムエラー:container_linux.go:265:コンテナープロセスの開始により「process_linux.go:368:コンテナーの初期化により\ "open / dev / ptmx:no such file or directory \"」が発生しました。

私は何をここまで言われてきたことは、おそらく理解しようとする人には非常に貴重ではないことを知っているVOLUME-v、それは確かにあなたが達成しようとする何のためのソリューションを提供していません。したがって、うまくいけば、次の例はこれらの問題にさらに光を当てるでしょう。

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

このDockerfileを考えると:

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

(私たちが指定した場合、このminitutorialの結果については、それは違いはありませんvol1 vol2/vol1 /vol2-なぜ私に聞かないでください)

ビルドする:

docker build -t my-openjdk

実行:

docker run --rm -it my-openjdk

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

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

コンテナーを実行docker volume lsしている間にホストマシンで実行すると、次のようなものが表示されます(簡潔にするために、名前の中央部分を3つのドットに置き換えました)。

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

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

このファイルは、ホストマシンの名前のないボリュームの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。引数は、コンテナー絶対パスでなければなりません。ホスト側では、新しい3番目のボリュームは匿名で、他の2つのボリュームと一緒に存在します/var/lib/docker/volumes/

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

これは「Hello、World!」と印刷します。

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

最後の発言

私はDockerを使い始めたばかりで、前述の「チュートリアル」は、3日間のコマンドラインハッカソンから集めた情報を反映しています。私の発言を裏付ける明確な英語のようなドキュメントへのリンクを提供できなかったことを恥ずかしく思いますが、正直なところ、これはドキュメントの不足と個人的な努力によるものではないと思います。私の例では、現在の設定である「Windows 10-> Vagrant 2.0.0-> Docker 17.09.0-ce」を使用して、これらの例が宣伝どおりに機能することを知っています。

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

最後VOLUMEに、Dockerfile で指定することは珍しいことではないが、を使用しないことがベストプラクティスであると直感しますVOLUME。2つの理由があります。すでに特定した最初の理由:ホストパスを指定できません。これは、Dockerfileがホストマシンの詳細に大きく依存しないため、これは良いことです。しかし、2番目の理由は--rm、コンテナーの実行時にオプションを使用するのを忘れることがあるためです。コンテナを削除することを覚えているかもしれませんが、ボリュームを削除するのを忘れているかもしれません。さらに、人間の記憶力が最も優れている場合でも、すべての匿名ボリュームのうちどれを削除しても安全かを判断するのは困難な作業になる場合があります。


2
名前なし/匿名ボリュームはいつ使用する必要がありますか?
Searene 2018年

10
@Martin、ありがとうございました。あなたのハッカソンとその結果のチュートリアルは、ここでは非常に熟考されています。
Beezer 2018年

6
「英語のようなドキュメントをクリアするためのリンクを提供できませんでした...正直なところ、これはドキュメントが不足しているためだと思います。」確認できます。これは、私が見つけた最も完全で最新のドキュメントであり、何時間も探していました。
user697576

4
docker volume prune実行中のコンテナーに接続されていない残りのボリュームをクリーンアップするために使用できます。IDだけで潜在的に重要なものを区別するのは簡単だということは言うまでもありません...
Jeremy

4
「このミニチュートリアルの結果については、vol1 vol2または/ vol1 / vol2を指定しても違いはありません。理由を聞かないでください。」@MartinAnderssonこれは、現在の作業ディレクトリが/であるためvol1、に相対的であり/、に解決されるため/vol1です。あなたが使用している場合WORKDIR以外の作業ディレクトリを指定し/vol1そして/vol1もはや同じディレクトリを指すないだろう。
セバスチャン

41

の指定 VOLUMEDockerfileで行をと、画像に少しメタデータが構成されますが、そのメタデータの使用方法は重要です。

まず、これらの2行は何をしましたか。

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

WORKDIRそこでの行は、ディレクトリが存在しない場合はディレクトリを作成し、すべての相対パスを指定するようにいくつかのイメージメタデータを更新しますRUNVOLUMEラインが2つのボリュームが指定する 1が相対パスである、.と他のIS /usr/src/app、両方がちょうど同じディレクトリにあることが起こります。ほとんどの場合VOLUME行には1つのディレクトリしか含まれていませんが、これまでのように複数のディレクトリを含めることも、json形式の配列にすることもできます。

Dockerfileでボリュームソースを指定することはできませんDockerfileでボリュームを指定すると、イメージのビルド時にソースとデスティネーションのランタイム構文を一致させようとすると混乱することがよくありますが、これは機能しません。。Dockerfileはボリュームの宛先のみを指定できます。誰かがDockerハブの共通イメージを更新してルートディレクトリをコンテナーにマウントし、コンテナー内のバックグラウンドプロセスをエントリポイントの一部としてコンテナー内で起動できるため、ボリュームのソースを定義できれば、それは些細なセキュリティエクスプロイトです。ログインを/ etc / passwdに追加し、次の再起動時にビットコインマイナーを起動するようにsystemdを構成するか、ファイルシステムでクレジットカード、SSN、およびリモートサイトに送信する秘密鍵を検索します。

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

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

ボリュームをどのように指定する必要がありますか?イメージにボリュームを含める場所を指定するには、を指定しますdocker-compose.yml。ユーザーはそれを変更してボリュームの場所をローカル環境に合わせることができ、公開ポートやネットワークなどの他のランタイム設定をキャプチャします。

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

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

...

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

36

VOLUMEコマンドDockerfileは非常に合法であり、完全に従来型であり、まったく問題なく使用でき、いずれにしても廃止されることはありません。それを理解する必要があるだけです。

これを使用して、コンテナ内のアプリが大量に書き込むディレクトリをポイントします。使わないVOLUME設定ファイルのようにホストとコンテナ間で共有したいという理由だけで。

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

VOLUMEコマンドで指定されたフォルダにマウントすると、VOLUMEコンテナの起動時にのみ実行されるため、コマンドは実行されませんENV

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

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

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


2
良い例と悪い例のユースケースに関して、Dockerの「dockerfile best-practices」ページには、「イメージの変更可能な部分やユーザーが保守可能な部分にはVOLUMEを使用することを強くお勧めします。」とあります。そこに設定があると思います。
OmerSch

2
VOLUMEconfigsのディレクトリについて明示することは問題ありません。ただし、実際に構成をマウントすると、そのディレクトリにマウントする必要があるため、VOLUMEコマンドは実行されません。したがってVOLUME、configに指定されたディレクトリでコマンドを使用しても意味がありません。また、単一の静的な読み取り専用ファイルでボリュームグラフを初期化することは、深刻な過剰です。だから私は私が言ったことを支持しVOLUMEます、設定でコマンドの必要はありません。
mr

ボリューム、実装の詳細により、異なるパフォーマンス特性をもたらす可能性があります。データベースデータファイルはこのユースケースに適合しますが、とにかく(エフェメラル)コンテナーストレージと一緒にデータを保存する意味は何でしょうか。つまり、ボリュームの存在をパフォーマンスに起因するものは正しくありません。
アンドレ・Werlang

33

volumedockerfile の指示をよりよく理解するために、mysqlの公式docker file実装での典型的なボリュームの使用方法を学びましょう。

VOLUME /var/lib/mysql

リファレンス:https : //github.com/docker-library/mysql/blob/3362baccb4352bcf0022014f67c1ec7e6808b8c5/8.0/Dockerfile

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

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

docker run mysql:8

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

/var/lib/docker/volumes/320752e0e70d1590e905b02d484c22689e69adcbd764a69e39b17bc330b984e4

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

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

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

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

mysql公式イメージの使用方法(「データの保存場所」セクションを確認してください):

リファレンス:https : //hub.docker.com/_/mysql/


2
私はあなたの説明がとても好きです。
LukaszTaraszka

しかし、Dockerはとにかく変更を保存します。また-v、Dockerfileにボリュームを設定せずにマウントパスを使用してマウントすることもできます
Alex78191

1

自分でイメージを作成していて、他の誰もそれを使用しない場合を除いて、私はVOLUMEの使用をどのような場合でも良いとは考えていません。

私が拡張したベースイメージで公開されたVOLUMEによって悪影響がありました。/var/www/htmlフォルダーがVOLUMEとして宣言されているワードプレスのように、イメージが既に実行された後にのみ問題を知ったため、追加または変更されたファイルがあることを意味しました。ビルド段階は考慮されず、知らない場合でもライブ変更が持続します。別の場所にWebディレクトリを定義するための醜い回避策がありますが、これはVOLUMEディレクティブを削除するだけで、よりシンプルな方法の悪い解決策です。

-vオプションを使用してボリュームの目的を簡単に達成できます。これにより、コンテナーのボリュームが(Dockerfileと親Dockerfileを確認する必要なく)何であるかが明確になるだけでなく、これによりコンシューマーはボリュームを使用するかどうか。

で述べられているように、以下の理由により、VOLUMESを使用することは基本的に悪いです この回答でです:

ただし、VOLUME命令にはコストがかかります。

  • ユーザーは、名前のないボリュームが作成されていることに気付かず、コンテナーが削除された後もDockerホストのストレージスペースを占有し続ける場合があります。
  • Dockerfileで宣言されたボリュームを削除する方法はありません。ダウンストリームイメージは、ボリュームが存在するパスにデータを追加できません。

後者の問題は、このような問題を引き起こします。

ボリュームの宣言を解除するオプションがあると役立ちますが、知っている場合のみ解除、イメージを生成したdockerfile(および親dockerfiles!)で定義されたボリュームいる。さらに、新しいバージョンのDockerfileにVOLUMEを追加すると、イメージのコンシューマーにとって予期せぬ事態が発生する可能性があります。

(別の良い説明のOracle画像有するVOLUME約し、除去):https://github.com/oracle/docker-images/issues/640#issuecomment-412647328

VOLUMEが他の人に迷惑をかけた他のケース:

親イメージ(VOLUMEを含む)のプロパティをリセットするオプションを追加するプルリクエストは閉じられ、ここで議論されています(そしていくつか のケース人々が持っている、悪dockerfilesで定義されたボリュームによる影響を受ける)コメント良いとしボリュームに対する説明:

DockerfileでVOLUMEを使用しても意味がありません。ユーザーが永続化を必要とする場合は、指定されたコンテナーを実行するときに必ずボリュームマッピングを提供します。ディレクトリの所有権(/ var / lib / influxdb)を設定できないという私の問題は、InfluxDBのDockerfileでのVOLUME宣言が原因であったことを突き止めることは非常に困難でした。UNVOLUMEタイプのオプションがない場合、またはそれを完全に取り除く場合、指定したフォルダーに関連するものを変更できません。これは理想的ではありません。特に、セキュリティを意識していて、特定のUIDを指定したい場合は、ランダムなユーザーを避け、必要以上の権限を持つホストでソフトウェアを実行しているため、イメージを実行する必要があります。

EXPOSEも悪いと思いますが、副作用は少ないです。私がVOLUMEとEXPOSEについて見ることができる唯一の良いことは、ドキュメントについてであり、それらが(副作用なしで)それだけに役立っていれば、私はそれらを良いと考えます。

TL; DR

VOLUMEの最適な使用方法は廃止されると思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.