研究のためのDocker入門 | moskomule log

研究のためのDocker入門

はじめに

「TensorFlowのCustomOpsが古いGCCでしかコンパイルできず詰んだ」「先輩から引き継いだコードを動かす環境構築が面倒」といった経験はないでしょうか.この記事ではDockerを使うことで環境依存/環境汚染の問題を減らすことを考えていきます.

1年ほど前からDockerの存在は知っていましたが,いざはじめようと「Docker入門」のような記事を読むとUbuntuのイメージをプルしてコンテナを作ってお疲れ様でした,という感じであまり御利益が分かりませんでした.

先日絶対に挫折しない!オープンソースソフトウェア「Docker」入門編 #04という記事でDockerfileを作って環境を構築する話を読んで少し得心できました.どこでも同一のコマンドで同一の環境を,ホストの環境を汚さずに作れるので非常に便利です.

インストール等

DockerCEをダウンロードしてインストールします.環境によってはDocker toolboxを利用する必要があります.

Linuxの場合はインストール後

$ sudo groupadd docker
$ sudo usermod -aG docker $USER
$ logout # login later

を行います.インストールが成功していれば以下のようになるはずです.

$ docker run --rm hello-world
...
Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

DockerではDockerfileに従って(Ubuntuのブートディスクのような)Dockerイメージを作ります.このイメージにはOSやアプリケーションなどが入っています.このイメージを起動して実体となったものがコンテナです.コンテナは複数起動したり,ホスト・他のコンテナとやりとりしたりすることができます.起動のコストが大きくないので必要に応じて使い捨てることもできます.

https://www.docker.com/what-container より

イメージを作る

DockerのイメージはDockerfileからビルドするほかにDockerHubなどから入手することも出来ます.先ほどの

$ docker run --rm hello-world

ではローカルにhello-worldイメージがないのでDockerHubからプルしてきています.Dockerのイメージは層状になっていて既存のものにファイルを加えたり環境変数を書き換えたりして自分の必要なものにしていきます.

それでは実際にDockerfileを書いてJupyterのイメージを作ってみましょう.空のディレクトリにDockerfilerequirements.txtを作ります.

# Dockerfile
FROM ubuntu:latest
RUN apt-get update \
    && apt-get install -y wget bzip2 \
    && apt-get clean && rm -rf /var/lib/apt/lists/*
WORKDIR /opt
RUN wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh \
    && bash miniconda.sh -b -p /opt/.miniconda \
    && rm miniconda.sh
ENV PATH /opt/.miniconda/bin:$PATH
COPY requirements.txt /opt
RUN pip install --no-cache-dir -r requirements.txt && rm requirements.txt
# requirements.txt
jupyter

Dockerfileの解説

  • FROM ubuntu:latestはubuntuの最新のものをベースとすることを示します.
  • RUNによってコマンドを実行できます.
    • RUNは独立なので注意が必要です.例えばRUN cd fooを行っても次のRUN pwdfooではありません.
    • イメージにはキャッシュも保存されてしまうので,たとえばapt-getならばapt-get clean && rm -rf /var/lib/apt/lists/*でキャッシュをなくすことが望ましいです.
    • 各行毎に中間イメージが作られて差分を重ねていき,他のイメージのビルドジにも再利用されます.このことを意識してコマンドをまとめましょう.
  • WORKDIRによって作業ディレクトリに移動します.RUN cd fooと異なり移動は引き継がれます.
  • ENVによって環境変数を設定できます.
  • COPY from_path to_pathによってDockerfileのあるディレクトリ以下にあるファイルを取り込むことが出来ます.

その他詳しくはDockerfileリファレンスDockerfileベストプラクティスをご覧下さい.

ビルド

$ docker build -t foo_name .

を実行するとfoo_name:latestイメージがビルドされます.-t foo_nameは随意ですが,(上の例ならmy-jupyterのような)名前を与えた方が便利です.作成したイメージはdocker imagesで確認できます.

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
jupyter             latest              052c2b285aea        4 days ago          925MB

コンテナの起動

コンテナはdocker run NAME COMMANDによって起動できます.

$ docker -it --rm jupyter bash
[email protected]:/opt#
[email protected]:/opt# pwd
/opt
[email protected]:/opt# exit
  • つまりdocker run [options] IMAGE_NAME COMMANDです.
  • -itによってインタラクティブに扱うことができます.-i--interactive-t--ttyと同一です.
  • --rmを指定することで終了後に自動でコンテナが消去されます.

実際にjupyter notebookを使うには以下のようにします.

docker run --rm \
    -p 8888:8888 \
    -v host/path:container/path \
    jupyter jupyter notebook --ip=0.0.0.0 --allow-root --no-browser
  • -p 8888:8888によってコンテナ側の8888ポートがホスト側のlocalhost:8888となります.
  • -v host/path:/container/pathによってホストとコンテナとでディレクトリを共有することが出来ます.この場合ホスト側のhost/pathがコンテナは/container/pathからアクセスできます.

shellスクリプトからコンテナを起動することで諸々のインストールの手続きを簡略化することなどもできると思います.

#!/bin/bash
# docker-mecab
docker run -it --rm mymecab mecab [email protected]

nvidia-docker

DockerでGPUを使うときにはドライバの指定が必要でいささか面倒です.これを解決するのがnvidia-dockerです.nvidia-dockerを用いることで一台のマシンで複数のcudaを使ったり,コンテナ毎に使用するGPUを分離したりすることができます.

基本的には上記のリンクに従えばインストールし,使用できると思いますが自分の環境(Ubuntu 16.04)ではインストール後に

$ nvidia-docker run --rm nvidia/cuda nvidia-smi
Error: Could not load UVM kernel module. Is nvidia-modprobe installed?

となったので,以下を行いました.

$ sudo apt-add-repository multiverse
$ sudo apt-get update
$ sudo apt-get install nvidia-modprobe

nvidia-dockerはラッパーなので,GPUを使う場合にはnvidia-docker run ...とするだけでそれ以外の差異はありません.

その他

  • 不要なイメージはdocker rmi IMAGE_NAMEで削除します.不要なイメージを全て消す場合はdocker rmi $(docker images -f "dangling=true" -q)とします.
  • 不要なコンテナはdocker rm CONTAINER_NAMEで削除します.使われていないコンテナを全て消す場合はdocker rm $(docker ps -aq)で削除します.
  • ホストがCentOSのときに不具合が多い経験があります.Dockerに依存しすぎるのは危険です
comments powered by Disqus