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!▍ Вам понравилось?
Можно продолжить обучение! Вот пара предложений:
- Вы можете попробовать дополнять готовый код, чтобы добавлять ресурс
Deployment
, относящийся к клиентскому приложению Kafka (сначала вам нужно написать его и упаковать как контейнер Docker) и способный обеспечивать доступ к созданному вами кластеру Kafka. Изучите, как можно получать параметры соединения. - Созданный нами кластер Kafka сконфигурирован так, чтобы иметь только доступ Internal. Изучите опции, позволяющее раскрыть его наружу (см. документацию Strimzi) и дополните код, чтобы реализовать это (изменение должно быть небольшим). На какие объекты Kubernetes повлияет это изменение?
Комментарии (4)
kksudo
09.08.2022 00:39Было бы интересно почитать про распределение класса задача, и +- среди подобных утилит: CDKTF, AWS CDK или Pulumi.
CDKTF
Two years ago, we announced a collaboration with Amazon Web Services (AWS) and its Cloud Development Kit (CDK) team, to offer a community preview of Cloud Development Kit for Terraform (CDKTF), a solution to provide a developer-friendly workflow for deploying cloud infrastructure.AWS CDK
The AWS Cloud Development Kit (AWS CDK) is an open-source software development framework to define your cloud application resources using familiar programming languages.Pulumi
Define infrastructure on any cloud using familiar programming languages. For developers, infrastructure teams, and everybody in between.
cdk8s использует первую версию AWS Cloud Development Kit ?
Там еще вышел CDK2, про отличия от первой версии тоже было бы интересно узнать!Буду рад подобным обзорам.
GryZin
Спасибо что познакомили с cdk8s (Cloud Development Kit for Kubernetes). Мне не понятно какую проблему решает этот инструмент? Из описание видно что позволяет определять приложения Kubernetes при помощи обычных языков программирования (вместо
yaml
), однако, не раскрыт вопрос какие трудности возникают с yaml и как cdk8s подход помогает их решить? Спасибо!akurilov
Одним йамлом дело не обходится, его надо шаблонизировать и подставлять параметры. Так возникают всякие ужасы вроде {{ %foo{{&bar..}} - -}} {{ indent somehow}}
mih-kopylov
Выглядит так, что cdk8s призван заменить helm чарты?
Было бы здорово почитать про сравнение этих инструментов