Напишите свою инфраструктуру Kubernetes — как код Go

—

от автора


cdk8s (Cloud Development Kit for Kubernetes) — это опенсорсный фреймворк (часть CNCF), при помощи которого можно определять приложения Kubernetes при помощи обычных языков программирования (вместо yaml). В предыдущих постах по этой теме я познакомил читателей с фреймворком и рассказал об использовании библиотеки cdk8s-plus для дальнейшего расширения базовых возможностей функций библиотеки cdk8s. В этом посте мы ещё больше расширим границы возможностей cdk8s.

Я продемонстрирую, как можно использовать Kubernetes Custom Resource Definitions при помощи cdk8s. Мы начнём с простого примера Nginx, а затем используем комбинацию CRD проекта Strimzi вместе с Go cdk8s для описания и развёртывания кластера Kafka на Kubernetes!

Я буду предполагать, что вы обладаете некоторыми знаниями Kubernetes Custom Resource Definitions и, возможно, даже использовали некоторые из них в виде операторов. Но даже если это не так, то всё в порядке! В документации Kubernetes они достаточно хорошо описаны. Вы всегда можете изучить её, вернуться и продолжить чтение!

cdk8s позволяет использовать объекты Kubernetes API непосредственно в коде, без необходимости импорта отдельных клиентских пакетов Go, благодаря cdk8s import. (также упомянутого в разделе «Wait, what about the Kubernetes API dependencies??» предыдущего поста). Но также можно использовать его для Custom Resource Definitions! Давайте посмотрим, как это происходит.

▍ Перед началом работы

Вам необходимо установить Go (v1.16 или выше) и cdk8s CLI. Также нужно иметь доступ к кластеру Kubernetes. Для изучения и экспериментов я рекомендую работающий локально одноузловой кластер, например, minikube, kind и т. п.

Я обычно использую minikube, где для настройки кластера достаточно ввести minikube start.

▍ Установка Cdk8s Cli

Можно выбрать один из представленных ниже вариантов:

#homebrew brew install cdk8s  #npm npm install -g cdk8s-cli  #yarn yarn global add cdk8s-cli

▍ Итак, давайте приступим

В этом посте будут представлены пошаговые инструкции, но вы всегда можете свериться с полным кодом на Github.

cdk8s значительно упрощает подготовку и запуск собственного приложения. Вам не нужно гадать, как структурировать свой проект, настроить зависимости и т. п., поскольку всё это сделает команда cdk8s init!

cdk8s init go-app  #output ....   Your cdk8s Go project is ready!     cat help      Prints this message      cdk8s synth   Synthesize k8s manifests to dist/    cdk8s import  Imports k8s API objects to "imports/k8s"    Deploy:    kubectl apply -f dist/

Измените файл генерации go.mod и замените его следующим, чтобы упростить себе задачу.

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

module cdk8s-crd  go 1.16  require (     github.com/aws/constructs-go/constructs/v10 v10.1.42     github.com/aws/jsii-runtime-go v1.61.0     github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2 v2.3.34

▍ Для начала давайте поработаем с очень (очень!) простым Custom Resource Definition

Я воспользуюсь примером CRD из примера Kubernetes. Откровенно говоря, он ничего не делает. Но поскольку мы только начинаем работу, этого будет достаточно!

Сначала установим/зарегистрируем сам ресурс CRD:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/sample-controller/master/artifacts/examples/crd.yaml

Проверим, был ли установлен CRD:

kubectl get crd  # output NAME                           CREATED AT foos.samplecontroller.k8s.io   2022-07-08T09:28:46Z  kubectl get foos.samplecontroller.k8s.io  #output (as expected) No resources found in default namespace.

Итак, мы установили CRD с именем foos.samplecontroller.k8s.io и типом Foo. Можно создать его экземпляр при помощи yaml, но…

▍ Мы здесь, чтобы писать код на Go!

Для этого сначала импортируем CRD как API при помощи cdk8s — это автоматически создаст соответствующие обозначения (struct и т. д.):

cdk8s import https://raw.githubusercontent.com/kubernetes/sample-controller/master/artifacts/examples/crd.yaml

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

imports/ └── samplecontrollerk8sio     ├── internal     │   └── types.go     ├── jsii     │   ├── jsii.go     │   └── samplecontrollerk8sio-0.0.0.tgz     ├── samplecontrollerk8sio.go     ├── samplecontrollerk8sio.init.go     └── version

Теперь мы можем использовать CRD почти как любой другой ресурс/API Kubernetes (наподобие Deployment) и импортировать его в код Go cdk8s. Создадим новый файл foo.go и скопируем туда следующий код:

package main  import (     "cdk8s-crd/imports/samplecontrollerk8sio"      "github.com/aws/constructs-go/constructs/v10"     "github.com/aws/jsii-runtime-go"     "github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2" )  type FooChartProps struct {     cdk8s.ChartProps }  func NewFooChart(scope constructs.Construct, id string, props *FooChartProps) cdk8s.Chart {     var cprops cdk8s.ChartProps     if props != nil {         cprops = props.ChartProps     }     chart := cdk8s.NewChart(scope, jsii.String(id), &cprops)      samplecontrollerk8sio.NewFoo(chart, jsii.String("foo1"), &samplecontrollerk8sio.FooProps{Spec: &samplecontrollerk8sio.FooSpec{DeploymentName: jsii.String("foo1-dep"), Replicas: jsii.Number(2)}})      return chart }

Посмотрим, как мы создали экземпляр samplecontrollerk8sio.Foo:

  • Импортировали автоматически сгенерированный CRD API из пакета cdk8s-crd/imports/samplecontrollerk8sio,
  • Использовали функцию NewFoo и передали метаданные при помощи FooProps

Заменим содержимое main.go следующим кодом:

package main  import (     "github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2" )  type MyChartProps struct {     cdk8s.ChartProps }  func main() {     app := cdk8s.NewApp(nil)     NewFooChart(app, "FooApp", nil)     app.Synth() }

Мы включили Chart, который только что определили (в foo.go) и включили его в App cdk8s.

▍ Создание ресурса Foo

Выполните cdk8s synth — в результате этого будет создан манифест в папке dist:

apiVersion: samplecontroller.k8s.io/v1alpha1 kind: Foo spec:   deploymentName: foo1-dep   replicas: 2 metadata:   name: fooapp-foo1-c80094ac

Чтобы создать его в Kubernetes:

kubectl apply -f dist

Можно убедиться, что всё сделано, выполнив следующую команду:

kubectl get foo kubectl get foos.samplecontroller.k8s.io

Для более глубокого изучения мы можем использовать имя созданного ресурса, т. е. kubectl describe foo/fooapp-foo1-c80094ac.

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

▍ Настройка Kafka на Kubernetes при помощи Strimzi, cdk8s и Go

Strimzi — это опенсорсный проект CNCF, один из моих любимых! Вполне нормально, если вы не знаете о Strimzi. Достаточно понимать, что он предоставляет способ запуска Apache Kafka на Kubernetes при помощи Custom Resource Definitions и соответствующих операторов для таких компонентов, как кластер Kafka, тема Kafka Connect, пользователи, Kafka Mirror и т. п.

Вот высокоуровневая диаграмма взаимодействия компонентов Strimzi. Поскольку глубокое изучение Strimzi выходит за рамки нашей статьи, я рекомендую обратиться за подробностями к его (превосходной!) документации.

Как и раньше, сначала нам нужно установить CRD (также можно сверяться с Strimzi Quickstart).

kubectl create namespace kafka kubectl create -f 'https://strimzi.io/install/latest?namespace=kafka' -n kafka  # wait for the Operator Pod to start up (Running) kubectl get pod -n kafka --watch

Также можно проверять логи Operator при помощи kubectl logs deployment/strimzi-cluster-operator -n kafka -f

Каждый поддерживаемый компонент Kafka (кластер, connect, пользователь и так далее) имеет соответствующее Custom Resource Definition. В этом посте мы будем использовать только CRD кластеров и тем Kafka. Давайте импортируем их как API:

cdk8s import https://raw.githubusercontent.com/strimzi/strimzi-kafka-operator/main/install/cluster-operator/040-Crd-kafka.yaml  cdk8s import kafkatopic:=https://raw.githubusercontent.com/strimzi/strimzi-kafka-operator/main/install/cluster-operator/043-Crd-kafkatopic.yaml

Обратите внимание, что для CRD темы Kafka я добавил в начало имени модуля kafkatopic

Проверьте папку imports — там должно быть две дополнительные папки с именами kafkastrimziio и kafkatopic_kafkastrimziio.

▍ Снова настало время для кода на Go

Создадим файл kafka_strimzi.go и скопируем код из репозитория на Github:

Или просто сделайте так: curl -o kafka.go https://raw.githubusercontent.com/abhirockzz/cdk8s-for-go-developers/master/part3-crd/kafka_strimzi.go

Здесь я объясню важные части кода. Начнём с функции NewKafkaChart, создающей новый Chart.

func NewKafkaChart(scope constructs.Construct, id string, props *KafkaChartProps) cdk8s.Chart {     //.... опущено для краткости     chart := cdk8s.NewChart(scope, jsii.String(id), &cprops)

Обратите внимание, что кластер Kafka задаётся при помощи структуры kafkastrimziio.KafkaProps (для подробного изучения каждого из этих компонентов можно обратиться к документации Strimzi). Мы указываем версию Kafka, количество узлов/реплик (будет использоваться одна реплика узла), способ раскрытия кластера и тому подобное.

//.... &kafkastrimziio.KafkaProps{             Spec: &kafkastrimziio.KafkaSpec{                 Kafka: &kafkastrimziio.KafkaSpecKafka{                      Version:  jsii.String("3.2.0"),                     Replicas: jsii.Number(1),                     Listeners: &[]*kafkastrimziio.KafkaSpecKafkaListeners{                         {                             Name: jsii.String("plain"),                             Port: jsii.Number(9092),                             Type: kafkastrimziio.KafkaSpecKafkaListenersType_INTERNAL,                             Tls:  jsii.Bool(false),                         },                     }, //....

Затем мы добавляем необходимую конфигурацию кластера Kafka (встроенную в код, потому что кластер имеет только один узел), а также хранилище (для этого примера подойдёт эфемерное хранилище).

//... Config: map[string]interface{}{                         "offsets.topic.replication.factor":         1,                         "transaction.state.log.replication.factor": 1,                         "transaction.state.log.min.isr":            1,                         "default.replication.factor":               1,                         "min.insync.replicas":                      1,                         "inter.broker.protocol.version":            "3.2",                     },                     Storage: &kafkastrimziio.KafkaSpecKafkaStorage{                         Type: kafkastrimziio.KafkaSpecKafkaStorageType_EPHEMERAL,                     }, //...

Наконец, мы настроим Zookeeper и оператор Entity, обрабатывающий темы Kafka (а также пользователей, хотя здесь мы это не используем).

//... Zookeeper: &kafkastrimziio.KafkaSpecZookeeper{                     Replicas: jsii.Number(1),                     Storage: &kafkastrimziio.KafkaSpecZookeeperStorage{                         Type: kafkastrimziio.KafkaSpecZookeeperStorageType_EPHEMERAL,                     },                 },                 EntityOperator: &kafkastrimziio.KafkaSpecEntityOperator{                     TopicOperator: &kafkastrimziio.KafkaSpecEntityOperatorTopicOperator{},                 }}}) //...

Чтобы соединить всё вместе, дополним файл main.go:

func main() {     app := cdk8s.NewApp(nil)     //NewFooChart(app, "FooApp", nil)     NewKafkaChart(app, "KafkaApp", nil)     app.Synth() }

▍ Для создания кластера Kafka при помощи CRD…

Выполним обычный процесс:

# generate manifest (check it in dist folder) cdk8s synth  # apply it (note the kafka namespace) kubectl apply -f dist/ -n kafka

Дождитесь создания ресурсов:

KAFKA_CRD_INSTANCE_NAME=$(kubectl get kafka -n kafka -o=jsonpath='{.items[0].metadata.name}') kubectl wait kafka/$KAFKA_CRD_INSTANCE_NAME --for=condition=Ready --timeout=300s -n kafka

После создания всех ресурсов кластера Kafka вы должны увидеть следующее сообщение — kafka.kafka.strimzi.io/<name of your Kafka CRD instance> condition met. Теперь кластер Kafka готов и мы можем протестировать его при помощи старых добрых издателя и потребителя Kafka CLI (инструкции можно найти в Strimzi Quickstart).

BOOSTRAP_SERVER=$(kubectl get kafka -n kafka -o=jsonpath='{.items[0].metadata.name}')-kafka-bootstrap  kubectl -n kafka run kafka-producer -ti --image=quay.io/strimzi/kafka:0.29.0-kafka-3.2.0 --rm=true --restart=Never -- bin/kafka-console-producer.sh --bootstrap-server $BOOSTRAP_SERVER:9092 --topic test-topic  kubectl -n kafka run kafka-consumer -ti --image=quay.io/strimzi/kafka:0.29.0-kafka-3.2.0 --rm=true --restart=Never -- bin/kafka-console-consumer.sh --bootstrap-server $BOOSTRAP_SERVER:9092 --topic test-topic --from-beginning

И на этом всё!

Подведём итог

Мы узнали о том, как комбинировать Kubernetes Custom Resource Definition c cdk8s. Это очень мощная система, позволяющая продолжать использование кода (в данном случае написанного на Go) для определения встроенных ресурсов Kubernetes (например, Deployment и так далее), а также Custom Resources!

▍ Вам понравилось?

Можно продолжить обучение! Вот пара предложений:

  1. Вы можете попробовать дополнять готовый код, чтобы добавлять ресурс Deployment, относящийся к клиентскому приложению Kafka (сначала вам нужно написать его и упаковать как контейнер Docker) и способный обеспечивать доступ к созданному вами кластеру Kafka. Изучите, как можно получать параметры соединения.
  2. Созданный нами кластер Kafka сконфигурирован так, чтобы иметь только доступ Internal. Изучите опции, позволяющее раскрыть его наружу (см. документацию Strimzi) и дополните код, чтобы реализовать это (изменение должно быть небольшим). На какие объекты Kubernetes повлияет это изменение?


ссылка на оригинал статьи https://habr.com/ru/company/ruvds/blog/680664/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *