アーキテクチャの理解

Docker とは何か?

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

Docker はこれを実現するために、カーネルのコンテナ化機能(containerization;コンテナライゼーション)がもたらすワークフローと手法(ツール)を組みあわせます。それゆえ、アプリケーション管理とデプロイの手助けになるでしょう。

中心となるのは、あらゆるアプリケーションをコンテナ内で安全に分離(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 は ユニオン・ファイルシステム(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 は他のコンテナ形式、例えば BSD Jail や Solaris Zone との統合をサポートするかも知れません。

次のステップ

Docker インストール

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

Docker ユーザガイド

より深く学びましょう。

参考

Understand the architecture
https://docs.docker.com/engine/understanding-docker/