Лабораторная 3.4: Продвинутые операции клиента
Связанный урок: Урок 3.4: Работа с Client-Go
Навигация: ← Предыдущая лабораторная: Логика согласования | Обзор модуля
Цели
- Использовать продвинутые операции клиента
- Реализовать отслеживание зависимых ресурсов
- Использовать strategic merge patch
- Обрабатывать конфликты с повторами
Предварительные требования
- Завершение Лабораторной 3.3
- Оператор PostgreSQL из предыдущей лабораторной
- Понимание операций клиента
Упражнение 1: получение списка ресурсов с фильтрами
Задача 1.1: список по пространству имён
Добавьте функцию для получения списка всех баз данных в пространстве имён:
func (r *DatabaseReconciler) listDatabasesInNamespace(ctx context.Context, namespace string) (*databasev1.DatabaseList, error) {
list := &databasev1.DatabaseList{}
err := r.List(ctx, list, client.InNamespace(namespace))
return list, err
}
Задача 1.2: список по меткам
func (r *DatabaseReconciler) listDatabasesByLabel(ctx context.Context, labels map[string]string) (*databasev1.DatabaseList, error) {
list := &databasev1.DatabaseList{}
err := r.List(ctx, list, client.MatchingLabels(labels))
return list, err
}
Упражнение 2: реализация strategic merge patch
Задача 2.1: патч реплик StatefulSet
Вместо полного обновления используйте патч:
func (r *DatabaseReconciler) patchStatefulSetReplicas(ctx context.Context, statefulSet *appsv1.StatefulSet, replicas int32) error {
patch := client.MergeFrom(statefulSet.DeepCopy())
statefulSet.Spec.Replicas = &replicas
return r.Patch(ctx, statefulSet, patch)
}
Задача 2.2: используйте в согласовании
Обновите свою функцию reconcileStatefulSet, чтобы использовать патч, когда меняются только реплики:
// If only replicas changed, use patch
if statefulSet.Spec.Replicas != desiredStatefulSet.Spec.Replicas {
return r.patchStatefulSetReplicas(ctx, statefulSet, *desiredStatefulSet.Spec.Replicas)
}
Упражнение 3: обработка конфликтов
Задача 3.1: реализуйте логику повторов
Добавьте вспомогательную функцию для повторов при конфликтах:
func (r *DatabaseReconciler) updateWithRetry(ctx context.Context, obj client.Object, maxRetries int) error {
for i := 0; i < maxRetries; i++ {
err := r.Update(ctx, obj)
if err == nil {
return nil
}
if !errors.IsConflict(err) {
return err
}
// Conflict - get fresh version and retry
key := client.ObjectKeyFromObject(obj)
if err := r.Get(ctx, key, obj); err != nil {
return err
}
time.Sleep(100 * time.Millisecond)
}
return fmt.Errorf("max retries exceeded")
}
Задача 3.2: используйте в согласовании
// Use retry logic for updates
if err := r.updateWithRetry(ctx, statefulSet, 3); err != nil {
return err
}
Упражнение 4: отслеживание зависимых ресурсов
Задача 4.1: настройте отслеживание
Измените SetupWithManager, чтобы отслеживать StatefulSet:
func (r *DatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&databasev1.Database{}).
Owns(&appsv1.StatefulSet{}). // Watch owned StatefulSets
Owns(&corev1.Service{}). // Watch owned Services
Owns(&corev1.Secret{}). // Watch owner Secrets
Complete(r)
}
Задача 4.2: обработка событий отслеживания
Когда StatefulSet меняется, Database будет согласован автоматически!
Упражнение 5: селекторы полей
Задача 5.1: найдите базы данных по владельцу
func (r *DatabaseReconciler) findDatabasesByOwner(ctx context.Context, ownerName string) (*databasev1.DatabaseList, error) {
list := &databasev1.DatabaseList{}
err := r.List(ctx, list, client.MatchingFields{
".metadata.ownerReferences[0].name": ownerName,
})
return list, err
}
Упражнение 6: тестирование продвинутых операций
Задача 6.1: протестируйте патч
# Create database
kubectl apply -f - <<EOF
apiVersion: database.example.com/v1
kind: Database
metadata:
name: my-database
spec:
image: postgres:14
replicas: 1
databaseName: mydb
username: admin
storage:
size: 10Gi
EOF
# Update replicas using patch (simulate)
kubectl patch database my-database --type merge -p '{"spec":{"replicas":2}}'
# Watch operator logs to see patch in action
# Validate 2 replicas are available
kubectl get database my-database -o jsonpath='{.spec.replicas}'
kubectl get statefulset my-database
Задача 6.2: протестируйте обработку конфликтов
# Quickly update multiple times to trigger conflicts
kubectl patch database my-database --type merge -p '{"spec":{"replicas":3}}'
kubectl patch database my-database --type merge -p '{"spec":{"replicas":4}}'
kubectl patch database my-database --type merge -p '{"spec":{"replicas":5}}'
# Observe how operator handles conflicts
# Validate 5 replicas are eventually available
kubectl get database my-database -o jsonpath='{.spec.replicas}'
kubectl get statefulset my-database
Задача 6.3: протестируйте отслеживание
# Manually delete StatefulSet
kubectl delete statefulset my-database
# Watch operator logs - should detect and recreate
kubectl get statefulset my-database
Очистка
# Delete test resources
kubectl delete databases --all
# Validate that all the resources are gone
kubectl get databases my-database
kubectl get statefulset my-database
kubectl get service my-database
kubectl get secret my-database-credentials
Итоги лабораторной
В этой лабораторной вы:
- Использовали продвинутые операции получения списка с фильтрами
- Реализовали strategic merge patch
- Добавили логику повторов при конфликтах
- Настроили отслеживание зависимых ресурсов
- Использовали селекторы полей
- Протестировали все операции
Ключевые уроки
- Операции получения списка можно эффективно фильтровать
- Патчи лучше подходят для частичных обновлений
- Конфликты требуют логики повторов
- Отслеживание обеспечивает реактивное согласование
- Селекторы полей предоставляют мощные запросы
- Продвинутые операции повышают эффективность оператора
Поздравляем!
Вы завершили Модуль 3! Теперь вы понимаете:
- Архитектуру controller-runtime
- Принципы проектирования API
- Логику согласования
- Продвинутые операции клиента
В Модуле 4 вы изучите продвинутые паттерны, такие как условия (conditions), финализаторы и многофазное согласование.