Напишите инфраструктуру Kubernetes как код на Go — использование пользовательских определений ресурсов с помощью cdk8s

cdk8s (Cloud Development Kit for Kubernetes) — это фреймворк с открытым исходным кодом (часть CNCF), используя который вы можете определять свои приложения Kubernetes с помощью обычных языков программирования (вместо yaml). В некоторых из предыдущих блогов на эту тему рассказывалось о начале работы и использовании библиотеки cdk8s-plus для дальнейшего улучшения основных возможностей библиотеки cdk8s. Мы собираемся продолжить и продвинуть cdk8s еще дальше.
В этой статье блога будет показано, как можно использовать Kubernetes Custom Resource Definitions с cdk8s. Мы начнем с простого примера Nginx, а затем вы будете использовать комбинацию CRDs проекта Strimzi вместе с Go и cdk8s для определения и развертывания кластера Kafka на Kubernetes!

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

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

Прежде чем начать…

Убедитесь, что у вас установлены 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
)
Вход в полноэкранный режим Выйти из полноэкранного режима

Для начала давайте поработаем с очень (очень!) простым пользовательским определением ресурса.

Я собираюсь использовать пример 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 — это автоматически создаст соответствующие представления Go API (structs и т.д.):

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) и импортировать его в код cdk8s Go. Создайте новый файл 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) и включить его в cdk8s App.

Чтобы создать ресурс 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 с помощью пользовательских определений ресурсов и соответствующих операторов для таких компонентов, как кластер Kafka, тема Kafka Connect, пользователи, зеркало Kafka и т.д.

Вот высокоуровневая диаграмма взаимодействия различных компонентов 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
Войти в полноэкранный режим Выйти из полноэкранного режима

Вы также можете проверить журналы Оператора, используя kubectl logs deployment/strimzi-cluster-operator -n kafka -f.

Каждый поддерживаемый компонент Kafka (кластер, соединение, пользователь и т.д.) имеет соответствующее 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
Вход в полноэкранный режим Выйти из полноэкранного режима

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

Проверьте папку 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 {
    //.... ommitted for brevity
    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 с cdk8s. Это действительно мощный инструмент и означает, что вы можете продолжать использовать код (в данном случае написанный на Go) для определения встроенных ресурсов Kubernetes (таких как Deployments и т.д.), а также пользовательских ресурсов!

Вам понравилось то, что вы попробовали?

Что ж, вы можете продолжить обучение! Пара предложений:

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

Счастливого кодирования!

Оцените статью
devanswers.ru
Добавить комментарий