•12 min read

GoでKubernetesOperatorを構築する:ステートフルアプリケーションの管理

GoでKubernetesOperatorを構築する:ステートフルアプリケーションの管理

Kubernetesは、コンテナ化されたワークロードをオーケストレーションする上で絶大な力を発揮し、クラウドのデファクトスタンダードなオペレーティングシステムとなっています。ステートレスなアプリケーションはすぐに扱える一方で、データベース、メッセージキュー、キャッシングシステムといったステートフルなアプリケーションのデプロイと管理は、異なる種類の課題を提示します。ステートフルなアプリケーションは、バックアップ、クラスタリング、スケーリング、グレースフルフェイルオーバーといったタスクを処理するために、ドメイン固有の知識を必要とします。ここでKubernetes Operatorパターンが登場します。

この包括的なガイドでは、Operatorパターンを深く掘り下げ、Custom Resource Definitions (CRD) やControl Loopsを含むそのコアコンポーネントを探り、ステートフルなアプリケーションを管理する上での微妙な違いについて議論し、Kubebuilderを使った実践的なGoコードスニペットを提供します。


Audio Briefing
0:00 / 0:00

Operatorパターンを理解する

Kubernetes Operatorの核心は、Kubernetesアプリケーションをパッケージ化、デプロイ、管理するための手法です。Operatorパターンは、人間の管理者の運用知識を捉え、それをクラスター内でネイティブに実行されるソフトウェアにエンコードすることを目指しています。Kubernetesの宣言型APIとその堅牢な自動化機能を活用して、アプリケーションをインテリジェントに管理します。

Custom Resource Definitions (CRD)

Kubernetesにおいて、「リソース」とは、特定の種類のAPIオブジェクトのコレクションを格納するAPIのエンドポイントです。KubernetesにはPod、Deployment、Serviceといった組み込みのリソースがありますが、Custom Resource Definitions (CRD) を使用してAPIを拡張することもできます。

CRDを使用すると、Kubernetesがネイティブなリソースと同様に格納および提供する、新しいカスタマイズされたオブジェクトの種類を定義できます。たとえば、PostgreSQLデータベースを管理するOperatorを構築している場合、PostgreSQLCluster CRDを定義するかもしれません。ユーザーがPostgreSQLClusterのマニフェストを送信すると、Operatorはそれを読み取り、データベースを稼働させるために必要な基盤となるリソース(StatefulSet、Service、PersistentVolumeClaimなど)をプロビジョニングします。

Control Loops (調停ループ)

CRDがKubernetes APIにおける名詞であるとすれば、Control Loop(またはController)は動詞です。Kubernetesは宣言型モデルで動作します。つまり、望ましい状態を宣言し、システムは現在の状態が望ましい状態と一致するように継続的に動作します。

この継続的なプロセスは調停ループ(reconciliation loop)と呼ばれます。Operatorのコントローラーは、カスタムリソース(およびそれらが所有する子リソース)の変更を監視します。イベントが発生するたび(リソースが作成、更新、または削除されるたび)に、コントローラーのReconcile関数がトリガーされます。コントローラーはクラスターの現在の状態を検査し、CRDで定義された望ましい状態と比較し、両者を一致させるために必要な運用手順(例:新しいPodの作成、ボリュームのリサイズ、バックアップの取得)を実行します。


Advertisement

Kubernetesにおけるステートフルアプリケーションの管理

ステートレスなアプリケーションは、クラスター内のどこで停止して再起動されても問題ありません。しかし、ステートフルなアプリケーションは、永続ストレージ、安定したネットワークID、順序付けられたデプロイとスケーリングを要求します。

Kubernetesは、これを支援するためにStatefulSetのようなプリミティブなワークロードを提供しています。StatefulSetは、固定されたIDと安定したストレージを提供します。しかし、複雑な分散システムにとっては、それだけでは不十分な場合がよくあります。たとえば、データベースのスケーリングはPodを追加するだけではありません。多くの場合、プライマリノードの再構成、データレプリケーションの開始、接続文字列の更新が必要です。

Operatorはこの複雑さを吸収します。ステートフルなワークロードを管理する場合、Operatorは通常、次のことを行います。

  1. インフラストラクチャのプロビジョニング: CRDの仕様に基づいて、StatefulSet、Service、PVCを動的に作成します。
  2. アプリケーションのブートストラップ: 設定を注入し、リーダー選出を処理し、データレプリカを初期化します。
  3. ライフサイクルイベントの処理: バックアップを自動化し、ゼロダウンタイムアップグレードを実行し、セカンダリレプリカをプライマリに昇格させることで、ノード障害を適切に処理します。

この運用ロジックをOperatorに組み込むことで、組織はステートフルなアプリケーションを、ステートレスなマイクロサービスと同じ信頼性と自動化でデプロイできます。


KubebuilderでOperatorを構築する

GoでOperatorを記述するには、開発者は通常フレームワークを使用します。Kubebuilder(およびOperator SDK)は業界標準です。これはボイラープレートコードを足場として生成し、CRDマニフェストを作成し、コントローラーロジックを設定するため、運用ドメインロジックに集中できます。

プロジェクトの足場を組む

まず、Kubebuilderを使用してプロジェクトを初期化し、APIを作成します。Kubebuilderがインストールされていると仮定します。

kubebuilder init --domain mycompany.com --repo github.com/mycompany/my-operator
kubebuilder create api --group db --version v1alpha1 --kind DatabaseCluster

これにより、CRDを表す必要なGo構造体と、調停ロジックが記述されるコントローラーファイルが生成されます。

カスタムリソースの定義

生成されたapi/v1alpha1/databasecluster_types.goファイルで、カスタムリソースのスキーマを定義します。これには、望ましい状態(Spec)と観測された状態(Status)が含まれます。

package v1alpha1

import (
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// DatabaseClusterSpec defines the desired state of DatabaseCluster
type DatabaseClusterSpec struct {
    // Replicas defines the number of database nodes
    Replicas int32 `json:"replicas,omitempty"`
    
    // StorageSize defines the size of the persistent volume
    StorageSize string `json:"storageSize,omitempty"`
    
    // Image specifies the database container image
    Image string `json:"image,omitempty"`
}

// DatabaseClusterStatus defines the observed state of DatabaseCluster
type DatabaseClusterStatus struct {
    // ReadyReplicas indicates how many replicas are fully operational
    ReadyReplicas int32 `json:"readyReplicas,omitempty"`
    
    // Conditions store the current health and state of the cluster
    Conditions []metav1.Condition `json:"conditions,omitempty"`
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status

// DatabaseCluster is the Schema for the databaseclusters API
type DatabaseCluster struct {
    metav1.TypeMeta   `json:",inline"`
    metav1.ObjectMeta `json:"metadata,omitempty"`

    Spec   DatabaseClusterSpec   `json:"spec,omitempty"`
    Status DatabaseClusterStatus `json:"status,omitempty"`
}

調停ループの実装

Operatorのコアロジックは、controllers/databasecluster_controller.go内のReconcileメソッドにあります。controller-runtimeライブラリは、Kubernetes APIとのやり取りを簡素化します。

package controllers

import (
    "context"
    "time"

    "k8s.io/apimachinery/pkg/api/errors"
    "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"

    dbv1alpha1 "github.com/mycompany/my-operator/api/v1alpha1"
)

// DatabaseClusterReconciler reconciles a DatabaseCluster object
type DatabaseClusterReconciler struct {
    client.Client
    Scheme *runtime.Scheme
}

//+kubebuilder:rbac:groups=db.mycompany.com,resources=databaseclusters,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=db.mycompany.com,resources=databaseclusters/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=apps,resources=statefulsets,verbs=get;list;watch;create;update;patch;delete

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

    // 1. Fetch the DatabaseCluster instance
    var dbCluster dbv1alpha1.DatabaseCluster
    if err := r.Get(ctx, req.NamespacedName, &dbCluster); err != nil {
        if errors.IsNotFound(err) {
            // Object not found, could have been deleted after reconcile request.
            logger.Info("DatabaseCluster resource not found. Ignoring since object must be deleted")
            return ctrl.Result{}, nil
        }
        // Error reading the object
        logger.Error(err, "Failed to get DatabaseCluster")
        return ctrl.Result{}, err
    }

    logger.Info("Reconciling DatabaseCluster", "Name", dbCluster.Name, "Replicas", dbCluster.Spec.Replicas)

    // 2. Perform Operational Logic
    // Here, you would typically:
    // - Check if a StatefulSet for this DatabaseCluster exists.
    // - If it doesn't, construct a new StatefulSet object and use r.Create() to deploy it.
    // - If it does exist, ensure its configuration (like replicas or image) matches dbCluster.Spec.
    // - Handle database-specific tasks like bootstrapping replication or taking backups.

    // Example: Update status to reflect current progress
    dbCluster.Status.ReadyReplicas = 1 // Simplified for demonstration
    if err := r.Status().Update(ctx, &dbCluster); err != nil {
        logger.Error(err, "Failed to update DatabaseCluster status")
        return ctrl.Result{}, err
    }

    // Return an empty result if successful. If you need to re-queue the request, 
    // you can return ctrl.Result{RequeueAfter: time.Minute * 1}
    return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *DatabaseClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
    return ctrl.NewControllerManagedBy(mgr).
        For(&dbv1alpha1.DatabaseCluster{}).
        // Owns(&appsv1.StatefulSet{}) indicates this controller manages StatefulSets created by the CRD
        Complete(r)
}

Reconcile関数からの重要なポイント

  1. 冪等性: Reconcile関数は冪等でなければなりません。同じ状態であっても、何度も呼び出される可能性があります。現在の状態が望ましい状態と異なる場合にのみ変更を行うべきです。
  2. ステータスの更新: ユーザーはSpecとやり取りしますが、OperatorはStatusを更新します。これは、ユーザーや他のシステムがアプリケーションの健全性と進行状況を監視する方法です。
  3. RBAC: //+kubebuilder:rbacマーカーは非常に重要です。これらは、Operatorがクラスター内でStatefulSetやServiceなどのリソースを管理するために必要なロールベースアクセス制御(RBAC)マニフェストを自動的に生成します。

結論

Kubernetesでステートフルなアプリケーションを管理することは、手作業でエラーが発生しやすい作業である必要はありません。Operatorパターンを採用することで、チームは運用ランブックを堅牢なソフトウェアコントローラーにコード化できます。

Kubebuilderのようなツールを使用することで、GoでのこれらのOperatorの開発は非常にアクセスしやすくなりました。明確なCustom Resource Definitionsを定義し、回復力のある調停ループを構築することで、Kubernetesインフラストラクチャを向上させ、ステートフルなデータベース、メッセージングキュー、キャッシュが、ステートレスなマイクロサービスと同じくらい高可用性、自動化、自己修復性を持つことを保証できます。

サードパーティのデータストアを管理している場合でも、独自の内部アプリケーションをラップしている場合でも、カスタムKubernetes Operatorを作成することは、真のインフラストラクチャ自動化に向けた強力な一歩です。

Advertisement

こちらもおすすめです

Share this article:

Stay Updated

Get the latest posts delivered straight to your inbox.

Free Developer Utilities

Free In-Browser Developer Tools

Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.

Explore Tools
Advertisement
Kubernetes OperatorsとCustom Resources: あらゆるものを自動化する
kubernetes

Kubernetes OperatorsとCustom Resources: あらゆるものを自動化する

KubernetesのコントロールプレーンをOperatorとCustom Resource Definition (CRD) で拡張し、複雑なステートフルアプリケーションのライフサイクル管理を自動化する方法を学び、reconciliationループ、RBAC、テスト、本番環境パターンについて解説します。

Read more