🐧 Linux 総合学習プラットフォーム
コンテナ Docker/Podman ・ 中級

コンテナとイメージの違い

コンテナを学ぶ最初の関門が「イメージ」と「コンテナ」の区別です。イメージはアプリと実行に必要なファイル一式を固めた読み取り専用の雛形で、コンテナはそのイメージから起動した実行中の実体です。1つのイメージから同じコンテナを何個でも作れます。クラスと インスタンス、あるいは料理のレシピと完成した料理の関係に例えると分かりやすいです。コンテナを消してもイメージは残るので、また同じものを起動できます。

コンテナの学習でほとんどの人が最初につまずくのが、「イメージ」と「コンテナ」という2つの言葉の区別です。この2つは密接に関係していますが、まったく別のものを指します。ここを曖昧なまま先に進むと、コマンドの出力が読めず、「消したはずのものがまだある」「同じものが何個も出てくる」といった混乱に必ずぶつかります。逆に、ここさえ腑に落ちれば、以降の起動・ビルド・削除といった操作はすべて同じ理屈の上に乗っていることが見えてきます。まずは概念をはっきり分けることから始めましょう。

イメージとは、アプリケーション本体と、それが動くのに必要なライブラリや設定ファイル一式をまとめて固めた、読み取り専用の雛形です。それ自体は動いておらず、ディスクの上に静かに置かれているだけの「素材」だと考えてください。これに対してコンテナは、そのイメージをもとに起動した、実行中(または停止中)の実体です。つまりイメージという型から、コンテナという実物を作り出す、という関係になっています。重要なのは、1つのイメージから同じコンテナを何個でも作れるという点です。同じ素材から、まったく同じ環境の実物を必要なだけ複製できる——これがコンテナの再現性の高さの源です。

クラスとインスタンス、レシピと料理のたとえ

プログラミングの経験がある人なら、クラスとインスタンスの関係がそのまま当てはまると考えると分かりやすいでしょう。イメージがクラス(設計図)、コンテナがそこから生成されたインスタンス(実体)です。1つのクラスから複数のインスタンスを作れるのと同じように、1つのイメージから複数のコンテナが作れます。プログラミングになじみがなければ、料理のレシピと完成した料理の関係を思い浮かべてください。レシピ(イメージ)は1枚あれば十分で、それを見ながら同じ料理(コンテナ)を何皿でも作れます。料理を食べてしまっても、レシピは手元に残ります。

このたとえがそのまま実務の挙動につながります。コンテナを削除しても、もとになったイメージはディスクに残り続けます。だから、間違って起動したコンテナを消してしまっても慌てる必要はなく、同じイメージからまた起動し直せばよいのです。逆に言えば、本当に容量を空けたいときは、コンテナを消すだけでなくイメージ自体も消す必要がある、ということでもあります。

レイヤの積み重ねでできている

もう一段踏み込むと、イメージは1枚岩ではなく、レイヤと呼ばれる薄い層の積み重ねでできています。たとえば「土台となる最小のOS部分」という層の上に、「必要なソフトを入れた」層、さらに「自分のアプリを配置した」層……という具合に、変更のたびに新しい層が上へ重なっていきます。各レイヤは読み取り専用で、コンテナとして起動するときだけ、その一番上に「書き込み用の薄い層」が1枚だけかぶさります。コンテナの中でファイルを作ったり書き換えたりした内容は、すべてこの最上層に記録されます。

このレイヤ構造には実用上のうれしさが2つあります。1つは、複数のイメージが共通の土台レイヤを持つ場合、その層はディスク上で1つだけ保持して共有されるため、容量を節約できることです。もう1つは、後で扱う Dockerfile からイメージを作り直すとき、変更のなかった層はそのまま再利用される(キャッシュが効く)ため、作り直しが速く済むことです。コンテナを起動するたびに変わるのは最上層の書き込み層だけで、下に積まれた読み取り専用のレイヤ群はすべてのコンテナで共有される、という点も、同じイメージから何個ものコンテナを軽量に作れる理由になっています。

実際のコマンドで見分ける

言葉の区別は、実際のコマンドの出力にそのまま表れます。手元にあるイメージの一覧は docker images で、起動中のコンテナの一覧は docker ps で確認します。両者はまったく別の一覧で、出てくる列も違います。docker images は REPOSITORY(イメージ名)・TAG・IMAGE ID・SIZE といったイメージの属性を並べ、docker ps は CONTAINER ID・IMAGE(どのイメージから作ったか)・STATUS・NAMES といったコンテナの属性を並べます。たとえば docker run nginx を3回実行すると、イメージ nginx は1つのままですが、コンテナは3つ作られ、docker ps -a には3行が並びます。「イメージ一覧には1個なのにコンテナ一覧には3個」という状態が、まさに1対多の関係を目に見える形にしたものです。

削除コマンドが2系統に分かれているのも、この区別がそのまま反映された結果です。コンテナ(実体)を消すのは docker rm コンテナ名 で、イメージ(雛形)を消すのは docker rmi イメージ名 です。名前がよく似ていて取り違えやすいので、rm はコンテナ、rmi(remove image)はイメージ、と対応で覚えてください。しかも、あるイメージから作ったコンテナが1つでも残っていると、そのイメージは docker rmi で消そうとしても「使用中で消せない」と断られます。雛形を片付けるには先に実体を片付ける、という順序になっているわけで、ここからも「イメージの上にコンテナがぶら下がっている」という関係が読み取れます。

よくある混乱と実務の使いどころ

初学者がよく口にするのが「コンテナを消したらアプリごと無くなった気がする」という不安ですが、消えたのは実行中の実体(コンテナ)だけで、雛形(イメージ)は残っています。同じ docker run でまた起動できるので心配は要りません。逆に「ディスクがいっぱいになった」ときは、停止済みのコンテナと、もう使わないイメージの両方を片付ける必要があります。実務では、配布したいのはイメージのほうだ、という点も重要です。開発した環境をイメージとして固めてレジストリに置けば、別のサーバではそのイメージを取得して docker run するだけで、まったく同じコンテナを再現できます。「自分の環境では動いたのに本番では動かない」という昔ながらの悩みを、イメージという再現可能な雛形を配ることで解消する——これがコンテナの最大の値打ちです。まずは『イメージ=動いていない雛形、コンテナ=動いている実体、1つの雛形から実体は何個でも』という対応関係を、繰り返し手を動かして体に入れておきましょう。

この項目に出てくる用語

コンテナこんてな
イメージから起動した、実行中のアプリの実体。
イメージいめーじ
アプリと実行環境を固めた読み取り専用の雛形。
レイヤれいや
イメージを構成する、差分の積み重ねの一段。

関連コマンド

docker rundocker imagesdocker ps

▶ 学習アプリでこの続きを学ぶ・演習する