rzv Data Engineering
СтатистикаАвторский канал о том, как я понимаю инжиниринг данных. Объясняю термины, best practice, делюсь описанием рабочих задачек. См закрепы Рассчитан на новичков в DE и инженеров до Senior. Чат: t.me/+jtQ1tjvNUtwzN2My По вопросам: @razvodov_de_mentor
- Последний пост
- 15 авг.
- Последнее чтение
- 15:40
- Постов за неделю
- 8
- Всего постов
- 31
- Тип
- открытый
- Язык
- русский
- Категория
- Блоги
- В каталоге с
- 12 авг.
- 1/24сутки в ленте
- 574
- 1/48двое суток
- 657
- 1/72трое суток
- 709
Оценка по просмотрам недавних постов: пост набирает почти всё за первые сутки.
Посты
В какие темы и технологии было бы интересно погрузиться с практикой? Пиши в комментариях, буду выбирать популярные идеи и формировать бэклог)
В общем, по поводу розыгрыша билета на конференцию и обсуждения под удалённым постом с рекламой: Нечасто провожу эти розыгрыши, и досадно что именно в этот раз довольно крупно накосячил со своей стороны. Как было по порядку: • В комментариях в конкурсе приняли участие два человека - Даниил и Сергей. • Я провёл розыгрыш, в котором победителем рандом выбрал Сергея, написал ему об этом под постом - но потом обнаружил, что не поставил OBS на запись. • Подумал, что доказательство всё-таки нужно, записал ещё один раз, где рандом выбрал Даниила. • Написал Даниилу об этом, и решил подчистить прошлое сообщение, где победитель - Сергей. • Сергей указал мне на эту несправедливость в комментах, и потом я пытался объясниться, но услышать друг друга не получилось. • Даниил пошёл навстречу и отказался от своего билета, чтобы в итоге он достался Сергею. Я написал организатору конференции, в понедельник будем договариваться на то, чтобы по билету досталось обоим участникам. Признаю, что поступил очень по-детски, поленившись пару раз крутануть барабан и заново сделать записи, чтобы первоначальный победитель был запечатлён на видео. Сергей честно победил в этом случайном отборе в первый раз. И приз был достаточно серьёзный, стоило подготовиться лучше. Или стоило хотя бы объяснить ситуацию на том же видео, и "покрутить этот барабан" на записи, пока снова не покажется Сергей. Решил "сэкономить" пару минут, в итоге потратил час времени, нервы людей, и теперь напрягаю людей договариваться о новом билете. Я косяк. Не делайте так) Приношу извинения за неразбериху и потрёпанные нервы p.s. Релиз mini-Lakehouse Lab откладывается до понедельника
видео или голосовое, без подписи
видео или голосовое, без подписи
видео или голосовое, без подписи
видео или голосовое, без подписи
Анонс новых учебных стендов по DE Я стремлюсь найти такие способы обучения технологиям, которые помогают разобраться и понять материал. Сейчас работаю над интерактивными стендами в стиле kodekloud, но для дата инженеров. Это такие лабы на 3-5 часов, где…
видео или голосовое, без подписи
Как записи конференций помогли мне быстрее стать сеньором Читай пост до конца — участвуй в розыгрыше билета на онлайн-участие в конференции SmartData2026! 🔸Мне нравится формат видео-эссе и докладов по играм и кино, поэтому года три назад я решил поискать что-то по своей профессии на youtube. Решил пройтись по Clickhouse, так как технология была на слуху, и всё чаще появлялась в вакансиях и на проектах. Наткнулся на записи SmartData, в которых большинство докладов как раз по теме Data engineering - и понеслось) Дальше я просто включал записи фоном, как подкасты. Пока делаешь какие-то задачи по быту, едешь по городу или просто выполняешь рутинные задачи на работе. 🔸 Вначале я не особо заметил пользу, ну слушал и слушал. Но потом это очень помогло на технических собеседованиях. Когда заходил разговор о сложной задаче, я вспоминал историю из доклада и приводил её как пример. Взять то же масштабирование связки кластеров Kafka & Clickhouse. Срабатывало это двумя путями: • Иногда собеседующий узнавал выступление, и между нами возникало ощущение общего контекста. И тогда мы могли уйти в обсуждение выступления или того, что ещё вместе смотрели. Так быстрее выстраивался более теплый коннект с потенциальным руководителем. • Иногда упоминания каких-то деталей было достаточно для подтверждения, что в теме я разобрался достаточно глубоко. Чужие достижения при этом присваивать не обязательно, достаточно было сказать, что я знаю про такую особенность и понимаю механизм. И вот уже осенью 2023 я впервые устроился в роли Senior DE :) 🔸Сами доклады дали мне конкретику, которую тяжело собрать по обрывкам статей. Например, вот доклады которые мне запомнились: • об особенностях (не)идемпотентной записи в разные движки таблиц, откуда может взяться перекос данных по шардам и партициям • о внутренней работе разреженных индексов и вставке в MergeTree • о том, как на Clickhouse строили DWH, и с какими проблемами столкнулись 🔸С теплотой рекомендую посетить конференцию SmartData в этом году всем заинтересованным. Также у подписчиков моего канала есть уникальная возможность приобрести персональный билет со скидкой 15% по промокоду: RzvDe ❕Пришли в комментарии свою историю, когда доклады конференций помогли тебе на собеседованиях или в работе. Случайным образом выберу победителя и подарю билет на онлайн-участие 23–24 сентября Реклама. ООО «Джуг Ру Груп». ИНН 7801341446
видео или голосовое, без подписи
Опыт миграции небольшого стека с Docker compose на Kubernetes 4/4 🔸 Что это даёт, по крайней мере для меня Прежде всего - интеграцию с готовой платформой для "прогерских лабораторных работ", где k8s-манифесты это пререквизит Возможность горизонтального масштабирования за пределы одной ВМ на будущее Более удобный способ из одного контейнера управлять состоянием другого, например для сервиса выдачи доступов (в docker-compose стеке есть bind-mount /var/run/docker.sock, но идёт вместе с уязвимостью в виде root права на запуск любых контейнеров) 🔸Выводы Для собственных проектов пока всё ещё не вижу смысла в k8s, как ни пытаюсь разглядеть. Всё в конечном итоге запускается на виртуальных машинах, за которые платишь. Даже в managed сервисе вроде "yandex cloud managed k8s" идёт отдельная аренда за месяц CPU/RAM/disk конкретных ВМ. Пока продолжаю всей душой любить Docker compose. Поделитесь в комментах, если есть удачный опыт переноса проектов на кубер, кроме случаев когда это кластеры на десятки ВМ. Я с интересом почитаю)
Опыт миграции небольшого стека с Docker compose на Kubernetes 3/4 Отдельно — Ingress: apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: trino spec: rules: - host: trino.example.com http: paths: - path: / pathType: Prefix backend: service: name: trino port: number: 8080 И отдельно — 2 ConfigMap вместо папки ./trino/etc: apiVersion: v1 kind: ConfigMap metadata: name: trino-etc data: config.properties: | coordinator=true node-scheduler.include-coordinator=true http-server.http.port=8080 discovery.uri=http://localhost:8080 jvm.config: | -server -Xmx2G -XX:+ExitOnOutOfMemoryError node.properties: | node.environment=lab node.id=trino-lab-1 ... Итого на стороне k8s: 6 объектов (Deployment, Secret, Service, Ingress, 2 ConfigMap), около 230 строк. 150 против 230, 2+7 файлов с описанием инфры против 6 объектов. В k8s нужно больше конфигурации на тот же набор параметров, потому что в k8s эти параметры обязательны и оформлены отдельными объектами. В Docker compose они описываются опциональными полями внутри одного сервиса.
Опыт миграции небольшого стека с Docker compose на Kubernetes 2/4 Что стало на k8s 🔸 Во-первых, сколько абстракций добавилось: - Deployment — чтобы кластер сам следил за нужным состоянием пода - Secret — API-объект с RBAC-управлением доступами на чтение и живой ротацией без передеплоя - Service — чтобы у пода был стабильный сетевой адрес: свой внутренний IP он теряет при каждом пересоздании - Ingress — чтобы к сервису можно было подключиться снаружи; по умолчанию кластер находится в полностью изолированном окружении "без окон и дверей" - ConfigMap — набор key-value пар параметров 🔸 Во-вторых, какие строчки конфига во что превратились: - image / container_name -> Deployment, containers[].image — без изменений - depends_on -> нативного аналога нет, приходится писать небольшой initContainer, который сам проверяет, что сервис-зависимость запущен и можно стартовать текущий - сеть по имени контейнера -> Service — обязательный отдельный объект, потому что у пода нет постоянного IP; адресация теперь по label-селектору, т.к. подов по умолчанию больше одного - открытые ports -> Service.ports + Ingress — тот же объём работы, что раньше делал nginx вне compose, просто теперь это объект кластера, а не отдельный конфиг на хосте - restart: healthcheck -> readinessProbe / livenessProbe с тем же смыслом - environment -> Secret - лимиты ресурсов железа -> resources.requests/limits — выглядит похоже, влияет на выбор планировщика "куда размещать новый под при масштабировании" - отдельные .config, .properties для Trino -> универсальные ConfigMap key-value файлы Для запуска использую облегчённую версию k3s на одной ВМ. Deployment: apiVersion: apps/v1 kind: Deployment metadata: name: trino spec: replicas: 1 selector: matchLabels: app: trino template: metadata: labels: app: trino spec: initContainers: - name: wait-for-hive-metastore image: busybox:1.36 command: ["sh", "-c", "until nc -z hive-metastore 9083; do sleep 2; done"] containers: - name: trino image: trinodb/trino:483 env: - name: AWS_ACCESS_KEY_ID valueFrom: secretKeyRef: name: minio-credentials key: access-key - name: AWS_SECRET_ACCESS_KEY valueFrom: secretKeyRef: name: minio-credentials key: secret-key ports: - containerPort: 8080 resources: requests: cpu: 250m memory: 2Gi limits: memory: 3Gi readinessProbe: httpGet: path: /v1/info port: 8080 initialDelaySeconds: 15 livenessProbe: tcpSocket: port: 8080 initialDelaySeconds: 30 volumeMounts: - name: trino-etc mountPath: /etc/trino/config.properties subPath: config.properties - name: trino-etc mountPath: /etc/trino/jvm.config subPath: jvm.config - name: trino-catalog mountPath: /etc/trino/catalog/iceberg.properties subPath: iceberg.properties volumes: - name: trino-etc configMap: name: trino-etc - name: trino-catalog configMap: name: trino-catalog Он ссылается на Secret через secretKeyRef, значит нужен и такой объект: apiVersion: v1 kind: Secret metadata: name: minio-credentials type: Opaque stringData: access-key: <access-key> secret-key: <secret-key> Дальше — Service: apiVersion: v1 kind: Service metadata: name: trino spec: selector: app: trino ports: - port: 8080 targetPort: 8080
Опыт миграции небольшого стека с Docker compose на Kubernetes 1/4 В посте попробую разобраться, в чём k8s может быть лучше чем Docker compose для инфры небольшого проекта. Пост больше по DataOps, но вам вроде такое иногда заходит. Вначале отладил сервис и "лабу" для своих менти на привычном окружении, теперь оборачиваю в "коробку" и готовлюсь открывать доступ для многих. Заодно решил потренироваться в переносе сервиса на kubernetes. Это те же контейнеры, должно быть несложно, правда?) 🔸 Было Вот весь блок trino: из compose, как есть: trino: image: trinodb/trino:483 container_name: trino restart: unless-stopped volumes: - ./trino/etc:/etc/trino:ro ports: - "8085:8080" environment: AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-} AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-} healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/v1/info"] interval: 15s timeout: 5s retries: 3 start_period: 30s deploy: resources: limits: memory: 3g reservations: cpus: "0.25" memory: 2g depends_on: hive-metastore: condition: service_healthy networks: - trino-network - main А чтобы к нему можно было подключиться снаружи по доменному имени и по HTTPS — добавляем nginx на хосте, вне этого docker-compose.yml: server { listen 80; server_name trino.example.com; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://$host$request_uri; } } server { listen 443 ssl; server_name trino.example.com; ssl_certificate /etc/letsencrypt/live/trino.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/trino.example.com/privkey.pem; location / { set $upstream_trino trino:8080; proxy_pass http://$upstream_trino; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } Итого: - docker compose: 30 строк - /trino/etc конфиги: 91 строка, 7 файлов формата .config и .properties (по 1 под каждый из 3х каталогов + 4 общих на кластер) - nginx: 29 строк
видео или голосовое, без подписи
видео или голосовое, без подписи
Анонс новых учебных стендов по DE Я стремлюсь найти такие способы обучения технологиям, которые помогают разобраться и понять материал. Сейчас работаю над интерактивными стендами в стиле kodekloud, но для дата инженеров. Это такие лабы на 3-5 часов, где можно знакомиться с технологиями через практику. Пользователь на платформе сможет создавать конфиги, запускать команды в терминале, обращаться к СУБД через SQL клиент, заходить на UI сервисов и тд. Опыт приближен к техническому взаимодействию с системой, как это бывает на работе. Планирую в августе зарелизить первый стенд по Lakehouse: Trino + Iceberg + S3 - ждите новостей) Закладываю опыт работы в американском стартапе, где ещё в 2024 удалось поработать с Lakehouse
Вопрос на подумать-порассуждать. Что делать, если меняется первичный ключ? Например, ты строишь CRM систему на основе телеграмма. И в качестве колонки, которая "однозначно определяет аккаунт", выбираешь телеграм никнейм. Запускаешь систему в прод, всё хорошо работает какое-то время. А потом ты узнаёшь, что некоторые клиенты поменяли свой телеграм никнейм. То есть теперь есть две разных строчки, которые на самом деле один клиент.
Как BI-аналитики воспринимают оптимизацию таблиц в СУБД под тяжёлые запросы https://rzvde.pro/clickhouse_query_optimization_demo и такой пересчёт происходит при каждом обращении к СУБД, например Clickhouse: • выбор другого отчётного периода • фильтрация по конкретным значением • изменение полей агрегации (симуляция, реальная база не пострадала)
Дата инженерный опыт работы с кубером 3/3 🔸 Выбор Executor под задачу Появляется выбор между CeleryExecutor и KubernetesExecutor. CeleryExecutor держит постоянный пул воркеров, которые ждут работу из очереди через брокер вроде Redis. Воркеры всегда “прогреты”, поэтому задача стартует почти сразу, и это выгодно при большом числе коротких частых операций. Плата за такой режим в том, что воркеры занимают ресурсы даже в простое. KubernetesExecutor поднимает отдельный под под каждую задачу и удаляет его после завершения. Старт такого пода занимает секунд 40, поскольку нужно подтянуть образ и дождаться планировщика, и полезная работа начинает выполняться далеко не сразу. Зато задача получает изоляцию, возможность занять ограниченные ресурсы под тяжёлую операцию и высвободить по выполнении. p.s. существует комбинированный CeleryKubernetesExecutor, который распределяет задачи по очередям. А на уровне тасок выбирается тип оператора KubernetesPodOperator для k8s / любой другой для Celery. Или можно указать в любом task через параметр executor. 🔸 Упаковка python-кода в образ В кубере единицей запуска служит контейнерный образ, поэтому python-скрипт обработки данных сначала превращают в образ через Dockerfile. В этом файле наследуют базовый образ через FROM, нужные библиотеки и сам код, дальше образ собирают, отправляют в реестр (н. gitlab container registry) и ссылаются на него из задачи, например через KubernetesPodOperator. Вместо установки пакетов на общие воркеры, теперь инженер описывает окружение в Dockerfile и обновляет образ. Такой подход даёт все плюсы использования Docker контейнеров вроде изоляции окружения и воспроизведения запусков. Но при этом добавляется работа по обслуживанию всех этих процессов, значительно усложняется CI/CD.