При построении платформы данных одна из ключевых задач — объединить под одной крышей весь необходимый зоопарк технологий и обеспечить возможность стабильной и надежной работы каждого модуля. Реализовать подобный проект не всегда просто, но с 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.

Схема fraud detector
Схема fraud detector

Предварительно у вас должны быть установлены утилиты 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
flink-ui
flink-ui

Для проверки работы можно зайти в 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
kafka-ui
kafka-ui

Больше кода можно посмотреть на GitHub.

Что в итоге

Строгих правил построения архитектуры Cloud-Native-платформы данных нет, поэтому каждая компания выбирает свои способы реализации. 

Мы на своем опыте убедились, что применение Kubernetes в качестве базы для такого проекта — достойный вариант, который дает много преимуществ. Например, он упрощает масштабирование и позволяет управлять компонентами в формате единого окна. Одновременно с этим построение платформы с Kubernetes не требует какой-то «сверхуникальной экспертизы». Более того, каждый может снизить порог входа в реализацию подобных проектов, выбирая не локально развернутый оркестратор, а Managed Kubernetes в облаке. Например, такой, как Cloud Containers на платформе VK Cloud.

Комментарии (10)


  1. SLASH_CyberPunk
    17.02.2025 14:06

    С Kubernetes задачи DevOps значительно упрощаются:

    И после этого простыня текста, кода/yaml, графиков как это "упростить"...

    Вы уверены, что знаете значение слова "упрощение"?


    1. totaki Автор
      17.02.2025 14:06

      Конечно знаю, по сравнению со всем остальным (ansible, bash, terraform) это точно упростить.


      1. SLASH_CyberPunk
        17.02.2025 14:06

        Непонятно, как кубер упрощает "по сравнению со всем остальным (ansible, bash, terraform)", если это два разных инструмента/-ов. Вы наверно хотели сравнить helm, но тут как в меме


        1. totaki Автор
          17.02.2025 14:06

          1. Нет, я именно сравниваю с теми инструментами, которые написал, потому что чаще всего при помощи них решают DevOps задачи.

          2. Helm пакетный менеджер для работы с kubernetes, а не отдельный инструмент, поэтому нет я с ним не сравнивал.

          3. Этом меме совсем про другое, kubernetes это не новый стандарт.


    1. levashove
      17.02.2025 14:06

      А с каким инструментом задачи DevOps тогда упрощаются?


      1. SLASH_CyberPunk
        17.02.2025 14:06

        Меньше инструментов - проще

        Меньше абстракций - проще


        1. levashove
          17.02.2025 14:06

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


        1. totaki Автор
          17.02.2025 14:06

          У каждого может свое мнение на этот счет, для наших задач был выбран инструмент, который закрывает наши потребности и не важно сколько в нем слоев абстракций. И это один инструмент, а не несколько.


  1. iwram
    17.02.2025 14:06

    Хотелось бы уточнить некоторые моменты.

    1. Какие pv используете в kubernetes? Если сетевые, то как разруливаете iops. Если локальные, то как производите миграцию данных между нодами?

    2. Оператор ставится на конкретный namespace и управляет только ресурсами одного namespace или ставится общий оператор на весь кластер и управляет кучей кафок.

    3. Как решаете проблемы доступа и безопасности. Ведь при переводе баз данных в кубер, площадь атаки от этого не уменьшится, а скорее наоборот.

    4. Как контроллируется автопереезд в случае необходимости обслуживания кубер ноды (а может все еще вручную) и как к этому подготовлены приложения?

    5. На больших кластерах существуют проблемы и в какие объемы уперлись вы и как нагнули etcd - на каком уровне решили делать мультитенантные кластера и тп

    Вы ведь крупная организация и знаете лучше меня про все тонкости и при этом не описали их в статье.


    1. totaki Автор
      17.02.2025 14:06

      1. На данный момент это high-ops сетевые.

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

      3. Безопасность достойна отдельной статьи и после review с стороны безопасников

      4. Стараемся автоматически, но все индивидуально, если с кафкой можно заскалировать node group и перевести брокер, потом потушить старую ноду, то с тем же Postgres все так просто не получится.

      5. На данный момент кластера не сильно большие и проблем нет, мультенант пока решается отсутствием коммуналки.

      Конкретно эта статья не про тонкости, про них мы обязательно расскажем позднее.