Kubernetes オペレーター: CRD、コントローラー パターン、オペレーター SDK
本番環境で PostgreSQL クラスターを管理するにはどうすればよいですか?プライマリを監視し、検出する必要があります。 障害、レプリカのプロモート、構成の更新、バックアップの実行 証明書のローテーションをスケジュールおよび管理します。これらは専門の DBA が行う操作です やり方は暗記しているが、何か問題が起きるたびに何時間もの肉体労働が必要になる 午前3時に曲がった。
パターン Kubernetes オペレーター この知識を体系化できます Kubernetes ネイティブ ソフトウェアで動作: クラスターの状態を監視するコントローラー、 それを望ましい状態と比較し、それらを調整するために必要なアクションを実行します。 自動的に。継続的に。人間の介入なしで。この記事では、 Operator SDK と Kubebuilder を使用し、コントローラーを完全に理解した完全な Operator パターンと調整ループ。
何を学ぶか
- Kubernetes Operator とは何ですか? Kubernetes Operator を構築する意味があるのはいつですか
- カスタム リソース定義 (CRD): スキーマ、バージョン管理、検証
- コントローラーパターンと調整ループ
- Operator SDK と Kubebuilder: 違いとどちらをいつ使用するか
- Kubebuilder を使用して完全な Operator を実装する
- オペレーター ハブとオペレーター ライフサイクル マネージャー (OLM)
- envtest を使用したオペレーターのテスト
- プロダクション オペレーター: Zalando Postgres オペレーター、Kafka 用 Strimzi
Kubernetes オペレーターとは
「オペレーター」という用語は、パターンを説明するために 2016 年に CoreOS によって導入されました。 などの特定のアプリケーションの運用ノウハウをカプセル化するソフトウェア。 Kubernetes コントローラーのセット。 Google の正式な定義:
Operator と、Kubernetes アプリケーションをパッケージ化、デプロイ、管理する方法。 オペレーターは、管理時に人間のオペレーターが行う一般的なタスクを実装および自動化します。 そのタイプのアプリケーション: デプロイメント、更新、バックアップ、フェイルオーバー、スケーリング。
Operator は、Kubernetes 宣言モデルを特定のドメインに拡張します。代わりに 「ポッドをください」、「3 つのレプリカと毎日のバックアップを備えた PostgreSQL クラスタをください」と言うことができます。 S3 では、自動フェイルオーバーと TLS 証明書」。オペレーターはこれを変換する方法を知っています 具体的な Kubernetes リソースの高レベルの仕様。
オペレーターの成熟度モデル
オペレーター能力モデルでは、成熟度の 5 つのレベルが定義されています。
| レベル | 名前 | 容量 |
|---|---|---|
| 1 | 基本インストール | 自動化されたアプリケーションプロビジョニング |
| 2 | シームレスなアップグレード | パッチとマイナーバージョンのアップグレード |
| 3 | フルライフサイクル | バックアップ、障害復旧、再構成 |
| 4 | 深い洞察 | メトリクス、アラート、ログ処理、ワークロード分析 |
| 5 | オートパイロット | 自動スケーリング、自動構成、異常検出 |
カスタム リソース定義 (CRD)
CRD は、カスタム リソース タイプを使用して Kubernetes API を拡張します。ただ使うのではなく、
ネイティブ リソース (ポッド、デプロイメント、サービス)、特定のドメイン リソースを定義できます
どうやって PostgresCluster, KafkaTopic, MLModel.
CRDを定義する
# postgres-cluster-crd.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: postgresclusters.database.example.com
spec:
group: database.example.com
scope: Namespaced
names:
plural: postgresclusters
singular: postgrescluster
kind: PostgresCluster
shortNames:
- pgc
versions:
- name: v1alpha1
served: true
storage: true
# Schema di validazione OpenAPI v3
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
required:
- replicas
- version
properties:
replicas:
type: integer
minimum: 1
maximum: 5
description: "Numero di repliche PostgreSQL"
version:
type: string
enum: ["14", "15", "16"]
description: "Versione PostgreSQL"
storage:
type: object
properties:
size:
type: string
pattern: "^[0-9]+Gi$"
default: "10Gi"
storageClass:
type: string
default: "fast-ssd"
backup:
type: object
properties:
enabled:
type: boolean
default: false
schedule:
type: string
description: "Cron expression per backup schedulato"
s3Bucket:
type: string
resources:
type: object
properties:
requests:
type: object
properties:
memory:
type: string
cpu:
type: string
limits:
type: object
properties:
memory:
type: string
cpu:
type: string
status:
type: object
properties:
phase:
type: string
enum: ["Pending", "Creating", "Running", "Degraded", "Failed"]
readyReplicas:
type: integer
primaryEndpoint:
type: string
conditions:
type: array
items:
type: object
properties:
type:
type: string
status:
type: string
reason:
type: string
message:
type: string
lastTransitionTime:
type: string
format: date-time
# Stampa colonne aggiuntive in kubectl get
additionalPrinterColumns:
- name: Replicas
type: integer
jsonPath: .spec.replicas
- name: Version
type: string
jsonPath: .spec.version
- name: Status
type: string
jsonPath: .status.phase
- name: Age
type: date
jsonPath: .metadata.creationTimestamp
# Subresource status (necessario per UpdateStatus)
subresources:
status: {}
カスタムリソースの動作
# my-postgres-cluster.yaml
apiVersion: database.example.com/v1alpha1
kind: PostgresCluster
metadata:
name: myapp-db
namespace: production
spec:
replicas: 3
version: "16"
storage:
size: "100Gi"
storageClass: fast-ssd
backup:
enabled: true
schedule: "0 2 * * *" # ogni notte alle 2:00
s3Bucket: "my-postgres-backups"
resources:
requests:
memory: "2Gi"
cpu: "1000m"
limits:
memory: "4Gi"
cpu: "2000m"
コントローラーパターンと調整ループ
オペレーターの心は、 コントローラ: 継続的に行われるプロセス クラスター内のリソースの現在の状態を監視し、それを望ましい状態と比較します。 カスタムリソースで宣言されます。差異(ドリフト)がある場合、コントローラーは実行します。 二つの状態を調和させるために必要な行動。このサイクルはと呼ばれます ループを調整する.
// Pseudocodice del reconcile loop
for {
desiredState = getDesiredState(customResource)
currentState = getCurrentState(cluster)
if currentState != desiredState {
actions = computeActions(desiredState, currentState)
execute(actions)
}
// Attendi il prossimo trigger (evento API server o requeueing)
waitForTrigger()
}
コントローラーは「純粋なイベント駆動型」アプローチ (各イベントがアクションをトリガーする) を使用しません。 仕様)しかしアプローチ レベルベース: 全体の状態と 和解する。これにより、コントローラーがより堅牢になります。イベント (クラッシュ、再起動) が見つからない場合、 いずれにせよ、コントローラーは再起動して正しい状態に収束します。
Kubebuilder: オペレーターの構築
Kubebuilder は、Go で Operator を構築するための公式 CNCF フレームワークです。を生成します。 プロジェクトのスキャフォールディング、API サーバーとの通信の管理、およびヘルパーの提供 調整ループ用。 Operator SDK は Kubebuilder に基づいており、次のサポートを追加しています。 Helm および Ansible オペレーター。
プロジェクトのセットアップ
# Installa Kubebuilder
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/
# Crea un nuovo progetto Operator
mkdir postgres-operator && cd postgres-operator
kubebuilder init \
--domain database.example.com \
--repo github.com/myorg/postgres-operator
# Genera l'API e il controller per PostgresCluster
kubebuilder create api \
--group database \
--version v1alpha1 \
--kind PostgresCluster \
--resource \
--controller
# Struttura generata:
# api/v1alpha1/
# postgrescluster_types.go <- Definizione della struct CRD
# groupversion_info.go
# internal/controller/
# postgrescluster_controller.go <- Logica del reconcile loop
# config/crd/ <- Manifest YAML della CRD
API タイプを定義する
// api/v1alpha1/postgrescluster_types.go
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corev1 "k8s.io/api/core/v1"
)
// PostgresClusterSpec definisce lo stato desiderato
type PostgresClusterSpec struct {
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=5
Replicas int32 `json:"replicas"`
// +kubebuilder:validation:Enum={"14","15","16"}
Version string `json:"version"`
Storage PostgresStorageSpec `json:"storage,omitempty"`
Backup PostgresBackupSpec `json:"backup,omitempty"`
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
}
type PostgresStorageSpec struct {
// +kubebuilder:default="10Gi"
Size string `json:"size,omitempty"`
StorageClass string `json:"storageClass,omitempty"`
}
type PostgresBackupSpec struct {
Enabled bool `json:"enabled,omitempty"`
Schedule string `json:"schedule,omitempty"`
S3Bucket string `json:"s3Bucket,omitempty"`
}
// PostgresClusterStatus descrive lo stato osservato
type PostgresClusterStatus struct {
Phase string `json:"phase,omitempty"`
ReadyReplicas int32 `json:"readyReplicas,omitempty"`
PrimaryEndpoint string `json:"primaryEndpoint,omitempty"`
Conditions []metav1.Condition `json:"conditions,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Replicas",type=integer,JSONPath=".spec.replicas"
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=".status.phase"
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=".metadata.creationTimestamp"
type PostgresCluster struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec PostgresClusterSpec `json:"spec,omitempty"`
Status PostgresClusterStatus `json:"status,omitempty"`
}
調整ループ: 実装
// internal/controller/postgrescluster_controller.go
package controller
import (
"context"
"fmt"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"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"
databasev1alpha1 "github.com/myorg/postgres-operator/api/v1alpha1"
)
type PostgresClusterReconciler struct {
client.Client
Scheme *runtime.Scheme
}
// +kubebuilder:rbac:groups=database.example.com,resources=postgresclusters,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=database.example.com,resources=postgresclusters/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=apps,resources=statefulsets,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete
func (r *PostgresClusterReconciler) Reconcile(
ctx context.Context,
req ctrl.Request,
) (ctrl.Result, error) {
logger := log.FromContext(ctx)
// 1. Ottieni la Custom Resource
pgCluster := &databasev1alpha1.PostgresCluster{}
if err := r.Get(ctx, req.NamespacedName, pgCluster); err != nil {
if errors.IsNotFound(err) {
// CR eliminata, pulizia gia gestita dai finalizer
return ctrl.Result{}, nil
}
return ctrl.Result{}, err
}
logger.Info("Reconciling PostgresCluster",
"name", pgCluster.Name,
"namespace", pgCluster.Namespace,
"replicas", pgCluster.Spec.Replicas)
// 2. Reconcilia il Service headless
if err := r.reconcileHeadlessService(ctx, pgCluster); err != nil {
return ctrl.Result{}, fmt.Errorf("failed to reconcile headless service: %w", err)
}
// 3. Reconcilia lo StatefulSet
sts, err := r.reconcileStatefulSet(ctx, pgCluster)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to reconcile statefulset: %w", err)
}
// 4. Aggiorna lo status della CR
pgCluster.Status.ReadyReplicas = sts.Status.ReadyReplicas
pgCluster.Status.PrimaryEndpoint = fmt.Sprintf(
"%s-0.%s.%s.svc.cluster.local:5432",
pgCluster.Name,
pgCluster.Name,
pgCluster.Namespace,
)
if sts.Status.ReadyReplicas == pgCluster.Spec.Replicas {
pgCluster.Status.Phase = "Running"
} else if sts.Status.ReadyReplicas > 0 {
pgCluster.Status.Phase = "Degraded"
} else {
pgCluster.Status.Phase = "Creating"
}
if err := r.Status().Update(ctx, pgCluster); err != nil {
return ctrl.Result{}, fmt.Errorf("failed to update status: %w", err)
}
logger.Info("Reconciliation complete",
"phase", pgCluster.Status.Phase,
"readyReplicas", pgCluster.Status.ReadyReplicas)
return ctrl.Result{}, nil
}
func (r *PostgresClusterReconciler) reconcileStatefulSet(
ctx context.Context,
pgCluster *databasev1alpha1.PostgresCluster,
) (*appsv1.StatefulSet, error) {
desired := r.buildStatefulSet(pgCluster)
// Imposta il owner reference per la garbage collection automatica
if err := ctrl.SetControllerReference(pgCluster, desired, r.Scheme); err != nil {
return nil, err
}
existing := &appsv1.StatefulSet{}
err := r.Get(ctx, client.ObjectKeyFromObject(desired), existing)
if errors.IsNotFound(err) {
// StatefulSet non esiste: crealo
if err := r.Create(ctx, desired); err != nil {
return nil, fmt.Errorf("failed to create StatefulSet: %w", err)
}
return desired, nil
}
if err != nil {
return nil, err
}
// StatefulSet esiste: aggiornalo se necessario
existing.Spec.Replicas = desired.Spec.Replicas
existing.Spec.Template = desired.Spec.Template
if err := r.Update(ctx, existing); err != nil {
return nil, fmt.Errorf("failed to update StatefulSet: %w", err)
}
return existing, nil
}
func (r *PostgresClusterReconciler) buildStatefulSet(
pgCluster *databasev1alpha1.PostgresCluster,
) *appsv1.StatefulSet {
image := fmt.Sprintf("postgres:%s", pgCluster.Spec.Version)
return &appsv1.StatefulSet{
ObjectMeta: metav1.ObjectMeta{
Name: pgCluster.Name,
Namespace: pgCluster.Namespace,
},
Spec: appsv1.StatefulSetSpec{
Replicas: &pgCluster.Spec.Replicas,
ServiceName: pgCluster.Name,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": pgCluster.Name},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"app": pgCluster.Name},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "postgres",
Image: image,
Resources: pgCluster.Spec.Resources,
},
},
},
},
VolumeClaimTemplates: []corev1.PersistentVolumeClaim{
{
ObjectMeta: metav1.ObjectMeta{
Name: "data",
},
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{
corev1.ReadWriteOnce,
},
StorageClassName: &pgCluster.Spec.Storage.StorageClass,
Resources: corev1.VolumeResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceStorage: pgCluster.Spec.Storage.ParsedSize(),
},
},
},
},
},
},
}
}
// SetupWithManager registra il controller con il manager
func (r *PostgresClusterReconciler) SetupWithManager(
mgr ctrl.Manager,
) error {
return ctrl.NewControllerManagedBy(mgr).
For(&databasev1alpha1.PostgresCluster{}).
Owns(&appsv1.StatefulSet{}). // Reconcilia quando cambia lo StatefulSet owned
Owns(&corev1.Service{}).
Complete(r)
}
ファイナライザー: リソースのクリーンアップ
ファイナライザーを使用すると、リソースが解放される前にクリーンアップ操作を実行できます。 排除された。ファイナライザーを使用しないと、PostgresCluster CR を削除すると CR も削除されますが、 S3 上のデータやバックアップとは限りません。ファイナライザーを使用すると、このクリーニングを管理できます。
// Aggiungi finalizer handling al Reconcile
const pgClusterFinalizer = "database.example.com/finalizer"
func (r *PostgresClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
pgCluster := &databasev1alpha1.PostgresCluster{}
if err := r.Get(ctx, req.NamespacedName, pgCluster); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// Gestione eliminazione
if !pgCluster.DeletionTimestamp.IsZero() {
if controllerutil.ContainsFinalizer(pgCluster, pgClusterFinalizer) {
// Esegui cleanup
if err := r.cleanupExternalResources(ctx, pgCluster); err != nil {
return ctrl.Result{}, err
}
// Rimuovi il finalizer
controllerutil.RemoveFinalizer(pgCluster, pgClusterFinalizer)
if err := r.Update(ctx, pgCluster); err != nil {
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}
// Aggiungi finalizer se non presente
if !controllerutil.ContainsFinalizer(pgCluster, pgClusterFinalizer) {
controllerutil.AddFinalizer(pgCluster, pgClusterFinalizer)
if err := r.Update(ctx, pgCluster); err != nil {
return ctrl.Result{}, err
}
}
// ... resto della logica di reconcile
return ctrl.Result{}, nil
}
envtest を使用したオペレーターのテスト
Kubebuilder が提供するもの 環境テスト、API サーバーを起動するテスト フレームワーク 統合された方法でコントローラーをテストするための実際の Kubernetes (kubelet とノードなし):
// internal/controller/postgrescluster_controller_test.go
package controller
import (
"context"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
databasev1alpha1 "github.com/myorg/postgres-operator/api/v1alpha1"
)
var _ = Describe("PostgresCluster Controller", func() {
const (
timeout = time.Second * 10
interval = time.Millisecond * 250
)
Context("Quando crea un PostgresCluster", func() {
It("Deve creare lo StatefulSet corrispondente", func() {
ctx := context.Background()
pgCluster := &databasev1alpha1.PostgresCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "test-postgres",
Namespace: "default",
},
Spec: databasev1alpha1.PostgresClusterSpec{
Replicas: 1,
Version: "16",
Storage: databasev1alpha1.PostgresStorageSpec{
Size: "10Gi",
StorageClass: "standard",
},
},
}
Expect(k8sClient.Create(ctx, pgCluster)).Should(Succeed())
// Verifica che lo StatefulSet venga creato
stsLookupKey := types.NamespacedName{
Name: "test-postgres",
Namespace: "default",
}
createdSts := &appsv1.StatefulSet{}
Eventually(func() bool {
err := k8sClient.Get(ctx, stsLookupKey, createdSts)
return err == nil
}, timeout, interval).Should(BeTrue())
// Verifica le specifiche dello StatefulSet
Expect(*createdSts.Spec.Replicas).Should(Equal(int32(1)))
Expect(createdSts.Spec.Template.Spec.Containers[0].Image).
Should(Equal("postgres:16"))
// Cleanup
Expect(k8sClient.Delete(ctx, pgCluster)).Should(Succeed())
})
})
})
オペレーターの構築と展開
# Build dell'immagine
make docker-build docker-push IMG="myregistry/postgres-operator:v0.1.0"
# Deploy dell'Operator nel cluster
make deploy IMG="myregistry/postgres-operator:v0.1.0"
# Verifica il deployment
kubectl get pods -n postgres-operator-system
kubectl logs -n postgres-operator-system deployment/postgres-operator-controller-manager
# Applica una CR
kubectl apply -f my-postgres-cluster.yaml
kubectl get postgresclusters -n production
kubectl describe postgrescluster myapp-db -n production
生産オペレーター: 実際の例
一般的なアプリケーションごとに Operator を構築する必要はありません。生態系 Kubernetes は、主要なステートフル アプリケーション向けに成熟した Operator を提供します。
Zalando Postgres オペレーター
# Installa il Postgres Operator di Zalando (level 5 maturity)
helm repo add postgres-operator-charts \
https://opensource.zalando.com/postgres-operator/charts/postgres-operator
helm install postgres-operator \
postgres-operator-charts/postgres-operator \
-n postgres-operator --create-namespace
# Crea un cluster PostgreSQL con HA e backup su S3
apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
name: myapp-postgres
namespace: production
spec:
teamId: "myteam"
volume:
size: 100Gi
storageClass: fast-ssd
numberOfInstances: 3
users:
myapp:
- superuser
- createdb
databases:
myapp: myapp
postgresql:
version: "16"
parameters:
shared_buffers: "1GB"
max_connections: "200"
resources:
requests:
cpu: 1000m
memory: 2Gi
limits:
cpu: 2000m
memory: 4Gi
patroni:
failsafe_mode: false
# Backup automatico su S3 con WAL-G
enableLogicalBackup: true
logicalBackupSchedule: "00 02 * * *"
Strimzi: Kubernetes 上の Kafka
# Kafka cluster con Strimzi (level 5 maturity)
apiVersion: kafka.strimzi.io/v1beta2
kind: Kafka
metadata:
name: production-cluster
namespace: kafka
spec:
kafka:
version: 3.7.0
replicas: 3
listeners:
- name: plain
port: 9092
type: internal
tls: false
- name: tls
port: 9093
type: internal
tls: true
config:
offsets.topic.replication.factor: 3
transaction.state.log.replication.factor: 3
transaction.state.log.min.isr: 2
default.replication.factor: 3
min.insync.replicas: 2
inter.broker.protocol.version: "3.7"
storage:
type: jbod
volumes:
- id: 0
type: persistent-claim
size: 200Gi
class: fast-ssd
deleteClaim: false
resources:
requests:
memory: 4Gi
cpu: 2000m
limits:
memory: 8Gi
cpu: 4000m
zookeeper:
replicas: 3
storage:
type: persistent-claim
size: 10Gi
class: fast-ssd
deleteClaim: false
entityOperator:
topicOperator: {}
userOperator: {}
オペレーターライフサイクルマネージャー (OLM)
OLM は、インストール、アップグレード、ライフサイクル管理を管理します。 クラスター内のオペレーター。 OperatorHub.io が Operator を配布するために使用するメカニズム。
# Installa OLM nel cluster
curl -sL https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.28.0/install.sh | bash -s v0.28.0
# Installa un Operator da OperatorHub tramite OLM
kubectl create -f https://operatorhub.io/install/postgres-operator.yaml
# Verifica gli Operator installati
kubectl get csv -n operators # ClusterServiceVersion
kubectl get subscription -n operators
オペレーターのためのベストプラクティス
生産オペレーターのためのチェックリスト
- ファイナライザーを使用します。 外部の副作用があるリソース (S3 バケット、DNS レコードなど) に対して常に使用します。
- ステータス条件を実装します。 Kubernetes の条件パターン (タイプ、ステータス、理由、メッセージ) に従ってください。
- 冪等性: 調整ループは、同じ結果を得るために複数回実行しても安全でなければなりません
- エラーを再試行して処理します。 アメリカ合衆国
ctrl.Result{RequeueAfter: time.Minute}一時的なエラーの場合 - ブロック操作を行わないでください。 調整はブロックしてはなりません。長時間の操作にはゴルーチンを使用する
- 最小RBAC: 注釈では厳密に必要な権限のみを使用してください
+kubebuilder:rbac - envtest を使用したテスト: 調整シナリオごとに統合テストを作成する
- バージョン管理 API: 移行にはバージョン管理 (v1alpha1 -> v1beta1 -> v1) と変換 Webhook を使用します。
オペレーターを構築してはいけない場合
オペレーターには多大な開発コストと保守コストがかかります。 1 つ構築する これは次の場合にのみ意味を持ちます: (1) OperatorHub 上に、次のアプリケーションの成熟した Operator が存在する。 あなたが管理しているので、それを使用してください。 (2) アプリケーションは複雑でステートフルであり、知識が必要です 特殊な操作を自動化する。 (3) 維持できる専任チームがいる 時間をかけてコードを進めていきます。単純な展開の場合、これは必要ありません。
結論と次のステップ
Kubernetes Operator パターンは、次の宣言的哲学の自然な拡張です。 Kubernetes から複雑なアプリケーション ドメインへ。データベースを手動で管理する代わりに、 メッセージング システムとステートフル サービスでは、運用上の知識を体系化します。 システムを望ましい状態に保つために年中無休で動作するコントローラー。
Kubebuilder と Operator SDK が基盤を提供します: プロジェクトの足場、管理 サーバー API のフレームワークを調整します。しかし、ビジネスロジック - どのように管理するか PostgreSQL フェイルオーバー、Kafka クラスターをスケーリングする方法、TLS 証明書をローテーションする方法 - 特定のアプリケーションに関する深い知識を持って実装する必要があります。
Kubernetes at Scale シリーズの今後の記事
関連リソースとシリーズ
- Kubernetes ネットワーキング: CNI、Cilium と eBPF
- Kubernetes の永続ストレージ
- MLOps: Kubernetes での ML のスケーリング — トレーニング パイプラインのオペレーター







