Создайте сервер GraphQL для поиска ресурсов Kubernetes в кластере.

При управлении общим кластером частью нашей ответственности является предоставление пользователям доступа к своим ресурсам, что мы выполняем с помощью RBAC, чтобы назначать разумные разрешения и разрешать запросы пользователей через kubectl. «Никакой барьер» для бэкенд-разработчиков, чего нельзя сказать о таких пользователях, как мобильные, веб-разработчики и разработчики данных, которые не знакомы с операциями командной строки или kubectl.

Тогда как мы сможем преодолеть барьер для наших пользователей?

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

Почему GraphQL

REST и GraphQL — два популярных варианта при реализации взаимодействия между интерфейсом и сервером. Первый уже много лет является интернет-стандартом, а второй — языком запросов API с открытым исходным кодом Facebook, основы которого вы можете найти на graphql.org. И между этими двумя делается много сравнений, но здесь я опубликую только одно из https://graphcms.com/blog/graphql-vs-rest-apis, которое лучше всего визуализирует различия.

Итак, нетрудно сделать вывод, почему мы выбираем GraphQL, а не REST.

  • GraphQL более гибкий.

Его противник REST является жестким. Взяв Pod querying(имя, пространство имен, метки, container_name, сопровождающий) в качестве примера, мы можем понять, почему: REST обычно разрабатывает API с несколькими запросами, такие как GET /pod/:name, GET /pod/:namespace. И есть только два подхода в сценариях, где возвращаемые данные нуждаются в настройке и нужна не вся информация, а только определенные поля.

  • Возвращайте все поля и фильтруйте их с помощью клиента, что значительно увеличивает нагрузку на сеть, когда ответ содержит десятки полей.
  • Сервер настраивает возвращаемые данные в зависимости от потребностей, что сильно влияет на рабочую нагрузку серверной части.

Как тяжело, когда в Kubernetes есть десятки типов ресурсов, каждый из которых поддерживает как минимум три запроса, поэтому нужно разработать сотни REST API, не говоря уже о будущем обслуживании.

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

query {
  Pod(namespace: $namespace, name: $name) {
    metadata {
      name
      namespace
      //labels
      //annotations
    }
    status {
      conditions {
        lastTransitionTime
        message
        reason
        status
        type
      }
    }
    spec {
      //spec fields
    }
  }
}
  • GraphQL более знаком нашим пользователям.

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

Включить GraphQL в Go

Схема (язык схем GraphQL) является ядром GraphQL и описывает модель данных, которую мы хотим запрашивать, среди которых наиболее простым и важным является абстрагирование и определение типа объекта.

При разработке GraphQL на Go мы используем фреймворк graphql-go, на основе которого мы получаем определение схемы GraphQL. 4 элемента

  • Схема типов, которая определяет имя запроса, аргументы, которые использует запрос, а также поля и типы, возвращаемые запросом.
  • Resolver, метод обратного вызова для заполнения возвращаемого сообщения.
  • Subscriber, метод обратного вызова для постепенного обновления результатов.
  • Мутация, метод изменения данных (в данном случае не в деталях).

Схема типа

Определите поле.

type Resource {
  name: String!
  labels: [Label!]
  status: Status!
}

Type так же, как Class в Java и struct в Go, состоит из набора полей, каждое из которых имеет соответствующий тип. Как показано в примере, существуют разные категории типов, среди которых Скалярный тип является наиболее распространенным, например, String в примере и многие из следующих.

GraphQL также поддерживает настраиваемые скалярные типы. Label и Status в примере относятся к Object Type, что позволяет нам определять типы GraphQL, такие как определение диаграмм классов, и связывать их вместе.

[], !, []! — это Type Modifier, которые используются для обозначения Field как массива или как непустого, например, [Label!] указывает, что labels — это массив, состоящий из Label типов, и массив может быть пустым, но Label не должен.

Для других типов взаимодействия, типов объединения и типов ввода, которые поддерживает GraphQL, вы можете обратиться к примерам в документации, если это интересно.

И на стороне graph-go есть также соответствующие типы один к одному, такие как graphql.String, каждое поле которых по умолчанию может быть пустым, и вы можете использовать graphql.NewNonNull для переноса типа, если требуется ненулевое значение.

Резольвер

Следующим шагом является назначение или анализ поля.

Resolver заполняет данные для возврата любым способом, который вы определяете. А для запроса ресурсов в кластере Kubernetes здесь используется client-go.

Подписчик

Зарегистрировав Subscriber, который очень похож на List/Watch, мы можем впоследствии обновлять данные GraphQL. Для вас это не новость, если вы знакомы с шаблоном Kubernetes Informer.

Поскольку мы используем запросы Kubernetes, информер client-go идеально подходит. Но при запросе к базе данных или хранилищу сообщений, такому как Kafka, должны быть другие способы поддержки этого.

После Type Schema, Resolver и Subscriber можно определить простую схему GraphQL для запросов Pod.

Кластер поиска и API-интерфейсы сборки GraphQL

Теперь о применении client-go, который считается лучшим выбором для взаимодействия Go с кластерами Kubernetes, для реализации методов resolver и subscriber.

Что касается запросов Pod, мы напрямую используем clientset API, комбинируя их с различными параметрами, которые мы определили выше.

Для подписчика настраиваем обновление события Add с Информером, возвращаем канал и обрабатываем сообщение об обновлении канала от подписчика с фреймворком graphql-go.

Тест

Давайте запустим сервер GraphQL с помощью Go httpserver, шаг за шагом.

  • Создайте graphqlHandler.
graphqlHandler := handler.New(&handler.Config{
   Schema:     &schema,
   Pretty:     true,
   GraphiQL:   false,
   Playground: true,
})
  • Определите http handler.
http.Handle("/graphql", graphqlHandler)
  • Старт server.
err = http.ListenAndServe(":8080", nil)

Теперь протестируйте его на http://localhost:8080/graphql.

Вход

query {
  Pod(namespace: "prometheus") {
    name
    status
  }
}

И мы получаем

{
   "data":{
      "Pod":{
         "name":"prometheus-khdf12",
         "status":"Running"
      }
   }
}

Развертывать

После инкапсуляции программы в образ докера и развертывания ее в кластере мы теперь готовы открыть сервис для пользователей.

Dockerfile 👇

Следует отметить, что WorkloadIdentity должен быть настроен для включения клиента в кластере GKE. Подробнее см. в разделе Управление кластером — периодическая очистка ресурсов.

Шаг вперед

Запрос GraphQL к поду Kubernetes, который мы выполнили, далек от совершенства, и мы хотим вернуть полную информацию о поде, а не простоеPodShort.

Однако есть несколько проблем, связанных с возвратом полей Pod вручную.

  • Громоздкий. Могут быть десятки полей, которые вложены друг в друга слоями.
  • Неудобен в обслуживании. Обновление кода неизбежно, если Kubernetes откажется или добавит некоторые поля в будущих версиях.
  • Не способствует расширению. Один тип Pod требует громоздких определений, что, если расширить его до десятков или даже сотен типов и CRD в кластере? Кажется, что это невыполнимая миссия.

Есть ли гибкий и расширяемый способ? Короткий ответ — применить client-go, чтобы получить определение CRD в кластере и разобрать его в коллекцию graph.Fields. Подробный ответ читайте в моей следующей статье.

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

Ссылка

https://graphcms.com/blog/graphql-vs-rest-apis

https://graphql.org/learn/schema/