続・Dockerことはじめ: コンテナにホスト側のディレクトリをマウントする

0. はじめに

前回の記事はこちら

  • 前回はTensorFlow公式のDocker Imageを入手してコンテナを起動してみました.
  • 今回は研究プロジェクトを実際にDocker コンテナを使って進めていく準備をしたいと思います.

動作環境

1. ホスト側のディレクトリをコンテナにマウントする

1-0. 何もマウントしないとどうなるか

自分の研究プロジェクトを共用GPUサーバーの /home/me/myproject 内に置いているとします.

me@server myproject$ pwd
/home/me/myproject
me@server myproject$ ls
README.md data docs models myenv notebooks papers src

ここで, TensorFlow公式のDocker Imageからtoy_containerという名前のコンテナを作って起動してみます.

me@server myproject$ sudo docker run -it --name toy_contrainer \
> tensorflow/tensorflow:latest-gpu-jupyter \
> bash
________                               _______________                
___  __/__________________________________  ____/__  /________      __
__  /  _  _ \_  __ \_  ___/  __ \_  ___/_  /_   __  /_  __ \_ | /| / /
_  /   /  __/  / / /(__  )/ /_/ /  /   _  __/   _  / / /_/ /_ |/ |/ / 
/_/    \___//_/ /_//____/ \____//_/    /_/      /_/  \____/____/|__/


WARNING: You are running this container as root, which can cause new files in
mounted volumes to be created as the root user on your host machine.

To avoid this, run the container by specifying your user's userid:

$ docker run -u $(id -u):$(id -g) args...

コンテナ内部を少し探検してみます.

root@0123456789ab:/tf# pwd   # 起動直後の作業ディレクトリはtfというディレクトリ
/tf
root@0123456789ab:/tf# cd ~
root@0123456789ab:~# pwd
/root
root@0123456789ab:~# ls      # /rootの中身には何も入っていない
root@0123456789ab:~# cd /
root@0123456789ab:/# ls      # ディレクトリツリーのトップをみてみる
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tf  tmp  usr  var

私の研究プロジェクトが見当たりませんね.
どうやらこのままではコンテナのファイルシステムはホスト側のファイルシステムとは隔離されているため, ホスト側に置いてあるファイルやスクリプトをコンテナ内で利用できないようです.

一旦このコンテナからは抜けて, コンテナも削除しておきます.

root@0123456789ab:/# exit
exit
me@server myproject$ sudo docker rm toy_container
toy_container

1-1. docker run -v でマウントする

docker run -v ホスト側のディレクトリ:マウント先のディレクトリ を使うと, ホスト側のディレクトリをマウントしたコンテナを作成し, 起動できます.

# ホスト側の /home/me 以下をコンテナ内の /home/me にマウントする
# コンテナ内にもともと /home/me ディレクトリは存在しないが, 新しく作成される
me@server myproject$ sudo docker run -it --name toy_contrainer \
> -v /home/me:/home/me \
> tensorflow/tensorflow:latest-gpu-jupyter \
> bash
________                               _______________                
___  __/__________________________________  ____/__  /________      __
__  /  _  _ \_  __ \_  ___/  __ \_  ___/_  /_   __  /_  __ \_ | /| / /
_  /   /  __/  / / /(__  )/ /_/ /  /   _  __/   _  / / /_/ /_ |/ |/ / 
/_/    \___//_/ /_//____/ \____//_/    /_/      /_/  \____/____/|__/


WARNING: You are running this container as root, which can cause new files in
mounted volumes to be created as the root user on your host machine.

To avoid this, run the container by specifying your user's userid:

$ docker run -u $(id -u):$(id -g) args...
root@123456789abc:/tf# cd /home/me/myproject
root@123456789abc:/myproject# ls
# たしかにホスト側のmyprojectの中身がコンテナの/home/me/myproject以下に置かれている
README.md data docs models myenv notebooks papers src

これでホスト側のファイルをコンテナ内でも利用する準備ができそうです!

1-2. コンテナ内でホスト側のファイルを読み書き

コンテナ内でi_am_in_the_container.txtという空のテキストファイルを作成してみます.

root@123456789abc:/myproject# touch i_am_in_the_container.txt
root@123456789abc:/myproject# ls
README.md data docs i_am_in_the_container.txt models myenv notebooks papers src

この状態でコンテナを抜けてホストに戻ると, i_am_in_the_container.txtが残っています.
たしかにホスト側のディレクトリをコンテナ内から操作できていたことがわかります.

root@123456789abc:/myproject# exit
exit
me@server myproject$ ls
README.md data docs i_am_in_the_container.txt models myenv notebooks papers src

docker inspect で現在マウントされているディレクトリを確認できます.

me@server myproject$ sudo docker inspect toy_container
... ()
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/home/me",
                "Destination": "/home/me",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],
... ()

コンテナは一旦削除しておきます.

me@server myproject$ sudo docker rm toy_container
toy_container

1-3. 注意点

基本的に, ディレクトリのマウントはコンテナの作成と同時に行わなければならないようです.
一旦作成されたコンテナに対して後からディレクトリを新規にマウントすることはできないと考えた方が良さそうです.

2. root以外のユーザーとして使う

2-0. rootでコンテナを作成するとどうなるか

これでホスト側のファイルをコンテナ内から読み書きできるようになりました.
そういえば, さっきからコンテナ起動時に

WARNING: You are running this container as root, which can cause new files in
mounted volumes to be created as the root user on your host machine.

というメッセージが出ているのは何なんでしょうね.
ファイルの権限を確かめてみましょう.

me@server myproject$ ls -al
... (中略) ...
-rw-r--r--.  1 root     root        0  41 12:34 i_am_using_docker.txt

どうやら, 先ほどのi_am_using_docker.txtはrootとして作成したことになっており, 一般ユーザーに編集権限がありません.
すると, Dockerコンテナ内で作成されたファイルはコンテナ外からは読み取り専用でしか開けなくなってしまいます.
これでは困りますね.

2-1. docker run -uでユーザーを指定する

docker run -u ユーザー名:ユーザーグループ によってコンテナを作成&起動すると, コンテナ内でもrootではなく指定したユーザーとして振舞うことができます.

なお, コンテナ内にはユーザー情報は格納されていないので, ホスト側の /etc/group/etc/passwd をマウントすることも忘れないようにしましょう.
(/etc/group/etc/passwd のマウントを忘れると, コンテナは未知のユーザー名を指定されたと思ってエラーを吐きます)

# -v foo:bar:ro のようにマウントしたディレクトリはコンテナ内では読み取り専用になる
me@server myproject$ sudo docker run -it --name tf_contrainer \
> -v /home/me:/home/me \
> -v /etc/group:/etc/group:ro \
> -v /etc/passwd:/etc/passwd:ro \
> -u $(id -u $USER):$(id -g $USER) \
> tensorflow/tensorflow:latest-gpu-jupyter \
> bash

すると, 今までとは違い, ホスト側と同じユーザーでコンテナを作成&起動することができています.

________                               _______________                
___  __/__________________________________  ____/__  /________      __
__  /  _  _ \_  __ \_  ___/  __ \_  ___/_  /_   __  /_  __ \_ | /| / /
_  /   /  __/  / / /(__  )/ /_/ /  /   _  __/   _  / / /_/ /_ |/ |/ / 
/_/    \___//_/ /_//____/ \____//_/    /_/      /_/  \____/____/|__/


You are running this container as user with ID 1234 and group 1234,
which should map to the ID and group for your user on the Docker host. Great!

2-2. 一般ユーザーとしてホスト側のファイルを読み書き

この状態で, コンテナ内でi_am_in_the_container_as_nonroot.txtという空のテキストファイルを作成してみます.

tf-docker /tf > cd /home/me/myproject
tf-docker /myproject > touch i_am_in_the_container_as_non_root.txt
tf-docker /myproject > ls
README.md  i_am_in_the_container.txt              myenv
data       i_am_in_the_container_as_non_root.txt  notebooks
docs       models                                 src

コンテナを抜けると, i_am_in_the_container_as_nonroot.txt は読み取り専用になってはいますが, オーナーは自分自身なので実質的にホスト側でも自由に編集可能です.

tf-docker /myproject > exit
exit
me@server myproject$ ls -al
... (中略) ...
-rw-r--r--.  1 root     root        0  41 12:34 i_am_using_docker.txt
-rw-r--r--.  1 me       me          0  41 12:45 i_am_in_the_container_as_non_root.txt

3. おわりに

Dockerコンテナからホスト側のファイルを読み書きできるようになりました.
これでDocker hubに上がっているDocker imageを自分の研究用プロジェクトに利用することができそうです.

参考にした記事:
https://qiita.com/yohm/items/047b2e68d008ebb0f001
https://qiita.com/Yarimizu14/items/52f4859027165a805630