Docker コンテナーのしくみ
先ほど、コンテナーはアプリの配布に使う単位になることを確認しました。 また、コンテナーは、開発者チームと運用チームの両方が使う標準化された形式であることも学習しました。
この例では、会社のさまざまな販売店が使用できる注文追跡ポータルを開発しています。 Docker イメージのビルドが済み、運用チームがデプロイ、更新プログラムのロールアウト、注文追跡ポータルの管理を担当するようになっています。
前のユニットでは、Docker イメージがどのように構築されているかを見てきました。 ここでは、Docker コンテナーのライフサイクルと、コンテナーの管理方法について説明します。 また、コンテナーのデータ ストレージとネットワーク オプションの構成に関する考え方についても説明します。
Docker コンテナーを管理する方法
Docker コンテナーには、コンテナーの状態を管理および追跡するために使用できるライフサイクルがあります。
コンテナーを実行状態にするには、run コマンドを使います。 既に実行されているコンテナーを再起動することもできます。 コンテナーを再起動すると、コンテナーは、コンテナーのカーネルが終了される前に、実行中のすべてのプロセスを正常にシャットダウンできるように、終了シグナルを受け取ります。
コンテナーは、一時停止、停止、または強制終了されるまで、実行中状態と見なされます。 ただし、コンテナーは単独で実行状態を終了することもあります。 コンテナーは、実行中のプロセスが完了した場合、またはプロセスがエラー状態になった場合に、それ自体で終了することができます。
実行中のコンテナーを一時停止するには、pause
コマンドを使用します。 このコマンドでは、コンテナー内のすべてのプロセスが中断されます。
実行中のコンテナーを停止するには、stop
コマンドを使用します。 stop
コマンドを使用すると、動作中のプロセスに終了シグナルを送信して正常にシャットダウンすることができます。 プロセスがシャットダウンすると、コンテナーのカーネルは終了します。
コンテナーを終了する必要がある場合は、kill
コマンドを使用して kill シグナルを送信します。 コンテナーのカーネルは kill シグナルの取り込みを行いますが、実行中のプロセスは行いません。 このコマンドを実行すると、コンテナーで動作中のプロセスは強制的に終了されます。
最後に、停止状態にあるコンテナーを削除するには、remove
コマンドを使用します。 コンテナーを削除すると、コンテナーに格納されているすべてのデータが破棄されます。
使用可能なコンテナーを表示する方法
実行中のコンテナーを一覧表示するには、docker ps
コマンドを実行します。 すべてのコンテナーのすべての状態を表示するには、-a
引数を渡します。
次に例を示します。
docker ps -a
このコマンドの出力は次のとおりです。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d93d40cc1ce9 tmp-ubuntu:latest "dotnet website.dll …" 6 seconds ago Up 5 seconds 8080/tcp happy_wilbur
33a6cf71f7c1 tmp-ubuntu:latest "dotnet website.dll …" 2 hours ago Exited (0) 9 seconds ago adoring_borg
前の出力では、次の 3 つの項目を確認できます。
IMAGE 列に表示されているイメージ名。この例では tmp-ubuntu: latest。 同じイメージから複数のコンテナーを作成できることに注意してください。 これは、ソリューションでスケーリングを可能にするために使用できる強力な管理機能です。
STATUS 列に表示されているコンテナーの状態。 この例では、1 つのコンテナーは実行中であり、もう 1 つのコンテナーは終了しています。 コンテナーの状態は、通常、コンテナーの正常性を示す最初のインジケーターです。
NAMES 列に表示されているコンテナー名。 最初の列のコンテナー ID とは別に、コンテナーは名前も受け取ります。 この例では、各コンテナーの名前を明示的に指定していませんでした。その結果、コンテナーには Docker によってランダムな名前が付けられています。 コンテナーに明示的な名前を付けるには、
run
コマンドの--name
フラグを使います。
コンテナーに名前が付けられる理由
この機能を使用すると、同じイメージの複数のコンテナー インスタンスを実行できます。 コンテナー名は一意であるため、名前を指定すると、その名前を再利用して新しいコンテナーを作成することはできません。 特定の名前を再利用する唯一の方法は、前のコンテナーを削除することです。
コンテナーを実行する方法
コンテナーを起動するには、docker run
コマンドを使います。 イメージからコンテナーを起動するには、実行するイメージを名前または ID で指定するだけです。 この方法で起動されたコンテナーには、対話型のエクスペリエンスが提供されます。
ここで、バックグラウンドで Web サイトを使用してコンテナーを実行するには、-d
フラグを追加します。
docker run -d tmp-ubuntu
この場合、コマンドからは新しいコンテナーの ID のみが返されます。
実行するイメージを指定すると、Docker によりイメージが検索され、イメージからコンテナーが読み込まれて、エントリ ポイントとして指定したコマンドが実行されます。 この時点で、コンテナーを管理できるようになります。
コンテナーを一時停止する方法
コンテナーを一時停止するには、docker pause
コマンドを実行します。 次に例を示します。
docker pause happy_wilbur
コンテナーを一時停止すると、すべてのプロセスが一時停止されます。 このコマンドを使用すると、コンテナーは後のステージでプロセスを続行できます。 docker unpause
コマンドを実行すると、指定されたコンテナー内のすべてのプロセスの一時停止が解除されます。
コンテナーを再起動する方法
コンテナーを再起動するには、docker restart
コマンドを実行します。 次に例を示します。
docker restart happy_wilbur
コンテナーは、stop コマンドを受け取った後、start コマンドを受け取ります。 コンテナーが stop コマンドに応答しない場合、kill シグナルが送信されます。
コンテナーを停止する方法
実行中のコンテナーを停止するには、docker stop
コマンドを実行します。 次に例を示します。
docker stop happy_wilbur
stop コマンドにより、コンテナーと、コンテナー内で実行されているプロセスに、終了シグナルが送信されます。
コンテナーを削除する方法
コンテナーを削除するには、docker rm
コマンドを実行します。 次に例を示します。
docker rm happy_wilbur
コンテナーを削除すると、コンテナー内のすべてのデータは破棄されます。 データの格納に関しては、コンテナーを常に一時的なものと考えることが不可欠です。
Docker コンテナーのストレージの構成
先ほど説明したように、コンテナー内のアプリでデータを格納する必要がある場合は、常にコンテナーを一時的なものと考えてください。
この追跡ポータルで、アプリのルートのサブフォルダー、つまりコンテナーのファイル システムに直接、ログ ファイルを作成するとします。 アプリでデータをログ ファイルに書き込むと、システムによってデータは書き込み可能なコンテナー レイヤーに書き込まれます。
このアプローチは機能しますが、残念なことに、いくつかの欠点があります。
コンテナー ストレージは一時的なものです。
このログ ファイルは、コンテナー インスタンス間で保持されません。 たとえば、コンテナーを停止して削除するとします。 新しいコンテナー インスタンスを起動すると、新しいインスタンスは指定したイメージに基づくものになり、以前のすべてのデータは失われます。 コンテナーを削除すると、コンテナー内のすべてのデータはコンテナーと共に破棄されることを忘れないでください。
コンテナー ストレージは、基となるホスト コンピューターと結合されます。
コンテナーは基となるホスト コンピューターと結合されているため、コンテナーからのログ ファイルへのアクセスや移動は困難です。 ファイルにアクセスするには、コンテナー インスタンスに接続する必要があります。
コンテナー ストレージ ドライブのパフォーマンスが低下します。
アプリからデータを書き込むことができるように、コンテナーにはストレージ ドライバーが実装されています。 このドライバーにより、ホストの OS カーネルと通信するための追加の抽象化が導入され、ホスト ファイルシステムに直接書き込む場合よりパフォーマンスが低下します。
コンテナーでは、データを永続化するために 2 つのオプションを使用できます。 1 つ目のオプションは "ボリューム" を使用するもので、2 つ目のオプションは "バインド マウント" です。
ボリュームとは
ボリュームは、特定のフォルダーの場所にあるホスト ファイル システムに格納されます。 Docker 以外のプロセスによってデータが変更されないことがわかっているフォルダーを選びます。
Docker で新しいボリュームを作成して管理するには、docker volume create
コマンドを実行します。 このコマンドは、Dockerfile 定義の一部にできます。つまり、コンテナー作成プロセスの一環としてボリュームを作成できます。 初めてコンテナーにボリュームをマウントしようとしたときにボリュームが存在しない場合は、Docker によって作成されます。
ボリュームは、ホスト ファイル システム上のディレクトリに格納されます。 Docker はコンテナー内のボリュームをマウントして管理します。 マウント後、これらのボリュームはホスト コンピューターから分離されます。
複数のコンテナーで、同じボリュームを同時に使用することができます。 また、ボリュームは、コンテナーでボリュームの使用が停止されても、自動的には削除されません。
この例では、追跡ポータルのコンテナーを作成するときに、コンテナー ホストにディレクトリを作成し、このボリュームをコンテナーにマウントできます。 追跡ポータルでデータがログに記録されたら、コンテナー ホストのファイル システムを介して、この情報にアクセスできます。 コンテナーが削除された後でも、このログ ファイルにはアクセスできます。
Docker には、ボリュームとして使用するアドオンをサードパーティ企業が構築する方法も用意されています。 たとえば、Azure Storage には、Azure Storage をボリュームとして Docker コンテナーにマウントするためのプラグインが用意されています。
バインド マウントとは
バインド マウントは、概念的にはボリュームと同じですが、特定のフォルダーを使うのではなく、ホスト上の任意のファイルまたはフォルダーをマウントできます。 また、これらのマウントの内容は、ホストによって変更される可能性があります。 ボリュームと同じように、マウントしようとしたバインド マウントがホストにまだ存在しない場合は、作成されます。
バインド マウントは、ボリュームと比較して機能が制限されており、パフォーマンスはボリュームより優れていますが、ホストに特定のフォルダー構造があることに依存しています。
ボリュームは、コンテナーで使うことが推奨されるデータストレージ戦略と考えられています。
Windows コンテナーの場合は、別のオプションを使用できます。SMB パスをボリュームとしてマウントし、それをコンテナーに提示できます。 これにより、さまざまなホスト上のコンテナーで同じ永続ストレージを使用できます。
Docker コンテナーのネットワークの構成
Docker ネットワークの既定の構成では、Docker ホスト上のコンテナーを分離できます。 この機能を使用すると、相互に安全に通信できるアプリをビルドして構成することができます。
Docker には、Linux および Windows でのさまざまなネットワーク設定が用意されています。
Linux には、次の 6 つの事前構成済みネットワーク オプションがあります。
- ブリッジ
- ホスト
- オーバーレイ
- IPvLan
- MACvLan
- なし
Windows には、次の 6 つの事前構成済みネットワーク オプションがあります。
- NAT (ネットワーク アドレス変換)
- 透明
- オーバーレイ
- L2Bridge
- L2Tunnel
- なし
ネットワークの要件に応じて、コンテナーに適用するネットワーク構成を選択できます。
ブリッジ ネットワークとは
ブリッジ ネットワークは、他のネットワーク構成を指定せずに起動した場合に、コンテナーに適用される既定の構成です。 このネットワークは、コンテナーによって使われる内部のプライベート ネットワークであり、コンテナー ネットワークは Docker ホスト ネットワークから分離されます。
ブリッジ ネットワーク内の各コンテナーには、IP アドレスとサブネット マスクが割り当てられ、ホスト名は既定でコンテナー名になります。 既定のブリッジ ネットワークに接続されたコンテナーは、ブリッジに接続された他のコンテナーに、IP アドレスを使ってアクセスできます。 ブリッジ ネットワークでは、コンテナー間の通信にホスト名を使用することはできません。
既定では、Docker によってコンテナーのポートが発行されることはありません。 コンテナーのポートと Docker ホストのポートの間でポート マッピングを有効にするには、Docker のポートで --publish
フラグを使用します。
publish フラグでは、ポートをマップするファイアウォール規則が実質的に構成されます。
この例の追跡ポータルには、クライアントでポート 80 を参照してアクセスできます。 コンテナーからホスト上の使用可能なポートに、ポート 80 をマップする必要があります。 ホスト上でポート 8080 が開いているため、次のようにフラグを設定できます。
--publish 8080:80
Docker ホストの IP とポート 8080 を参照するすべてのクライアントは、追跡ポータルにアクセスできます。
Linux 固有の構成とは別に、Windows ホスト上の NAT ネットワークはブリッジ ネットワークと同じように機能します。 また、NAT は Windows 上の既定のネットワークであり、特に指定されていない限り、すべてのコンテナーがそれに接続されます。
ホスト ネットワークとは
ホスト ネットワークを使用すると、ホスト ネットワーク上でコンテナーを直接実行することができます。 この構成では、ネットワーク レベルでのホストとコンテナーの分離が実質的になくなります。
このモジュールの例で、ネットワーク構成をホスト ネットワーク オプションに変更するものとします。 この追跡ポータルには、ホスト IP を使用して引き続きアクセスできます。 これで、マップされたポートではなく、既知のポート 80 を使用できるようになります。
このコンテナーが使用できるのは、ホストがまだ使っていないポートのみであることに注意してください。
Windows では、ホスト ネットワークを使用できません。 Windows ホストには、ホストとコンテナーの間で同じ IP アドレス (ネットワーク スタック) を共有するオプションがありません。 NAT ネットワークはブリッジ ネットワークと同じように機能します。また、[オーバーレイ] オプションを使うと、ホストと "同じ" ネットワークからコンテナーに IP アドレスが指定されますが、同じ IP アドレスではありません。
オーバーレイとその他のネットワーク オプション
より高度なシナリオでは、Linux と Windows の両方に、追加のネットワーク オプションが用意されています。 たとえば、オーバーレイ オプションを使うと、ホスト ネットワークから仮想スイッチが作成されるため、そのネットワーク上のコンテナーで DHCP サーバーから IP アドレスを取得したり、そのネットワーク セグメントから IP アドレスを操作したりすることができます。 また、Docker を使用すると、サードパーティ ベンダーによるネットワーク プラグインの作成が可能になります。
なしネットワークとは
コンテナーのネットワークを無効にするには、[なし] のネットワーク オプションを使います。 これが便利なのは、ネットワークを使わないアプリケーションがある場合や、コンテナーでアプリケーションが想定どおりに実行されていることを検証したい場合です。
オペレーティング システムに関する考慮事項
Docker のネットワーク構成オプションは、デスクトップ オペレーティング システムによって異なることに注意してください。 たとえば、ブリッジ ネットワークを使っている場合、macOS では Docker0 ネットワーク インターフェイスを使用できません。また、Windows と macOS のデスクトップでは、ホスト ネットワーク構成の使用はサポートされていません。
これらの違いは、開発者がコンテナー開発を管理するためのワークフローを構成する方法に影響を与える可能性があります。 さらに、コンテナー オーケストレーターには、Docker セットアップに加え、他のネットワーク構成も用意されている場合があります。