Ответ
Да, разрабатывал кастомные операторы на Go с использованием Kubebuilder для управления состоянием специфичных для компании ресурсов. Оператор — это предпочтительный способ автоматизации сложной логики в k8s, которую нельзя описать стандартными контроллерами.
Мой опыт включает:
- Оператор для управления сетевыми политиками Calico: Автоматически создавал
NetworkPolicyна основе аннотаций в Deployment'ах. Оператор следил за Deployments с аннотациейnetpolicy.company.com/enabled: "true"и генерировал соответствующие политики, изолирующие трафик. - Оператор для канареечного деплоя: Управлял канареечными релизами, постепенно переводя трафик с одного Deployment на другой на основе анализа метрик из Prometheus (например, процент ошибок HTTP 5xx).
Ключевые аспекты разработки:
- Определение Custom Resource Definition (CRD): Создание CRD, например,
CanaryDeployment, с полямиspec.image,spec.trafficStep. - Реализация reconcile-цикла: Логика в контроллере, которая приводит фактическое состояние кластера (существующие Deployments, Services) к желаемому, описанному в CR.
- Обработка ошибок и повторные попытки: Корректная настройка
RequeueAfterпри временных ошибках. - Тестирование: Использование
envtestот Kubebuilder для модульного тестирования логики контроллера.
Упрощенный фрагмент reconcile-логики для оператора NetworkPolicy:
func (r *NetworkPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
var dep appsv1.Deployment
if err := r.Get(ctx, req.NamespacedName, &dep); err != nil {
// Обработка удаления Deployment
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// Проверяем аннотацию
if dep.Annotations["netpolicy.company.com/enabled"] != "true" {
// Удаляем созданную ранее NetworkPolicy, если она есть
return ctrl.Result{}, r.cleanupNetworkPolicy(ctx, dep)
}
// Создаем или обновляем NetworkPolicy
desiredPolicy := buildNetworkPolicy(&dep)
if err := r.createOrUpdatePolicy(ctx, desiredPolicy); err != nil {
// В случае ошибки просим перезапустить reconcile через 10 секунд
return ctrl.Result{RequeueAfter: 10 * time.Second}, err
}
return ctrl.Result{}, nil
}