
При построении платформы данных одна из ключевых задач — объединить под одной крышей весь необходимый зоопарк технологий и обеспечить возможность стабильной и надежной работы каждого модуля. Реализовать подобный проект не всегда просто, но с Kubernetes это возможно.
Привет, Хабр. Меня зовут Сергей Емельянов. Я ведущий программист VK Tech. Мы с командой смогли построить Cloud-Native-архитектуру платформы данных на базе Kubernetes. И в этой статье я хочу рассказать, как это было — от задачи до полученных результатов.
Исходные условия: точка отсчета
Аналитика пользовательских и внутренних данных — критически важная задача для любой компании. От возможности сбора и качественного анализа данных зависит, насколько точно и своевременно бизнес будет подстраиваться под запросы пользователей, условия рынка и специфику внутренних процессов.
Но в условиях, когда данных, как и задач на аналитику, много, использование разрозненного стека инструментов не лучший вариант. Нужна платформа данных, в которой будут консолидированы все процессы работы с данными.
С необходимостью построения такого решения столкнулись и мы в VK Cloud.
Мы стартовали разработку, имея зоопарк технологий:
10 модулей захвата, обработки и доставки данных;
10 модулей хранения;
3 модуля машинного обучения;
3 модуля бизнес-аналитики.
При реализации нам надо было обеспечить соответствие конечной платформы ряду нефункциональных требований. В том числе были важны:
отказоустойчивость платформы разработки;
возможность гибкого масштабирования;
возможность делать идентичные окружения (prod, stage, dev);
возможность быстро подниматься на разных стендах (публичное и приватное облако);
скорость добавления новых продуктов;
простота обновления;
простота настройки продуктов.
Варианты реализации
Построение архитектуры столь крупного проекта — довольно нетривиальная задача, при реализации которой было бы оптимально:
использовать мало человеческих ресурсов;
использовать мало вычислительных ресурсов;
иметь возможность быстро вносить изменения.
Но, как хорошо известно из треугольника Хопкинса, соблюсти сразу три условия невозможно.
Соответственно, перед нами стоял выбор.
Делать медленно, но верно:
используем мало человеческих ресурсов;
используем мало вычислительных ресурсов;
но не можем быстро вносить изменения.
Привлечь большую команду:
задействуем много человеческих ресурсов;
используем мало вычислительных ресурсов;
получаем возможность быстро вносить изменения.
Увеличить объем задействованных мощностей:
используем мало человеческих ресурсов;
используем много вычислительных ресурсов;
получаем возможность быстро вносить изменения.
Качество и возможность комфортного администрирования платформы — приоритет для нас. Поэтому первый вариант нам не подошел. Не подошел и второй, который подразумевает привлечение большой команды. Соответственно, мы остановились на третьем, который вполне можно реализовать на базе Kubernetes.
Важно отметить, что Kubernetes в рамках реализации Cloud-Native-платформы данных позволяет выстроить работу со всеми компонентами, как с микросервисами. То есть мы получаем возможность гибче управлять ресурсами, задачами, сервисами и другими компонентами. В том числе легче их масштабировать с учетом динамически меняющейся нагрузки. Таким образом, с K8s решаются ограничения Stateful-приложений: в Kubernetes Stateful-приложения легко горизонтально масштабировать, добавляя больше партиций и брокеров.
Кроме упомянутых преимуществ, выбор для построения платформы данных Kubernetes дал нам возможность исключить необходимость создания отдельных инсталляторов под каждый сервис и использовать их независимо. Это важно для нас, поскольку в условиях большого зоопарка технологий, как в нашем случае, нужно обеспечивать сетевую связность и возможность конфигурирования компонентов дата-платформы из одного места. Одновременно с этим Kubernetes обеспечивает соответствие архитектуры выстраиваемой платформы исходным нефункциональным требованиям.
Влияние перехода на Kubernetes
DevOps
Выбор в пользу K8s позволил нам существенно расширить возможности администрирования и конфигурирования архитектуры платформы и одновременно упростить эти задачи.
Так, до внедрения Kubernetes алгоритм работы был следующим:
пишем Inventory для Ansible либо файл со списком хостов и приложений, которые там должны стоять;
копирование файлов и каталогов из системы выполняем с помощью Rsync, SCP, Ansible Copy;
добавляем ноду, переносим файлы конфигурации, начинаем накатывать скриптами установку вручную;
ограничиваемся SSH-ключами и доступами вида RWX;
для обновления надо вносить новый дистрибутив на машину, менять файлы, перезапускать systemd.
С Kubernetes задачи DevOps значительно упрощаются:
для конфигурирования достаточно выполнить kubectl apply, далее kube-scheduler сам все разрулит;
для хранения конфигураций и системных данных можно использовать ConfigMap и Secret;
настройку приложений можно осуществлять с помощью kubectl apply;
можно реализовать полноценный RBAC;
для обновления компонента достаточно изменить версию образа и выполнить kubectl apply.
Более того, у многих приложений есть Helm для установки в Kubernetes. В том числе такие Helm есть у Apache Airflow, Apache Superset и Trino, которые нужны для реализации инфраструктуры нашей платформы данных.
Операторы
Также K8s позволяет работать с различными операторами. С их помощью можно установить и настроить целевые приложения или инфраструктуры приложения. Примером такого оператора является Strimzi (оператор Kafka), который позволяет установить и настроить, а также обслуживать приложения, выполнять ребалансировку, делать бэкапы и выполнять другие операции.
apiVersion: kafka.strimzi.io/v1beta2
kind: Kafka
metadata:
name: my-cluster
spec:
kafka:
version: 3.9.0
metadataVersion: 3.9-IV0
listeners:
- name: plain
port: 9092
type: internal
tls: false
config:
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
Cloud OS
Kubernetes можно рассматривать в качестве мультиплатформенной облачной операционной системы. То есть неважно, где развернут K8s (в частном или публичном облаке): можно настроить его на одной платформе и использовать на других. Это очень удобно, когда есть несколько одинаковых окружений, например, для тестирования и для прода. Таким образом, в зависимости от потребностей можно настраивать Kubernetes на разных уровнях, но при этом описывать все одинаково.
С чего начать построение архитектуры Cloud-Native-платформы данных
Kubernetes — интуитивно понятный инструмент с внушительным комьюнити и большим пулом совместимых сервисов. Благодаря этому порог входа в построение архитектуры Cloud-Native-платформ данных на базе K8s довольно низкий.
При построении своего проекта мы выделили несколько основных этапов и рекомендаций, которые стоит учесть на начальных этапах подготовки архитектуры дата-платформ.
Платформа
Kubernetes можно развернуть локально на своем железе или воспользоваться Managed Kubernetes в облаке. Например, пользователям облачной платформы VK Cloud доступен сервис Cloud Containers, который позволяет клиентам развертывать приложения в контейнерах в пуле вычислительных хостов и впоследствии управлять этими контейнерами с помощью K8s. VK Cloud гарантирует доступность Kubernetes как сервиса с SLA 99,95%, автоматизирует операции Life cycle, обеспечивает высокий уровень надежности и доступности, а также сокращает сетевые задержки за счет геораспределенной федерации Kubernetes.
Операторы
После выбора платформы для развертывания K8s важно определиться со стеком операторов. Их довольно много под разные задачи, инструменты и сценарии применения. Мы выделили несколько:
OperatorHub.io — Registry операторов;
stackable.tech — оператор на все случаи жизни для data-приложений, с помощью которого можно поднять целый зоопарк технологий;
sdk.operatorframework.io — решение для написания своего оператора, если существующие не подходят.
Helm- и Ansible-операторы
Если нет нужного оператора, то всегда можно упаковать Helm или Ansible в отдельный оператор. На выходе мы получаем CRD, на которую можно навесить RBAC, вместо того чтобы управлять каждой группой объектов отдельно.
Пример конфигурирования компонентов с Kubernetes
Чтобы убедиться, что выбор Kubernetes в качестве базы для построения архитектуры Cloud-Native-платформы данных полностью оправдан, разберем несколько примеров простого конфигурирования Kafka, Kafka-UI, Flink и простой схемы fraud detection.

Предварительно у вас должны быть установлены утилиты kubectl и helm. Первым делом мы установим cert-manager, который необходим для работы webhook:
kubectl create -f https://github.com/jetstack/cert-manager/releases/download/v1.8.2/cert-manager.yaml
Установим Strimzi оператор (управляет Kafka кластером) и Kafka:
helm install my-strimzi-kafka-operator oci://quay.io/strimzi-helm/strimzi-kafka-operator --version 0.45.0
kubectl apply -f kafka.yaml
kafka.yaml
apiVersion: kafka.strimzi.io/v1beta2
kind: KafkaNodePool
metadata:
name: dual-role
labels:
strimzi.io/cluster: my-rnd-1
spec:
replicas: 1
roles:
- controller
- broker
resources:
requests:
cpu: 1
memory: 512Mi
limits:
cpu: 2
memory: 2Gi
storage:
type: jbod
volumes:
- id: 0
class: "csi-ceph-ssd-me1"
type: persistent-claim
size: 20Gi
deleteClaim: false
kraftMetadata: shared
---
apiVersion: kafka.strimzi.io/v1beta2
kind: Kafka
metadata:
name: my-rnd-1
annotations:
strimzi.io/node-pools: enabled
strimzi.io/kraft: enabled
spec:
kafka:
version: 3.9.0
metadataVersion: 3.9-IV0
listeners:
- name: plain
port: 9092
type: internal
tls: false
- name: tls
port: 9093
type: internal
tls: true
config:
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
entityOperator:
topicOperator: {}
userOperator: {}
Добавим туда же Flink оператор и сам Flink в конфигурации 1 Job manager и 1 Task Manager:
helm repo add flink-operator-repo https://downloads.apache.org/flink/flink-kubernetes-operator-1.10.0/
helm install flink-kubernetes-operator flink-operator-repo/flink-kubernetes-operator
kubectl apply -f flink.yaml
flink.yaml
apiVersion: flink.apache.org/v1beta1
kind: FlinkDeployment
metadata:
namespace: default
name: basic-example
spec:
image: flink:1.17
flinkVersion: v1_17
flinkConfiguration:
taskmanager.numberOfTaskSlots: "3"
serviceAccount: flink
mode: "standalone"
jobManager:
resource:
memory: "2048m"
cpu: 1
taskManager:
resource:
memory: "2048m"
cpu: 1
Для удобства работой с Kafka установим Kafka UI и подключим к текущему кластеру Kafka:
helm repo add kafka-ui https://provectus.github.io/kafka-ui-charts
helm install -f kafka-ui.yaml my-kafka-ui appscode/kafka-ui --version 2024.7.3-rc.0
kafka-ui.yaml
yamlApplicationConfig:
kafka:
clusters:
- name: strimzi
bootstrapServers: my-rnd-1-kafka-bootstrap:9092
То есть не надо делать множество действий, описывать Install, SystemID и другие параметры — делаешь apply, helm install и готово.
Самое время запустить генератор фрода:
kubectl apply -f fraud-generator.yaml
fraud-generator.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: generator
spec:
replicas: 1
selector:
matchLabels:
generator: my-rnd-generator
template:
metadata:
name: flink-demo-generator
labels:
generator: my-rnd-generator
spec:
containers:
- name: flink-demo-generator
image: mesosphere/flink-generator:0.1
command: ["/generator-linux"]
imagePullPolicy: Always
args: ["--broker", "my-rnd-1-kafka-bootstrap:9092"]
Теперь осталось добавить детекцию фрода, для это получим доступ к flink ui, загрузим jar файл и запустим его:
export POD_NAME=$(kubectl get pods --namespace default -l "app=basic-example,component=jobmanager" -o jsonpath="{.items[0].metadata.name}")
kubectl --namespace default port-forward $POD_NAME 8081:8081

Для проверки работы можно зайти в kafka-ui и увидеть наличие топиков, откуда поступают данные транзакций и результаты работы flink:
export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=kafka-ui,app.kubernetes.io/instance=my-kafka-ui" -o jsonpath="{.items[0].metadata.name}")
kubectl --namespace default port-forward $POD_NAME 8080:8080

Больше кода можно посмотреть на GitHub.
Что в итоге
Строгих правил построения архитектуры Cloud-Native-платформы данных нет, поэтому каждая компания выбирает свои способы реализации.
Мы на своем опыте убедились, что применение Kubernetes в качестве базы для такого проекта — достойный вариант, который дает много преимуществ. Например, он упрощает масштабирование и позволяет управлять компонентами в формате единого окна. Одновременно с этим построение платформы с Kubernetes не требует какой-то «сверхуникальной экспертизы». Более того, каждый может снизить порог входа в реализацию подобных проектов, выбирая не локально развернутый оркестратор, а Managed Kubernetes в облаке. Например, такой, как Cloud Containers на платформе VK Cloud.
Комментарии (10)
iwram
17.02.2025 14:06Хотелось бы уточнить некоторые моменты.
Какие pv используете в kubernetes? Если сетевые, то как разруливаете iops. Если локальные, то как производите миграцию данных между нодами?
Оператор ставится на конкретный namespace и управляет только ресурсами одного namespace или ставится общий оператор на весь кластер и управляет кучей кафок.
Как решаете проблемы доступа и безопасности. Ведь при переводе баз данных в кубер, площадь атаки от этого не уменьшится, а скорее наоборот.
Как контроллируется автопереезд в случае необходимости обслуживания кубер ноды (а может все еще вручную) и как к этому подготовлены приложения?
На больших кластерах существуют проблемы и в какие объемы уперлись вы и как нагнули etcd - на каком уровне решили делать мультитенантные кластера и тп
Вы ведь крупная организация и знаете лучше меня про все тонкости и при этом не описали их в статье.
totaki Автор
17.02.2025 14:06На данный момент это high-ops сетевые.
В общей namespace, но скорее всего придется этим управлять, потому что разные версии операторов обычно поддерживают разные версии приложений.
Безопасность достойна отдельной статьи и после review с стороны безопасников
Стараемся автоматически, но все индивидуально, если с кафкой можно заскалировать node group и перевести брокер, потом потушить старую ноду, то с тем же Postgres все так просто не получится.
На данный момент кластера не сильно большие и проблем нет, мультенант пока решается отсутствием коммуналки.
Конкретно эта статья не про тонкости, про них мы обязательно расскажем позднее.
SLASH_CyberPunk
И после этого простыня текста, кода/yaml, графиков как это "упростить"...
Вы уверены, что знаете значение слова "упрощение"?
totaki Автор
Конечно знаю, по сравнению со всем остальным (ansible, bash, terraform) это точно упростить.
SLASH_CyberPunk
Непонятно, как кубер упрощает "по сравнению со всем остальным (ansible, bash, terraform)", если это два разных инструмента/-ов. Вы наверно хотели сравнить helm, но тут как в меме
totaki Автор
Нет, я именно сравниваю с теми инструментами, которые написал, потому что чаще всего при помощи них решают DevOps задачи.
Helm пакетный менеджер для работы с kubernetes, а не отдельный инструмент, поэтому нет я с ним не сравнивал.
Этом меме совсем про другое, kubernetes это не новый стандарт.
levashove
А с каким инструментом задачи DevOps тогда упрощаются?
SLASH_CyberPunk
Меньше инструментов - проще
Меньше абстракций - проще
levashove
Построение таких платформ — это задача, которая в любом случае потребует большого количества абстракций и инструментов. И, кажется, тут вопрос в выборе самых удобных, безопасных и предсказуемых.
totaki Автор
У каждого может свое мнение на этот счет, для наших задач был выбран инструмент, который закрывает наши потребности и не важно сколько в нем слоев абстракций. И это один инструмент, а не несколько.