В одной из моих предыдущих записей в блоге рассказывалось о том, как начать работу с cdk8s (Cloud Development Kit for Kubernetes), который представляет собой фреймворк с открытым исходным кодом (часть CNCF), с помощью которого вы можете создавать свои приложения Kubernetes, используя обычные языки программирования (вместо yaml
).
Вы смогли настроить простой nginx
Deployment и получить доступ к нему через Service
— все это было сделано с помощью Go, который затем был преобразован в yaml
(с помощью cdk8s synth
) и отправлен на кластер с помощью kubectl
. Это было хорошее начало. Однако, поскольку ядро библиотеки cdk8s
довольно низкоуровневое (по уважительной причине!), код включал в себя много шаблонов (вы можете обратиться к коду здесь).
cdk8s-plus
использует строительные блоки из основной библиотеки cdk8s
, тем самым помогая уменьшить многословность и сложность, предоставляя абстракции/API более высокого уровня для всех объектов Kubernetes, таких как Deployment
s, Service
s и т.д. В этом блоге мы увидим 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.
Счастливого кодинга!