DcokerfileのVOLUME命令とはなにか?どのように利用すると便利か?

Docker,Kubernetes,Linux

はじめに

Dockerを使うに当たりDockerfileの各種命令の理解は必須です。

ただ、VOLUMEという命令を使う機会が多くなかったため、改めて理解を整理してみます。
また、どういうときに便利に利用できるのかについても取り上げてみたいと思います。

検証環境

$ uname -moi
x86_64 MacBookPro10,1 Darwin

$ docker --version
Docker version 19.03.5, build 633a0ea

VOLUME命令の記述方法

まずは動作確認のために、VOLUME命令を使用したかんたんなDockerfileを作成してみます。

$ cat <<EOF >Dockerfile
FROM alpine:3.11.3

RUN mkdir -p /hoge /fuga

VOLUME ["/hoge"]
EOF

記述している内容は3命令しかありません。

  • FROM : ベースとして利用するコンテナイメージ。何でも良いがここでは Alpine Linux を利用しています。
  • RUN : ルートディレクトリ直下に2つのディレクトリを作成しています。( /hoge, /fuga
  • VOLUME : 配列で複数指定可能。ボリュームとしたいパスを指定。

ここでは、 /hoge というディレクトリをボリュームとして定義しています。

では、VOLUME にするとどんなメリットがあるのでしょうか?

VOLUME命令で指定したパスは別コンテナからマウントできる

実用例をあげてみていきましょう。

先ほど作成した Dockerfile を使って、Dockerイメージをビルドします。
( genzouw/workという名前のDockerイメージを作成しています。 )

$ docker build -t genzouw/work .

Sending build context to Docker daemon  17.41kB
Step 1/3 : FROM alpine:3.11.3
 ---> e7d92cdc71fe
Step 2/3 : RUN mkdir -p /hoge /fuga
 ---> Running in 84af16062cc4
Removing intermediate container 84af16062cc4
 ---> 832aa51d694f
Step 3/3 : VOLUME ["/hoge"]
 ---> Running in 52ddeb24f993
Removing intermediate container 52ddeb24f993
 ---> c0092d1de9c6
Successfully built c0092d1de9c6
Successfully tagged genzouw/work:latest

無事ビルドされました。
このイメージを使ってコンテナプロセスを立ち上げてみます。
後々利用したいので、 work_container という名前をつけて起動しておきます。

$ docker run -it --name work_container genzouw/work sh

$ ls -l
total 64
drwxr-xr-x    2 root     root          4096 Jan 16 21:52 bin
drwxr-xr-x    5 root     root           360 Feb  8 12:44 dev
drwxr-xr-x    1 root     root          4096 Feb  8 12:44 etc
drwxr-xr-x    2 root     root          4096 Feb  8 12:43 fuga
drwxr-xr-x    2 root     root          4096 Feb  8 12:43 hoge
drwxr-xr-x    2 root     root          4096 Jan 16 21:52 home
drwxr-xr-x    5 root     root          4096 Jan 16 21:52 lib
drwxr-xr-x    5 root     root          4096 Jan 16 21:52 media
...(省略)...

ここで、Dockerfileで作成した2つのディレクトリに、適当なファイルを作成してみます。

# touchコマンドを使って、1.txt / 2.txt というテキストファイルを作成
$ touch /hoge/1.txt /fuga/2.txt

# 1.txt / 2.txt の2つのテキストファイルができているかを確認
$ ls -l /hoge /fuga
/fuga:
total 4
-rw-r--r--    1 root     root             0 Feb  8 12:44 2.txt

/hoge:
total 4
-rw-r--r--    1 root     root             0 Feb  8 12:44 1.txt

# ここで、このコンテナからexitして終了してしまいましょう。
$ exit

今度は別のコンテナを立ち上げてみますが、先程作成したコンテナ(名前は work_container でしたね)のボリュームを利用するための --volumes-from オプションを指定します。

更にこの状態でルートディレクトリの状態を確認してみましょう。

$ docker run --rm -it --volumes-from work_container alpine sh

# /fuga ディレクトリがない
$ ls -l
total 60
drwxr-xr-x    2 root     root          4096 Jan 16 21:52 bin
drwxr-xr-x    5 root     root           360 Feb  8 12:48 dev
drwxr-xr-x    1 root     root          4096 Feb  8 12:48 etc
drwxr-xr-x    2 root     root          4096 Feb  8 12:45 hoge
drwxr-xr-x    2 root     root          4096 Jan 16 21:52 home
drwxr-xr-x    5 root     root          4096 Jan 16 21:52 lib
drwxr-xr-x    5 root     root          4096 Jan 16 21:52 media
drwxr-xr-x    2 root     root          4096 Jan 16 21:52 mnt
...(省略)...

# /fuga ディレクトリがないが、 /hoge ディレクトリはある。
$ ls -l /hoge /fuga
ls: /fuga: No such file or directory
/hoge:
total 4
-rw-r--r--    1 root     root             0 Feb  8 12:45 1.txt

$ exit

ここまで起こったことを整理してみます。

DockerfileのVOLUME命令に指定されたパスは docker run --volumes-from でマウントできる

VOLUME命令に指定されたパスは docker run --volumes-from でマウントできる、ということですね。

ちなみに、参照できなかった/fugaは参照はできていませんが、終了したコンテナプロセス内に残っています。

# 終了したプロセスを再開させて、中身を覗いてみる
$ docker start -i work_container

# 両方のディレクトリが残っている
$ ls -l /hoge /fuga
/fuga:
total 4
-rw-r--r--    1 root     root             0 Feb  8 12:45 2.txt

/hoge:
total 4
-rw-r--r--    1 root     root             0 Feb  8 12:45 1.txt

さて、どんなふうに利用すると便利なのでしょうか?

awsコマンドやgclodコマンドで認証情報を保存しておくのに便利

awsコマンドやgclodコマンドで認証情報を保存しておくのに便利です。
実際に機能を利用しているGoogleのコンテナを見てみます。

Dockerfile を参照すると最終行に以下の命令が記述されています。

VOLUME ["/root/.config"]

Dockerのデフォルトユーザはrootですので、デフォルトユーザのための設定ファイルをボリューム化していることがわかります。

このコンテナを利用するためには、まず以下のコマンドを実行します。
( ここではgcloud-configというコンテナ名で実行しています。 )

$ docker run -ti --name gcloud-config google/cloud-sdk gcloud auth login

認証情報や初期設定は gcloud-config コンテナのボリュームに保存されます。
認証完了後は以下のコマンドのように、 --volumes-from gcloud-config で認証情報ファイルをマウントさせ再利用できるというわけです。

$ docker run --rm -ti --volumes-from gcloud-config google/cloud-sdk gcloud compute instances list --project your_project
NAME        ZONE           MACHINE_TYPE   PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP      STATUS
instance-1  us-central1-a  n1-standard-1               10.240.0.2   8.34.219.29      RUNNING

td コマンド(Treasure Data用のクライアント)の認証などでも利用できる

以下のようなDockerfileがあれば、同じような使い方ができます。

FROM ruby:2.7.0

LABEL maintainer "genzouw <genzouw@gmail.com>"

RUN gem install td

VOLUME ["/root/.td"]

ENTRYPOINT ["td"]
# 1.ビルド
$ docker build -t genzouw/td .

# 2.認証設定
$ docker run --name td-config genzouw/td apikey:set '*********************************************'
API key is set.
Use 'td db:create <db_name>' to create a database.

# 3.コマンド実行
$ docker run --rm --volumes-from td-config genzouw/td db:list
+--------------------+------------+
| Name               | Count      |
+--------------------+------------+
| sample_datasets    | 0          |
| information_schema | 0          |
| genzouw_db         | 1153098416 |
+--------------------+------------+
3 rows in set

ひとこと

ちょっと文章が雑でわかりにくいかもしれませんね。