Как написать оператор Kubernetes?
Будучи бэкенд-разработчиком, ежедневно имеющим дело с Kubernetes, я всегда хотел написать оператор, пополнив таким образом багаж знаний. Однако достижение этой цели осложнялось возникавшими препятствиями.
Это история о том, как я написал gobackup-operator во время службы в армии.
Можете перейти сразу к проекту.
Разгорелась подготовка
Желание написать оператор Kubernetes усиливалось. Я читал статьи, изучал репозитории GitHub, консультировался с коллегами. Не сказать чтоб суперуспешно.
Однако результатом всех этих усилий стала коллекция проектов-руководств, хранящихся в моем GitHub.
К практической реализации своего намерения я приступил год назад, с момента первого знакомства с Kubernetes: смотрел руководство гуру для CKAD, затем руководство Nana на YouTube.
Но обратилась в пепел
Меня призвали в армию.
Там не было ни интернета, ни даже какого-либо электронного устройства. Только книги в твердом переплете, волейбол и завораживающие виды восхода и заката.
В этой ситуации мысли о создании оператора угасли. Все, что оставалось: есть, читать и иногда наслаждаться свободой, то есть ходить в увалы. Впрочем, свобода эта непродолжительная. Как однажды заметил командир:
«Радость от увала заканчивается в тот момент, когда покидаешь казарму».
И вот сборы закончились, я стал работать в офисе, но и там ощущалась нехватка интернета! Только по вечерам, уходя из офиса, я занимался любимым делом. Иногда результат получается выше, когда время ограничено. Итак, с 16:00 до 19:00 нужно было создать что-то особенное. Для меня это и было особенным.
Он сказал: «Поехали!»…
В итоге с помощью серии статей удалось написать другой оператор Kubernetes, по руководствам и на этот раз по-иному.
Мои коллеги уже разработали систему резервного копирования, но она оказалась не слишком рабочей. Тогда они стали искать другое решение и наткнулись на проект gobackup, посвященный регулярному созданию резервных копий баз данных и отправлению их в хранилище.
Проблема заключалась в том, что отсутствовала поддержка базы данных etcd. Коллеги приняли участие в проекте, добавив поддержку etcd под имевшиеся требования. В итоге появился новый релиз, на основе которого в мое отсутствие они решили разработать оператор Kubernetes.
Для меня это был важный шаг. Когда они рассказали мне о проекте, я с нетерпением изучил его и подумал: «Ну наконец-то, оператор почти готов».
Пока читал проект, заметил проблему в его README: одна из ссылок вела на страницу 404. Исправив проблему, отправил запрос на включение изменений в репозиторий проекта. Владельцы приняли его с распростертыми объятиями.
Видя такую открытость и желая, чтобы больше людей участвовали в развитии этого оператора, коллеги предложили разместить его в организации gobackup.
Открыв вопрос, я предложил создать в gobackup репозиторий, меня снова приняли как родного.
Днем я служил в армии, а вечерами коммитил проект оператора на gobackup.
Собственно проект
Я начал с настройки среды.
На моем компьютере уже были установлены Golang
, Docker
и kubectl
. С запускаемыми на локальной машине кластерами Kubernetes, такими как Kind
, и инструментами для создания операторов вроде kubebuilder
я познакомился ранее.
Поэтому, запустив код оператора:
$ kubebuilder init --domain gobackup.io --repo github.com/gobackup/gobackup-operator
Я приступил к созданию API-интерфейсов сперва для оператора:
$ kubebuilder create api --group gobackup --version v1 --kind Backup
Create Resource [y/n]
y
Create Controller [y/n]
y
Потом для баз данных и хранилищ:
$ kubebuilder create api --group database.gobackup --version v1 --kind PostgreSQL
Create Resource [y/n]
y
Create Controller [y/n]
y
$ kubebuilder create api --group storage.gobackup --version v1 --kind S3
Create Resource [y/n]
y
Create Controller [y/n]
y
Исходя из конкретных требований проекта, внес изменения в API-интерфейсы:
// Backup — это схема для API резервных копий
type Backup struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec BackupSpec `json:"spec,omitempty"`
Status BackupStatus `json:"status,omitempty"`
BackupModelRef BackupModelRef `json:"backupModelRef,omitempty"`
StorageRefs []StorageRef `json:"storageRefs,omitempty"`
DatabaseRefs []DatabaseRef `json:"databaseRefs,omitempty"`
}
Затем в метод Reconcile
:
//+kubebuilder:rbac:groups=gobackup.io,resources=backups,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=gobackup.io,resources=backups/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=gobackup.io,resources=backups/finalizers,verbs=update
func (r *BackupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// реализация «reconcile»
}
Протестируем
Но прежде подготовим тестовую базу данных для создания из нее резервных копий, создаем развертывание PostgreSQL из этого доступного yaml-файла gobackup-operator-postgres-deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres-deployment
spec:
selector:
matchLabels:
app: postgres
replicas: 1
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:14.11
env:
- name: POSTGRES_USER
value: ""
- name: POSTGRES_PASSWORD
value: ""
- name: PGDATA
value: "/var/lib/postgresql/data/pgdata"
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: postgredb
volumes:
- name: postgredb
persistentVolumeClaim:
claimName: postgres-pvc
Не забываем поменять в манифесте POSTGRES_USER и POSTGRES_PASSWORD и применить:
kubectl apply -f example/gobackup-opetator-postgres-deployment.yaml,
example/gobackup-opetator-postgres-service.yaml
Чтобы протестировать оператор в кластере Kubernetes, я добавил в каталог gobackup-operator/example/
ресурсы: развертывания, роли, clusterroles, serviceaccounts и т. д.
Для добавления базовых ресурсов применяем этот манифест:
kubectl apply -f example/gobackup-opetator-serviceaccount.yaml,
gobackup-opetator-pvc.yaml,
gobackup-opetator-namespace.yaml,
gobackup-opetator-clusterrolebinding.yaml,
gobackup-opetator-clusterrole.yaml
А затем манифесты хранилища и базы данных:
kubectl apply -f example/gobackup-opetator-storage/*
kubectl apply -f example/gobackup-opetator-database/*
Запускаем оператор на локальном компьютере, используя такой манифест:
kubectl apply -f example/gobackup-opetator-deployment.yaml
Когда создается или изменяется объект Backup или CronBackup, оператором выполняются необходимые задачи.
Чтобы создать модель резервного копирования, задаем конфигурацию резервного копирования:
kubectl apply -f example/gobackup-opetator/gobackup-opetator-backupmodel.yaml
При применении в каталоге gobackup-operator/example/gobackup-operator
одного из манифестов — backup или cronbackup — активируется оператор, которым запустится резервное копирование:
kubectl apply -f example/gobackup-opetator/gobackup-opetator-cronbackup.yaml
Заключение
Сначала я чувствовал неловкость из-за внесения столь малого изменения в файл README. Это было похоже на один из тех запросов на включение изменений в репозиторий, которые делаются только для того, чтобы поучаствовать в коммитах на Hacktoberfest.
Но потом я задумался: даже те однострочные коммиты были эффективными. Кто знает, не внеси я то изменение в документ README, создал бы вообще оператор или нет?
Важен и малый шаг.
Загляните и вы сюда поучаствовать. Меняйте даже файл README, если нужно.
Читайте также:
Перевод статьи Payam Qorbanpour: How to Write a Kubernetes Operator