LoginSignup
6
2

More than 3 years have passed since last update.

【Docker】コンテナにソースコードをバインドマウントしている時のビルド方法

Last updated at Posted at 2020-10-26
1 / 7

はじめに

前提

Dockerの基本的な操作等については触れません。
バインドマウントを利用して、アプリのソースコードをコンテナ内にマウントして開発している状況を想定しています。

バージョン情報

Docker Engine: 19.03.13

大まかな流れ

  1. まず結論
  2. バインドマウントとは
  3. 順を追ってビルド方法を確認していく
  4. おまけ
  5. 感想

当記事のゴール

バインドマウントの特徴を理解し、アプリのソースごとビルドして、イメージ化出来るようになる。


まず結論

開発時にソースコードをコンテナにバインドマウントするのはよくある手法だが、デプロイなどの際にソースコードを含めてイメージ化するには一工夫する必要がある。
その方法は拍子抜けするほどシンプルだが、別途ビルド用のDockerfileを用意し、明示的にソースをCOPYすればいい。
(自分で調べていて、「ほんとにこんなシンプルな話なの・・・?」と思わず疑ってしまった。)


バインドマウントとは

Dockerで使えるボリュームのマウントタイプは主に3つある。
それぞれの違いを完結に説明すると

  • ボリューム
    • Dockerによって管理される領域に対してマウントする
    • 複数のコンテナが共有可能
  • バインドマウント
    • ホスト上のパスに対してマウントさせる
    • ホストとコンテナ、相互に読み書きが出来てしまう
  • tmpfsマウント
    • ホストのメモリ上に保存される
    • コンテナが起動している間のみ、コンテナが利用するもの
    • 一時的な状態や機密情報などを保存する

image.png

また、ボリュームとバインドマウントには以下のような特徴もある
公式のストレージ概要から引用

バインドマウントとボリュームを使う際のヒント
バインドマウントとボリュームのどちらかを用いる場合には、以下のことを忘れないでください。

コンテナー内のディレクトリに 空のボリューム をマウントしようとしていて、そのディレクトリ内にファイルやディレクトリが存在する場合、そのファイルやディレクトリはボリューム内にコピーされます。 コンテナー起動時に指定したボリュームがまだ存在していなかった場合は、空のボリュームが生成されます。 コンテナーの求めに応じて事前にデータを提供しておく方法として用いられます。

コンテナー内のディレクトリに バインドマウントか、空ではないボリューム をマウントしようとしていて、そのディレクトリ内にファイルやディレクトリが存在する場合、マウントによってそのファイルやディレクトリは隠れてしまいます。 それはたとえば、Linux マシン上の /mnt にファイルを保存した後に、/mnt に対して USB ドライブをマウントしたような場合と同じです。 /mnt に存在していた内容は USB ドライブの内容によって隠されてしまい、USB ドライブがアンマウントされるまで続きます。 隠されてしまったファイルは、削除されるわけでなく変更もされません。 しかしバインドマウントやボリュームがアンマウントされない限り、アクセスすることはできません。


順を追ってビルド方法を確認していく

シチュエーション

シンプルにNginxコンテナを構築し、ソースコードをコンテナにマウントさせ開発を行っていると想定する。
下記にサンプルのディレクトリ構成とDockerfileを示す。

ディレクトリ構成
project-directory/
  └html/
    └index.html
  └Dockerfile

Dockerfile
FROM nginx
COPY ./html /usr/share/nginx/html
EXPOSE 80
index.html
<!DOCTYPE html>
<head>
<title>ContainerA</title>
</head>
<body>
    <h1>ContainerA!</h1>
</body>

下記のコマンドでソースコードがバインドマウントされたNginxコンテナを作る


docker run --name containerA --mount type=bind,source=(pwd)/html,target=/usr/share/nginx/html -d -p 81:80 nginx

image.png

コマンド解説

DockerHub公式リポジトリのnginxイメージをベースに、以下のオプションでコンテナを作成する

  • --nameでコンテナ名を指定
  • --mountでボリュームをマウント
    • ボリュームには旧来のオプションとして-v or --volumeもあるが、公式で--mountを推奨しているので従う
    • typeはマウントタイプを指定(今回はバインドマウントなのでbind)
    • sourceはマウント元のパスを指定(ホスト)
    • targetはマウント先のパスを指定(コンテナ内)
  • -dでデタッチモード(バックグラウンドで起動)
  • -pでポートフォワードを設定
    • ホストのlocalhost:81に対して、コンテナ内のloclahost:80を紐付ける

http://localhost:81 にアクセスして、画面を表示してみる

下記のような画面が表示される
image.png

文言を追加して、コンテナ内に反映されることを確認する

index.html
<!DOCTYPE html>
~省略~
<body>
    <h1>ContainerA!</h1>
    <p>Add message at Host.</p>
</body>

再度読み込むと、追加した文言が表示されている。
これで、ホストとコンテナが確実にバインドマウントされていることも確認できた。

image.png

Dockerfileをビルドし、ソースごとイメージ化する


docker build -t build_with_bind_data ./

Dockerfileが存在するディレクトリで実行する
↓は実行結果
image.png

ビルドされたイメージを確認する

docker image ls

下記のようにbuild_with_bind_dataというイメージがビルドされている

image.png

ビルドしたイメージを元にコンテナを作成し、ソースも含められているか確認する


docker run --name containerB -d -p 82:80 build_with_bind_data

image.png

http://localhost:82 にアクセスすると
ContaierAで表示させていたものと全く一緒なのが確認できた。
image.png

一応、containerB内部のソースも確認しておくと


docker exec containerB bash -c "cat /usr/share/nginx/html/index.html"

ビルド時に、COPYコマンドによって、指定のディレクトリ/ファイルがコピーされていることが分かる。
image.png

おまけ

COMMITコマンドでコンテナはイメージ化出来るが、ボリュームマウントのデータは含まれない

COMMITコマンドでイメージを固めたくなるが、これはあくまでコンテナ内部での設定や変更をイメージ化するものであり、コンテナにボリュームマウント1されたデータは、コミットで作成されたイメージには含まれない、という点に注意する。
なので、ビルド時にCOPYコマンドでアプリのソースコードを配置する必要がある。


感想

多分内容としては初歩的なものです。
今までのDockerを使ってきた経験として、ビルドしてイメージを管理する機会が無かったので、ソースを含めたイメージ化の具体的な手法がわからず、苦戦しました。
COMMITコマンドの特徴に気づかず、「なんでコミットしてもソースが含まれないんだ!?」なんて風に驚いていました。
が、公式の解説等をじっくり読んで自分なりに理解を深められたので、忘れないように記事に残しました。


  1. マウントタイプが、ボリューム/バインドマウント/tmpfsマウントのいずれか。 

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2