Kubernetesの標準リソース(DeploymentやServiceなど)だけでは、「自社独自の複雑なマイクロサービスのライフサイクル管理」や「ミドルウェアの自動バックアップ・フェイルオーバー」といった要件を満たしきれなくなり、頭を抱えた経験はないでしょうか。
こんにちは。インフラの自動化とカオスの制御に人生を捧げているSREエンジニアです。
既存のツールでどうにもならない壁にぶぶつかったとき、あなたを救う究極の武器が「Kubernetesカスタムコントローラー(Operator)」です。
「なんだか難しそう…」「Go言語の深い知識が必要なんでしょ?」と思うかもしれませんが、ご安心ください。Operator SDKという強力なフレームワークを使えば、驚くほど直感的に、そして堅牢に独自のコントローラーを開発できます。
これをマスターすれば、Kubernetesが「あなた専用のスマートなインフラプラットフォーム」に生まれ変わり、日々の運用作業が劇的に楽になりますよ。さあ、一緒にその扉を開けてみましょう!
—
1. なぜカスタムコントローラー(Operator)が必要なのか?
Kubernetesの心臓部は「制御ループ(Control Loop)」です。
1. Observe(観測): 現在のクラスタの状態を常時見張り、
2. Analyze(比較): 「あるべき姿(Desired State)」と「現実(Current State)」のギャップを計算し、
3. Act(実行): ギャップを埋めるためのアクションを起こす。
この仕組みを、あなたが定義した「独自のカスタムリソース(CRD)」に対して行えるようにするのがカスタムコントローラーです。
例えば、「`DatabaseCluster`という独自リソースを作ったら、自動的にSecretを生成し、PersistentVolumeClaimを組み立て、監視用Podまでデプロイして初期化する」といった一連の複雑なワークフローを、すべて自動化・コード化(IaCのその先へ)できます。
—
2. 開発環境のセットアップとツールの役割
まずは、魔法の杖となるツールたちをインストールしましょう。今回は以下の環境を前提とします。
- Go: 1.21以上
- Docker / Podman: コンテナビルド用
- kubectl: クラスタ操作用
- Minikube / Kind: ローカルKubernetes環境(動作確認用)
- Operator SDK: コントローラーの scaffolding(骨組み作成)ツール
Operator SDKのインストール
OSに応じたバイナリのダウンロード (Linux x86_64の例)
export ARCH=$(case $(uname -m) in x86_64) echo -n amd64 ;; aarch64) echo -n arm64 ;; esac)
export OS=$(uname | awk ‘{print tolower($0)}’)
export OPERATOR_SDK_DL_URL=https://github.com/operator-framework/operator-sdk/releases/download/v1.32.0
curl -L -O ${OPERATOR_SDK_DL_URL}/operator-sdk_${OS}_${ARCH}
バイナリをパスの通った場所に配置し、実行権限を付与
chmod +x operator-sdk_${OS}_${ARCH}
sudo mv operator-sdk_${OS}_${ARCH} /usr/local/bin/operator-sdk
バージョン確認
operator-sdk version
—
3. プロジェクトの初期化と「HelloWorld」の設計
今回は、クラスタ内に「`Greeting`」という独自リソースを作ると、指定したメッセージをログに出力し、ステータスを自動更新してくれる優しいコントローラーを作ります。
プロジェクトの作成
適度な作業ディレクトリで、以下のコマンドを実行します。
mkdir -p $GOPATH/src/github.com/your-name/greeting-operator
cd $GOPATH/src/github.com/your-name/greeting-operator
モジュールの初期化とプロジェクトの骨組み生成
operator-sdk init \
–domain example.com \
–repo github.com/your-name/greeting-operator
これによって、Kubernetes Operatorとしての標準的なディレクトリ構造(`config/`, `controllers/` または `internal/controller/` など)が自動生成されます。
API(CRD)の追加
次に、カスタムリソースの設計図(API)を追加します。
operator-sdk create api \
–group cache \
–version v1alpha1 \
–kind Greeting \
–resource \
–controller
「おっ、何かたくさんのファイルが生成されたぞ!」とワクワクしましたか?
このコマンドにより、「あるべき姿を定義する構造体(API)」と、「それを監視・制御するロジック(コントローラー)」の雛形が完璧に用意されます。
—
4. コードの実装:制御ループに魂を吹き込む
ここからがエンジニアの見せ所です。
今回は「`Greeting`リソースが作成されると、`.spec.message` の内容を読み取ってログに吐き出し、`.status.phase` を `Completed` に書き換える」というロジックを実装します。
① API構造体の定義
`api/v1alpha1/greeting_types.go` を開き、ユーザーが指定するパラメータ(Spec)と、コントローラーが書き込む状態(Status)を定義します。
// api/v1alpha1/greeting_types.go
type GreetingSpec struct {
// 挨拶のメッセージ (例: “Hello, Kubernetes!”)
// +kubebuilder:validation:Required
Message string `json:”message”`
}
type GreetingStatus struct {
// 処理の現在の状態 (Pending, Completed 等)
Phase string `json:”phase,omitempty”`
}
② コントローラーのロジック実装
次に、核心部分である `internal/controller/greeting_controller.go`(または `controllers/greeting_controller.go`)を開きます。
`Reconcile` メソッドの中に、制御ループの魂を書き込みます。
// internal/controller/greeting_controller.go
package controller
import (
“context”
“fmt”
“k8s.io/apimachinery/pkg/runtime”
ctrl “sigs.k8s.io/controller-runtime”
“sigs.k8s.io/controller-runtime/pkg/client”
“sigs.k8s.io/controller-runtime/pkg/log”
cachev1alpha1 “github.com/your-name/greeting-operator/api/v1alpha1”
)
type GreetingReconciler struct {
client.Client
Scheme runtime.Scheme
}
// +kubebuilder:rbac:groups=cache.example.com,resources=greetings,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=cache.example.com,resources=greetings/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=cache.example.com,resources=greetings/finalizers,verbs=update
func (r GreetingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := log.FromContext(ctx)
// 1. 該当する Greeting リソースの取得
var greeting cachev1alpha1.Greeting
if err := r.Get(ctx, req.NamespacedName, &greeting); err != nil {
// リソースが削除されている場合などはエラーを無視して終了
return ctrl.Result{}, client.IgnoreNotFound(err)
}
logger.Info(“Greeting リソースを検知しました!”, “Message”, greeting.Spec.Message)
// 2. すでに完了していれば何もしない(無限ループ防止の冪等性担保)
if greeting.Status.Phase == “Completed” {
return ctrl.Result{}, nil
}
// 3. 独自のビジネスロジック(今回はメッセージを表示するだけ)
fmt.Println(“==================================================”)
fmt.Printf(” [OPERATOR LOG] 📢 Message: %s\n”, greeting.Spec.Message)
fmt.Println(“==================================================”)
// 4. ステータスを “Completed” に更新
greeting.Status.Phase = “Completed”
if err := r.Status().Update(ctx, &greeting); err != nil {
logger.Error(err, “ステータスの更新に失敗しました”)
// 失敗した場合はリトライさせるためにエラーを返す
return ctrl.Result{}, err
}
logger.Info(“Greeting リソースの処理が正常に完了しました。”)
// 再度キューに入れない(処理完了)
return ctrl.Result{}, nil
}
func (r GreetingReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&cachev1alpha1.Greeting5). // 監視対象のリソース
Complete(r)
}
たったこれだけのコードで、Kubernetesのイベント駆動な非同期処理の仕組みに完璧にフックするコントローラーが完成しました。
—
5. クラスタへのデプロイと動作確認
さあ、いよいよ自分たちの手で作ったコントローラーをKubernetesクラスタへ解き放ちます。今回は手元の検証用クラスタ(MinikubeやKindなど)を使用してください。
CRDのインストール
まずはKubernetesに新しいカスタムリソース(`greetings.cache.example.com`)の定義を教え込みます。
make install
コントローラーのローカル実行(デバッグに便利!)
開発中は、わざわざコンテナビルドしてイメージをプッシュしなくても、ローカルのPCから直接クラスタに接続してコントローラーを動かせます。
make run
別ターミナルを開き、いよいよ「あるべき姿(Manifest)」をクラスタに投入してみましょう!
カスタムリソースの作成(Manifestの適用)
プロジェクト直下に `config/samples/cache_v1alpha1_greeting.yaml` が生成されているはずです。中身を以下のように書き換えてみてください。
apiVersion: cache.example.com/v1alpha1
kind: Greeting
metadata:
labels:
app.kubernetes.io/name: greeting-operator
app.kubernetes.io/managed-by: kustomize
name: greeting-sample
spec:
message: “こんにちは、世界!Operatorの世界へようこそ!”
これをクラスタに適用します。
kubectl apply -f config/samples/cache_v1alpha1_greeting.yaml
【感動の瞬間】
`make run` を動かしているターミナル画面を見てください。次のようなログが出力されているはずです!
2.345678s INFO Greeting リソースを検知しました! {“controller”: “greeting”, “controllerGroup”: “cache.example.com”, “controllerKind”: “Greeting”, “Greeting”: {“name”:”greeting-sample”,”namespace”:”default”}, “Message”: “こんにちは、世界!Operatorの世界へようこそ!”}
==================================================
[OPERATOR LOG] 📢 Message: こんにちは、世界!Operatorの世界へようこそ!
==================================================
2.351234s INFO Greeting リソースの処理が正常に完了しました。 {“controller”: “greeting”, …}
さらに、リソースのステータスを確認してみましょう。
kubectl get greeting greeting-sample -o yaml
出力結果の `status:` セクションに、しっかりと `phase: Completed` が刻まれているのが確認できます。あなたが書いたコードが、Kubernetesの制御ループの一部となり、自律的にリソースを調停した瞬間です。
—
まとめ
お疲れ様でした!
今回は Operator SDK を用いて、CRDの定義からカスタムコントローラーの実装、そしてローカルクラスタでの動作確認までを一気通貫で解説しました。
- 制御ループの本質(Observe → Analyze → Act)
- Operator SDKによる爆速のプロジェクト・API生成
- 冪等性(Idempotency)を意識したReconcile関数の実装
これをマスターすれば、社内のインフラ要件に合わせた「完全独自の自動化ツール」を自由自在に生み出せるようになります。面倒な定型作業はすべてコントローラーに任せて、私たちはよりクリエイティブなアーキテクチャ設計に集中しましょう。
あなたのインフラ自動化の旅が、ここからさらに加速することを心から応援しています!