第1章 Linux コンテナーの紹介

1.1. 概要

Linux コンテナーは、軽量なアプリケーションの分離と柔軟なイメージベースのデプロイ方法を組み合わせた、主要オープンソースアプリケーションのパッケージングおよび配信テクノロジーとして登場しました。

Red Hat Enterprise Linux 7 では、リソースを管理するコントロールグループ (cgroup)、プロセスを分離する名前空間、セキュリティーを担う SELinux などの中核テクノロジーを使用して Linux コンテナーを実装し、セキュアなマルチテナント機能を実現し、セキュリティーの脆弱性が悪用されるリスクを軽減します。

1.2. Linux コンテナーのアーキテクチャー

Linux コンテナーを正常に機能させるにはいくつかのコンポーネントが必要になりますが、それらのほとんどは Linux カーネルによって提供されます。カーネルの 名前空間 はプロセスを分離し、cgroup を使用するとシステムリソースを制御できます。SELinux を使用すると、ホストとコンテナー間の分離や、個々のコンテナー間の分離を確実に実行できます。管理インターフェース はこれらのカーネルコンポーネントと対話する上位層を構成し、コンテナーを構築し、管理するためのツールを提供します。

以下の図は、Red Hat Enterprise Linux 7 における Linux コンテナーのアーキテクチャーを示しています。

Linux コンテナーのアーキテクチャー

名前空間

カーネルは、複数のコンテナーに別々の 名前空間 を作成することによりプロセスを分離します。名前空間は、特定のグローバルシステムリソースを抽象化し、名前空間のプロセスに対し、それを分離したインスタンスとして表示させます。そのため、複数のコンテナーが競合せずに同じリソースを同時に使用することができます。名前空間には以下のような種類があります。

  • マウント名前空間 は、プロセスグループが認識するファイルシステムの一連のマウントポイントを分離するため、異なるマウント名前空間にあるプロセスにそれぞれ異なるファイルシステム階層のビューを持たせることができます。マウント名前空間により、mount() および umount() システムコールは、(すべてのプロセスに表示される) マウントポイントのグローバルセットに対する操作を停止し、代わりに該当コンテナープロセスに関連付けられたマウント名前空間のみに影響を与える操作を実行します。たとえば、各コンテナーには独自の /tmp または /var ディレクトリーか、または完全に異なるユーザー空間を持たせることができます。
  • UTS 名前空間 は、uname() システムコールから返される 2 つのシステム識別子 (nodename および domainname) を分離します。これにより、各コンテナーはそれぞれ独自のホスト名と NIS ドメイン名を持つことができるため、それらの名前に基づいて初期化および設定スクリプトを設定できます。ホストシステムとコンテナーの両方で hostname コマンドを実行して異なる結果が出されることを確認することにより、この分離が行われていることをテストすることができます。
  • IPC 名前空間 はシステム V IPC オブジェクトや POSIX メッセージキューなどの特定のプロセス間通信 (IPC) リソースを分離します。つまり、2 つのコンテナーは同じ名前の共有メモリーセグメントやセマフォーを作成できますが、他のコンテナーのメモリーセグメントや共有メモリーと連携することはできません。
  • PID 名前空間 は、異なるコンテナーのプロセスが同じ PID を持てるようにします。これにより、各種のシステム初期化タスクとコンテナーのライフサイクルを管理するために各コンテナーに独自の init (PID1) プロセスを持たせることができます。さらに、各コンテナーにはそれぞれ固有の /proc ディレクトリーがあります。コンテナーからは、そのコンテナー内で実行されているプロセスしか監視できないことに注意してください。つまり、コンテナーはそのネイティブなプロセスのみを認識でき、システムの別の部分で実行されているプロセスを「認識」しません。一方、ホストのオペレーティングシステムは、コンテナー内で実行されているプロセスを認識できますが、それらのプロセスに異なる PID 番号を割り当てます。たとえば、ホスト上で ps -eZ | grep systemd$ コマンドを実行すると、コンテナー内で実行されているものも含む、systemd のすべてのインスタンスを表示できます。
  • ネットワーク名前空間 は、ネットワークコントロラー、ネットワークに関連するシステムリソース、ファイアウォールおよびルーティングテーブルの分離を行います。これにより、コンテナーは別々の仮想ネットワークスタック、ループバックデバイス、およびプロセス空間を使用できます。また、仮想デバイスまたは物理デバイスをコンテナーに追加し、それらに独自の IP アドレスや iptables の完全ルールを割り当てることができます。ホスト上およびコンテナー内の両方で ip addr コマンドを実行すると、異なるネットワーク設定を表示できます。

注意

ほかにも ユーザー名前空間 と呼ばれる別のタイプの名前空間があります。ユーザー名前空間は PID 名前空間に似ており、これらを使用して、コンテナーに割り当てられたホストの UID の範囲を指定できます。そのため、プロセスにはコンテナー内の各種操作を実行するための完全 root 特権が割り当てられますが、コンテナー外での操作に対する権限はありません。互換性を維持する理由から、ユーザー名前空間は現行バージョンの Red Hat Enterprise Linux 7 では使用されていませんが、近い将来に有効にされる予定です。

コントロールグループ (cgroup)

カーネルは、システムリソース管理の目的でプロセスをグループ化するために cgroup を使用します。cgroup は、CPU 時間、システムメモリー、ネットワーク帯域幅、またはこれらのさまざまな組み合わせをユーザー定義のタスクグループに割り当てます。Red Hat Enterprise Linux 7 では、cgroup は systemd のスライス、スコープおよびサービスユニットを使用して管理されます。cgroup の詳細は、Red Hat Enterprise Linux 7 リソース管理ガイド を参照してください。

SELinux

SELinux は、SELinux ポリシーとラベルを適用することで、コンテナーのセキュアな分離を行います。また、sVirt テクノロジーを使用して、仮想デバイスと統合します。詳細は、Red Hat Enterprise Linux 7 SELinux ユーザーおよび管理者のガイド を参照してください。

管理インターフェース

1.3. SELinux によるコンテナーの保護

セキュリティー上の理由から、ホストシステムをコンテナーから分離したり、コンテナーを相互に分離したりする必要があります。コンテナーが使用するカーネル機能 (cgoup および名前空間など) はそれ自体である程度のセキュリティーを提供します。cgroup は単一コンテナーがシステムリソースを大量に使用できないようにすることで、一部のサービス拒否攻撃 (DoS) を防ぎます。名前空間により、コンテナー内に作成される /dev ディレクトリーは各コンテナーに対して非公開となるため、ホストの変更によって影響を受けることはありません。ただし、システム全体の名前空間が設定されたり、システム全体がコンテナー化される訳ではないため、悪意のあるプロセスがコンテナーから発生することを完全に防ぐことはできません。そのため、SELinux を使用して別の分離レベルを設定する必要があります。

SELinux (Security-Enhanced Linux) は、Linux カーネルにおける強制アクセス制御 (MAC) メカニズム、マルチレベルのセキュリティー (MLS)、およびマルチカテゴリーセキュリティー (MCS) の実装です。sVirt プロジェクトは SELinux を基盤として構築されており、Libvirt と統合して仮想マシンとコンテナーの MAC フレームワークを提供します。このアーキテクチャーは、コンテナー内の root プロセスがコンテナー外で実行されている他のプロセスを干渉するのを防ぐため、コンテナーの安全な分離を可能にします。Docker で作成されるコンテナーには、SELinux ポリシーで指定される SELinux コンテキストが自動的に割り当てられます。

デフォルトでは、libvirt ツールで作成されるコンテナーには virtd_lxc_t ラベルが割り当てられます (ps -eZ | grep virtd_lxc_t を実行)。コンテナー内のプロセスに静的または動的なラベルを設定することにより sVirt を適用することができます。

注意

SELinux がホストシステムで enforcing (強制) モードで実行されている場合であっても、コンテナー内では無効になっている場合があります。これは、ホストとコンテナー内の両方で getenforce コマンドを実行して確認することができます。これは、setenforce などの SElinux 対応のユーティリティーがコンテナー内で SELinux の動作を実行することを回避するために生じます。

SELinux がホストマシン上で無効にされているか、または permissive (許容) モードで実行されている場合、コンテナーは安全に分離されないことに注意してください。SELinux についての詳細は、Red Hat Enterprise Linux 7 SELinux ユーザーおよび管理者のガイド を参照してください。sVirt については、Red Hat Enterprise Linux 7 仮想化セキュリティーガイド で説明されています。

注意

現在、Btrfs (B-tree file system) 上に SELinux を有効にした状態でコンテナーを実行することはできません。したがって、推奨される方法として SELinux を有効にした状態で Docker を使用する場合は、/var/lib/docker を Brtfs に置かないようにしてください。Brtfs で Docker を実行する必要がある場合は、/lib/systemd/system/docker.service 設定ファイルから --selinux-enabled エントリーを削除して SElinux を無効にします。

1.4. コンテナーの使用事例

Red Hat Enterprise Linux 7 で Linux コンテナーを使用する方法として、2 つの一般的なシナリオを紹介します。まずは、ホストコンテナー をアプリケーションをサンドボックス化するためのツールとして使用できます。また、イメージベースのコンテナー の拡張機能を使用することもできます。

1.4.1. ホストコンテナー

Linux コンテナー機能を搭載した Red Hat Enterprise Linux 7 ホストオペレーティングシステムでは、軽量のアプリケーションサンドボックスとしてコンテナーを作成できます。起動するすべてのホストコンテナーは以下の点で同一です。各コンテナーはホストシステムと同じユーザー空間を実行するため、ホストコンテナー内で実行されるすべてのアプリケーションは Red Hat Enterprise Linux 7 のユーザー空間とランタイムをベースとします。このアプローチの利点は、yum update コマンドを使用して、セキュリティーエラータや他のアップデートをこれらのコンテナーに簡単に適用できる点にあります。

ホストコンテナーのアーキテクチャーを説明する図。

1.4.2. イメージベースのコンテナー

イメージベースのコンテナーでは、アプリケーションは個別のランタイムスタックでパッケージングされます。これにより、アプリケーションをホストオペレーティングシステムから切り離すことができます。また、異なるプラットフォーム用に開発されたアプリケーションの複数のインスタンスを実行することができます。これはコンテナーのランタイムとアプリケーションのランタイムがイメージ形式でデプロイされるために可能になります。たとえば、図にある Runtime A が Red Hat Enterprise Linux 6.5 であることも、Runtime B がバージョン 6.6 である可能性もあります。

イメージベースのコンテナーを説明する図。

イメージベースのコンテナーは、アプリケーションの複数のインスタンスとバージョンをホストし、オーバーヘッドを最小化し、柔軟性を高めることができます。このようなコンテナーはホスト固有の設定に関連付けられないため、移植できます。

Docker フォーマットは、Red Hat Enterprise Linux 7 にコピーオンライト (COW) を実装するための LVM スナップショットの高度な機能である device mapper のシンプロビジョニング 技術を使用しています。

Docker で使用されるイメージ層を説明する図。

上の図は、イメージベースのコンテナーの基本的なコンポーネントを示しています。

  • コンテナー (狭義) – アプリケーションが実行されるアクティブなコンポーネントです。各コンテナーは、必要な設定データを保持する イメージ をベースとしています。イメージからコンポーネントを起動すると、書き込み可能な層がこのイメージの上部に追加されます。コンテナーのコミット (docker commit コマンドを使用) を実行するたびに、変更を保存するためのイメージ層が新たに追加されます。
  • イメージ - コンテナー設定の静的なスナップショットです。イメージは変更されない読み取り専用層になります。すべての変更は最上位の書き込み可能な層で行われ、保存は新しいイメージを作成することで行われます。それぞれのイメージは、1 つまたは複数の親イメージによって異なります。
  • プラットフォームイメージ – 親を持たないイメージです。プラットフォームイメージは、コンテナー化されたアプリケーションの実行に必要なランタイム環境、パッケージおよびユーティリティーを定義します。通常、Docker の使用はプラットフォームイメージを取得するところから始まります。プラットフォームイメージは読み取り専用であるため、すべての変更はプラットフォームイメージの上部に重ねられるコピーイメージに反映されます。Red Hat は、Red Hat Enterprise 6 のほかにも Red Hat Enterprise Linux 7 のプラットフォームイメージを提供しています。

プラットフォームイメージの上部に重ねられるイメージは、コンテナー化されたアプリケーションのソフトウェア依存関係を含む アプリケーション層 を作成します。たとえば、重ねられたイメージは、コンテナー化されたアプリケーションが必要とするソフトウェアの依存関係を追加している可能性があります。

アプリケーション層に含まれるパッケージの数によって、コンテナーの全体サイズは非常に大きくなるか、または非常に小さくなる可能性があります。さらに、サードパーティーの独立ソフトウェアベンダーのソフトウェアなどの、イメージの層をさらに重ねることができます。ユーザーからは 1 つのコンテナーがあるように見えますが、運用上は、その背後に多くの層が存在する場合があります。

1.5. Linux コンテナーと KVM 仮想化の比較

KVM 仮想マシンは、独自のカーネルを必要とします。Linux コンテナーはホストオペレーティングシステムのカーネルを共有します。通常、同じハードウェアで、仮想マシンよりもはるかに多くの数のコンテナーを起動することができます。

Linux コンテナーと KVM 仮想化にはそれぞれの長所と短所があるため、どちらの技術がより一般的に選択されるかはその使用事例によって異なります。

KVM 仮想化:

  • KVM 仮想化は、Linux 以外のシステムも含め、様々な種類のフルオペレーティングシステムの起動を可能にします。仮想マシンのリソース集約型の性質 (コンテナーとの対比) は、ホストで実行できる仮想マシンの数が同じホストで実行できるコンテナーの数よりも少なくなることを示します。
  • 通常カーネルインスタンスを別々に実行することにより、分離が行われ、セキュリティー機能が提供されます。いずれかのカーネルが予期せずに終了しても、システム全体が無効になることはありません。
  • ゲスト仮想マシンはホストの変更の影響を受けません。そのため、ホストと仮想マシンで同じアプリケーションの異なるバージョンをそれぞれ実行することができます。さらに KVM はライブマイグレーションなどの数多くの便利な機能を提供します。これらの機能の詳細は、Red Hat Enterprise Linux 7 仮想化の導入および管理ガイド を参照してください。

Linux コンテナー:

  • Linux コンテナーは 1 つ以上のアプリケーションの分離をサポートするために設計されています。
  • システム全体の変更は各コンテナーで確認できます。たとえば、ホストマシンでアプリケーションをアップグレードすると、この変更はこのアプリケーションの各種インスタンスを実行するすべてのサンドボックスにも適用されます。
  • コンテナーは軽量であるため、多数のコンテナーをホストマシンで同時に実行することができます。理論上、コンテナーの最大数は 6000で、root のファイルシステムディレクトリーのバインドマウント数は 12,000 です。

1.6. その他のリソース

Linux コンテナーの一般的な原則およびアーキテクチャーについての詳細は、以下のリソースを参照してください。

インストールされているドキュメント

  • docker(1): docker コマンドの man ページ。

オンラインドキュメント