Пишите инфраструктуру Kubernetes как код на Go — cdk8s-plus в действии!

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

Вы смогли настроить простой nginx Deployment и получить доступ к нему через Service — все это было сделано с помощью Go, который затем был преобразован в yaml (с помощью cdk8s synth) и отправлен на кластер с помощью kubectl. Это было хорошее начало. Однако, поскольку ядро библиотеки cdk8s довольно низкоуровневое (по уважительной причине!), код включал в себя много шаблонов (вы можете обратиться к коду здесь).

cdk8s-plus использует строительные блоки из основной библиотеки cdk8s, тем самым помогая уменьшить многословность и сложность, предоставляя абстракции/API более высокого уровня для всех объектов Kubernetes, таких как Deployments, Services и т.д. В этом блоге мы увидим cdk8s-plus в действии и даже развернем с его помощью WordPress на Kubernetes!


Начнем с переделки развертывания Nginx.

Чтобы увидеть, как работает cdk8s-plus, лучше всего посмотреть на код.

Он доступен на Github.

Я проведу вас по коду по ходу дела.

func NewNginxChart(scope constructs.Construct, id string, props *NginxChartProps) cdk8s.Chart {
    var cprops cdk8s.ChartProps
    if props != nil {
        cprops = props.ChartProps
    }
    chart := cdk8s.NewChart(scope, jsii.String(id), &cprops)

    dep := cdk8splus22.NewDeployment(chart, jsii.String("deployment"), &cdk8splus22.DeploymentProps{Metadata: &cdk8s.ApiObjectMetadata{Name: jsii.String("nginx-deployment-cdk8s-plus")}})

    dep.AddContainer(&cdk8splus22.ContainerProps{
        Name:  jsii.String("nginx-container"),
        Image: jsii.String("nginx"),
        Port:  jsii.Number(80)})

    dep.ExposeViaService(&cdk8splus22.DeploymentExposeViaServiceOptions{
        Name:        jsii.String("nginx-container-service"),
        ServiceType: cdk8splus22.ServiceType_LOAD_BALANCER,
        Ports:       &[]*cdk8splus22.ServicePort{{Port: jsii.Number(9090), TargetPort: jsii.Number(80)}}})

    return chart
}
Вход в полноэкранный режим Выход из полноэкранного режима

Мы начинаем с создания развертывания, затем добавляем контейнер и, наконец, открываем его с помощью службы. Это довольно интуитивно понятно и удобно для пользователя.

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

Чтобы создать манифест Kubernetes, просто запустите cdk8s synth. Это сгенерирует yaml в папке dist. Вот пример (некоторые имена, метки и т.д. в вашем случае будут другими):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-cdk8s-plus
spec:
  minReadySeconds: 0
  progressDeadlineSeconds: 600
  replicas: 1
  selector:
    matchLabels:
      cdk8s.io/metadata.addr: nginx-cdk8s-plus-deployment-c84b388e
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      labels:
        cdk8s.io/metadata.addr: nginx-cdk8s-plus-deployment-c84b388e
    spec:
      automountServiceAccountToken: true
      containers:
        - image: nginx
          imagePullPolicy: Always
          name: nginx-container
          ports:
            - containerPort: 80
          securityContext:
            privileged: false
            readOnlyRootFilesystem: false
            runAsNonRoot: false
      dnsPolicy: ClusterFirst
      securityContext:
        fsGroupChangePolicy: Always
        runAsNonRoot: false
      setHostnameAsFQDN: false
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-container-service
spec:
  externalIPs: []
  ports:
    - port: 9090
      targetPort: 80
  selector:
    cdk8s.io/metadata.addr: nginx-cdk8s-plus-deployment-c84b388e
  type: LoadBalancer
Войти в полноэкранный режим Выйти из полноэкранного режима

И Deployment, и Service присутствуют в одном манифесте, поскольку они были объявлены в одном и том же Chart.

Стоит отметить, что не было необходимости указывать какие-либо селекторы меток Pod, шаблонные метки (в коде Deployment) или селектор Service. cdk8s-plus позаботился об этом, автоматически генерируя cdk8s.io/metadata.addr: nginx-cdk8s-plus-deployment-c84b388e, который использовался в spec. selector.matchLabels и spec.template.metadata.labels, вместе со службой selector в nginx-container-service.

Замечание о зависимостях

В go.mod перечислены все модули:

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.31
    github.com/cdk8s-team/cdk8s-plus-go/cdk8splus22/v2 v2.0.0-rc.23
)
Вход в полноэкранный режим Выход из полноэкранного режима

Обратите внимание, что мы используем cdk8splus22. Причина такого именования в том, что каждая библиотека cdk8s-plus поставляется отдельно для конкретной версии Kubernetes — 22 в конце означает, что эта зависимость будет работать с Kubernetes 1.22.

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

Чтобы протестировать это локально…

… вы можете использовать minikube, kind и т.д.

git clone https://github.com/abhirockzz/cdk8s-for-go-developers
cd part2-cdk8s-plus-in-action/nginx-example

# make sure cluster is running
minikube start

# create the resources
kubectl apply -f dist/
kubectl get pods -w
Вход в полноэкранный режим Выйдите из полноэкранного режима

Когда Pod запущен, проверьте Service:

kubectl get svc
Войти в полноэкранный режим Выйти из полноэкранного режима

В терминале выполните эту команду (она запускается как отдельный процесс):

minikube tunnel
Войти в полноэкранный режим Выйти из полноэкранного режима

Чтобы получить доступ к серверу nginx, перейдите на внешний IP (согласно Service). В случае с minikube вы можете просто использовать localhost:9090 или 127.0.0.0:9090.


Как насчет установки WordPress на Kubernetes?

Мне нравится этот пример — он не слишком сложный, но достаточно реалистичный, поскольку в нем есть множество подвижных частей, включающих комбинацию stateless и stateful компонентов, различные виды сервисов и т.д.

Этот пост не является глубоким погружением в WordPress, а написан по мотивам этой статьи в документации Kubernetes, с которой, как я предполагаю, люди могут быть знакомы.

Функция main даст вам представление о том, что вас ждет впереди:

func main() {
    app := cdk8s.NewApp(nil)

    mySQLChart := NewMySQLChart(app, "mysql", nil)
    wordpressChart := NewWordpressChart(app, "wordpress", nil)

    wordpressChart.AddDependency(mySQLChart)

    app.Synth()
}
Вход в полноэкранный режим Выход из полноэкранного режима

До сих пор мы имели дело с одним графиком. Наше приложение WordPress cdk8s имеет два отдельных графика — один для базы данных MySQL, а другой для WordPress. Это приведет к тому, что в результате процесса cdk8s synth будут созданы два разных манифеста.

Давайте сначала рассмотрим схему MySQL

часть кода опущена для краткости

Начнем с определения Kubernetes Secret для хранения пароля MySQL (с помощью NewSecret):

func NewMySQLChart(scope constructs.Construct, id string, props *MyChartProps) cdk8s.Chart {
    //....
    secretName := "mysql-pass"
    password := "Password123"

    mysqlSecret := cdk8splus22.NewSecret(chart, jsii.String("mysql-secret"),
        &cdk8splus22.SecretProps{
            Metadata: &cdk8s.ApiObjectMetadata{Name: jsii.String(secretName)}})

    secretKey := "password"
    mysqlSecret.AddStringData(jsii.String(secretKey), jsii.String(password))
Вход в полноэкранный режим Выход из полноэкранного режима

Пароль MySQL был объявлен в коде — ни в коем случае не лучшая практика, просто для демонстрации. Не делайте этого в продакшене!

Затем мы создаем Deployment и предоставляем детали контейнера. Обратите внимание, что Secret был добавлен в контейнер в качестве переменной окружения:

  • Сначала мы получили значение EnvValue с помощью EnvValue_FromSecretValue.
  • Оно было добавлено в контейнер с помощью Env#AddVariable
    dep := cdk8splus22.NewDeployment(chart, jsii.String("mysql-deployment-cdk8splus"), &cdk8splus22.DeploymentProps{})

    containerImage := "mysql"

    mysqlContainer := dep.AddContainer(&cdk8splus22.ContainerProps{
        Name:  jsii.String("mysql-container"),
        Image: jsii.String(containerImage),
        Port:  jsii.Number(3306),
    })

    envValFromSecret := cdk8splus22.EnvValue_FromSecretValue(&cdk8splus22.SecretValue{Key: jsii.String(secretKey), Secret: mysqlSecret}, &cdk8splus22.EnvValueFromSecretOptions{Optional: jsii.Bool(false)})

    mySQLPasswordEnvName := "MYSQL_ROOT_PASSWORD"

    mysqlContainer.Env().AddVariable(jsii.String(mySQLPasswordEnvName), envValFromSecret)
Вход в полноэкранный режим Выйти из полноэкранного режима

Для долговременного хранения мы создаем PersistentVolumeClaim, используем его для определения тома и монтируем его в контейнер по пути /var/lib/mysql.

    mysqlPVC := cdk8splus22.NewPersistentVolumeClaim(chart, jsii.String("mysql-pvc"), &cdk8splus22.PersistentVolumeClaimProps{
        AccessModes: &[]cdk8splus22.PersistentVolumeAccessMode{cdk8splus22.PersistentVolumeAccessMode_READ_WRITE_ONCE},
        Storage:     cdk8s.Size_Gibibytes(jsii.Number(2))})

    mysqlVolumeName := "mysql-persistent-storage"
    mysqlVolume := cdk8splus22.Volume_FromPersistentVolumeClaim(chart, jsii.String("mysql-vol-pvc"), mysqlPVC, &cdk8splus22.PersistentVolumeClaimVolumeOptions{Name: jsii.String(mysqlVolumeName)})

    mySQLVolumeMountPath := "/var/lib/mysql"
    mysqlContainer.Mount(jsii.String(mySQLVolumeMountPath), mysqlVolume, &cdk8splus22.MountOptions{})
Вход в полноэкранный режим Выход из полноэкранного режима

Наконец, мы создаем службу:

    mySQLServiceName := "mysql-service"
    clusterIPNone := "None"

    cdk8splus22.NewService(chart, jsii.String("mysql-service"), &cdk8splus22.ServiceProps{
        Metadata:  &cdk8s.ApiObjectMetadata{Name: jsii.String(mySQLServiceName)},
        Selector:  dep,
        ClusterIP: jsii.String(clusterIPNone),
        Ports:     &[]*cdk8splus22.ServicePort{{Port: jsii.Number(3306)}},
    })
Войти в полноэкранный режим Выйти из полноэкранного режима

В отличие от предыдущего примера, мы создаем Service явно, а затем ссылаемся на объект Deployment в селекторе сервиса.

Диаграмма WordPress — за исключением незначительных различий, это то же самое, что и диаграмма MySQL с конфигурацией, специфичной для WordPress. Поэтому я не буду повторять это здесь — не стесняйтесь изучать код.

Момент истины настал!

Повторяйте и повторяйте — cdk8s synth для создания манифеста и применения его с помощью kubectl:

cd part2-cdk8s-plus-in-action/wordpress

#create manifests
cdk8s synth

#apply them
kubectl apply -f dist/

#output - you will see something similar to:

secret/mysql-pass created
deployment.apps/mysql-mysql-deployment-cdk8splus-c83762d9 created
persistentvolumeclaim/mysql-mysql-pvc-c8799bba created
service/mysql-service created
deployment.apps/wordpress-wordpress-deployment-cdk8splus-c8252da7 created
service/wordpress-service created
persistentvolumeclaim/wordpress-wordpress-pvc-c8334a29 created
Войти в полноэкранный режим Выйдите из полноэкранного режима

В другом терминале запустите (если он еще не запущен):

minikube tunnel
Войти в полноэкранный режим Выйти из полноэкранного режима

С помощью браузера перейдите на сайт http://localhost:80. Вы должны увидеть знакомый экран установки WordPress.

Завершите установку и войдите в свой экземпляр WordPress. Не стесняйтесь экспериментировать. Возможно, попробуйте удалить развертывание MySQL и создать его заново. Благодаря PersistentVolume, данные MySQL должны быть восстановлены, и wordpress продолжит работать.

Заключение

Потрясающе! В этом блоге вы увидели выразительность cdk8s-plus. Мы начали с компактной и менее многословной версии развертывания Nginx, а закончили полноценным экземпляром WordPress — и все это с помощью Go.

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

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