研究生活に役立つDocker入門
※走り書きです。
Dockerとは?+注意書き
※Linux限定で書いてしまうが、WindowsやmacOSでも利用可能
超軽量のコンテナ型仮想化を実現するOSSソフトウェア。VMのイメージで扱うと大変危険。 コンテナはシステムの資源を間借できる高機能chroot環境と思うのが吉。
Dockerでは、Linuxのカーネルをホストとコンテナで共有する。これはコンテナがLinuxカーネルの機能で実現されているからである。 コンテナの中から見えるファイルシステムは厳密に独立していないので、/bootとかは触ってはだめ(/etcはOK)。 systemd(pid 1系)は使えないので気をつけて。 先述の注意点がクリティカルな場合は素直にVagrantなりVirtualBoxなり使うのが賢明。
Dockerがなぜ研究生活向けなのか?
研究室で受け継がれるVMイメージはときに邪悪である。 OSのインストールは良いとして、その後に何をどうビルドしてインストールしたのかが全く明らかではない。 別にドキュメントを用意することが必要になってしまう。 また起動と実行のためにCPUとメモリを多く奪われてしまうのは全くイケてない。
Dockerを使って研究を進められる環境を作ると次のようなメリットがある。
- Dockerfile(後述)がそのまま環境構築手順書になる
- 起動が速く、ホストとシームレスにつながる(※ホストPCがLinuxの場合に限る)
- Dockerfileをgitなどでバージョン管理すると、いついつのときのコンテナを起動することができる(※image(後述)をビルドする時に細工が必要)
- システム変更を記録に残す癖がついて良い
- コンテナ起動後にした変更が破棄されるので、同じコンテナを使っている限り、システム側が原因で「前まで使えたのになぜか使えなくなった」が起こり得ない
- 試しにいじって上手く行ったらDockerfileに反映するアプローチも良し
ただし、以下のデメリットもあるので注意が必要。
- ウインドウマネージャの恩恵を受けられない(が、GUIアプリの画面を出すことは可能)
- 複数のターミナルを出すのがサーバーと同じくらい少し大変(が、こんなのは慣れの問題)
- Dockerfileを変更してコンテナを更新するまでに大変時間がかかることがある(変更方法に依存するが、覚悟が必要)
- Dockerfile作成にあまりネットで言及されないコツが必要(後述)
インストール方法
Ubuntuは以下の記事で良いと思われる。 [Docker] ubuntu 14.04/16.04にDockerをインストール - Qiita
Arch Linuxの場合は、
sudo pacman -S docker ### 自分にdockerグループを追加 sudo usermod -aG docker `whoami` ### dockerを自動起動 sudo systemctl enable docker
ここで完了させておくことは、
- dockerのインストール
- ユーザーにdockerグループを追加
- dockerデーモンの有効化(ないしは
sudo docker daemon
をLinuxが起動する度に手動実行)
である。
コンテナ起動
コンテナ起動時に実行するプログラムを指定する。通常はbashを起動する。 後述するDockerfileにCMDとして起動するプログラムを明示していないケース/誤ったCMDの指定がされることがあるので気をつけること。 Dockerfileを信頼せず、コマンドラインで明示するのが安全である。
コンテナ起動のコマンドは次の通り:
### ubuntu 14.04の場合 ### オプションは "run it" で覚えるのがおすすめ docker run -it ubuntu:14.04 bash #### runの前に `docker pull ubuntu:14.04` が必要かも ### プロセス終了時にコンテナを破棄したいとき(--rm) ### オプションは "run, remove it" で覚えるのがおすすめ docker run --rm -it ubuntu:14.04 bash
動作確認のために上記コマンドを実行してUbuntuのbashシェルが出ることを確認してほしい。 ネットワークからimageがダウンロードされるので初回の起動は時間がかかる。 imageとはコンテナを起動する種になるものである。 これは次節で説明する。
高度なコンテナ起動オプションとDockerあるあるについては後半で述べる。
imageの作成
imageはコンテナ起動の元になる「種」のようなものである。 imageの作成は2通りの方法がある。
Dockerhub Dockerfile | | pull build | | [ image ] | run | [ container ]
1つ目はDockerhubからpullする方法である。あまり使わないので説明は省略。気になる人は「docker pull dockerhub」でググって。
2つ目はDockerfileからbuildする方法である。 Dockerfileはこの名前で存在するファイルである。 Dockerfileがあるディレクトリまで移動(cd)して、
docker build -t IMAGE_NAME . # IMAGE_NAME must be lowercase ### or docker build -t IMAGE_NAME -f DOCKERFILE_PATH ### without cache docker build -t IMAGE_NAME . --no-cache=true
のどれかの形式で実行すると良い。IMAGE_NAMEは制約があるので小文字でいい感じに付けると良い。 buildはキャッシュを使ってされる。apt updateで不都合が起こる時はこのオプションを付けるとよい。
Dockerfileの構成
大抵の場合上の行から、
- FROM(必ず1番上に書くこと)
- MAINTANER(省略可)
- RUN または ADD または ENV または WORKDIR または USER(これが複数行続く)
- CMD または ENTRYPOINT (省略可;1行のみ)
の順に書かれている。
FROM
FROMはベースとなるimageを指定する。何も考えずに
FROM ubuntu:16.04
か
FROM ubuntu:14.04
と書くといい。
MAINTANER
メンテナーの情報を書く欄。自由に
RUN
ベースのimageで実行するコマンドを書く。複数行に分けたい時は行末に「半角スペース+バックスラッシュ」
例えば、
RUN sed -i.bak -e "s%http://archive.ubuntu.com/ubuntu/%http://ftp.iij.ad.jp/pub/linux/ubuntu/archive/%g" /etc/apt/sources.list && \ apt update && \ apt install -y git automake pkg-config libssl-dev RUN apt install -y curl wget bash-completion net-tools strace silversearcher-ag mlocate vim
シェルスクリプトの要領でガリガリ書いていく。コマンドはroot権限で実行されるのでsudoは付けなくて良い。 ちなみにRUNごとにキャッシュが生成される。
1つ前のRUNでcdして、次のRUNでそこの場所でコマンドを実行はできない。RUNごとにカレントディレクトリがリセットする。 cdのし忘れには気をつけよう。面倒な時は後述のWORKDIRが便利。
ADD
外部ファイルをimageに追加するコマンド。Dockerfileから見た相対パスで書く。 パッチファイルを取り込みたいときに便利。
ADD ./my-awesome.patch /home/docker ADD ./my-awesome.patch $PATCH_FILE_DIR
面倒でなければENVで設定した環境変数を使うようにしてください
ENV
docker buildで有効な環境変数を設定するコマンド。$HOMEを変更するときはWORKDIRを使わないとだめ。 以下例。
ENV PATCH_FILE_DIR /home/docker/projects/qemu/patches
WORKDIR
カレントディレクトリを変更するコマンド。 以下例。
WORKDIR /home/docker/projects
これをするとその後のRUNから/home/docker/projectsがカレントディレクトリになる。
USER
コマンドの実行主を変更するコマンド。このときユーザーが自動的に作成されるかは知りません。
USER docker
CMD, ENTRYPOINT
両方は使えなかったはず。複数回もできなかったはず。 コンテナが起動したときに自動実行するコマンドを指定できる。 どっちかが、コマンドラインで指定したコマンドに置き換えられるはず。
Dockerfileのトラブルシューティングのコツ
- 前もって別の環境で実行して、このコマンドで上手くいくことを確認する
- apt updateを冒頭で必ずする
- ファイルがあることを確認するために
RUN ls *
する - buildメッセージに載っているキャッシュされたimageのIDを控える→
docker run --rm -it CACHE_IMAGE_ID
してインタラクティブに確認【一番オススメ】 - RUNの単位を細かくする。
&&
で頑張って繋げない - RUNで複数実行するときは
;
でなく&&
でコマンドをつなげる
日頃気をつけること
- システムに残したい変更をしたら、そのときに実行したコマンドをDockerfileに残すのを忘れないこと。
高度なdocker run
privillegedオプション
Dockerのコンテナは触れるシステム資源が制限されている。 USBはこれに該当する。USBを使えるようにする前提で説明する。
まず Dockerfile 後半に以下の行を追記する必要がある。
VOLUME /dev/bus/usb:/dev/bus/usb
これは、ホストとコンテナの間でUSBを共有する設定。
privilegedモードでの起動は
docker run --rm -it --privileged IMAGE_NAME[:tag] bash
特権が付いているかどうかは次のように確認できる。
コンテナの外から:
docker inspect --format='{{.HostConfig.Privileged}}' CONTAINER_ID
コンテナの中から:
ip link add dummy0 type dummy
デタッチしたコンテナを復活させる
--rm
を付けずにdocker runした場合のみ。
docker run で起動したbashをexitしたとき、コンテナはdocker ps
(※起動中のコンテナを確認するコマンド)には表示されなくなる。
次の手順でコンテナIDを取得し、re-attachすることができる。
### check container name or ID docker ps -a ### wakeup contianer with its id or its name docker start CONTAINER_ID/CONTAINER_NAME ### you can check your container restarted docker ps ### now you can re-attach to it docker attach CONTAINER_ID/CONTAINER_NAME # hit ENTER
docker attachしたターミナルで、アタッチ後にEnterを押さないとプロンプトが返らないことがある。
ショート版の手順がこちら:
docker ps -a docker attach `docker start CONTAINER_ID/CONTAINER_NAME`
tmux
シェルが1つしかないのは不便なので、tmuxのようなターミナルマルチプレクサを使うとシェルをいくつも開けて便利。
VirtualBoxみたいにファイル共有(フォルダ共有)したい
起動時に「-v マウント元(ホスト上のパス):マウント先(コンテナ上のパス)」のオプションを与える。
-v A:B -v C:D
の感じで複数並べることも可能
### read-write mode docker -it -v VOLUME_NAME:/home/guest docker -it -v `pwd`/HOST_DIR_NAME:/home/guest ### read-only mode docker -it -v VOLUME_NAME:/home/guest:ro docker -it -v `pwd`/HOST_DIR_NAME:/home/guest:ro
高度なdocker build
docker build を一々叩くのは面倒なので ./build.sh なり、ビルド用のシェルスクリプトを用意すると便利である。 docker buildに成功したらタグを付けるようにしたほうがいい。タグ名はちゃんと管理できる自信があるならバージョン番号でも良いし、 gitのコミットハッシュでもよい。後者を前提とした build.sh のテンプレートが以下:
count_x_files() { COUNT=$(git status --porcelain | grep -v "/patch/" | grep $1 | wc -l) echo $COUNT # return $COUNT } IMAGE="my-ubuntu" docker build -t $IMAGE . || (echo "[!] ERROR: docker build. exit" ; exit) echo "[*] DONE: docker build" if [ $(count_x_files "MM") -ne "0" ]; then echo "[!] There's not staged changes. git add it now"; exit fi if [ $(count_x_files "??") -ne "0" ]; then echo "[!] There's untracked patch files. git add it now"; exit fi docker tag $IMAGE $IMAGE:$(git rev-parse --short HEAD) echo "[*] DONE: docker tag"
このテンプレはgit add -u && git commit のし忘れを指摘してくれる。
Dockerあるあると小ネタ
コンテナーのIDを知りたい
docker ps
(docker buildで作成された)特定のコンテナのログを見たい
docker log CONTAINER_ID
ID は docker build のログ Running in dc043f01ca19
から dc043f01ca19
を拾ってくる
imageの一覧を見たい
docker images
イメージを消したい
### remove specific image docker rmi IMAGE_NAME [IMAGE_NAME ...] ### force remove docker rmi -f IMAGE_NAME [IMAGE_NAME ...]
イメージ名を改名したい
### copy docker tag OLD_IMAGE_NAME OLD_IMAGE_NAME ### and remove docker rmi OLD_IMAGE_NAME
停止したコンテナだけ一覧で出したい
docker ps --filter "status=exited"
使ってないコンテナ(exitしたコンテナ)を消したい
docker rm `docker ps --filter "status=exited" -q` ### or docker rm $(docker ps -a -q)
タグがついてないimageを消したい (like <none>:<none>
)
docker rmi $(docker images | egrep "^<none>" | awk '{print $3}')