Kubernetes

歴史

Kubernetes（κυβερνήτης、koo-ber-nay'-tace、ギリシャ語で航海長またはパイロットを意味する）は、当初Joe Beda、Brendan Burns、Craig McLuckieの3人によって開発が始まり、すぐにBrian GrantやTim Hockinなど、他のGoogleのエンジニアも参加するようになり、2014年中ごろにGoogleから初めて発表された。Kubernetesの開発や設計はGoogleのBorgシステムから強い影響を受けており、Borgのトップコントリビュータの多くが開発に参加している。Google内でのKubernetesのもともとのコードネームはProject Sevenであり、スター・トレックのキャラクターで、優しいBorgである、セブン・オブ・ナインの名前に由来する。Kubernetesのロゴの輪にある7つのスポークは、このコードネームに由来している。
Kubernetes v1.0は2015年7月21日にリリースされた。Kubernetes v1.0のリリースと同時に、GoogleはLinux Foundationと共同でCloud Native Computing Foundation（CNCF）を設立し、Kubernetesを種となる技術として提供した。2018年3月6日には、KubernetesプロジェクトはGitHubにおけるコミット数で第9位に到達し、コントリビューター数とissue数では、Linuxカーネルプロジェクトに次いで第2位となった。

Kubernetesにおけるオブジェクト

Kubernetesは、いくつかのビルディングブロック（"primitives"）を定義している。このビルディングブロックのおかげで、アプリケーションのデプロイ、メンテナンス、スケールのメカニズムを、CPU、メモリ、あるいはカスタムの指標に基づいて正しく提供することが可能になっている。Kubernetesは、さまざまなワークロードに対応できるよう、疎結合かつ拡張可能なシステムとなっている。この拡張性の大部分は、Kubernete上で動作する拡張機能やコンテナだけでなく内部コンポーネントによっても使用されているKubernetes APIにより提供されている。Kubernetesでは、コンピューティングとストレージのリソースを制御するため、リソースを以下のような「オブジェクト」として定義している。

ポッド（pod）

Kubernetesでは、スケジューリングの基本となる単位は、ポッドである。コンテナ化されたコンポーネントをポッドとしてグループ化することで、1つ高いレベルの抽象化を与えている。1つのポッドは1つ以上のコンテナから構成され、同じポッドのコンテナは同じホストマシンにデプロイされるため、リソースの共有が保証される。
Kubernetes内の各ポッドには、クラスタ内でユニークなPod IPアドレスが割り当てられる。これにより、アプリケーションはポートの衝突の危険を考える必要がない。ポッド内では、すべてのコンテナがlocalhost上で互いに参照できる。しかし、あるポッド内のコンテナが、他のポッド内の他のコンテナを直接アドレスで参照する方法は存在せず、そのためには、必ずPod IPアドレスを利用しなければならない。しかし、アプリケーションの開発者はPod IP アドレスを決して使用するべきではない。なぜなら、他のポッドの機能にアクセスあるいは実行する時に利用するPod IPアドレスは一時的なものであり、参照しようとしている特定のポッドが再起動したりすると、そのPod IPアドレスは別のポッドに割り当てられてしまう可能性があるからである。その代りに、開発者は、特定のPod IPアドレスを持つ対象ポッドへのアクセスが可能な、サービスに対してアクセスしなければならない。
1つのポッドは、ローカルディスクのディレクトリあるいはネットワークディスクとして、1つのボリュームを定義し、ポッド内のコンテナに対して公開することができる。ポッドの管理は、Kubernetes APIで手動で行ったり、コントローラに管理を委譲したりすることができる。また、定義したボリュームは、ConfigMaps（設定の内容を、コンテナからファイルシステム上で参照可能にする機能）やSecrets（リモートのリソースに安全にアクセスするために必要な証明書を、認証されたコンテナのみでファイルシステム上で参照可能にする機能）など、Kubernetesのいくつかの機能の基礎となっている。

サービス（service）

Kubernetesサービスはいくつかのポッドの集まりであり、1ティアまたはマルチティアのアプリケーションと協調して動作する。サービスを構成する複数のポッドは、ラベルセレクターを用いて定義される。Kubernetesはサービスディスカバリーの手段として、環境変数を用いるモードと、Kubernetes DNSを用いるモードの2種類の方法を提供している。サービスディスカバリは、静的なIPアドレスとDNS名を各サービスに束縛させ、そのIPアドレスのネットワークコネクションに対して、セレクターにマッチしたポッド間でラウンドロビン方式でのロードバランシングを行う（セレクターを用いるのは、障害によりポッドがあるマシンから別のマシンに移動したとしても動作するようにするためである）。デフォルトでは、サービスはクラスタの内部にのみ公開されている（たとえば、バックエンド用のポッドは、ロードバランシングされた複数のフロントエンド用ポッドからのリクエストに対して、1つのサービスとしてグループ化されているかもしれない）が、サービスはクラスターの外部に公開することもできる（たとえば、外部のクライアントがフロントエンド用ポッドにアクセスする場合など）。

ボリューム（volume）

Kubernetesコンテナ内のファイルシステムは、デフォルトでは一時ストレージを提供する。つまり、コンテナを再起動すると内部のデータが削除されてしまうため、重要なアプリケーションで利用するには制限がある。Kebernetesボリュームは永続的なストレージを提供するため、コンテナが再起動しても、ポッドが生存している間はデータが削除されない。このストレージは、同じポッド内のコンテナ同士で共有ディスク空間としても利用可能である。ボリュームは、ポッドの設定ファイル上で定義したコンテナ内の特定のマウントポイントとしてマウントされ、それ以外のボリュームにマウントしたり、他のボリュームとリンクすることができないようになっている。ただし、同じボリュームでも、異なるコンテナであれば、ファイルシステムツリー上の別の場所にマウントすることは可能である。

ネームスペース（namespace）

Kubernetesは、リソースのパーティショニング機能を提供し、ネームスペース（名前空間）と呼ばれる重複のない部分に分割する。多数のチームやプロジェクトに渡る多数のユーザーの利用を想定している。これにより、開発、テスト、本番などの環境さえ完全に分離することができる。

Kubernetesのオブジェクトの管理

Kubernetesは、オブジェクトの管理、選択、操作を可能にするメカニズムを提供している。

ラベルとセレクター（label and selector）

Kubernetesは、ポッドやノードなどのシステム上のあらゆるAPIオブジェクトに対して、クライアント（ユーザーや内部のコンポーネント）が「ラベル」と呼ばれるキーバリューペアを付加できるようにしている。それに対応して、「ラベルセレクター」はラベルに対してクエリーを実行し、一致するオブジェクトに解決する。あるサービスを定義すると、サービスルーターやロードバランサーがトラフィックをルーティングするポッドインスタンスを選択するために使用する、ラベルセレクターを定義できるようになる。したがって、単にポッドのラベルやサービスのラベルセレクターを変更するだけで、どのポッドにトラフィックを送るかを制御することが可能になる。この方法により、ブルー・グリーンデプロイやABテストなど、さまざまなデプロイパタンを利用できる。このようなサービスが使用するリソースを動的にコントロールする機能のおかげで、インフラ内での疎結合が実現されている。

たとえば、あるアプリケーションのpodsがシステムtier（たとえば、front-end、back-endなどの値を持つ）とrelease_track（canaryやproductionなど）というラベルを持つとき、back-endかつcanaryであるノード全てを指定するには、以下のようなラベルセレクターが使える。
tier=back-end AND release_track=canary

フィールドセレクター（field selector）

ラベルと同様に、フィールドセレクターもKubernetesの任意のリソースを選択可能にするものである。ラベルとは異なり、選択は、ユーザーが定義したカテゴリではなく、リソースに予め付与される属性の値に基づいて行われる。metadata.nameやmetadata.namespaceは、すべてのKubernetesオブジェクトに存在するフィールドセレクターである。使用される他のセレクターは、オブジェクトやリソースのタイプによって異なる。

アーキテクチャ

Kubernetesは、マスター・スレーブアーキテクチャで構成される。Kubernetesのコンポーネントは、各ノードを管理するノード（node）と、コントロールプレーンであるマスター（master）の2つに分けられる。
マスターがクラスターの管理を行い、コンテナ（実際にはPodと呼ばれる単位で管理される）は各ノード上にデプロイされる。マスターやノードを構成するコンポーネントは下記のようになっている。

Kubernetesコントロールプレーン（マスター）

Kubernetesのマスターは、クラスターのメインとなるコントロールユニットである。ワークロードとシステム全体に渡る直接的な通信を管理する。Kubernetesコントロールプレーンは複数のさまざまなコンポーネントから構成されている。各コンポーネントは独立したプロセスとして実行され、1つのマスターノードでも、高可用クラスタをサポートするために複数のマスターノードで実行することも可能である。各種コンポーネントには、次のようなものがある。

etcd

etcdは、CoreOSにより開発された、永続的な軽量な分散キーバリューストアである。クラスタの設定データを確実に保存し、任意の時点でのクラスタ全体の状態を保存する。Apache ZooKeeperと同様、etcdは、ネットワーク部分のイベントにおいて、可用性（Availability）よりも一貫性（Consistency）を重視するシステムである（CAP定理を参照）。この一貫性は、正しいスケジューリングとサービスのオペレーションにとって非常に重要である。Kubernetes APIサーバーは、etcdのwatch APIを用いて、クラスターや重要な設定の変更を監視したり、クラスタの状態にデプロイ時に宣言された状態との齟齬が生じた時、宣言された状態になるように修復する。一例として、もしデプロイ時に特定のポッドのインスタンス3つが実行している必要があると指定すると、この情報はetcdに保存される。もし、実行中のインスタンスが2つしか見つからなかった場合、この違いがetcdのデータと比較され、Kubernetesはこの情報を用いて、追加のポッドインスタンスを作成するスケジュールを立てる。

API server

API serverはキーとなるコンポーネントであり、HTTP上のJSONを利用したKubernetes APIのサーバーである。Kubernetesに対する内部および外部向けのインターフェイスの両方を提供する。API serverはRESTリクエストの処理と検証を行い、etcdに保存されたAPIオブジェクトの状態を更新する。したがって、Kubernetes APIを利用することで、クライアントはワーカーノード全体に渡るワークロードとコンテナの設定を行うことが可能になる。

Scheduler（スケジューラー）

schedulerはプラガブルなコンポーネントで、未スケジュールのポッド（schedulerが管理する基本エンティティ）をどのノードで実行するのかを、リソースの可用性に基づいて選択する役割を担う。schedulerは各ノード上でのリソースの使用量をトラッキングし、ワークロードが利用可能なリソースを超過しないことを保証する。この目的を達成するためには、schedulerは、リソースの要求量、リソースの可用性、その他のユーザーが提供する制約、そして、サービス品質、親和性・非親和性の要件、データの局所性などのポリシーのディレクティブを知っている必要がある。本質的には、schedulerの役割は、リソースの「供給」をワークロードの「需要」に合わせることであると言える。

Controller manager（コントローラーマネージャー）

controllerは、実際のクラスタの状態を期待されるクラスタの状態と一致するように駆動するループである。これは、ポッド群を管理することで行われる。コントローラの1つには、replication controller（レプリケーションコントローラー）がある。このコントローラーは、ポッドのレプリカ数をクラスタ全体で指定した数になるようにレプリケーションとスケーリングを操作する。また、管理しているノードに障害が発生した場合には、代替のポッドの生成の操作も実行する。Kubernetesシステムの一部である他のコントローラの1つには、DaemonSet Controller（デーモンセットコントローラー）がある。これは、1台のマシンでポッドが必ず1つのみ実行されるようにする。Job Controller（ジョブコントローラー）は、たとえばバッチジョブの一部として、ポッドをジョブが完了するまで実行させる。コントローラーが管理するポッド群は、コントローラーの定義の一部である、ラベルセレクターを利用して特定する。
Controller manager（コントローラーマネージャー）は、DaemonSet ControllerやReplication ControllerなどのKubernetesのコアとなるコントローラーを実行するプロセスである。コントローラーは、APIサーバーと通信することで、管理対象のリソース（ポッド、サービスエンドポイントなど）の作成、更新、削除を実行する。

Kubernetesノード（スレーブ）

ノード（ワーカーまたはMinionとも呼ばれる）は、コンテナ（ワークロード）が実際にデプロイされるマシンである。クラスタ内のすべてのノードはDockerなどのコンテナランタイムを実行する必要があり、コンテナのネットワーク設定のためにマスターと通信を行う、以下に説明するようなコンポーネントも実行する。

Kubelet

Kubeletは、各ノードの実行状態に対する責務を担い、ノード内のすべてのコンテナが健全な状態であることを保証する。コントロールプレーンの指示を受けて、アプリケーションコンテナ群をスタート、ストップ、管理し、ポッドとして組織する。
Kubeletはポッドの状態を監視し、指定された状態から逸脱していることを検出すると、ポッドを同じノードに自動的に再デプロイする。ノードの状態は数秒ごとにハートビートメッセージとしてマスターに送信される。マスターがノードの障害を検知するとすぐに、Replication Controllerがこの状態を確認し、他の健全な状態にあるノードでポッドを新たに起動する。

Container（コンテナ）

containerはポッドの中に置かれる。containerは最も低レベルなマイクロサービスであり、実行中のアプリケーション、ライブラリ、およびその依存関係が格納される。containerは外部IPアドレスを割り当てて世界に対して公開することもできる。Kubernetesは最初のバージョン以来Dockerコンテナをサポートしており、2016年7月には、rktコンテナエンジンが追加された。

Kube-proxy

Kube-proxyはネットワークプロキシおよびロードバランサーの実装であり、サービスの抽象化やその他のネットワーク操作をサポートする。トラフィックのルーティングを、受信したリクエストのIPアドレスとポート番号を元に、適切なコンテナにルーティングする責務を担う。

cAdvisor

cAdvisorは、各ノード上にあるコンテナのCPU、メモリ、ファイル、ネットワーク使用量といった、リソースの使用量と性能の指標を監視・収集するエージェントである。

マイクロサービス

Kubernetesはマイクロサービスをベースとしたアプリケーションの実装方法としてよく使用されている。というのも、Kubernetesとその周辺ツールによるエコシステムは、マイクロサービスアーキテクチャが重視している重要な概念に必要な機能のすべてを提供しているためである。これに関して詳しくは、マイクロサービスの項目を参照。