Docker 初心者が Dockerfile からDocker イメージを作成するまで


はじめに

インターネットにつながっていない環境で numpy や pandas といった Python ライブラリを使って処理をする必要がでてきたので、Docker を使って必要なライブラリやファイルを Docker イメージとして固めて、別のサーバで docker load するまでの手順をまとめます。

内容に誤りや、修正すべき箇所があればご教示いただけますと幸いです。

環境

  • Mac OS Catalina 10.15.6
  • Homebrew 2.5.1

Docker の準備

インストールと起動

こちらの記事を参考に Homebrew から Docker をインストールし、Docker.app を起動します。
DockerをHomebrewでMac OSに導入する方法 - Qiita

Docker を起動しました。チュートリアルを無視したのでまっさらな状態です。
Docker を起動することで、ターミナル上で docker コマンドが打てるようになります。なお、Docker.app(GUI画面)はこのあと使いませんので閉じてしまって大丈夫です。

kaz@Kaz-MBP ~ % docker --version
Docker version 19.03.12, build 48a66213fe

イメージとコンテナ

この後「イメージ」と「コンテナ」の違いを理解している前提で話を進めます。「イメージ」と「コンテナ」については以下のサイトの解説が個人的に分かりやすくて好きです。
dockerのコンテナとイメージの違い|ハックノート

Docker Hub から大元となるイメージを取得

これから Docker イメージの作成に向けて頑張っていくのですが、どうやらゼロから Docker イメージを作るのではなく、別の Docker イメージをベースにオリジナルの Docker イメージを作成するのが主流だそうなので、私もそれに倣います。
今回は Python とそれに関連するライブラリが必要なので、Miniconda3 が乗っかった Docker イメージをベースにしたいと思います。
continuumio/miniconda3 - Docker Hub

では早速 pull します。

kaz@Kaz-MBP ~ % docker pull continuumio/miniconda3
Using default tag: latest
latest: Pulling from continuumio/miniconda3
68ced04f60ab: Pull complete
9c388eb6d33c: Pull complete
96cf53b3a9dd: Pull complete
Digest: sha256:456e3196bf3ffb13fee7c9216db4b18b5e6f4d37090b31df3e0309926e98cfe2
Status: Downloaded newer image for continuumio/miniconda3:latest
docker.io/continuumio/miniconda3:latest

pull が完了すると、以下の通り docker imagesコマンドでその結果が確認できます。continuumio/miniconda3 という名前のリポジトリが登録されていれば OK です。docker image ls でも同じ結果が表示されます。

kaz@Kaz-MBP ~ % docker images
REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
continuumio/miniconda3   latest              b4adc22212f1        6 months ago        429MB

試しに Docker イメージからコンテナを実行してみる

ではこの miniconda3 の Docker イメージから、docker run --rm -it continuumio/miniconda3 コマンドでコンテナを実行してみます。

kaz@Kaz-MBP ~ % docker run --rm -it continuumio/miniconda3
(base) root@8993959bca74:/#
  • --rm はコンテナ終了後に自動的にコンテナを削除するオプションです(Docker イメージは削除されません)
  • -it はインタラクティブシェルでコンテナを操作するためのオプションです
  • continuumio/miniconda3 は、スラッシュも含めて Docker イメージの名前です(記号ではない)

実行すると、ターミナル左側のプロンプト表示が変わります。この状態になれば、想定どおり Docker イメージからコンテナが作成できインタラクティブシェルで操作ができます。

(base) root@8993959bca74:/# ls
bin   dev  home  lib64  mnt  proc  run   srv  tmp  var
boot  etc  lib   media  opt  root  sbin  sys  usr
(base) root@8993959bca74:/#
(base) root@8993959bca74:/# conda info -e
# conda environments:
#
base                  *  /opt/conda

(base) root@8993959bca74:/#
(base) root@8993959bca74:/# python --version
Python 3.7.6

exit を入力するとコンテナが終了します。run コマンド実行時に --rm オプションをつけたのでこの時実行されたコンテナは消え去ります。

(base) root@8993959bca74:/# exit
exit
kaz@Kaz-MBP ~ %
kaz@Kaz-MBP ~ % python --version
Python 2.7.16

この Docker イメージが使えそうであることが確認できたので、これをベースにオリジナルの Docker イメージを作ってみます。

Dockerfile の作成

オリジナルの Docker イメージを作るのに必要な設計図のようなものが Dockerfile になります。Dockerfile ではベースとなる Docker イメージと、実行したいコマンドなどを定義する必要があります。
今回は numpy, pandas, jupyter notebook をインストールしつつ、/usr/workspace ディレクトリにローカルで作成した Python スクリプトを含む Docker イメージを作るための Dockerfile を作成します。

FROM continuumio/miniconda3
RUN conda install numpy pandas jupyter -y
RUN mkdir /usr/workspace/
COPY helloworld.py /usr/workspace/
  • FROM では元となる Docker イメージを指定します
    • pull していない Docker イメージも指定することができます
  • RUN では実行したいシェルスクリプトを指定します
    • RUN の実行結果はその次の RUN に引き継がれないことに気を付けてください。例えば、cd /usr/workspace/ でカレントディレクトリを移動したとしても、次の RUN ではカレントディレクトリはホームディレクトリである / になります
  • COPY では Dockerfile と同一ディレクトリにある Python スクリプト(helloworld.py)を RUN で作成した /usr/workspace/ ディレクトリ に配置します

ではこの Dockerfile から、Docker イメージを作成します。Dockerfile が保存されているディレクトリまで cd した後、docker build -t test . コマンドを実行します。
- -t は今回作成する Docker イメージに名前を指定します。今回は 'test' という名前にしました
- 最後の . は Dockerfile が保存されているディレクトリを指定します。今回はカレントディレクトリを指定するために . としています。

kaz@Kaz-MBP qiita % docker build -t test .
Sending build context to Docker daemon  3.072kB
Step 1/4 : FROM continuumio/miniconda3
 ---> b4adc22212f1
Step 2/4 : RUN conda install numpy pandas jupyter -y
 ---> Running in fce2bbf772f7
Collecting package metadata (current_repodata.json): ...working... done
Solving environment: ...working... failed with initial frozen solve. Retrying with flexible solve.
Solving environment: ...working... failed with repodata from current_repodata.json, will retry with next repodata source.
Collecting package metadata (repodata.json): ...working... done
Solving environment: ...working... done

## Package Plan ##

  environment location: /opt/conda

  added / updated specs:
    - jupyter
    - numpy
    - pandas


The following packages will be downloaded:

    package                    |            build
    ---------------------------|-----------------

(中略)

Preparing transaction: ...working... done
Verifying transaction: ...working... done
Executing transaction: ...working... done
Removing intermediate container fce2bbf772f7
 ---> 1b4aaad5eec1
Step 3/4 : RUN mkdir /usr/workspace/
 ---> Running in 3c29b1840f3b
Removing intermediate container 3c29b1840f3b
 ---> 6b9931871d40
Step 4/4 : COPY helloworld.py /usr/workspace/
 ---> bf07bc8332af
Successfully built bf07bc8332af
Successfully tagged test:latest
kaz@Kaz-MBP qiita %

Dockerfile には合わせて 4 行記載していたので、Step n/4 として進捗が表示されます。
Docker イメージの作成が完了すると、先と同様に docker images コマンドで結果を確認します。

kaz@Kaz-MBP qiita % docker images
REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
test                     latest              bf07bc8332af        40 seconds ago      2.46GB
continuumio/miniconda3   latest              b4adc22212f1        6 months ago        429MB
kaz@Kaz-MBP qiita %

test という名前のイメージが追加されています!ではこちらも同様にコンテナとして実行してみましょう。

kaz@Kaz-MBP qiita % docker run --rm -it test
(base) root@ba61e23ba183:/#
(base) root@ba61e23ba183:/# ipython
Python 3.7.6 (default, Jan  8 2020, 19:59:22)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.18.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import numpy, pandas

In [2]: exit
(base) root@ba61e23ba183:/#
(base) root@ba61e23ba183:/# ls /usr/workspace/
helloworld.py
(base) root@ba61e23ba183:/#
(base) root@ba61e23ba183:/# python /usr/workspace/helloworld.py
Hello, world!
(base) root@ba61e23ba183:/#
(base) root@ba61e23ba183:/#
(base) root@ba61e23ba183:/# exit
exit
kaz@Kaz-MBP qiita %

IPython が実行でき、numpy も pandas もインポートできました。/usr/workspace/helloworld.py も正しく Docker イメージに含まれています。

Docker イメージをファイルとして保存

docker save コマンドで保存できます。ちなみに save は Docker イメージの保存、export は Docker コンテナの保存にそれぞれ用いるらしいです。
Docker save→load export→import どっち? - Qiita

kaz@Kaz-MBP qiita % docker save test > saved_img.tar
kaz@Kaz-MBP qiita %
kaz@Kaz-MBP qiita % ls
Dockerfile  helloworld.py   saved_img.tar
kaz@Kaz-MBP qiita %

docker save により tar ファイルとして書き出されます。この tar ファイルは docker load -i saved_img.tar で読み込めます。

Docker イメージの読み込み

最後に、既存の "test" Docker イメージを削除した状態から、save した saved_img.tar を load して正しくコンテナが動作するかを確認します。

既存 Docker イメージの削除

kaz@Kaz-MBP qiita % docker images
REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
test                     latest              bf07bc8332af        24 minutes ago      2.46GB
continuumio/miniconda3   latest              b4adc22212f1        6 months ago        429MB
kaz@Kaz-MBP qiita %
kaz@Kaz-MBP qiita % docker rmi test
Untagged: test:latest
Deleted: sha256:bf07bc8332af060f8711f1610d331c1b5e28555942f1d78b57d8522c04206645
Deleted: sha256:375aa2957526e2da07a6057f47463dfc1568f4eb486df466ecf152c88e1069c4
Deleted: sha256:6b9931871d407a8f3506e64a8e2487a1ef418190308c34cc1d16ea4b97ea310b
Deleted: sha256:33afa4a779be5b75042a62c62563d16940f9d8c71a754317f33d74220f275f4c
Deleted: sha256:1b4aaad5eec1c08bf8b7dd8a3023fdd856c33b835467281d058694e3874d4d9c
Deleted: sha256:77a6450e1f92f5757655aafa4b74ea2c413b7a93b7fbcf1c15741bd3f9c0669e
kaz@Kaz-MBP qiita %
kaz@Kaz-MBP qiita % docker images
REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
continuumio/miniconda3   latest              b4adc22212f1        6 months ago        429MB
kaz@Kaz-MBP qiita %

.tar ファイルのロード

kaz@Kaz-MBP qiita % docker load -i saved_img.tar
7ee81e12667e: Loading layer   2.07GB/2.07GB
8f3d70528b79: Loading layer   2.56kB/2.56kB
771c1ba5862d: Loading layer  3.072kB/3.072kB
Loaded image: test:latest
kaz@Kaz-MBP qiita %
kaz@Kaz-MBP qiita %
kaz@Kaz-MBP qiita % docker images
REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
test                     latest              bf07bc8332af        28 minutes ago      2.46GB
continuumio/miniconda3   latest              b4adc22212f1        6 months ago        429MB
kaz@Kaz-MBP qiita %

ロードした Docker イメージからコンテナを実行

kaz@Kaz-MBP qiita % docker run --rm -it test
(base) root@d4a072d6c4bd:/#
(base) root@d4a072d6c4bd:/# ipython
Python 3.7.6 (default, Jan  8 2020, 19:59:22)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.18.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import numpy, pandas

In [2]: exit
(base) root@d4a072d6c4bd:/#
(base) root@d4a072d6c4bd:/# python /usr/workspace/helloworld.py
Hello, world!
(base) root@d4a072d6c4bd:/#
(base) root@d4a072d6c4bd:/# exit
exit
kaz@Kaz-MBP qiita %

.tar ファイルのロード後も同じようにライブラリ・スクリプトが利用できました!これでやりたいことは達成です。

まとめ

  • Docker で動くコンテナを用意するためには、Docker イメージを作成する必要があります
    • 今回は Dockerfile を作成・ビルドして Docker イメージを作成しました
  • Dockerfile には元となる Docker イメージの名前と実行したい処理を定義します
  • Docker イメージを run することでコンテナが実行されます