Перевели туториал об основах контроллеров, операторов и CRD. В качестве практики вы можете создать кастомный оператор ConfigmapSync для синхронизации Configmap между пространствами имен. Рассказываем, как его написать и развернуть его с помощью Kubebuilder.
Что такое операторы Kubernetes?
В своей простейшей технической форме оператор добавляет в API Kubernetes эндпоинт, называемый custom resource (CR), а также control plane component (controller), который отслеживает и обслуживает ресурсы нового типа. Другими словами, операторы — это программные расширения, которые используют кастомные ресурсы для управления приложениями и их компонентами.
Оператор состоит из трех компонентов: контроллера, кастом ресурса (CR) и состояния (state). Контроллер — это просто некоторая логика — что должно управляться. Обычно он представляет собой цикл наблюдения и настройки: наблюдаем за текущим состоянием, сравниваем его с желаемым состоянием и корректируем состояние. Состояние (state) просто хранит информацию о желаемом состоянии ресурса, а ресурс — это то, чем вы управляете.
Жизненный цикл оператора
Роль оператора заключается в согласовании фактического состояния приложения с желаемым состоянием, используя связку управления control loop, внутри которой оператор может автоматически масштабировать, обновлять или перезапускать приложение.
CRD (Custom Resource Definitions) используются для расширения функциональности Kubernetes путем внедрения новых типов ресурсов, которые не являются частью основного API Kubernetes. Определяя CRD, пользователи или операторы могут создавать свои собственные кастомные ресурсы и определять, как этими ресурсами управлять.
Зачем нам нужен оператор Kubernetes?
Операторы позволяют разработчикам определять кастом ресурсы и связанные с ними контроллеры для управления этими ресурсами. Это нужно для автоматизации сложных задач типа управления приложениями с состоянием, распространения пользовательских обновлений, автоматического масштабирования сервисов в зависимости от спроса.
Используя операторы, разработчики могут создавать кастомные абстракции, которые инкапсулируют их знания о том, как управлять конкретными приложениями или компонентами инфраструктуры.
Всё это повышает производительность и согласованность в управлении сложными приложениями и инфраструктурой, а также упрощает автоматизацию и улучшает контроль над ресурсами.
Преимущество операторов заключается в расширении возможностей автоматизации Kubernetes. Наличие широкой экосистемы K8s Operators позволяет находить готовые решения для большинства задач.
Некоторые примеры использования операторов Kubernetes в реальном мире
С помощью Istio Operator мы можем автоматически устанавливать, обновлять и устранять конфликты Istio. У этого оператора очень мало предварительных требований (фактически только istioctl), и он позволяет как устанавливать, так и проверять все API.
ИсPrometheus Operator для Kubernetes позволяет легко определять параметры мониторинга для сервисов Kubernetes, а также развертывать и управлять экземплярами Prometheus.
Elastic Kubernetes Operator — официально реализованный проектом Elastic - позволяет автоматизировать Elastic Search и Kibana на Kubernetes с помощью значительно упрощенных конфигураций развертывания и легкого управления.
Введение в Kubebuilder: SDK для создания API Kubernetes с использованием CRD.
Kubebuilder — это фреймворк для создания API Kubernetes с использованием библиотеки controller-runtime. Он предоставляет инструменты для генерации кода, создания образов и развертывания контроллеров в качестве ресурсов Kubernetes.
Чтобы создать кастомный оператор с помощью Kubebuilder, вам нужно выполнить следующие шаги. Это позволит загрузить Kubebuilder и сгенерировать необходимый код для добавления нашего кастом контроллера.
Установите Kubebuilder. Следуйте инструкциям здесь.
Создайте новый проект. Выполните следующую команду, чтобы создать новый проект с примером API и контроллера:
$ mkdir -p $GOPATH/src/github.com/example/
$ cd $GOPATH/src/github.com/example/
$ kubebuilder init --domain example.com --repo=github.com/example/my-operator
$ kubebuilder create api --group=mygroup --version=v1alpha1 --kind=MyKind
В результате будет сгенерирована базовая структура для вашего оператора, включая пример API и контроллер.
Определите свой кастом ресурс. Откройте файл
api/v1alpha1/mykind_types.go
и измените структурыMyKindSpec
иMyKindStatus
, чтобы определить нужные поля и статус вашего кастом ресурса.Сгенерируйте CRD. Выполните следующую команду, чтобы сгенерировать CRD для вашего кастом ресурса:
$ make manifests
В результате будет создан файл config/crd/bases/mygroup.example.com_mykinds.yaml
, который содержит CRD для вашего кастом ресурса.
Напишите логику контроллера. Откройте файл
controllers/mykind_controller.go
и напишите логику для вашего контроллера. Обычно это включает в себя наблюдение за изменениями вашего кастом ресурса, сверку желаемого состояния с фактическим и обновление состояния ресурса по мере необходимости.Создайте и разверните свой оператор. Выполните следующие команды для сборки и развертывания оператора:
$ make docker-build docker-push IMG=example/my-operator:latest
$ make deploy IMG=example/my-operator:latest
Это позволит создать образ Docker для вашего оператора, разместить его в реестре и развернуть на кластере Kubernetes.
Создаём свой кастомный оператор — ConfigmapSync
Имея в своем распоряжении инструменты и ресурсы, давайте используем этот механизм для создания специализированного оператора, известного как ConfigmapSync
. Этот оператор предназначен для плавной синхронизации карт конфигурации из одного пространства имен в другие в пределах кластера.
Вот пример оператора Kubernetes, созданного с помощью Kubebuilder для синхронизации ConfigMap из одного пространства имен в другое. Это базовый набросок, иллюстрирующий концепцию:
Сначала создайте новый API в Kubebuilder:
Создайте API. С помощью Kubebuilder создайте новый API под названием
ConfigMapSync
.
kubebuilder create api --group apps --version v1 --kind ConfigMapSync
Определите спецификацию и статус. Обновите файл API
types.go
, чтобы включить в него поля для пространств имен источника и назначения. Файлtypes.go
определяет кастом ресурсConfigMapSync
с его спецификацией и статусом.
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ConfigMapSyncSpec defines the desired state of ConfigMapSync
type ConfigMapSyncSpec struct {
SourceNamespace string `json:"sourceNamespace"`
DestinationNamespace string `json:"destinationNamespace"`
ConfigMapName string `json:"configMapName"`
}
// ConfigMapSyncStatus defines the observed state of ConfigMapSync
type ConfigMapSyncStatus struct {
LastSyncTime metav1.Time `json:"lastSyncTime"`
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// ConfigMapSync is the Schema for the configmapsyncs API
type ConfigMapSync struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ConfigMapSyncSpec `json:"spec,omitempty"`
Status ConfigMapSyncStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// ConfigMapSyncList contains a list of ConfigMapSync
type ConfigMapSyncList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ConfigMapSync `json:"items"`
}
func init() {
SchemeBuilder.Register(&ConfigMapSync{}, &ConfigMapSyncList{})
}
Напишите логику согласования. Реализуйте цикл сверки в контроллере.
// Reconcile method to sync ConfigMap
func (r *ConfigMapSyncReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
ctx := context.Background()
log := r.Log.WithValues("configmapsync", req.NamespacedName)
// Fetch the ConfigMapSync instance
configMapSync := &appsv1.ConfigMapSync{}
if err := r.Get(ctx, req.NamespacedName, configMapSync); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// Fetch the source ConfigMap
sourceConfigMap := &corev1.ConfigMap{}
sourceConfigMapName := types.NamespacedName{
Namespace: configMapSync.Spec.SourceNamespace,
Name: configMapSync.Spec.ConfigMapName,
}
if err := r.Get(ctx, sourceConfigMapName, sourceConfigMap); err != nil {
return ctrl.Result{}, err
}
// Create or Update the destination ConfigMap in the target namespace
destinationConfigMap := &corev1.ConfigMap{}
destinationConfigMapName := types.NamespacedName{
Namespace: configMapSync.Spec.DestinationNamespace,
Name: configMapSync.Spec.ConfigMapName,
}
if err := r.Get(ctx, destinationConfigMapName, destinationConfigMap); err != nil {
if errors.IsNotFound(err) {
log.Info("Creating ConfigMap in destination namespace", "Namespace", configMapSync.Spec.DestinationNamespace)
destinationConfigMap = &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: configMapSync.Spec.ConfigMapName,
Namespace: configMapSync.Spec.DestinationNamespace,
},
Data: sourceConfigMap.Data, // Copy data from source to destination
}
if err := r.Create(ctx, destinationConfigMap); err != nil {
return ctrl.Result{}, err
}
} else {
return ctrl.Result{}, err
}
} else {
log.Info("Updating ConfigMap in destination namespace", "Namespace", configMapSync.Spec.DestinationNamespace)
destinationConfigMap.Data = sourceConfigMap.Data // Update data from source to destination
if err := r.Update(ctx, destinationConfigMap); err != nil {
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}
Создайте и разверните оператор. Выполните следующие команды для сборки и развертывания оператора:
make docker-build docker-push IMG=registry/configmapsync-operator:latest
make deploy IMG=registry/configmapsync-operator:latest
Вы также можете развернуть оператора, используя приведенный ниже манифест:
apiVersion: apps/v1
kind: Deployment
metadata:
name: configmapsync-operator
spec:
replicas: 1
selector:
matchLabels:
app: configmapsync-operator
template:
metadata:
labels:
app: configmapsync-operator
spec:
containers:
- name: configmapsync-operator
image: registry/configmapsync-operator:latest # Replace with your image location
Примените образец манифеста на основе нового CRD оператора к кластеру
apiVersion: apps.bixbite.io/v1
kind: ConfigMapSync
metadata:
labels:
app.kubernetes.io/name: configmapsync
app.kubernetes.io/instance: configmapsync-sample
app.kubernetes.io/part-of: configmapsync
app.kubernetes.io/managed-by: kustomize
app.kubernetes.io/created-by: configmapsync
name: configmapsync-sample
spec:
SourceNamespace: "sourceNamespace"
DestinationNamespace: "destinationNamespace"
ConfigMapName: "configMapName"
Заключение
Мы рассмотрели, как оператор ConfigMapSync упрощает управление ConfigMaps, облегчая их синхронизацию между пространствами имен в кластере Kubernetes. Для этого мы определили CRD, написали логику согласования и развертывание оператора для обеспечения плавной и эффективной синхронизации.
Ещё больше о создании собственных операторов
Если вы хотите больше узнать о создании собственных операторов и заглянуть глубоко под капот Kubernetes, приходите в Слёрм на курс Kubernetes: Мега.
На курсе вы узнаете как работает Kubernetes внутри: изучите тонкости установки и конфигурации production-ready кластера («the-not-so-easy-way»), механизмы обеспечения стабильности и безопасности, отказоустойчивости приложений и масштабирование, разберете стратегические задачи, касающиеся инфраструктуры.