Kubernetes・コンテナ

Kubebuilderとは?kubectlとの違い、コントローラーとOperatorの違いと開発手順【v4.16】

Kubebuilderとは?kubectlとの違い、コントローラーとOperatorの違いと開発手順【v4.16】

Kubebuilderは、CustomResourceDefinition(CRD)とコントローラーを組み合わせて、Kubernetesを独自のAPIで拡張するためのGo製SDKです。kubectlがクラスタに「命令を送る道具」であるのに対し、Kubebuilderはクラスタの中で動き続ける「Operatorを作る道具」で、役割は重なりません。この記事では、2026年9月10日公開のKubebuilder v4.16.0とkubectl v1.37.0で、プロジェクト作成からReconcileの実装、envtestでのテスト、kubectlによるカスタムリソースの操作までを実行した出力をもとに、コントローラーとOperatorの違い、Operator SDKなど他の選択肢との選び分けを整理します。

まとめ:kubectlとKubebuilderの違いとOperator開発の要点

  • 役割の違い:kubectlはkube-apiserverへリクエストを送るCLIクライアント。KubebuilderはCRDとコントローラーのコード・マニフェスト・テスト環境を生成する開発用SDKで、完成したOperatorのCRDを登録したり動作を確かめたりする場面でkubectlを使います。
  • コントローラーとOperator:コントローラーは望ましい状態へ実際の状態を近づける制御ループ全般。Operatorはそのうち、カスタムリソースを使ってアプリケーションの運用知識をコード化したものです。Operatorはすべてコントローラーですが、逆は成り立ちません。
  • 開発の流れ:kubebuilder init→kubebuilder create api→型定義とReconcileの実装→make manifests→make test(envtest)→make installとmake runでクラスタに当てる、の順です。
  • 2026年9月時点の版:Kubebuilder v4.16.0、controller-runtime v0.25.1、Operator SDK v1.42.3。v4.16.0の生成物はgo.modでgo 1.26.0を宣言します。
  • 選び方:GoでOperatorを書くならKubebuilderが基準。OLMでの配布やAnsible・Helmベースが要るならOperator SDK。CRDの検証だけが目的ならコントローラーを書かずにCRDのスキーマやValidatingAdmissionPolicyで足ります。

kubectlとKubebuilderの役割の違い

両者は名前が似ていますが、使う時点も動く場所も違います。kubectlは運用者や開発者の端末で実行し、kube-apiserverへ作成・取得・更新・削除のリクエストを送るクライアントです。Kubernetesのバージョンスキューポリシーでは、kubectlは通信先のkube-apiserverに対して前後1マイナーバージョンの範囲でサポートされます。Kubebuilderは開発時にだけ使うコード生成ツールで、生成したOperatorはコンテナとしてクラスタ内で常駐します。

観点 kubectl Kubebuilder
種類 CLIクライアント Operator開発用SDK
使う時点 日々の運用・デバッグ Operatorの開発時
動く場所 手元の端末 生成物がクラスタ内で常駐
主な出力 リソースの作成・取得結果 Goコード・CRD・RBAC・Makefile
CRDとの関係 CRDの登録と操作 CRDの定義と生成
2026年9月の版 v1.37.0 v4.16.0

生成されたMakefileのinstallとdeployターゲットは、内部でkustomizeの出力をkubectl applyに渡します。KubebuilderはkubectlをCRDの登録手段として前提にしており、どちらか一方を選ぶ関係ではありません。Kubernetes自体の構成要素から押さえたい場合はKubernetes(クバネティス)の仕組みとDockerとの違いを先に読むと、以降の用語がつながります。

コントローラーとOperatorの違い

コントローラーはリソースの状態を合わせる制御ループ

Kubernetesの公式ドキュメントは、コントローラーを「1種類のリソースを望ましい状態として参照し、管理対象をその状態に近づける制御ループ」と説明しています。Deploymentにreplicas: 3と書けばPodが3つに保たれるのは、kube-controller-manager内のコントローラーが差分を検知して埋め続けているためです。コントローラーの処理は「今の状態を読み、望ましい状態との差を埋める」ことに限られ、何回呼ばれても同じ結果になる(冪等である)ことが求められます。

Operatorはカスタムリソースで運用知識をコード化したコントローラー

Operatorについて、公式ドキュメントは「カスタムリソースを使ってアプリケーションとその構成要素を管理するKubernetesのソフトウェア拡張」と定義しています。データベースのバックアップ、バージョンアップの手順、障害時のフェイルオーバーのように、人が手順書で行っていた運用をコントローラーに書き込み、kind: PostgresClusterのような独自リソースで宣言できるようにしたものです。Ceph向けのRookはその代表例で、Rook CephがKubernetes上でCephを動かすオペレータの仕組みで構成を追えます。

区別の基準は、カスタムリソースを使ってアプリケーション固有の運用知識を実装しているかです。カスタムリソースの提供方法にはCRDとAPI Aggregationがあり、独自のCRDを持つこと自体が必須条件ではありません。組み込みリソースだけを監視してラベルを付け直すコントローラーは、Operatorとは呼びません。

CRDだけで足りる場合とコントローラーが必要な場合

CRDを登録しただけでは、kube-apiserverがカスタムリソースを保存して返すだけで、何も動きません。設定値の置き場としてCRDを使い、別のツールが読むだけなら、コントローラーは不要です。カスタムリソースの作成や変更をきっかけにPod・ConfigMap・外部のクラウドリソースを作る、あるいは状態をstatusに書き戻す必要があるときに、初めてコントローラーを書く意味が出ます。CRDとAPIグループの関係はKubernetes APIのリソース・エンドポイント構造とCRD拡張で詳しく扱っています。

Kubebuilderの構成要素と位置付け

KubebuilderはKubernetesのSIG API Machineryが管理するkubernetes-sigs配下のプロジェクトで、次の部品を束ねてひな形を作ります。

  • controller-runtime:Manager・Client・キャッシュ・Reconcilerの実行基盤。v4.16.0の生成物はv0.25.0を依存に書き込みました(公開中の最新はv0.25.1)。
  • controller-tools(controller-gen):Goのコメントに書いたマーカーから、CRD・RBAC・DeepCopyメソッドを生成します。生成物はv0.22.0を取得しました。
  • setup-envtest:テスト用のkube-apiserverとetcdのバイナリを取得します。
  • kustomize:config/配下のマニフェストを組み立てます。生成物はv5.8.1を指定していました。

既定のプラグインはgo/v4で、公式ドキュメントではkustomize.common.kubebuilder.io/v2とbase.go.kubebuilder.io/v4を合成したものと説明されています。Operator SDKもKubebuilderをCLIとプラグインのライブラリとして使っているため、Go製Operatorのディレクトリ構成はどちらで作っても同じ標準に沿います。

Kubebuilderのインストールとプロジェクト作成手順

Kubebuilderの動作前提とインストール

公式Quick Startが挙げる前提は、Go v1.24.6以上、Docker 17.03以上、kubectl v1.11.3以上、v1.11.3以上のクラスタへのアクセスです。ただしv4.16.0で生成したgo.modはgo 1.26.0を宣言しており、Go 1.21以降のツールチェーンはこれより古い版ではGOTOOLCHAINの設定に従って新しい版を取得するか、ビルドを止めます。手元のGoは1.26系にそろえておくと迷いません。検証用のクラスタはkindでローカルにk8sクラスタを構築する手順で用意できます。

バイナリは公式Quick Startと同じ次のURLで取得します。このURLはリダイレクトを経てGitHub Releasesの最新版(2026年9月17日時点でv4.16.0)に着地します。

curl -L -o kubebuilder "https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH)"
chmod +x kubebuilder && sudo mv kubebuilder /usr/local/bin/
kubebuilder version
# KubeBuilder:          v4.16.0
# Kubernetes:           1.37.0
# Go OS/Arch:           darwin/amd64

kubebuilder initとcreate apiで生成されるファイル

公式のGuestbookの例に沿って、プロジェクトの初期化とAPIの追加を行います。--domainはAPIグループの接尾辞になり、次の例ではwebapp.my.domainというグループが作られます。

mkdir guestbook && cd guestbook
kubebuilder init --domain my.domain --repo my.domain/guestbook
kubebuilder create api --group webapp --version v1 --kind Guestbook --resource --controller

実行後の主なファイルは次のとおりです(.githubやconfig/prometheusなどは省略)。

api/v1/guestbook_types.go              # Spec・Statusの型定義
api/v1/zz_generated.deepcopy.go        # controller-genが生成
cmd/main.go                            # Managerの起動
internal/controller/guestbook_controller.go
internal/controller/suite_test.go      # envtestの起動
config/crd/  config/rbac/  config/manager/  config/samples/
test/e2e/                             # Kindを使うE2Eテスト
Dockerfile  Makefile  PROJECT  AGENTS.md

PROJECTファイルにはcliVersion: 4.16.0とlayout: go.kubebuilder.io/v4が記録されます。kubebuilder alpha generateはこのファイルからプロジェクトを作り直し、kubebuilder alpha updateは3方向マージで新しいKubebuilderの版へ追従させるコマンドです。PROJECTを手で消すと、この追従手段を失います。

マーカーによるバリデーションとkubectl表示の指定

型定義のコメントに書く+kubebuilder:マーカーが、CRDのOpenAPIスキーマやkubectlの表示に変換されます。次の例は、messageを必須かつ1文字以上にし、短縮名gbと表示列を追加したものです。

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:shortName=gb
// +kubebuilder:printcolumn:name="Message",type=string,JSONPath=".spec.message"
// +kubebuilder:printcolumn:name="ConfigMap",type=string,JSONPath=".status.configMapName"
type Guestbook struct { ... }

type GuestbookSpec struct {
	// +kubebuilder:validation:MinLength=1
	Message string `json:"message"`
}

type GuestbookStatus struct {
	// +optional
	ConfigMapName string `json:"configMapName,omitempty"`
	// 生成時からあるConditionsはそのまま残す
}

make manifestsを実行すると、config/crd/bases/webapp.my.domain_guestbooks.yamlにapiVersion: apiextensions.k8s.io/v1のCRDが出力され、messageにはminLength: 1とrequiredが付きます。この例ではmessageが必須になります。ただし、+kubebuilder:validation:Optionalをフィールドやパッケージに指定した場合などは扱いが変わるため、生成されたスキーマのrequiredも確認してください。

表示列を1つでも定義すると、kubectlの既定表示からAGE列が消えます。作成からの経過時間を残したい場合は、JSONPath=".metadata.creationTimestamp"とtype=dateの列を自分で足します。

Reconcileの実装とenvtestによるテスト

Reconcile関数の最小実装

internal/controller/guestbook_controller.goのReconcileとSetupWithManagerを以下の実装に置き換えます。既存のGuestbook用RBACマーカーは残し、ConfigMap用マーカーを追加します。importブロックには、既存のimportに加えて次の3行が必要です。

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

次のコードは、Guestbookが作られたら同じ名前空間に<名前>-messageというConfigMapを作り、spec.messageの値を書き込むコントローラーです。CreateOrUpdateで冪等にし、SetControllerReferenceで所有者参照を付けるため、Guestbookを消すとConfigMapもガベージコレクションで消えます。

// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;patch;delete

func (r *GuestbookReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	log := logf.FromContext(ctx)

	var gb webappv1.Guestbook
	if err := r.Get(ctx, req.NamespacedName, &gb); err != nil {
		return ctrl.Result{}, client.IgnoreNotFound(err)
	}

	cm := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{
		Name: gb.Name + "-message", Namespace: gb.Namespace,
	}}
	op, err := controllerutil.CreateOrUpdate(ctx, r.Client, cm, func() error {
		cm.Data = map[string]string{"message": gb.Spec.Message}
		return controllerutil.SetControllerReference(&gb, cm, r.Scheme)
	})
	if err != nil {
		return ctrl.Result{}, err
	}
	log.Info("configmap reconciled", "name", cm.Name, "operation", op)

	if gb.Status.ConfigMapName != cm.Name {
		gb.Status.ConfigMapName = cm.Name
		if err := r.Status().Update(ctx, &gb); err != nil {
			return ctrl.Result{}, err
		}
	}
	return ctrl.Result{}, nil
}

func (r *GuestbookReconciler) SetupWithManager(mgr ctrl.Manager) error {
	return ctrl.NewControllerManagedBy(mgr).
		For(&webappv1.Guestbook{}).
		Owns(&corev1.ConfigMap{}).
		Named("guestbook").
		Complete(r)
}

設計上の要点は3つあります。Reconcileは「どのイベントで呼ばれたか」を受け取らないので、毎回その時点の状態を読み直して判断すること。Ownsを書くと、子のConfigMapを誰かが手で消したときにも親のReconcileが呼ばれて作り直されること。そして、ConfigMapを扱うRBACマーカーを書き忘れると、envtestでは通るのにクラスタ上では権限エラーになることです。envtestのテストクライアントは管理者権限で動くため、この誤りをテストでは検出できません。

envtestでの単体テストの実行結果

生成されたinternal/controller/guestbook_controller_test.goはGinkgoとGomegaで書かれています。上の実装に合わせて、SpecにMessage: "hello"を入れ、Reconcile後にConfigMapの中身・所有者参照・statusを確かめるアサーションを足しました。make testはsetup-envtestでKubernetes 1.37のkube-apiserverとetcdをbin/k8s/へ取得してからgo testを走らせます。

$ make test
Setting up envtest binaries for Kubernetes version 1.37...
ok  	my.domain/guestbook/internal/controller	10.578s	coverage: 76.5% of statements

macOS(Intel)での初回はバイナリ取得を含めて約1分30秒、2回目以降はコントローラーのテストだけで10〜15秒でした。envtestには実際のkube-apiserverが動くので、CRDのバリデーションも本番と同じく効きます。messageを空にしたGuestbookの作成がIsInvalidエラーになることも同じテストで確認できました。一方、kube-controller-managerは起動しないため、所有者参照によるガベージコレクションやDeploymentからのPod作成は起きません。この挙動は、Kindを使うtest/e2e/に、Guestbookの削除後にConfigMapが消えることを確かめるテストを追加して検証します。生成されたE2Eテストだけでは、この独自の挙動は確認できません。

kubectlでCRDとカスタムリソースを操作するコマンド

クラスタにCRDを登録するのがmake install、手元からコントローラーを動かすのがmake runです。登録後はkubectlでカスタムリソースを組み込みリソースと同じように扱えます。操作には次の2つのマニフェストを使います。

# guestbook.yaml
apiVersion: webapp.my.domain/v1
kind: Guestbook
metadata:
  name: guestbook-sample
spec:
  message: hello
---
# empty.yaml(バリデーション違反の確認用。実際は別ファイルに保存)
apiVersion: webapp.my.domain/v1
kind: Guestbook
metadata:
  name: empty
spec:
  message: ""

以下は、envtestのkube-apiserver(v1.37.0)にCRDだけを登録し、コントローラーは起動していない状態で、kubectl v1.37.0から操作した出力です。

$ kubectl get crd
NAME                          SCOPE        VERSIONS      CREATED AT
guestbooks.webapp.my.domain   Namespaced   v1(storage)   2026-09-17T08:46:10Z

$ kubectl api-resources --api-group=webapp.my.domain
NAME         SHORTNAMES   APIVERSION            NAMESPACED   KIND
guestbooks   gb           webapp.my.domain/v1   true         Guestbook

$ kubectl explain guestbook.spec
FIELDS:
  message	<string> -required-

$ kubectl apply -f guestbook.yaml
guestbook.webapp.my.domain/guestbook-sample created

$ kubectl get gb
NAME               MESSAGE   CONFIGMAP
guestbook-sample   hello     

CONFIGMAP列が空なのは、status.configMapNameを書き込むコントローラーが動いていないためです。make runでコントローラーを起動すると、Reconcileがguestbook-sample-messageを作成し、この列に名前が表示されます。

kubectl get crdのcrdはcustomresourcedefinitionsの短縮名です。短縮名gbはマーカーで指定したときだけ使え、指定しないCRDでkubectl get gbを打つとthe server doesn't have a resource type "gb"になります。バリデーションに反するリソースは、kube-apiserverが保存前に拒否します。

$ kubectl apply -f empty.yaml
The Guestbook "empty" is invalid: spec.message: Invalid value: "": spec.message in body should be at least 1 chars long

変更を当てる前に差分だけ見たいときはkubectl diff -f guestbook.yamlを使います。生成されたconfig/samples/webapp_v1_guestbook.yamlはspec:の下がコメントだけなので、そのまま適用すると空文字の場合とは別のエラーで拒否されます。messageを書き足してから使ってください。

$ kubectl apply -f config/samples/webapp_v1_guestbook.yaml
The Guestbook "guestbook-sample" is invalid: spec: Required value

イメージのビルドとHelmチャートでのデプロイ

クラスタ内でOperatorを動かすには、コンテナイメージを作ってDeploymentとして配置します。公式Quick Startの手順は次の2行です。

make docker-build docker-push IMG=<registry>/guestbook:tag
make deploy IMG=<registry>/guestbook:tag

Helmで配布したい場合は、helm/v2-alphaプラグインがkustomizeの出力からチャートを生成します。実行するとdist/chart/にChart.yamlとvalues.yaml、RBACやメトリクスのテンプレートが作られ、Makefileにhelm-deploy・helm-uninstall・helm-status・helm-history・helm-rollbackの5ターゲットが追加されました。

kubebuilder edit --plugins=helm/v2-alpha

名前のとおりalpha段階で、Kubebuilderは「頻繁に変更され、使う時点の間で壊れうる」段階と定義しています。社外に配るチャートの正本にするなら、生成後の差分をレビューする運用を前提にしてください。チャートの基本はHelmのチャート・リポジトリ・リリースの基本で解説しています。

Operator SDK・Metacontroller・KUDOとの選び分け

ツール 最新リリース 記述言語 向く場面
Kubebuilder v4.16.0(2026-09-10) Go GoでOperatorを書く標準
Operator SDK v1.42.3(2026-06-26) Go・Ansible・Helm OLMで配布する
Metacontroller v4.17.2(2026-08-13) 任意(Webhook) Go以外で軽く書く
KUDO v0.19.0(2021-04-27) YAML宣言 新規採用は避ける

Operator SDKはKubebuilderを内部で使っているので、Goで書く部分のコードはほぼ共通です。差はOperator Lifecycle Manager(OLM)向けのバンドル生成や、AnsibleとHelmでOperatorを作れる点にあります。OpenShiftやOperatorHubへの掲載を予定しているならOperator SDK、そうでなければ依存の少ないKubebuilderから始めるのが順当です。

Metacontrollerは、同期処理を任意の言語のWebhookとして書き、ループの管理をMetacontroller側に任せる方式です。Goを書けないチームでも作れますが、Webhookサーバーの可用性がそのままOperatorの可用性になります。

KUDOはGitHubのリポジトリこそアーカイブされていないものの、最終リリースは2021年4月のv0.19.0、最終のpushは2023年8月です。新規採用では、機能の比較に加えて、現在のKubernetesへの追従状況と保守体制を判断材料にします。これから作るOperatorの選択肢から外してください。

Kubebuilderを採用すべきでない場面

Kubebuilderはコード量もテスト基盤も本格的なので、次の条件に当てはまるなら使わない方が保守は軽くなります。

  • 入力値の検証だけが目的:CRDのOpenAPIスキーマで表せる制約はマーカーだけで済みます。組み込みリソースへの独自ルールなら、Kubernetes v1.30でstableになったValidatingAdmissionPolicyでCELの式を書けば、Webhookサーバーを運用せずに済みます。
  • マニフェストのテンプレート化だけが目的:環境ごとの値の差し替えや一括デプロイは、HelmやKustomize、GitOpsツールの仕事です。状態を監視して直し続ける必要がないなら、コントローラーは過剰です。
  • クラウドリソースの宣言的管理:S3バケットやRDSをカスタムリソースで管理したいだけなら、既存の実装があるCrossplaneのクラウドインフラ管理の仕組みを先に検討します。
  • Goのコードを保守できる人がいない:Reconcileの不具合でリソースの作成・更新が繰り返されると、APIサーバーへの負荷増加や管理対象の障害を招くおそれがあります。書いた人しか直せない状態でOperatorを本番に入れるのは避けてください。

逆に、ステートフルなミドルウェアのバックアップ・昇格・バージョンアップのように、手順書が長く、順序を間違えると壊れる運用があるなら、Kubebuilderで書く価値があります。

よくある質問

Kubebuilderとkubectlはどちらを覚えればよいですか?

Kubernetesの日常的な操作に使うのはkubectlなので、先に覚えるのはkubectlです。Kubebuilderは、GoでカスタムコントローラーやWebhookを開発するときに使うツールで、作ったOperatorのCRD登録や動作確認にもkubectlを使います。Kubebuilderで作ったOperatorも、CRDの登録や動作確認にはkubectlを使います。

Kubernetesのコントローラーとオペレーターの違いは何ですか?

コントローラーは、リソースの望ましい状態へ実際の状態を近づける制御ループの総称です。Operatorは、カスタムリソースを使ってアプリケーション固有の運用を自動化するコントローラーを指します。Operatorはすべてコントローラーですが、Deploymentコントローラーのような組み込みのものはOperatorとは呼びません。

Operator SDKとKubebuilderの違いは何ですか?

Operator SDKはKubebuilderをCLIとプラグインのライブラリとして使っており、Go製Operatorの構成は共通です。Operator SDKはOLM向けのバンドル生成と、Ansible・HelmベースのOperatorに対応している点が加わります。

KubebuilderでHelmチャートは作れますか?

作れます。kubebuilder edit --plugins=helm/v2-alphaでdist/chart/にチャートが生成され、Makefileにhelm-deployなどのターゲットが追加されます。ただしプラグインはalpha段階で、版によって破壊的変更が入る可能性があります。

Kubebuilderのテストにクラスタは必要ですか?

単体テストには不要です。make testがsetup-envtestでkube-apiserverとetcdを取得してローカルで起動します。ただしkube-controller-managerは動かないため、ガベージコレクションなどを確かめるにはKindを使うtest/e2e/のテストが必要です。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.06 テックブログ 大和証券の不正アクセスと約11万人分の口座番号:問い合わせ管理の委託先に残さない設計
  2. 2026.10.06 テックブログ 焼肉きんぐの不正アクセスと1,078万件の会員情報|全件規模の流出を防ぐAPIとログの点検
  3. 2024.06.11 コラム 個人情報漏えい件数の推移をグラフで解説|最新データと過去最多(約1.9万件)
  4. 2026.10.05 テックブログ WSL Containersとは?wslcの使い方とDocker Desktopとの使い分け【WSL 3.0.1時点】
  5. 2026.10.05 テックブログ 東京メトロ(メトポ)の不正アクセスと約5.9万件のメールアドレス|配信停止リストを残さない設計

RELATED POSTS 関連記事

目次