昨年末、 kubernetes-sigs のリポジトリを眺めていたときに、 Cluster API というプロジェクトを見つけました。

ドキュメントを読んでいると、なんだか夢がありそうで面白そうだったので、ここしばらくのあいだ実際に動かして検証したり OpenStack 向けのプロバイダに Pull Request を送ったりしていました。

この記事では Cluster API について調べたことをまとめてみたいと思います。 もし内容に誤りなどがあれば、ぜひご指摘いただけるとうれしいです。 また、開発が進んでいるプロジェクトのため、ここでまとめた内容は今後変わることもあるかもしれません。

Cluster API とは

kubernetes-sigs/cluster-api
Home for the Cluster Management API work, a subproject of sig-cluster-lifecycle - kubernetes-sigs/cluster-api

Cluster API (Cluster Management API) は、宣言的な (Kubernetes-style) API を使って、 Kubernetes クラスタを管理するためのものプロジェクトです。 Kubernetes のインストールやアップグレード、ノードの追加や削除など、クラスタのライフサイクルに関わる共通の操作を、異なるクラウドの環境上で行えるようになります。 SIG Cluster Lifecycle のプロジェクトとして開発が行われています。

Kubernetes にコンテナ化したアプリケーションをデプロイするときは、 Deployment や Pod などの定義を YAML マニフェストとして書いて、それを適用する操作を行うかと思います。 Cluster API を使うと、このコンテナなデプロイと同じようなやり方で、 Kubernetes クラスタをつくったり、ノードの数を変更したりできるようになります。 ほしいクラスタのあるべき状態 (k8s のバージョンやノードの台数など) を YAML で書いて kubectl apply -f cluster.yaml したら、 Kubernetes がそのクラスタを実際に作ってくれるというわけです。 すごい!

この “クラスタによるクラスタへの操作” は Custom Resource と Custom Controller によって実現されています。 いわゆる Operator パターンと呼ばれるものと同様の仕組みです。

クラスタやノードの設定は、 Custom Resource Definitions (CRDs) を使って Kubernetes 上のリソースとして扱えるようになっています。 このリソースの内容に基づいて、呼び出された Custom Controller のアクションがクラウド環境の API を呼び出し、VM を作成したり削除したりといった操作を行う仕組みです。

プロバイダの実装

Cluster API の関連プロジェクトとして、 Kubernetes クラスタをデプロイする先の各環境ごとに cluster-api-provider というものが用意されています。 このプロバイダによって、 AWS や OpenStack などのそれぞれのクラウドごとに異なる API を呼び出し、環境固有の処理が行われます。

クラスタ操作の主要なフロー自体は Cluster API 本体が提供し、プロバイダ側のコードが実際のインフラへの変更を加えます。 Cluster API の立ち位置としては、クラスタサイクル操作の “フレームワーク” に近いものと考えられそうです。

現在用意されている主なプロバイダの一覧は Cluster API 本家のリポジトリに載っています。 プロバイダの実装についての詳細は、別の記事にてまとめてみようと思います。

どんな Resource が用意されているのか?

Cluster Resource

Cluster リソースは Kubernetes クラスタを spec として抽象化したものです。

apiVersion: "cluster-api.k8s.io/v1alpha1"
kind: Cluster
metadata:  name: my-first-cluster
spec:
  providerSpec:
    ...
  clusterNetwork:
    services:
      cidrBlocks: ["10.96.0.0/12"]
    pods:
      cidrBlocks: ["192.168.0.0/16"]
  serviceDomain: "cluster.local"

Service や Pod の IP レンジや DNS ドメインなど、ほしいクラスタの状態が spec として書かれています。

Machine Resource

Machine リソースはクラスタを構成する node を抽象化してものです。

apiVersion: "cluster.k8s.io/v1alpha1"
kind: Machine
metadata:
  name: my-first-machine
  labels:
    set: node
spec:
  providerSpec:
    ...
  versions:
    kubelet: 1.12.0

この spec に基づいて、 VM や物理マシンなど各環境に合わせた node が用意されます。

MachineSet, MachineDeployment

MachineSet および MachineDeployment は、複数台の同じ種類の Machine をまとめて表現したリソースです。

apiVersion: "cluster.k8s.io/v1alpha1"
kind: MachineSet
metadata:
  name: my-first-machine-set
spec:
  replicas: 3
  template:
    metadata:
      spec:
        ...
apiVersion: "cluster.k8s.io/v1alpha1"
kind: MachineDeployment
metadata:
  name: my-first-machine-deployment
spec:
  replias: 3
  template:
    ...
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1

MachineSet によって必要な台数が決められ、 MachineDeployment により作成時の挙動などが決定されます。 Pod と ReplicaSet/Deployment との関係と似ていますね。

ProviderSpec, MachineClass

ProviderSpec は、デプロイ先のプラットフォームや、クラウドプロバイダ固有の設定を持つリソースです。 Machine や Cluster リソースで使われます。

apiVersion: "cluster-api.k8s.io/v1alpha1"
kind: MachineClass
metadata:
  name: my-first-machine-class
spec:
  providerSpec:
    apiversion: "gceproviderconfig/v1alpha1"
    kind: "GCEMachineProviderConfig"
    project: "gcp-project"
    zone: "us-central1-f"
    machineType: "n1-standard-2"
    os: "ubuntu-1604-lts"
---
apiVersion: "cluster-api.k8s.io/v1alpha1"
kind: Machineme
metadata:
  name: my-first-machine
spec:
  providerSpec:
    valueFrom:
      kind: MachineClass
      name: my-first-machine-class

Machine の spec 内に値を直接書き込むことできますし、 valueFrom を使って MachineClass を参照することで、複数リソースで同じ ProviderSpec を共有することもできるようです。

クラスタ作成のサイクル

本家リポジトリにあるのアーキテクチャの図をみながら、 Cluster API によって Kubernetes クラスタが作られる一連の流れを順に追ってみましょう。

ここでは、手元に用意した作業用 PC を使って、クラスタの初期構築を行う場面を想定して説明します。

Cluster API Architecture
Cluster API Architecture
  1. 作業用 PC 上で clusterctl create cluster コマンドを実行する
  2. ブートストラップ用の Kubernetes クラスタが用意される
    • “クラスタをつくるためのクラスタ”
    • clusterctl を実行したマシン上で minikube の環境が立ち上がる
  3. ブートストラップ用クラスタに Custom Resource と Custom Controller がデプロイされる
  4. Machine Controller によってインフラレイヤーの構築処理が行われる
    • クラウドプロバイダが提供する API が呼び出されノードとして使う VM が作成される
  5. 立ち上げられた VM 内で kubeadm が実行されクラスタが構築される
    • 実行されるコマンドは user-data スクリプトを使って VM 起動後に実行される
    • 必要なパッケージ類のインストール後 kubeadm が実行されクラスタが組まれる
      • master node では kubeadm init, worker node では kubeadm join

上記の初期構築のタイミングでは、Cluster API の Custom Resource と Custom Controller はブートストラップ用クラスタとして立ち上がった minikube 環境上にデプロイされています。 これらのリソースとコントローラを、新しく出来上がったクラスタのほうにデプロイすることで、ブートストラップに使った minikube 環境は削除することができます (Pivoting) 。

リソースとコントローラがあることで、そのクラスタは自分自身のノードの操作を行ったり、別のクラスタを作成することができるようになります。

さいごに

この記事では Cluster API プロジェクトの概要についてまとめました。

クラスタの初期構築に minikube を使い、ブートストラップ処理自体を (別途専用のツールなどを使うこと無く) Cluster API の仕組み自体を使うところはスマートだなと思いました。 その後の Pivoting の動きによって、完成したクラスタ自体にクラスタ管理の能力を持たせることができるのもおもしろいです。 Kubernetes をうまく活用して「クラスタを作るのに必要なクラスタをどうするか」問題を解決している点がかっこいいです。

会社のプライベートクラウド環境で cluster-api-provider-openstack を動かしてみるということもやってみました。 この記事だけでは説明が不足している部分もありますので、検証結果も交えながら次の記事でまとめてみようかなと思っています。

参考情報