Лабораторная 4.3: Настройка отслеживания и индексов
Связанный урок: Урок 4.3: Отслеживание и индексирование
Навигация: ← Предыдущая лабораторная: Финализаторы | Обзор модуля | Следующая лабораторная: Продвинутые паттерны →
Цели
- Настроить отслеживание зависимых ресурсов
- Создать индексы для эффективного поиска
- Обрабатывать события отслеживания
- Протестировать поведение отслеживания
Предварительные требования
- Завершение Лабораторной 4.2
- Оператор Database с финализаторами
- Понимание паттернов отслеживания
Упражнение 1: отслеживание подчинённых ресурсов
Задача 1.1: обновите SetupWithManager
Мы уже изменили SetupWithManager, чтобы отслеживать подчинённые ресурсы:
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 owned Secrets
Complete(r)
}
Задача 1.2: протестируйте поведение отслеживания
# Install and run operator
make install
make run
# Create Database
kubectl apply -f - <<EOF
apiVersion: database.example.com/v1
kind: Database
metadata:
name: test-db
spec:
image: postgres:14
replicas: 1
databaseName: mydb
username: admin
storage:
size: 10Gi
EOF
# Manually delete StatefulSet
kubectl delete statefulset test-db
# Watch operator logs - should detect and recreate
# Validate the deleted statefulset appears
kubectl get statefulset test-db
# Delete the database
kubectl delete database test-db
Упражнение 2: отслеживание неподчинённых ресурсов
Задача 2.1: отслеживание Secret
Добавьте отслеживание Secret, на которые ссылаются Database:
import (
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/source"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"k8s.io/apimachinery/pkg/types"
)
func (r *DatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&databasev1.Database{}).
Owns(&appsv1.StatefulSet{}).
Owns(&corev1.Service{}).
// deliberately removing Owns(&corev1.Secret{}). to demonstrate non-owned resources
Watches(
&corev1.Secret{},
handler.EnqueueRequestsFromMapFunc(r.findDatabasesForSecret),
).
Complete(r)
}
func (r *DatabaseReconciler) findDatabasesForSecret(ctx context.Context, secret client.Object) []reconcile.Request {
databases := &databasev1.DatabaseList{}
r.List(context.Background(), databases)
var requests []reconcile.Request
for _, db := range databases.Items {
// If Database references this Secret
if r.secretName(&db) == secret.GetName() &&
db.Namespace == secret.GetNamespace() {
requests = append(requests, reconcile.Request{
NamespacedName: types.NamespacedName{
Name: db.Name,
Namespace: db.Namespace,
},
})
}
}
return requests
}
Задача 2.2: протестируйте отслеживание Secret
# Install and run operator
make install
make run
# Create Database
kubectl apply -f - <<EOF
apiVersion: database.example.com/v1
kind: Database
metadata:
name: test-db
spec:
image: postgres:14
replicas: 1
databaseName: mydb
username: admin
storage:
size: 10Gi
EOF
# Update the Secret
kubectl patch secret test-db-credentials --type merge -p '{"data":{"password":"newpassword"}}'
# Watch operator logs - should reconcile Database
Упражнение 3: создание индексов
Индексы позволяют эффективно искать ресурсы по значениям полей без сканирования всех объектов.
Задача 3.1: настройте индекс
Добавьте индекс для поля image, чтобы быстро находить все Database, использующие конкретную версию PostgreSQL:
func (r *DatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error {
// Create index for image field
if err := mgr.GetFieldIndexer().IndexField(
context.Background(),
&databasev1.Database{},
"spec.image",
func(obj client.Object) []string {
db, ok := obj.(*databasev1.Database)
if !ok {
return nil
}
if db.Spec.Image != "" {
return []string{db.Spec.Image}
}
return nil
},
); err != nil {
return err
}
return ctrl.NewControllerManagedBy(mgr).
For(&databasev1.Database{}).
Owns(&appsv1.StatefulSet{}).
Owns(&corev1.Service{}).
Watches(
&corev1.Secret{},
handler.EnqueueRequestsFromMapFunc(r.findDatabasesForSecret),
).
Complete(r)
}
Задача 3.2: используйте индекс в запросе
Используйте индекс, чтобы эффективно находить все Database, использующие конкретный образ:
// findDatabasesByImage finds all Databases using a specific PostgreSQL image
func (r *DatabaseReconciler) findDatabasesByImage(ctx context.Context, image string) ([]databasev1.Database, error) {
databases := &databasev1.DatabaseList{}
err := r.List(ctx, databases, client.MatchingFields{
"spec.image": image,
})
if err != nil {
return nil, err
}
return databases.Items, nil
}
Задача 3.3: протестируйте использование индекса
# Install and run operator
make install
make run
# Create databases with different images
kubectl apply -f - <<EOF
apiVersion: database.example.com/v1
kind: Database
metadata:
name: db-postgres14
spec:
image: postgres:14
replicas: 1
databaseName: mydb
username: admin
storage:
size: 10Gi
---
apiVersion: database.example.com/v1
kind: Database
metadata:
name: db-postgres15
spec:
image: postgres:15
replicas: 1
databaseName: mydb
username: admin
storage:
size: 10Gi
---
apiVersion: database.example.com/v1
kind: Database
metadata:
name: db-postgres14-2
spec:
image: postgres:14
replicas: 1
databaseName: testdb
username: admin
storage:
size: 5Gi
EOF
# The index allows efficient lookup - finding all postgres:14 databases
# doesn't require scanning every Database object
Примечание: индексы особенно полезны, когда у вас много ресурсов и нужно быстро находить подмножества. Без индекса
List()с сопоставлением по полям пришлось бы сканировать все объекты.
Упражнение 4: предикаты событий
Задача 4.1: добавьте предикаты
Фильтруйте события, чтобы согласовывать только при важных изменениях.
Важно: при фильтрации обновлений StatefulSet вы должны включать как изменения spec (Generation), ТАК И изменения status (ReadyReplicas). Иначе Database никогда не станет Ready, потому что обновления status будут отфильтрованы!
import (
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/event"
)
func (r *DatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&databasev1.Database{}).
Owns(&appsv1.StatefulSet{}, builder.WithPredicates(predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
oldSS := e.ObjectOld.(*appsv1.StatefulSet)
newSS := e.ObjectNew.(*appsv1.StatefulSet)
// Reconcile on spec changes (Generation) OR status changes (ReadyReplicas)
// Without checking ReadyReplicas, Database status would never update to Ready!
return oldSS.Generation != newSS.Generation ||
oldSS.Status.ReadyReplicas != newSS.Status.ReadyReplicas
},
CreateFunc: func(e event.CreateEvent) bool {
return true
},
DeleteFunc: func(e event.DeleteEvent) bool {
return true
},
})).
Owns(&corev1.Service{}).
Watches(
&corev1.Secret{},
handler.EnqueueRequestsFromMapFunc(r.findDatabasesForSecret),
).
Complete(r)
}
Упражнение 5: тестирование производительности отслеживания
Задача 5.1: создайте несколько ресурсов
# Install and run operator
make install
make run
# Create multiple Databases
for i in {1..10}; do
kubectl apply -f - <<EOF
apiVersion: database.example.com/v1
kind: Database
metadata:
name: db-$i
spec:
image: postgres:14
replicas: 1
databaseName: db$i
username: admin
storage:
size: 10Gi
EOF
done
Задача 5.2: понаблюдайте за поведением отслеживания
# Watch operator logs
# Should see efficient reconciliation
# Update one Database
kubectl patch database db-5 --type merge -p '{"spec":{"replicas":2}}'
# Only db-5 should be reconciled
Очистка
# Delete all test resources
kubectl delete databases --all
Итоги лабораторной
В этой лабораторной вы:
- Настроили отслеживание подчинённых ресурсов
- Отследили неподчинённые ресурсы
- Создали индексы для эффективного поиска
- Добавили предикаты событий
- Протестировали производительность отслеживания
Ключевые уроки
- Отслеживайте подчинённые ресурсы с помощью
Owns() - Отслеживайте неподчинённые ресурсы с помощью
Watches() - Индексы обеспечивают быстрый поиск
- Предикаты событий фильтруют события
- Отслеживание делает контроллеры реактивными
- Правильное отслеживание повышает производительность
Решения
Полные рабочие решения для этой лабораторной доступны в каталоге решений:
- Watch Setup — примеры настройки отслеживания подчинённых и неподчинённых ресурсов
Дальнейшие шаги
Теперь давайте реализуем продвинутые паттерны, такие как многофазное согласование!