Edited at

Dockerのライフサイクルがよくわからなかった

More than 3 years have passed since last update.

Docker、コンテナやらイメージやらという用語が混同してしまったり、データや構成がどこで保持されてどこで破棄されるのかといったライフサイクルが理解できていなかったのでまとめた。


イメージとコンテナ

イメージはコンテナのもととなるデータ。DockerHubからダウンロードして使ったりDockerfileからbuildしたり。コンテナは実際にイメージから起動したマシンを指す。

イメージは一から自分で作成することも不可能ではないが、多くの場合はDocker Hubと呼ばれるレポジトリに上がったデータを取得して使用する。Docker Hubから任意のイメージを取得(pull)し、内部構成をカスタマイズして保存(run && commit/Dockerfile && build)、そのイメージからコンテナを起動(run)して利用、要らなくなったらコンテナを停止(stop)して破棄(rm)というのが一様のライフサイクルになる。まとめると、


  • イメージはマシンではないため、状態は持たない。

  • コンテナはマシンとして扱う。状態を持ち、停止(stop)しても状態は保持される。

  • コンテナは明示的に破棄(rm)しない限り、停止しても存在し続ける(それなりにちゃんと消さないとどんどん容量を食う)。docker psコマンドはオプション無しだと起動中のコンテナしか表示しないため、あたかも停止したコンテナが破棄されたように見えたりする。

  • コンテナは使い捨てる。状態を永続化したい場合はcommitするか、Dockerfileを書く。

以下、各ライフサイクルでのコマンドを見ていく。


イメージの取得

$ docker search <keyword>

イメージをDocker Hubで検索する。例えば任意のミドルウェアを導入済みのイメージなど、そのミドルウェア名から検索できる。Docker Hubには誰でもホストできてしまうので、信頼のおけるイメージ(企業の公式イメージ等)を使うようにした方がよい。

$ docker pull <image name>

イメージをDocker Hubからダウンロードする。

$ docker rmi <repository:tag>

要らなくなったイメージは削除できる。


コンテナの起動

$ docker run <repository:tag> <command>

コンテナを起動する。起動時に任意のコマンドを実行させることができるので、これによってbashを立ち上げてコンテナ内に入ったり、ウェブアプリケーションを起動したりできる。

-t 擬似端末(pseudo-tty)

-i インタラクティブ実行

-b バックグラウンド

-P ポートフォワード(自動割り当てなので、任意のポート番号を指定したい場合は-p 0000

-v ローカルディレクトリをマウント。絶対パスで<local path>:<container path>の形で指定。

$ docker ps

起動しているコンテナの一覧を表示する。

-l 最後に起動したコンテナを表示

-a 停止しているコンテナを含め表示

-q コンテナIDだけを出力


コンテナの運用

$ docker stop

$ docker start
$ docker restart

それぞれコンテナの停止、開始、再起動。一度停止したら内部データは破棄されるかと思ってたのだが、そうじゃないっぽく、破棄されるまでコンテナに加えた変更は保持される。docker runで渡したコマンドの実行が終わればコンテナは終了するが、docker startで再度起動できる。

$ docker attach

アタッチ(コンテナ内部に入る)。デタッチはC-p C-q(exitするとコンテナが終了する)。

$ docker logs

バックグランド実行しているコンテナの標準出力を表示。-fオプションでtail -fと同様の効果。

$ docker inspect

dockerコンテナの情報をjsonで出力。


コンテナの破棄

前述のとおり、コンテナの破棄は明示的なコマンド実行が必要。

$ docker rm

コンテナの削除。rmiがイメージ削除でちょっと紛らわしい。


コンテナの保存

実際に保存されるのはイメージなので、見出しは正確ではないかもしれない。Dockerコンテナを起動して構成を変更した後、その状態を保存したいとなれば、commitコマンドでイメージとして保存することになる。

$ docker commit -m <commit message> -a <user> <image name/ID> <image repository:tag>

ただ、実際にはコンテナの入って手作業で構成を変更してcommitを行うより、構成をコード化したDockerfileを作成し、buildコマンドでイメージ化することの方が多い。

$ docker build -t <image repository:tag> <Dockerfile path>

なお、作成したイメージはdocker pushでDocker Hubへ登録することもできるようなのだが、やったことないので割愛。


その他tips


dockerコマンドのsudoが面倒

コマンド実行ユーザーをdockerグループに追加すればsudo要らなくなる。


docker runに複数コマンド渡したい

/bin/sh -cの引数として渡せば良い。

$ docker run chroju/centos /bin/bash -c 'bundle install && bundle exec spec'

おそらくだが、コンテナ内はshが起動していない状態であるため、シェルスクリプトの感覚で&&;を渡そうとしても、解釈するシェルがいないということのなのだと思う。故に明示的に/bin/bash -cから実行する必要がある。たぶん。またcdコマンドについても/bin/bash -cで渡す必要があった。シェルが起動していないからカレントディレクトリの概念がない、ということ、かも、しれない。

(参考)