過去に社内勉強会で発表した内容をまとめて記事にしてみました。
この記事ではDockerのコンテナ技術のもとになっている概念について掘り下げつつ、Dockerコンテナの特徴である「携帯性」「軽量」「揮発性」について紹介していければと思います。
Dockerの特徴について
コンテナの特徴として以下の項目が挙げられます。
- 軽量
- 携帯性
- 揮発性
軽量
まず、最初に挙げられるコンテナの特徴としてはとても軽量であることです。 以下の図のように抽象化しているレイヤーが従来の仮想化技術と比較すると低レイヤーであるためアプリケーションを動作させるためのリソースが少なくて済むものと考えられます。
低いレイヤーでの抽象化が可能になったのは、namespace(Linuxのプロセス分離を行う)、cgroupe(CPU, Memoryなどのハードウェアのリソースを分離する)といった機能がLinux上で実装されるようにってからと言われています。 ちなみに2002年ごろには10もの異なる名前空間が提案されており混沌としていたといわれています。 それから今は6つの名前空間に落ち着き現在の形になりました。
Namespace | 略称 | 概要 |
---|---|---|
Mount | mnt | Mount 名前空間はファイルシステムを仮想的に分割します。別々のマウント・ネームスペースで実行されているプロセスは、そのマウント・ポイント外のファイルにはアクセスできません。 |
Process | pid | PID 名前空間は、プロセス ID (PID) 番号を分離し、同じシステムで実行されているプロセスが同じ PID を持つことができるようにします。 |
Inter process Communication | ipc | このネームスペースは、プロセスが互いに直接対話できるかどうかを制御します。 |
Network | net | このネームスペースは、プロセスが参照できるネットワーク・デバイスを管理します。 |
User | この名前空間は、親システムへの実際のルートアクセスを持たずに、プロセスが自身の名前空間内に「仮想ルート」を持つことを可能にする。また、UID およびGID 情報を分割して、子ネームスペースに独自のユーザー構成を持たせることができます。 | |
UTS | この名前空間は、ホスト名とドメイン情報を制御して、プロセスが別の名前のサーバーで実行していると認識できるようにします。 |
Dockerコンテナは軽量であることによって、起動・破棄が高速で行えるため、高いスケーラビリティを持つことになります。 これによって、サービスの利用が集中する時間帯にコンテナ数を増やしたりといったことが容易になりました。 また、軽量であることによってコンテナはマイクロサービスの手段として優れています。
携帯性
コンテナの携帯性によって、開発者のOSや導入済の個別ソフトウェアの影響を受けずに、アプリケーションを動作させることができます。 コンテナ技術が普及するまでは、詳細な手順書を用意して、開発者がその手順書通りに作業する必要がありました。また、手順書のメンテナンスをする必要があり、それだけでもかなりの手間がかかっていました。
アプリケーションの動作環境を記述したDockerfileを共有することで、同様の動作環境を開発者間で共有することができるようになりました。
揮発性
最後に揮発性についてです。 この揮発性については、直感的にメリットが感じずらい部分かと思うのですが、この特徴によって、アプリケーションの再起動が容易になります。 何らか、不具合が発生したときに、一度コンテナを破棄して、再度立ち上げることによって不具合の原因となったものは消えた状態で再起動できるためです。
言葉だけだとイメージが湧きずらいかと思うので、下の図で言うところの「コンテナレイヤー」が揮発性という特徴を持っています。
コンテナはレイヤー構造を持つことによって、上記のような特徴を持つことになりました。 ここから先はレイヤー構造がどういったものかといった観点で掘り下げていきます。
コンテナのレイヤー構造について
概念的な話
左側がDockerイメージの設計図になるDockerfileを表しています。 右側の箱がDockerイメージの実体を表しています。
docker build
を実行すると、Dockerfileの上の行から実行していき、実行した順に
層が下から積まれていきます。
上図左側に示したDockerfileで言うところの
FROM ubuntu:15.04
でDocker HubからUbuntuのDocker公式イメージがダウンロードされて上図の右側でいう
「親レイヤー」が生成されます。
その上にCOPY ./app
, RUN make /app
の実行結果が「子レイヤー」として上に積まれていく形になります。
最後のCMD python /app/app.py
でDockerコンテナをRUNした時のデフォルト引数を指定することになっており、
Docker Imageが実行された時点で一番上の「コンテナレイヤー」が生成されます。
下画像が実際のコンテナの中身を見たときのイメージになっており、 我々が外からみてコンテナと呼んでいるのは"merged"という"Container Layer"と"Image Layer"をマウントしたものになっています。
引用元:Use the OverlayFS storage driver | Docker Documentation
実際にコンテナの中身を覗いてみる
概念的な話はここまでとして、実際に手を動かしてコンテナの中身を見ていきましょう。
以下の画像はDockerコマンドを使ってコンテナを外から見た場合のスクリーンショットになります。
次に、コンテナが配置されたディレクトリへ移動して中身を見たスクリーンショットになります。 下のものは実際に.NETで作成したアプリケーションをビルドしてコンテナに配置したものになります。 コンテナのLayerの場所が分かると、下のようにコンテナのレイヤーがフォルダ構造になっていることが分かるかと思います。
docer info
コマンドでDockerエンジンの各種設定が確認できるので、ここの情報を追っていくとDockerコンテナの実体を見ることができます。
自分の環境だと/mnt/wsl/docker-desktop-data/version-pack-data/community/docker/overlay2/
が実際のコンテナが配置されているパスになっていました。
最後に
Dockerを使いながらコンテナを理解しようとするとどうしても抽象的になってしまうので、実際に中身を見てみると実体が分かると、理解が進むと思いました。