アーキテクチャの理解

Docker とは何ですか?

Docker はアプリケーションを開発(developing)・転送(shipping)・実行(running)するための、オープンなプラットフォームです。Docker はアプリケーションをより速く運ぶ(deliver)するために設計されました。Docker を使うことで、アプリケーションを基盤から分離し、アプリケーションを管理するようにインフラを扱えるようにします。Dockerはコードの転送をより速くし、テストを速くし、デプロイを速くし、コードの記述とコードの実行におけるサイクルを短くします。

Docker はこれを、軽量なコンテナ仮想化プラットフォームを使ったワークフローとツールの連携で実現します。これがアプリケーションの管理とデプロイの手助けになるでしょう。

中心となるのは、あらゆるアプリケーションをコンテナ内で安全に分離(isolated)して実行する手法を、Docker が提供することです。分離とセキュリティにより、ホスト上で擬似的に多くのコンテナを実行できます。コンテナは軽量な性質のため、実行のためにハイパーバイザーのような外部負荷を必要としません。つまり、ハードウェアに依存しません。

コンテナ仮装化技術を取りまくツールとプラットフォームは、様々な場所で役立つでしょう。

  • アプリケーション(と、必要なコンポーネントを)を Docker コンテナの中に入れる
  • 更なる開発やテストのために、これらのコンテナをチームに配布・転送する
  • プロダクション環境にアプリケーションをデプロイ

Docker を何に使うのですか?

アプリケーションの速いデリバリ

Docker は 開発のライフサイクルの手助けに最適です。Docker は開発者がローカルのコンテナで開発できるようにし、そこでアプリケーションとサービスを入れられます。そして、継続的インテグレーションや、デプロイのワークフローと統合できます。

例えば、開発者がローカルでコードを書き、Docker 上の開発スタックを同僚と共有します。準備が整えば、コードとスタックを開発環境からテスト環境に移動し、開発環境のスタックをテスト環境に移動し、必要なテストを実行します。テスト環境のあとで、Docker イメージをプロダクションに送信し、コードをデプロイできるのです。

デプロイとスケールをより簡単に

Docker のコンテナを基盤としたプラットフォームは、ワークロードの高い可用性をもたらします。Docker コンテナは開発者のローカルホスト上で実行できるだけでなく、データセンタの物理環境や仮想マシン上、クラウド上でも実行できます。

また、Docker のポータビリティと軽量な性質によって、動的なワークロードの管理を簡単にします。Docker を使えば、アプリケーションやサービスのスケールアップやティアダウンを簡単に行います。Docker のスピードが意味するのは、スケールをほぼリアルタイムに近く行えることです。

高密度と更なるワークロードの実行を実現

Docker は軽量で速いです。これはハイパーバイザーをベースとした仮想化マシンより費用対効果を高くします。これが特に使いやすいのは高密度の環境でしょう。例えば、自分たちのクラウドや PaaS(プラットフォーム・アズ・ア・サービス)においてです。しかし、自分たちが持っているリソースからより多くを得たい中小規模のデプロイでも便利です。

Docker の主な構成要素は?

Docker は2つの主要コンポーネントを持ちます。

  • Docker:オープンソースのコンテナ仮想化プラットフォーム
  • Docker Hub Docker コンテナを共有・管理する私たちの SaaS プラットフォームです。

注釈

Docker はオープンソースの Apache 2.0 ライセンスの元で提供されています。

Docker のアーキテクチャとは?

Docker はクライアント・サーバ型のアーキテクチャです。Docker クライアント は Docker デーモン と通信することで、Docker コンテナの構築・実行・配布といった力仕事をします。Docker クライアントとデーモンのいずれも同じシステム上でも実行できます。あるいは、Docker クライアントはリモートの Docker デーモンに接続することも可能です。Docker クライアントとデーモンは、お互いにソケットか RESTful API を通して通信します。

Docker アーキテクチャ図

Docker デーモン

先ほどの図で見たように、Docker デーモンはホストマシン上で動きます。ユーザは直接デーモンと通信せず、Docker クライアントを通して行います。

Docker クライアント

Docker クライアントは docker バイナリの形式です。これは主にユーザが Docker との通信に使います。ユーザからのコマンドを受け付けると、その先にある Docker デーモンと通信し返します。

Docker の内部

Docker 内部の理解には、3つのコンポーネントを知る必要があります。

  • Docker イメージ(image)
  • Docker レジストリ(registry)
  • Docker コンテナ(container)

Docker イメージ

Docker イメージとは、読み込み専用(read-only)なテンプレートです。例えば、イメージには Ubuntu オペレーティング・システムに Apache とウェブ・アプリケーションが含まれるでしょう。イメージは Docker コンテナの作成時に使われます。Docker は新しいイメージの構築や、既存イメージの更新を行います。あるいは、他の人が既に作成した Docker イメージをダウンロードすることも可能です。Docker イメージとは Docker における 構築(build) コンポーネントです。

Docker レジストリ

Docker レジストリはイメージを保持します。パブリックもしくはプライベートに保管されているイメージのアップロードやダウンロードを行えます。パブリックな Docker レジストリとして Docker Hub が提供されています。たくさんの利用可能なイメージが提供されています。イメージを自分自身で作れるだけでなく、他人が作成したイメージも利用できます。Docker レジストりとは Docker における 配布(distribution) コンポーネントです。

Docker コンテナ

Docker コンテナはディレクトリと似ています。Docker コンテナにはアプリケーションの実行に必要な全てが含まれています。各コンテナは Docker イメージによって作られます。Docker コンテナは実行・開始・停止・移動・削除できます。各コンテナは分離されており、安全なアプリケーションのプラットフォームです。Docker コンテナとは Docker における 実行(run) コンポーネントです。

では Docker はどのように動きますか?

これまで次のことを学びました。

  1. アプリケーションを保持する Docker イメージを構築できます。
  2. これら Docker イメージでアプリケーションを実行する Docker コンテナを作成できます。
  3. これら Docker イメージを Docker Hub や自分のレジストリで共有できます。

それでは、それぞれの要素が一緒になり、Docker をどのように動かしているかを見ていきましょう。

Docker イメージの役割は?

これまで、Docker イメージとは読み込み専用のテンプレートであり、これを使って Docker コンテナを起動するのが分かりました。各イメージはレイヤの積み重ねで構成されます。Docker は union ファイルシステム(UnionFS) を使い、これらのレイヤを単一のイメージに連結します。ユニオン・ファイルシステムは、ブランチとしても知られています。これは透過的な重ね合わせ(overlaid)と、互いに密着した(coherent)ファイルシステムを形成します。

Docker が軽量な理由の1つが、これらのレイヤによるものです。Docker イメージに変更を加えたとしましょう。例えば、アプリケーションを新しいバージョンに更新したとします。そうすると、新しいレイヤが構築されます。つまり、仮想マシン上で何か作業をした結果、イメージが置き換えられたり完全に再構築されるというよりは、単純にレイヤーが追加されるか更新されるだけなのです。この新しいイメージの配布に関する心配は不要です。単に更新されただけであり、Docker イメージを速く簡単に配布できるようにします。

各イメージはベース・イメージ(base image)から作られます。例えば、 ubuntu は ベース Ubuntu イメージですし、 fedora は ベース Fedora イメージです。また、自分自身で新しいイメージの元も作れます。たとえば、自分でベース Apache イメージを作れば、これをつかって自分用のウェブ・アプリケーション・イメージのベース(基礎)として使えます。

注釈

Docker は常にこれらのベース・イメージを Docker Hub から取得します。

Docker イメージは、これらのベース・イメージから構築できるようにするために、簡単な手順を記述した集まりを 命令 (instructions) と呼びます。それぞれの命令はイメージ上に新しいレイヤを作成します。命令は次のような動作をします。

  • コマンドの実行
  • ファイルやディレクトリの追加
  • 環境変数の作成
  • 対象イメージを使ってコンテナを起動するとき、どのプロセスを実行するか

これらの命令は Dockerfile と呼ばれるファイルに保管します。Docker にイメージの構築を依頼すると、Docker はこの Dockerfile を読み込み、命令を実行し、最終的なイメージを返します。

Docker レジストリの役割は?

Docker レジストリは Docker イメージを保管します。Docker イメージを構築後、 Docker Hub のような公開レジストリに 送信(push) したり、あるいはファイアウォール背後にある自分のレジストリで動くレジストリに送信したりできます。

Docker クライアントを使い、公開済みのイメージを検索出来ます。そして、自分の Docker ホスト上にイメージをダウンロード(pull)し、これを使ってコンテナを構築できます。

Docker Hub はイメージを保管するため、パブリックとプライベートなストレージを提供しています。パブリック・ストレージは誰でも検索可能でダウンロードできるものです。プライベート・ストレージは検索結果から除外され、自分もしくは許可されたユーザだけがイメージを取得し、コンテナを構築できるようにします。 ストレージのプランはこちらでサインアップ できます。

コンテナの役割は?

コンテナにはオペレーティング・システム、ユーザが追加したファイル、メタデータが含まれます。これまで見てきたように、各コンテナはイメージから構築されます。そのイメージは、 Docker に対してどのコンテナの中に何があるか、コンテナ起動時に何のプロセスを実行するか、その他のデータに関する設定確認をします。Docker イメージは読み込み専用です。Docker がイメージからコンテナを実行する時、読み書き可能なレイヤを既存イメージ上に追加し(先ほど見たとおり、ユニオン・ファイルシステムを使います)、アプリケーションを実行できるようにします。

コンテナを実行すると何が起きますか?

docker バイナリか API を経由して、Docker クライアントは Docker デーモンにコンテナ実行を伝えます。

$ docker run -i -t ubuntu /bin/bash

このコマンドを分解してみましょう。Docker クライアントは docker バイナリを使って実行され、 run オプションは新しいコンテナの起動を伝えます。Docker クライアントが Docker デーモンに対してコンテナを起動するために最低限必要なのは、

  • コンテナが何の Docker イメージで構築されるのか。ここでは ubuntu というベース Ubuntu イメージを使い、
  • コンテナを起動したら、その中で何のコマンドを実行したいのか、ここでは /bin/bash を指定し、新しいコンテナの中で Bash シェルを開始します。

それでは、このコマンドの水面下では何が起こっているのでしょうか。

Docker の処理内容を、順番に見ていきます。

  • ubuntu イメージの取得 :Docker は ubuntu イメージの存在を確認し、もしローカルホスト上に存在しなければ、 Docker Hub からダウンロードします。イメージが既にあれば、Docker はこれを新しいコンテナのために使います。
  • 新しいコンテナを作成 :Docker がイメージを入手した後、それを使ってコンテナを作成します。
  • ファイルシステムを割り当て、読み書き可能なレイヤをマウント :コンテナを新しいファイルシステム上に作成し、読み込み可能な(イメージの)レイヤをイメージに追加します。
  • ネットワークとブリッジインターフェースの割り当て :Docker コンテナがローカルホストと通信できるようにするためのネットワーク・インターフェースを作成します。
  • IP アドレスを設定 :プールされている範囲内で利用可能な IP アドレスを探して(コンテナに)追加します。
  • 指定したプロセスを実行 :アプリケーションを実行し、そして、
  • アプリケーションの出力を収集・表示 :コンテナに接続し、アプリケーションを実行したことによる標準入力・標準出力・エラーを記録・表示します。

これでコンテナが動きました! 以降は自分でコンテナを管理し、アプリケーションと双方向にやりとりし、利用し終わったらコンテナを停止・削除できます。

基礎技術

Docker は Go 言語で書かれており、これまで見てきた機能は、複数のカーネル機能を利用しています。

名前空間(namespaces)

Docker は名前空間(ネームスペース)と呼ばれる技術を利用し、コンテナ (container) と呼ぶワークスペース(作業空間)の分離をもたらします。Docker はコンテナ毎に 名前空間 の集まりを作成します。

これはレイヤの分離をもたらします。つまり、コンテナを実行すると、それぞれが自身の名前空間を持ち、そこから外にはアクセスできないように見えます。

Docker が使う Linux 上の名前空間は、次の通りです。

  • pid 名前区間 :プロセスの分離に使います(PID:プロセス ID)
  • net 名前区間 :ネットワーク・インターフェースの管理に使います(NET:ネットワーキング)
  • ipc 名前区間 :IPC リソースに対するアクセス管理に使います(IPC:InterProcess Communication、内部プロセスの通信)
  • mnt 名前区間 :マウント・ポイントの管理に使います(MNT:マウント)
  • uts 名前区間 :カーネルとバージョン認識の隔離に使います(UTS:Unix Timesharing System、Unix タイムシェアリング・システム)

コントロール・グループ( Control groups)

Linux 上の Docker は、 cgroup やコントロール・グループと呼ばれる技術を使います。アプリケーション実行の鍵となるのは、自身が必要なリソースのみを分離することです。これがあるので、ホスト上で複数の利用者がいても、コンテナを使えるようにします。また、コントロール・グループにより、Docker はコンテナに対して利用可能なハードウェア・リソースを共有し、必要があればコンテナが必要なリソース上限を設定できます。例えば、特定のコンテナに対する利用可能なメモリに制限を加えます。

ユニオン・ファイル・システム

ユニオン・ファイル・システム、あるいは UnionFS はファイルシステムです。これは作成されたレイヤーを操作することで、非常に軽量かつ高速です。Docker はコンテナ毎にブロックを構築するためにユニオン・ファイル・システムを使います。Docker は AUFS、btrfs、vfs、DeviceMapper を含む複数のユニオン・ファイル・システム派生を利用できます。

コンテナの形式(フォーマット)

Docker はこれらのコンポーネントを連結し、包み込んでいます。これをコンテナ形式(フォーマット)と読んでいます。デフォルトのコンテナ形式は libcontainer と呼ばれています。また、Docker は LXC を使う伝統的な Linux コンテナもサポートしています。いずれ、Docker は他のコンテナ形式、例えば BSD Jail や Solaris Zone との統合をサポートするかもしれません。

次のステップ

Docker インストール

インストールの章 をご覧ください。

Docker ユーザ・ガイド

さらに深く学びましょう