Help us understand the problem. What is going on with this article?

docker (nvidia-docker) を使ってマルチノードで ChainerMN を実行する方法(仮)

More than 1 year has passed since last update.

docker (nvidia-docker) を使ってマルチノードで ChainerMN を実行する方法(仮)

編集履歴: (2017/12/14) nvidia/cuda:9.0-cudnn7-devel-ubuntu16.04 を使う場合 nccl-repo-ubuntu1604-2.0.5-ga-cuda9.0_3-1_amd64.deb をダウンロードして配置しなくても apt-get install libnccl2 libnccl-dev できたので変更

シングルノード

シングルノードなら特に難しい所はない。docker コンテナ内に OpenMPI や ChainerMN をインストールして、nvidia-docker run で mpiexec すれば良い。

Dockerfile

FROM nvidia/cuda:9.0-cudnn7-devel-ubuntu16.04

RUN apt-get update \
  && apt-get install -y --no-install-recommends python3-dev python3-pip \
  && pip3 install setuptools && pip3 install --upgrade pip \
  && apt-get install -y --no-install-recommends git m4 autoconf automake libtool flex \
  && apt-get install -y --no-install-recommends libnccl2 libnccl-dev \
  && apt-get install -y --no-install-recommends ssh \
  && apt-get clean
RUN git clone --depth=1 -b v3.0.0 https://github.com/open-mpi/ompi.git /tmp/ompi \
  && cd /tmp/ompi \
  && ./autogen.pl \
  && ./configure --with-cuda \
  && make -j4 \
  && make install \
  && rm -rf /tmp/ompi

ENV LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
ENV CPATH=/usr/local/include:$CPATH
ENV CUDA_PATH=/usr/local/cuda
ENV PATH=$CUDA_PATH/bin:$PATH
ENV CPATH=$CUDA_PATH/include:$CPATH
ENV LD_LIBRARY_PATH=$CUDA_PATH/lib64:$CUDA_PATH/lib:$LD_LIBRARY_PATH

RUN pip3 install cupy==3.0.0a1 chainer==4.0.0a1
RUN pip3 install cython && pip3 install chainermn

ビルド

mkdir docker
vim docker/Dockerfile
cd docker
nvidia-docker build -t chainermn .

実行

docker コンテナ内で mpiexec する

$ git clone https://github.com/chainer/chainermn # examples が欲しいだけ
$ nvidia-docker run -v $(pwd):/mnt $HOME/.chainer:/root/.chainer chainermn \
  mpiexec --allow-run-as-root -n 8 \
  python3 /mnt/chainermn/examples/mnist/train_mnist.py --gpu

-n 8 はGPU数にあわせて調整

マルチノード

mpiexec すると、ホストそれぞれに ssh して、mpiexec の引数のコマンドを実行する。
ホストAのコンテナから、ホストBのコンテナにsshできるようにする必要がある。

Dockerfile

FROM nvidia/cuda:9.0-cudnn7-devel-ubuntu16.04

RUN apt-get update \
  && apt-get install -y --no-install-recommends python3-dev python3-pip \
  && pip3 install setuptools && pip3 install --upgrade pip \
  && apt-get install -y --no-install-recommends git m4 autoconf automake libtool flex \
  && apt-get install -y --no-install-recommends libnccl2 libnccl-dev \
  && apt-get install -y --no-install-recommends ssh \
  && apt-get clean
RUN git clone --depth=1 -b v3.0.0 https://github.com/open-mpi/ompi.git /tmp/ompi \
  && cd /tmp/ompi \
  && ./autogen.pl \
  && ./configure --with-cuda \
  && make -j4 \
  && make install \
  && rm -rf /tmp/ompi

ENV LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
ENV CPATH=/usr/local/include:$CPATH
ENV CUDA_PATH=/usr/local/cuda
ENV PATH=$CUDA_PATH/bin:$PATH
ENV CPATH=$CUDA_PATH/include:$CPATH
ENV LD_LIBRARY_PATH=$CUDA_PATH/lib64:$CUDA_PATH/lib:$LD_LIBRARY_PATH

RUN apt-get install -y --no-install-recommends openssh-server \
  && mkdir /var/run/sshd \
  && sed -i 's/Port 22/Port 10022/' /etc/ssh/sshd_config \
  && ssh-keygen -t rsa -N '' -f /root/.ssh/id_rsa \
  && cp /root/.ssh/id_rsa.pub /root/.ssh/authorized_keys \
  && chmod 600 /root/.ssh/authorized_keys \
  && echo 'Host *' > /root/.ssh/config \
  && echo '  Port 10022' >> /root/.ssh/config \
  && echo '  StrictHostKeyChecking no' >> /root/.ssh/config \
  && echo '  UserKnownHostsFile /dev/null' >> /root/.ssh/config

RUN echo 'export CUDA_PATH="/usr/local/cuda"\n\
export PATH="$CUDA_PATH/bin:$PATH"\n\
export CPATH="$CUDA_PATH/include:$CPATH"\n\
export LD_LIBRARY_PATH="$CUDA_PATH/lib64:$CUDA_PATH/lib:$LD_LIBRARY_PATH"\n\
export PATH="/usr/local/nvidia/bin:$PATH"\n\
export LD_LIBRARY_PATH="/usr/local/nvidia/lib:/usr/local/nvidia/lib64:$LD_LIBRARY_PATH"\n\
export NCCL_SOCKET_IFNAME=^docker0' > /etc/profile.d/chainermn.sh

RUN pip3 install cupy==3.0.0a1 chainer==4.0.0a1
RUN pip3 install cython && pip3 install chainermn

EXPOSE 10022

シングルノード用の Dockerfile に対して、sshd のセットアップと、ssh したときに必要な環境変数を設定できるように /etc/profile.d/chainermn.sh ファイルの作成を追加している。

ホストに向けたMPI通信をコンテナに簡易にフォワードするため、またネットワーク速度向上のため、docker run 時には --net=host を指定する。ホストの sshd 22 番ポートと被らないように、コンテナの sshd は 10022 番ポートで起動するようにしている。

ビルド

mkdir docker
vim docker/Dockerfile
cd docker
nvidia-docker build -t chainermn .

※ ここでビルドしたイメージを全ホストで利用すること。今回の Dockerfile では、それぞれのホストで docker build すると、別の ssh 鍵ができてコンテナ間 ssh できなくなってしまう。

実行

従属ホスト(ホストB)

コンテナで sshd を起動だけしておく

$ git clone https://github.com/chainer/chainermn # examples が欲しいだけ
$ nvidia-docker run --rm -it --net=host \
  -v $(pwd):/mnt -v $HOME/.chainer:/root/.chainer chainermn \
  /bin/bash -lc '/etc/init.d/ssh start && /bin/bash'

マスターホスト(ホストA)

hostfile を用意。cpu=N に並列実行数を指定する。

hostfile
ホストA cpu=8
ホストB cpu=8

実行

$ git clone https://github.com/chainer/chainermn # examples が欲しいだけ
$ nvidia-docker run --rm -it --net=host \
  -v $(pwd):/mnt -v $HOME/.chainer:/root/.chainer chainermn \
  /bin/bash -lc '/etc/init.d/ssh start && \
  mpiexec --allow-run-as-root --mca btl_tcp_if_exclude docker0,lo --hostfile /mnt/hostfile \
  /bin/bash -lc "python3 /mnt/chainermn/examples/mnist/train_mnist.py --gpu"'

今回は、実行ファイル(chainermn の examples)を各ホストに配置しているが、Docker イメージに含める方法もあるとは思う。chainermn のルールとして、全ホストに実行ファイルがないとダメ。

便利スクリプト

全従属ホストでコンテナを立ち上げて、マスターホストで mpiexec を実行する便利スクリプト。
git clone https://github.com/chainer/chainermn は実行したいスクリプトに合わせてご編集ください。
python3 /mnt/chainermn/examples/mnist/train_mnist.py --gpu も実行したいスクリプトに合わせてご編集ください。

run_chainermn.sh
#!/bin/bash

PREPARE_SCRIPT=/tmp/run_chainermn_prepare.sh
SLAVE_SCRIPT=/tmp/run_chainermn_slave.sh
MASTER_SCRIPT=/tmp/run_chainermn_master.sh

cat <<'EOF' > $PREPARE_SCRIPT
git clone https://github.com/chainer/chainermn chainermn
EOF

cp $PREPARE_SCRIPT $SLAVE_SCRIPT
cat <<'EOF' >> $SLAVE_SCRIPT
nvidia-docker run --rm -it --net=host \
  -v $(pwd):/mnt -v $HOME/.chainer:/root/.chainer chainermn \
  /usr/sbin/sshd -D
EOF

cp $PREPARE_SCRIPT $MASTER_SCRIPT
cat <<'EOF' >> $MASTER_SCRIPT
nvidia-docker run --rm -it --net=host \
  -v $(pwd):/mnt -v $HOME/.chainer:/root/.chainer chainermn \
  /bin/bash -lc '/etc/init.d/ssh start && \
  mpiexec --allow-run-as-root --mca btl_tcp_if_exclude docker0,lo --hostfile /mnt/hostfile \
  /bin/bash -lc "python3 /mnt/chainermn/examples/mnist/train_mnist.py --gpu"'
EOF

master_host=$(hostname)
slave_hosts=$(grep -v ${master_host} hostfile | awk '{print $1}')
hosts=$(cat hostfile | awk '{print $1}')
n_hosts=$(cat hostfile | wc -l)

echo "$slave_hosts" | xargs -I{} -P${n_hosts} echo scp $SLAVE_SCRIPT {}:/tmp/chainermn.sh
echo "$slave_hosts" | xargs -I{} -P${n_hosts} scp $SLAVE_SCRIPT {}:/tmp/chainermn.sh
echo cp $MASTER_SCRIPT /tmp/chainermn.sh
cp $MASTER_SCRIPT /tmp/chainermn.sh

function cleanup() {
  echo "$hosts" | xargs -I{} -P${n_hosts} echo ssh {} docker stop chainermn
  echo "$hosts" | xargs -I{} -P${n_hosts} ssh {} docker stop chainermn
}
trap cleanup EXIT

echo "$hosts" | xargs -I{} -P${n_hosts} echo ssh -tt {} /bin/bash /tmp/chainermn.sh
echo "$hosts" | xargs -I{} -P${n_hosts} ssh -tt {} /bin/bash /tmp/chainermn.sh

hostfile を用意して、実行

bash run_chainermn.sh

Ctrl-C するとリモートホストの docker コンテナが stop される (ちょっと時間かかるのでお待ちください)

トラブルシューティング

See also AWS GPU インスタンスにおける ChainerMN の分散効率に対する評価#トラブルシューティング

Unexpected end of /proc/mounts line `overlay

コンテナ内で mpiexec すると以下のような warning が出る

Unexpected end of /proc/mounts line `overlay / overlay rw,relatime,lowerdir=/var/lib/docker/overlay2/l/ETGN4JWRCAAOR5SDTEX4LJN7Z6:/var/lib/docker/overlay2/l/6GKXZXZ25OYNPLCCTBNRKHDQG6:/var/lib/docker/overlay2/l/OUCL4BE45M72TBGXLVGOUGULMH:/var/lib/docker/overlay2/l/DHW4TE7ZW55P5UOZBX547LDZ77:/var/lib/docker/overlay2/l/YSIOMV4IYP3T2YVN77UFZFQ5XI:/var/lib/docker/overlay2/l/F5AUK2J5LITCBCVWGBCJD3LETY:/var/lib/docker/overlay2/l/ZPR6AAOCLV6SOQA7I6DIDMRY7G:/var/lib/docker/overlay2/l/UE6YIXVHWKAVHG2UJAMUWEUE5Y:/var/lib/docker/overlay2/l/UY4QMTUE5XDUS'
Unexpected end of /proc/mounts line `INR6Y7546V2HV:/var/lib/docker/overlay2/l/F6JQBSMJYZOOQ3QIHYUG3VVHMW:/var/lib/docker/overlay2/l/VIP7WZGLNKZMRDKH6KBINRIJSW:/var/lib/docker/overlay2/l/TDLKNGLFEVYF4SI73USXIW4A4A:/var/lib/docker/overlay2/l/VZ6RLPGCUQFQ4K5HHWOSZXXK75:/var/lib/docker/overlay2/l/UVHFPFXQRL3XJS32BZRUMP6QEP:/var/lib/docker/overlay2/l/STHMTEHKNZWTCGWFDSYT6J3ND4:/var/lib/docker/overlay2/l/L7ANIC2YQAOSWMBVRR6QZS5SZ2:/var/lib/docker/overlay2/l/DC7BAJG5WLKXZVJJM73DGCNIKE:/var/lib/docker/overlay2/l/4W423XWKPJV3WINYECSJQ7JBY3:/var/lib/do'
Unexpected end of /proc/mounts line `cker/overlay2/l/FJF6DA25TZWDVGOMCZ7J7PRT7Q:/var/lib/docker/overlay2/l/4PHONTLPJTZAVDXCRN4W2BCQPW:/var/lib/docker/overlay2/l/RU7ZODNEXSEKWG5J4UU53AYGDB:/var/lib/docker/overlay2/l/6VF7XXRRSZC2HH24Z3JJRVZBVA:/var/lib/docker/overlay2/l/M3WQNRNV5IA3KXFBHV5GY3KD6U:/var/lib/docker/overlay2/l/ML2ZZUHFRV5HELDLE5O5NDWPLX:/var/lib/docker/overlay2/l/T535AZCEOGAX5BT2XGDEBQ3RH3:/var/lib/docker/overlay2/l/4SZPXUFR4LU2WRUTITUPSOPIJE,upperdir=/var/lib/docker/overlay2/333f260a32ed9645c209c166c9e2d6ce807ec87b0ee351a86dc365fa'

https://stackoverflow.com/questions/46138549/docker-openmpi-and-unexpected-end-of-proc-mounts-line この問題らしいが、無視して動作したので無視した。

MCA parameter "plm_rsh_agent" was set to a path

The value of the MCA parameter "plm_rsh_agent" was set to a path
that could not be found:

  plm_rsh_agent: ssh : rsh

Please either unset the parameter, or check that the path is correct

apt-get install ssh して解決

今後の発展

マルチノードでの docker コンテナの管理を、docker swarn や kubernetes のようなコンテナクラスタ管理ツールを使って行えるようにすると立ち上げるのが簡単になりそう。

一方で、うまく MPI や GPU を扱えるようにするには設定が難しそう (少なくも GKE のようなマネージドサービスを使うのは難しそう)。https://github.com/NLKNguyen/alpine-mpich が参考になりそう (docker swarn を使っている)。

sonots
A Ruby, Fluentd, and Chainer Committer. SRE Engineer. Qiitaは小ネタの投稿場所として利用しています。業務コードで、なぜそういう書き方をしているのか解説をQiitaに書いて、コードにはQiitaへのリンクを張る、という使い方をしていることが多いです(自己紹介じゃない)
https://medium.com/@sonots
zozotech
70億人のファッションを技術の力で変えていく
https://tech.zozo.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした