Поиграйте с волшебством с клиентским DiscoveryClient

В первом эпизоде ​​Kubernetes GraphQL Query in Go мы создали серверный код, используя GraphQL для запросов к модулям в кластере. Однако такие запросы не отличаются гибкостью и масштабируемостью, поскольку все определения связанных graphql.Feilds жестко запрограммированы, что требует рефакторинга.

А вот и второй эпизод Dynamic Query, где client-go DiscoveryClient используется для гибкого определения структуры запроса GraphQL.

Определения пользовательских ресурсов (CRD)

Большинству пользователей Kubernetes известно, что объекты ресурсов Kubernetes, такие как Pod и Deployment, являются собственными типами ресурсов Kubernetes, и, по сути, родной Kubernetes Operator реализован в форме CRD + Controller, подобных тем настраиваемые пользователем операторы. Единственная разница в том, что нативные типы ресурсов разбросаны по разным пакетам в исходном коде Kubernetes.

  • Пользователь отправляет в кластер файл YAML, соответствующий определению CRD.
  • Kubernetes APIServer хранит ресурсы в etcd на основе соответствующих типов CRD.
  • Контроллер, соответствующий CRD, находит ресурсы для обработки и выполняет процесс согласования, включая обновление статуса ресурса.
  • Сохраните последний статус ресурса в etcd.

Легко сделать вывод, что CRD — это схема, соответствующая типу ресурса Kubernetes, точно так же, как инструкция DDL для таблицы базы данных или файл XSD для файла XML.

Читать схему

При построении запроса GraphQL, соответствующего всем типам ресурсов в кластере, первая трудность, с которой мы сталкиваемся, заключается в том, как получить схему всех типов ресурсов.

Команда Кубектл

Команда kubectl всегда является первым подходом, который мы пробуем в запросах, связанных с Kubernetes.

Все типы ресурсов в текущем кластере могут быть возвращены с помощью kubectl get api-resources, например Pod, Deployment и т. д.

Но это не тот случай, когда при запуске kubectl get crd мы не видим возврата собственного типа ресурса, такого как pod.

Нам определенно нужно искать дополнительные, чтобы найти схему собственных типов ресурсов, и давайте попробуем некоторые.

  • Некоторые инструменты проверки YAML, такие как kubeval, которые указывают на некоторые репозитории GitHub, содержащие схемы различных версий Kubernetes, такие как yannh/kubernetes-json-schema. Эти схемы GitHub также поддерживают получение схемы версии JSON через соответствующий URL-адрес, например схему модуля для версии 1.22.8.
  • kubectl explain, которая может распечатать схему соответствующего типа.

Однако у обоих вышеперечисленных способов есть скрытые недостатки: первый ограничен версиями, и после обновления кластера требуется обновление кода; Второй может возвращать только схему искомого типа, но не схемы вложенного типа, и не поддерживает формат JSON.

  • Discovery API — это то, что я, наконец, нашел, чтобы решить эту сложную проблему.

Пакет обнаружения используется для обнаружения API, поддерживаемых сервером API Kubernetes.

OpenAPISchema извлекает схему open API v2 с помощью оставшегося клиента и анализирует прототип.

API OpenAPISchema в DiscoveryClient возвращает определения схемы для всех типов, определенных в кластере. Вы не можете в полной мере ощутить мощь этого API из его краткого введения, особенно если в файле client-go не отображается ни одного соответствующего примера.

Но попытка большего никогда не разочарует вас. Вначале я пытался использовать этот API только для получения определения CRD в соответствии с руководством документации Kubernetes «CRD — это необязательная схема проверки на основе OpenAPI v3». Затем мне посчастливилось обнаружить, что он может возвращать все определения схемы в кластере, включая нативные типы.

Кубернетес API

Давайте копнем глубже и посмотрим, почему OpenAPISchema function может возвращать все схемы?

Ничего волшебного, просто вызов Kubernetes /openapi/v2 API.

d.restClient.Get().AbsPath(“/openapi/v2”).SetHeader(“Accept”, openAPIV2mimePb).Do(context.TODO()).Raw()

Вернитесь к предыдущей команде kubectl explain, добавьте флаг -v и запустите ее снова.

kubectl explain pods -v 9 отображает

Как видно, именно OpenApi обрабатывает команду explain, но получает данные в формате protobuf и предлагает пользователям более высокую читабельность.

Мы также можем попробовать вызвать OpenApi в CLI, и следующая команда предназначена для GKE.

curl -X GET https://{ip}/openapi/v2 --header "Authorization: Bearer $(gcloud auth application-default print-access-token)" --insecure

Разобрать схему

Мы должны полностью понять полученную информацию, прежде чем использовать ее с пользой.

Экспортируя вывод команды curl в файл, мы видим, что все, что мы получаем, — это анализ каждого API в Kubernetes, точно так же, как документация API swagger. И спуститесь вниз, чтобы увидеть definitions, где показаны определения всех полей для всех типов ресурсов.

Определение пода будет длинным, поэтому в качестве примера возьмем поле resources ниже.

client-go разобрал схему в openapi_v2 Document, который готов к применению в разработке.

Но нельзя игнорировать одну проблему: когда возвращаются все типы ресурсов, трудно отличить настоящие типы ресурсов верхнего уровня, например, мы не хотим, чтобы пользователи запрашивали PodAffinity, а только Pod.

DiscoveryClient также предоставляет API ServerGroups и ServerResourcesForGroupVersion для помощи в создании необходимого GKV(GroupKindVersion).

Теперь мы наконец можем приступить к разбору схемы.

  • Найдите тип ресурса для анализа
  • Найдите поля в типе.
  • Постройте соответствующий graphql.Fields.

Построить запрос GraphQL

Ключом к этому шагу является создание связи между соответствующими типами Kubernetes Go и типами GraphQL с помощью функции graphqlType.

  • Для общего типа подключайтесь напрямую. Такой как
// a TypeItem from the schema
switch openApiType {
case "boolean":
   return graphql.Boolean
case "integer":
   return graphql.Int
case "number":
   return graphql.Float
case "string":
   return graphql.String
  • Для типа объекта создайте тип объекта в GraphQL после рекурсивного синтаксического анализа.
case "object":
   objectFields := parse(document, schema)
   return graphql.NewObject(graphql.ObjectConfig{
      Name:        rt.Kind + "Props",
      Description: schema.GetDescription(),
      Fields:      objectFields,
   })
  • Для типа массива проанализируйте тип GraphQL, соответствующий первому элементу, а затем объедините его в тип списка GraphQL.
case "array":
   iType = graphqlType(docuemnt, possible_type,     schema.GetItems().GetSchema()[0])
   return graphql.NewList(iTypes)

После цикла мы получаем GraphQL Fields, соответствующий всем типам ресурсов в кластере, который можно дополнительно объединить в GraphQL Object.

Теперь давайте проведем быстрый тест в localhost:8080/graphql. Перезапустите программу и попробуйте получить информацию о ресурсах PrometheusRule. Отличный! GraphQL не только возвращает соответствующую информацию, но и соответствующие подсказки при редактировании запроса.

Заворачивать

GraphQL появился 10 лет назад, но не так широко распространен, как REST. Трудно объяснить, почему, так же, как вы не можете понять, почему многие Java-программы до сих пор остаются с Java8 или даже с Java 7.

Но я верю в GraphQL. Он имеет огромные преимущества по сравнению с REST во многих аспектах, например, он значительно упрощает взаимодействие между внешним и внутренним API и повышает эффективность разработки. И поскольку его используют все больше и больше интернет-компаний, в будущем он станет стандартом разработки.

Спасибо за прочтение!

Ссылка

discovery package — k8s.io/client-go/discovery — Go Packages