Использование CRD в качестве API
cdk8s (Cloud Development Kit for Kubernetes) — это фреймворк с открытым исходным кодом (часть CNCF), с помощью которого вы можете определять свои приложения Kubernetes с помощью обычных языков программирования (вместо yaml
). В некоторых из предыдущих блогов на эту тему рассказывалось о том, как начать работу и использовать библиотеку cdk8s-plus
для дальнейшего улучшения основных функций библиотеки cdk8s
. Мы продолжим и продвинем cdk8s
еще дальше.
Этот пост в блоге продемонстрирует, как вы можете использовать Определения пользовательских ресурсов Kubernetes с cdk8s
. Мы начнем с простого примера Nginx
, а затем вы будете использовать комбинацию CRD проекта 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 (struct
s и т. д.):
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 с открытым исходным кодом, один из моих любимых! Если вы не знаете о Стримзи, ничего страшного. Достаточно понять, что он предоставляет способ запуска Apache Kafka в Kubernetes с помощью пользовательских определений ресурсов и соответствующих операторов для таких компонентов, как кластер Kafka, тема Kafka Connect, пользователи, зеркало Kafka и т. д.
Вот общая диаграмма того, как взаимодействуют различные компоненты Strimzi. Поскольку глубокое погружение Стримзи выходит за рамки, я бы порекомендовал вам обратиться к его (отличной!) документации за подробностями.
Как и прежде, нам нужно сначала установить сам CRD (также можно обратиться к Быстрому старту Стримзи)
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 (кластер, подключение, пользователь и т. д.) имеет соответствующее определение пользовательского ресурса — для целей этого сообщения в блоге мы просто будем использовать 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
к имени модуля для темы CRD Kafka
Проверьте папку 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 operator, который обрабатывает топики 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).
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 с cdk8s
. Это действительно мощно и означает, что вы можете продолжать использовать код (в данном случае написанный на Go) для определения встроенных ресурсов Kubernetes (таких как Deployment
s и т. д.), а также пользовательских ресурсов!
Вам понравилось то, что вы пробовали?
Что ж, можно продолжать учиться!
Несколько предложений:
- Вы можете попробовать обновить существующий код, чтобы добавить ресурс
Deployment
, который ссылается на клиентское приложение Kafka (сначала его нужно написать и упаковать как контейнер Docker) и получить доступ к созданному вами кластеру Kafka. Узнайте, как получить параметры подключения. - Созданный нами кластер Kafka был настроен только на внутренний доступ. Изучите варианты, чтобы выставить его извне (обратитесь к документации Strimzi) и обновите код, чтобы реализовать это (должно быть небольшое изменение). На какие объекты Kubernetes это повлияет?
Удачного кодирования!