アーキテクチャの理解

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 は 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 は他のコンテナ形式、例えば BSD Jail や Solaris Zone との統合をサポートするかもしれません。

次のステップ

Docker インストール

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

Docker ユーザ・ガイド

さらに深く学びましょう。

参考

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