# 遺伝研でもポータブルな環境を利用するためにDockerに入門する [TOC] ## 目的 + できるようになること + 環境をビルドしてどこでも実行可能な環境を得られる + docker Hubを経由して遺伝研で実行できる + できるようにならないこと + dockerに対する深い理解 + singularityに対する深い理解 + その他諸々の知識 ## 前準備 + dockerのアカウントの作成とインストール + https://www.docker.com/ + 自分のDockerfileをUploadするためには必要です ## 前置き + 気になる人はDockerを読んでください + https://www.oreilly.co.jp/books/9784873117768/ ### コンテナとはなにか + コンテナは,ホストOSとリソースを共有するのでVM(仮想マシン)より効率的.コンテナの起動や停止は一瞬で行なる.コンテナ内で動作するアプリケーションのオーバーヘッドはホストOSで直接動作するアプリケーションと比較してもごく僅かかほとんどない. + コンテナは可搬性が高いので,動作環境の僅かな違いに起因するタイプのバグを撲滅できる可能性が高い + 軽量.VMを上げるよりはるかに多くのコンテナを同時起動できる + 環境設定に時間を掛ける必要がない.すでにあるベースのイメージを元に作成すれば自分で環境を構築する必要がなく複雑なプログラムが動作する. ### 三行で + **どこでも** + **かんたん** + **速い** ## docker事始め ### 1. dockerの存在確認 ```shell $ docker version Client: Docker Engine - Community Version: 18.09.2 API version: 1.39 Go version: go1.10.8 Git commit: 6247962 Built: Sun Feb 10 04:12:39 2019 OS/Arch: darwin/amd64 Experimental: false Server: Docker Engine - Community Engine: Version: 18.09.2 API version: 1.39 (minimum version 1.12) Go version: go1.10.6 Git commit: 6247962 Built: Sun Feb 10 04:13:06 2019 OS/Arch: linux/amd64 Experimental: false ``` ### 2. bashの実行 dockerでdebianを動かしてbashを起動してみよう! ```shell $ docker run -it debian /bin/bash root@f3bb2f034004:/# echo "hello world!" hello world! root@f3bb2f034004:/# exit exit ``` ### 3. 起動しているdockerの確認 ```shell $ docker run -h CONTAINER -it debian /bin/bash root@CONTAINER:/# ls bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var root@CONTAINER:/# mv /bin /basket root@CONTAINER:/# ls bash: /bin/ls: No such file or directory root@CONTAINER:/# ``` #### 3.1. この状態で別窓でTerminalを起動して中身を確認してみる ```shell $ docker ps CONTAINER ID IMAGE COMMAND ... NAMES 6ae21eea7dd6 debian "/bin/bash" ... pedantic_kalam ``` #### 3.2. `pedantic_kalam`の中身の変更の確認 ``` $ docker diff pedantic_kalam A /basket A /basket/zgrep A /basket/cat A /basket/chmod A /basket/dmesg A /basket/gunzip A /basket/mount A /basket/stty ... ``` #### 3.3. 実行ログの確認 ```shell $ docker logs pedantic_kalam root@CONTAINER:/# ls bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var root@CONTAINER:/# mv /bin /basket root@CONTAINER:/# ls bash: /bin/ls: No such file or directory ``` ### 4. 実行中のshellに戻ってコンテナの削除 ```shell root@CONTAINER:/# exit exit $ docker rm pedantic_kalam pedantic_kalam ``` ## 動くCapRのコンテナを作成してみる ### 1. debianのイメージを引っ張って実行してみよう ```shell $ docker run -it debian root@ae8a726edf91:/# ``` + `run`:コンテナの起動 + `-it`:インタラクティブセッションの開始 ### 2. `mkdir`してみる ``` root@ae8a726edf91:/# mkdir sample root@ae8a726edf91:/# ls bin boot dev etc home lib lib64 media mnt opt proc root run sample sbin srv sys tmp usr var ``` ### 3. exitで抜けられる ```shell root@ae8a726edf91:/# exit exit ``` ### 4. 過去に実行したものに接続するときは`docker start -i [name]` 略称でもいい。今回はae8a726edf91ではなくae8でも良い。 ```shell $ docker start -i ae8 root@ae8a726edf91:/# ls bin boot dev etc home lib lib64 media mnt opt proc root run sample sbin srv sys tmp usr var ``` ### 5. 必要なライブラリをインストールしよう ``` root@ae8a726edf91:/# apt-get update root@ae8a726edf91:/# apt-get install g++ make git ``` ### 6. CapRを引っ張ってこよう ``` root@ae8a726edf91:/# git clone https://github.com/fukunagatsu/CapR.git ``` ### 7. makeしよう ```shell root@ae8a726edf91:/# cd CapR root@ae8a726edf91:/# make ``` ### 8. (コンテナ内で)実行してみよう ```shell root@ae8a726edf91:/# apt-get install wget root@ae8a726edf91:/# wget https://molb7621.github.io/workshop/_downloads/sample.fa root@ae8a726edf91:/# /CapR/CapR sample.fa sample.out 10 root@ae8a726edf91:/# head sample.out ``` ### 9. commitする 以後`debian`と書いていたところを`test/capr`と書けば実行できるようになるぞ! ```shell $ docker commit ae8 test/capr ``` ### 10. 実行してみよう https://molb7621.github.io/workshop/_downloads/sample.fa ```shell $ wget https://molb7621.github.io/workshop/_downloads/sample.fa $ docker run -it -v $PWD:/home test/capr /CapR/CapR /home/sample.fa /home/sample.out 10 $ head sample.out ``` + `-v $PWD:/home`:現在のディレクトリを`/home`にバインドする ### 11. Dockerfileを書いてみよう + Dockerfileに書くと複雑な処理がわかりやすくなる まずはディレクトリを作ってその中にDockerfileを作成する ```sh $ mkdir capr $ cd capr $ touch Dockerfile ``` Dockerfileに以下を記述する ```dockerfile FROM debian RUN apt-get update RUN apt-get install -y g++ make git RUN git clone https://github.com/fukunagatsu/CapR.git WORKDIR /CapR RUN make ENTRYPOINT ["/CapR/CapR"] ``` ### 12. ビルドする カレントディレクトリのDockerfileを元にビルドする ```shell $ docker build -t test/capr . ``` これで`docker run -it test/capr`すれば利用できるようになる ### 13. Docker Hubへのアップロード DockerHubへアップロードすることでどこからでも利用可能になる ``` $ docker build -t natuski/capr . $ docker login && docker push natuski/capr ``` ### 14. 容量の確認 作成したイメージの大きさは`docker images`で確認できる ```shell $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE natuski/capr latest 4709262dcf12 5 seconds ago 375MB ``` このままだと実行に必要ないライブラリまで入っていることになる. ### 15. 軽量OSを使ってみよう docker用の軽量OS`alpine`を使って書くとこんな感じになる ```dockerfile FROM alpine AS builder RUN apk add --no-cache git make g++ RUN git clone https://github.com/fukunagatsu/CapR.git WORKDIR /CapR RUN make ENTRYPOINT ["/CapR/CapR"] ``` これでビルドしてみると, ``` $ docker build -t natuski/capr_alpine . $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE natuski/capr latest 4709262dcf12 5 seconds ago 375MB natuski/capr_alpine latest 4a4140483354 2 minutes ago 177MB ``` ### 16. もっと軽量化してみよう [マルチステージビルド](https://blog.alexellis.io/mutli-stage-docker-builds/)を利用するともっと軽くなる.ビルドするイメージとバイナリを置くイメージを別にできる.Dockerfileをこね直して,ビルドした結果を新しいイメージにコピーする.動作に必要なライブラリも追加しておく. ```dockerfile FROM alpine AS builder RUN apk add --no-cache git make g++ RUN git clone https://github.com/fukunagatsu/CapR.git WORKDIR /CapR RUN make FROM alpine RUN apk add --no-cache libstdc++ COPY --from=builder /CapR/CapR /CapR ENTRYPOINT ["/CapR"] ``` これでビルドすると驚異の7.3MB! ```shell $ docker build -t natuski/capr_alpine_bin . $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE natuski/capr latest 4709262dcf12 5 seconds ago 375MB natuski/capr_alpine_bin latest 732436cac06d 2 minutes ago 7.3MB natuski/capr_alpine latest 4a4140483354 2 minutes ago 177MB ``` これをuploadしておこう. ```shell $ docker login && docker push natuski/capr_alpine_bin ``` ### 17. 遺伝研で使ってみよう 遺伝研で使用する場合には ```shell # モジュールのロード $ module load singularity # イメージのプル $ singularity pull docker://natuski/capr_alpine_bin ``` runするとエントリポイントが呼ばれる.そのままでも自分のディレクトリはマウントされているので,サンプルのfastaファイルを引っ張ってきて実行すればok ```shell $ wget https://molb7621.github.io/workshop/_downloads/sample.fa $ singularity run capr_alpine_bin.simg ./sample.fa ./sample.out 10 $ head ./sample.out ``` ## jupyter-notebookを作成してみる ### 1. `python:3.7`のイメージを引っ張ってくる エントリポイントがpythonになっているので,何も指定しないとpythonが起動する ```shell $ docker run -it python:3.7 Python 3.7.3 (default, Jun 11 2019, 01:05:09) [GCC 6.3.0 20170516] on linux Type "help", "copyright", "credits" or "license" for more information. >>> ``` ### 2. pipのイメージを入れたいので`bash`を上げる 名前は`jupyter`にでもしておこう.実行したイメージに続けて実行したいプログラムを記載する.今回は`bash` ```shell $ docker run -it --name jupyter --hostname jupyter python:3.7 bash root@jupyter:/# ipython bash: ipython: command not found root@jupyter:/# pip install jupyter Collecting jupyter Downloading https://files.pythonhosted.org/packages/83/df/0f5dd132200728a86190397e1ea87cd76244e42d39ec5e88efd25b2abd7e/jupyter-1.0.0-py2.py3-none-any.whl Collecting nbconvert (from jupyter) Downloading https://files.pythonhosted.org/packages/35/e7/f46c9d65f149271e47fca6ab084ef5c6e4cb1870f4c5cce6690feac55231/nbconvert-5.5.0-py2.py3-none-any.whl (447kB) |████████████████████████████████| 450kB 3.2MB/s ... root@jupyter:/# ipython Python 3.7.3 (default, Jun 11 2019, 01:05:09) Type 'copyright', 'credits' or 'license' for more information IPython 7.5.0 -- An enhanced Interactive Python. Type '?' for help. In [1]: ``` ### 3. ipythonを終了し,`exit`して`commit`する ```shell root@jupyter:/# exit exit $ docker commit jupyter test/jupyter sha256:84078021c81c2f764f77f98db9a3bb7f646fd0ef490e65f0860b695fa2488fbc ``` イメージの作成を行っている.帰ってきた値がイメージのユニークなID ### 4. commitしたら実行するのは簡単 ```shell $ docker run -it test/jupyter ipython Python 3.7.3 (default, Jun 11 2019, 01:05:09) Type 'copyright', 'credits' or 'license' for more information IPython 7.5.0 -- An enhanced Interactive Python. Type '?' for help. In [1]: ``` ### 5. jupyter-notebookを実行する際にはたくさん引数を渡せばok ```shell $ docker run -it -p 8888:8888 test/jupyter jupyter notebook --no-browser --allow-root --ip=0.0.0.0 [I 13:07:45.648 NotebookApp] Writing notebook server cookie secret to /root/.local/share/jupyter/runtime/notebook_cookie_secret [I 13:07:46.928 NotebookApp] Serving notebooks from local directory: / [I 13:07:46.928 NotebookApp] The Jupyter Notebook is running at: [I 13:07:46.928 NotebookApp] http://(278d34ce0b5d or 127.0.0.1):8888/?token=4f9b53ef3ed0f5dbd97f82ef5d1d4d78bd46a6a3e9333190 [I 13:07:46.929 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation). [C 13:07:46.939 NotebookApp] To access the notebook, open this file in a browser: file:///root/.local/share/jupyter/runtime/nbserver-1-open.html Or copy and paste one of these URLs: http://(278d34ce0b5d or 127.0.0.1):8888/?token=4f9b53ef3ed0f5dbd97f82ef5d1d4d78bd46a6a3e9333190 ``` この状態で`http://127.0.0.1:8888/?token=...`に接続するとjupyter-notebookが利用できる + `-p`:コンテナ内のポートと手元のポートをつなげる + `--no-browser`:ブラウザなし + `--allow-root`:ルート権限で実行許可 + `--ip=0.0.0.0`:どのIPからの接続もok ### 6. 毎回これをやるのは面倒なので`Dockerfile`を使おう Dockerfileはイメージの作成手順を記載したテキストファイル.適当なディレクトリを作成して中にDockerfileを作成する ```shell $ mkdir jupyter $ cd jupyter $ touch Dockerfile ``` ### 7. `vi`でも`emacs`でも`code`でも`nano`でもいいので編集する ```dockerfile FROM python:3.7 RUN pip install jupyter ``` を書く. ### 8. ビルド 書いたらbuildする ```shell $ docker built -t test/jupyter-dockerfile . ``` 現在のディレクトリ(`.`)の中にあるDockerfileに基づいて`test/jupyter-dockerfile`というタグを付けてビルドする. ### 8. 再度実行してみる ```shell $ docker run -it -p 8888:8888 test/jupyter-dockerfile jupyter notebook --no-browser --allow-root --ip=0.0.0.0 [I 13:19:30.666 NotebookApp] Writing notebook server cookie secret to /root/.local/share/jupyter/runtime/notebook_cookie_secret [I 13:19:31.991 NotebookApp] Serving notebooks from local directory: / [I 13:19:31.991 NotebookApp] The Jupyter Notebook is running at: [I 13:19:31.992 NotebookApp] http://(502b62e6c74b or 127.0.0.1):8888/?token=6989be6638b1669eff54039e08156a73727849ce8b84c739 [I 13:19:31.994 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation). [C 13:19:32.002 NotebookApp] To access the notebook, open this file in a browser: file:///root/.local/share/jupyter/runtime/nbserver-1-open.html Or copy and paste one of these URLs: http://(502b62e6c74b or 127.0.0.1):8888/?token=6989be6638b1669eff54039e08156a73727849ce8b84c739 ``` ### 9. 毎回引数を渡すのは面倒くさい -> Dockerfileに書こう 便利な`ENTRYPOINT`というのが存在する. ```dockerfile FROM python:3.7 RUN pip install jupyter ENTRYPOINT [ "jupyter","notebook","--port=8000","--no-browser","--allow-root","--ip=0.0.0.0"] ``` と書いてあげると実行時に引数を省略できる. ### 10. このままだとちょっと大変 + 毎回起動するときのディレクトリが`/`になっている + トークンの入力が大変 + ホームディレクトリを既定のディレクトリにしたい これらを解決するためにはDockerfileの記述を変更しよう ```dockerfile FROM python:3.7 RUN pip install jupyter RUN jupyter notebook --generate-config RUN echo "c = get_config();c.NotebookApp.token = 'XXXX'" >> ~/.jupyter/jupyter_notebook_config.py WORKDIR /home EXPOSE 8000 ENTRYPOINT [ "jupyter","notebook","--port=8000","--no-browser","--allow-root","--ip=0.0.0.0"] ``` 以下の点を変更した + configを作成してパスワードを`XXXX`に設定 + 起動時のディレクトリを(コンテナ内の)`/home`に設定 あとは起動時に`-v ホストのディレクトリ:コンテナのディレクトリ`のオプションを渡して ```shell $ docker run -p 8000:8000 -it -v $HOME:/home test/jupyter-dockerfile ``` とすればホームディレクトリを`/home`とつなげることができる ### 11. 作ったイメージをアクセス可能にする **!Docker Hubのアカウントが必要です!** アカウントを作成したら,自分のユーザーネームのタグを付けてビルドして, ``` $ docker build -t natuski/jupyter-dockerfile . ``` push! ``` $ docker login && docker push natuski/jupyter-dockerfile ``` で自分のリポジトリにアップロードできる.これでどこでも`docker pull natuski/jupyter-dockerfile`すれば利用できる! # 終わり ## サンプルのDockerfile ### CapRを動かすサンプル ```dockerfile FROM alpine AS builder RUN apk add --no-cache git make g++ RUN git clone https://github.com/fukunagatsu/CapR.git WORKDIR /CapR RUN make FROM alpine RUN apk add --no-cache libstdc++ COPY --from=builder /CapR/CapR /CapR ENTRYPOINT ["/CapR"] ``` `Dockerfile`に以上を書いた上で`docker build .`する イメージの大きさを見たりする場合には`docker images` ```shell $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE natuski/capr_alpine latest 245d66b1731c 11 minutes ago 7.3MB ``` 遺伝研で使用する場合には ```shell= # メモリを明示的に要求してqlogin(sifファイル作成にメモリを食うらしい) $ qlogin -l s_vmem=32G -l mem_req=32G # モジュールのロード $ module load singularity # イメージのプル $ singularity pull docker://natuski/capr_alpine # イメージの実行 $ singularity run capr_alpine.simg ``` runするとエントリポイントが呼ばれる