Docker 0.6の(多くの)機能の1つは、コンテナの新しい「特権」モードです。 これにより、カーネル機能とデバイスアクセスに関して、ホストマシンの(ほぼ)すべての機能を備えたいくつかのコンテナを実行できます。
「特権」モードの(多くの)可能性の中で、Docker自体の中でDockerを実行できるようになりました。 まず、それを実現する方法を見ていきます。次に、内部で何が関係しているかを説明し、最後に、DockerでDockerよりもさらに強力なものを示します。
Docker-in-Docker の動作を参照してください
Docker 0.6をお持ちの場合は、次のことを行う必要があります。
docker run -privileged -t -i jpetazzo/dind
これにより、特別なDockerイメージがダウンロードされ(なぜ特別なのかは後でわかります)、新しい特権モードで実行されます。 デフォルトでは、ローカル docker
デーモンを実行し、シェルにドロップします。 そのシェルで、古典的な「Docker 101」コマンドを試してみましょう。
docker run -t -i ubuntu bash
Docker を実行しているコンテナーから最も内側のコンテナーに移行するときに、コンテナー ID がどのように変化するかに注意してください。
私の dind
イメージで何が特別なのですか?
ほとんど何もありません! これは、通常の Dockerfile で構築されています。 そのドッカーファイルに何が入っているか見てみましょう。
まず、いくつかのパッケージをインストールします: lxc
および iptables
(Dockerがそれらを必要とするため)、および(Dockerインデックスおよび ca-certificates
レジストリと通信するときに、DockerはSSL証明書を検証する必要があるため)。
Dockerfile は、ボリューム /var/lib/docker
である必要があることも示しています。 コンテナのファイルシステムは複数の ブランチで構成されるAUFSマウントポイントであるため、これは重要です。そして、それらのブランチは「通常の」ファイルシステムでなければなりません(つまり、 AUFS マウントポイントではありません)。 つまり、 /var/lib/docker
Docker がコンテナーを格納する場所は、AUFS ファイルシステムにすることはできません。 そのため、このパスを ボリュームにする必要があることを Docker に指示します。 ボリュームには多くの目的がありますが、このシナリオでは、ホストマシンの「通常の」ファイルシステムへのパススルーとして使用します。 入れ子になった Docker のディレクトリは /var/lib/docker
、ホスト システムのどこかに /var/lib/docker/volumes
存在します。
そしてもちろん、DockerfileはDockerバイナリと ヘルパースクリプトをイメージに挿入します。 ヘルパースクリプトは3つのことを扱います。
- これにより、cgroup 擬似ファイルシステムが適切にマウントされます (Docker (より正確には
lxc-start
) がそれらを必要とするためです。 - 親プロセスからリークされた可能性のある無関係なファイル記述子を閉じます。 これは厳密には必要ではありませんが、そうしないと奇妙な副作用に気付くかもしれません。だから私はあなたのためにそれを世話しました。
- コマンドラインオプションで環境変数
-e PORT=...
を指定したPORT
かどうかをチェックします。その場合、Docker デーモンはフォアグラウンドで起動し、指定された TCP ポートで API 要求をリッスンします。 変数を指定しPORT
なかった場合は、バックグラウンドで Docker が起動し、対話型シェルが表示されます。
次のセクションでは、この PORT
環境変数 が非常に 役立つと思う理由を説明します。
サービスとしてのドッカー
Docker-in-Docker を試してみたいだけの場合は、上記のようにイメージを対話形式で開始するだけです。 ここで、サービスとしての Docker を提供するとしましょう。ここではサービスとしてのコンテナについて話しているのではなく、Dockerインスタンス全体について話しているのです。 まあ、誰かが自分のプライベートDockerインスタンスを望むたびに、これを実行するだけです:
docker run -privileged -d -p 1234 -e PORT=1234 jpetazzo/dind
docker inspect
次に、を使用して、そのコンテナーに割り当てられたパブリック ポートを取得し、ユーザーに提供します。彼らは、Dockerクライアントをあなたが与えたIPアドレスとポートにポイントすることで、この「プライベートDocker」上にコンテナを作成できます。 (同様の例については、 サービスとしての Memcached を参照してください)。
ただし、プライベートDockerインスタンスは特権モードで実行されるため、ホストに簡単にエスカレートできるため、おそらくこれは望ましくありません。 本当にこのようなものを実行して公開したい場合は、LXCテンプレートファイルを微調整して、Dockerインスタンスで使用できる機能とデバイスを制限する必要があります。 将来的には、Docker ではきめ細かなアクセス許可管理が可能になります。しかし今のところ、「ロックダウン」と「特権」を切り替える機能は素晴らしい第一歩だと思います。
Docker-in-Docker-in-Docker-in...
Docker-in-Docker-in-Dockerを実行できますか?はい。 特権コンテナー内にいる場合は、いつでももう 1 つのレベルをネストできます。
docker run -t -i -privileged jpetazzo/dind
そして、結果のコンテナで、プロセス、 アドリブを繰り返すことができます。
また、ネストされたDockerコンテナを終了すると、これが起こります(ルートプロンプトに注意してください)。
root@975423921ac5:/# 終了
root@6b2ae8bf2f10:/# 終了
root@419a67dfdf27:/# 終了
root@bc9f450caf22:/# 終了
jpetazzo@tarrasque:~/Work/DOTCLOUD/dind$
その時点で、回転するコマを回転させながら、スピーカーでハンス・ジマーの 夢が崩壊 しているのを😀爆破する必要があります
それは動作しません!
さまざまな環境でDocker-in-Dockerをテストしているときに、2つの考えられる問題が見つかりました。
LXC ツールは、デバイスコントロールグループが独自の階層にない場合、ネストされたコンテナを起動できないようです。 の内容 /proc/1/cgroup
を確認してください:それ自体でラインに立っている場合 devices
は、問題ありません。 別のコントロールグループが同じ行にある場合、Docker-in-Docker は機能しません。 ラッパー スクリプトはこの状況を検出し、警告を発行します。 この問題を回避するには、実行中のすべてのコンテナーを停止し、すべてのコントロール グループをアンマウントして、それぞれ独自の階層に 1 つずつ再マウントする必要があります。
また、AppArmor を使用する場合は、入れ子になったコンテナーをサポートするための特別なポリシーが必要です。 Docker-in-Docker が機能しない場合は、カーネル ログ ( と dmesg
) を確認し、AppArmor に関連するメッセージが表示された場合は、次のように制限のないモードで Docker を起動できます。
docker run -privileged -lxc-conf="aa_profile=unconfined" -t -i dind
私をあなたのリポジトリに連れて行ってください
Dockerfile、ラッパー、およびいくつかの追加のドキュメントは、私のgithubリポジトリで入手できます。 https://github.com/jpetazzo/dind。