1. Введение
В этом руководстве мы рассмотрим операции CRUD с ресурсами Kubernetes с использованием официального Java API.
Мы уже рассмотрели основы использования этого API в предыдущих статьях, включая базовую настройку проекта и различные способы его использования для получения информации о работающем кластере.
Как правило, развертывания Kubernetes в основном статичны. Мы создаем некоторые артефакты (например, файлы YAML), описывающие то, что мы хотим создать, и отправляем их в конвейер DevOps. Затем части нашей системы остаются неизменными, пока мы не добавим новый компонент или не обновим существующий.
Однако бывают случаи, когда нам нужно добавлять ресурсы на лету. Распространенным является запуск заданий в ответ на запрос, инициированный пользователем. В ответ приложение запускало фоновое задание для обработки отчета и делало его доступным для последующего извлечения.
Ключевым моментом здесь является то, что, используя эти API, мы можем лучше использовать доступную инфраструктуру, поскольку мы можем потреблять ресурсы только тогда, когда они необходимы, а затем освобождать их.
2. Создание нового ресурса
В этом примере мы создадим ресурс Job в кластере Kubernetes. Задание — это своего рода рабочая нагрузка Kubernetes, которая, в отличие от других видов, выполняется до завершения. То есть, как только программы, работающие в его модуле, завершатся, само задание также завершится. Его представление YAML мало чем отличается от других ресурсов:
apiVersion: batch/v1
kind: Job
metadata:
namespace: jobs
name: report-job
labels:
app: reports
spec:
template:
metadata:
name: payroll-report
spec:
containers:
- name: main
image: report-runner
command:
- payroll
args:
- --date
- 2021-05-01
restartPolicy: Never
API Kubernetes предлагает два способа создания эквивалентного объекта Java:
- Создание POJOS с
новыми
и заполнение всех необходимых свойств через сеттеры - Использование свободного API для создания представления ресурсов Java
Какой подход использовать, в основном зависит от личных предпочтений. Здесь мы будем использовать гибкий подход для создания объекта V1Job
, так как процесс сборки очень похож на его аналог YAML:
ApiClient client = Config.defaultClient();
BatchV1Api api = new BatchV1Api(client);
V1Job body = new V1JobBuilder()
.withNewMetadata()
.withNamespace("report-jobs")
.withName("payroll-report-job")
.endMetadata()
.withNewSpec()
.withNewTemplate()
.withNewMetadata()
.addToLabels("name", "payroll-report")
.endMetadata()
.editOrNewSpec()
.addNewContainer()
.withName("main")
.withImage("report-runner")
.addNewCommand("payroll")
.addNewArg("--date")
.addNewArg("2021-05-01")
.endContainer()
.withRestartPolicy("Never")
.endSpec()
.endTemplate()
.endSpec()
.build();
V1Job createdJob = api.createNamespacedJob("report-jobs", body, null, null, null);
Мы начинаем с создания ApiClient,
а затем экземпляра заглушки API. Ресурсы заданий
являются частью API пакетной службы,
поэтому мы создаем экземпляр BatchV1Api
, который будем использовать для вызова сервера API кластера.
Затем мы создаем экземпляр V1JobBuilder
, который как бы ведет нас через процесс заполнения всех свойств. Обратите внимание на использование вложенных построителей: чтобы «закрыть» вложенный построитель, мы должны вызвать его метод endXXX()
, который возвращает нас к его родительскому построителю.
В качестве альтернативы также можно использовать метод withXXX
для непосредственного внедрения вложенного объекта. Это полезно, когда мы хотим повторно использовать общий набор свойств, таких как метаданные, метки и аннотации.
Последний шаг — это просто вызов заглушки API. Это сериализует наш объект ресурса и отправит `` запрос на сервер. Как и ожидалось, существуют синхронная (использованная выше) и асинхронная версии API.
Возвращенный объект будет содержать метаданные и поля состояния, связанные с созданным заданием. В случае с Job
мы можем использовать его поле состояния, чтобы проверить, когда оно завершено. Мы также можем использовать один из методов, представленных в нашей статье о мониторинге ресурсов, для получения этого уведомления.
3. Обновление существующего ресурса
Обновление существующего ресурса состоит из отправки запроса PATCH на сервер Kubernetes API, содержащего поля, которые мы хотим изменить. Начиная с Kubernetes версии 1.16, есть четыре способа указать эти поля:
- Патч JSON (RFC 6092)
- Патч слияния JSON (RFC 7396)
- Патч стратегического слияния
- Применить YAML
Из них последний является самым простым в использовании, поскольку он оставляет все слияния и разрешения конфликтов на сервер: все, что нам нужно сделать, это отправить документ YAML с полями, которые мы хотим изменить.
К сожалению, Java API не предлагает простого способа создания этого частичного документа YAML. Вместо этого мы должны прибегнуть к вспомогательному классу PatchUtil
для отправки необработанной строки YAML или JSON. Однако мы можем использовать встроенный сериализатор JSON, доступный через объект ApiClient
, чтобы получить его:
V1Job patchedJob = new V1JobBuilder(createdJob)
.withNewMetadata()
.withName(createdJob.getMetadata().getName())
.withNamespace(createdJob.getMetadata().getNamespace())
.endMetadata()
.editSpec()
.withParallelism(2)
.endSpec()
.build();
String patchedJobJSON = client.getJSON().serialize(patchedJob);
PatchUtils.patch(
V1Job.class,
() -> api.patchNamespacedJobCall(
createdJob.getMetadata().getName(),
createdJob.getMetadata().getNamespace(),
new V1Patch(patchedJobJSON),
null,
null,
"foreach",
true,
null),
V1Patch.PATCH_FORMAT_APPLY_YAML,
api.getApiClient());
Здесь мы используем объект, возвращаемый функцией createNamespacedJob()
, в качестве шаблона, из которого мы создадим исправленную версию. В данном случае мы просто увеличиваем значение параллелизма
с одного до двух, оставляя все остальные поля без изменений. Важным моментом здесь является то, что при построении измененного ресурса мы должны использовать метод withNewMetadata().
Это гарантирует, что мы не создадим объект, содержащий управляемые поля, которые присутствуют в возвращаемом объекте, который мы получили после создания ресурса. Полное описание управляемых полей и их использования в Kubernetes см. в документации .
Создав объект с измененными полями, мы преобразуем его в представление JSON с помощью метода сериализации
. Затем мы используем эту сериализованную версию для создания объекта V1Patch
, используемого в качестве полезной нагрузки для вызова PATCH. Метод patch
также принимает дополнительный аргумент, в котором мы сообщаем тип данных, присутствующих в запросе. В нашем случае это PATCH_FORMAT_APPLY_YAML
, который библиотека использует как заголовок Content-Type
, включенный в HTTP-запрос.
Значение «foreach»
, переданное параметру fieldManager
, определяет имя субъекта, который манипулирует полями ресурса. Kubernetes использует это значение для внутреннего разрешения возможного конфликта, когда два или более клиентов пытаются изменить один и тот же ресурс. Мы также передаем значение true
в параметре force
, что означает, что мы будем владеть любым измененным полем.
4. Удаление ресурса
По сравнению с предыдущими операциями удаление ресурса выполняется довольно просто:
V1Status response = api.deleteNamespacedJob(
createdJob.getMetadata().getName(),
createdJob.getMetadata().getNamespace(),
null,
null,
null,
null,
null,
null ) ;
Здесь мы просто используем метод deleteNamespacedJob
для удаления задания, используя параметры по умолчанию для этого конкретного типа ресурса. При необходимости мы можем использовать последний параметр для управления деталями процесса удаления. Это принимает форму объекта V1DeleteOptions
, который мы можем использовать для указания льготного периода и каскадного поведения для любых зависимых ресурсов.
5. Вывод
В этой статье мы рассмотрели, как управлять ресурсами Kubernetes с помощью библиотеки API Java Kubernetes. Как обычно, полный исходный код примеров можно найти на GitHub .