Создайте сервер 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
. Подробный ответ читайте в моей следующей статье.
Спасибо за прочтение!