tgindex
rzv Data Engineering

rzv Data Engineering

Статистика
@rzv_deБлогирусский

Авторский канал о том, как я понимаю инжиниринг данных. Объясняю термины, best practice, делюсь описанием рабочих задачек. См закрепы Рассчитан на новичков в DE и инженеров до Senior. Чат: t.me/+jtQ1tjvNUtwzN2My По вопросам: @razvodov_de_mentor

Последний пост
15 авг.
Последнее чтение
15:40
Постов за неделю
8
Всего постов
31
Тип
открытый
Язык
русский
Категория
Блоги
В каталоге с
12 авг.
Подписчики
2 968
−3 за 4 дн.
Сутки
+2
+0,07%
Неделя
 
Месяц
 
Просмотров на пост
1 102
31 постов
Вовлечённость
37,1%
к подписчикам
Постов в день
1,1
всего 31
Упоминаний
0
каналов
Охват размещения
оценка
1/24сутки в ленте
574
1/48двое суток
657
1/72трое суток
709

Оценка по просмотрам недавних постов: пост набирает почти всё за первые сутки.

Посты

  • В какие темы и технологии было бы интересно погрузиться с практикой? Пиши в комментариях, буду выбирать популярные идеи и формировать бэклог)

  • В общем, по поводу розыгрыша билета на конференцию и обсуждения под удалённым постом с рекламой: Нечасто провожу эти розыгрыши, и досадно что именно в этот раз довольно крупно накосячил со своей стороны. Как было по порядку: • В комментариях в конкурсе приняли участие два человека - Даниил и Сергей. • Я провёл розыгрыш, в котором победителем рандом выбрал Сергея, написал ему об этом под постом - но потом обнаружил, что не поставил OBS на запись. • Подумал, что доказательство всё-таки нужно, записал ещё один раз, где рандом выбрал Даниила. • Написал Даниилу об этом, и решил подчистить прошлое сообщение, где победитель - Сергей. • Сергей указал мне на эту несправедливость в комментах, и потом я пытался объясниться, но услышать друг друга не получилось. • Даниил пошёл навстречу и отказался от своего билета, чтобы в итоге он достался Сергею. Я написал организатору конференции, в понедельник будем договариваться на то, чтобы по билету досталось обоим участникам. Признаю, что поступил очень по-детски, поленившись пару раз крутануть барабан и заново сделать записи, чтобы первоначальный победитель был запечатлён на видео. Сергей честно победил в этом случайном отборе в первый раз. И приз был достаточно серьёзный, стоило подготовиться лучше. Или стоило хотя бы объяснить ситуацию на том же видео, и "покрутить этот барабан" на записи, пока снова не покажется Сергей. Решил "сэкономить" пару минут, в итоге потратил час времени, нервы людей, и теперь напрягаю людей договариваться о новом билете. Я косяк. Не делайте так) Приношу извинения за неразбериху и потрёпанные нервы p.s. Релиз mini-Lakehouse Lab откладывается до понедельника

  • видео или голосовое, без подписи

  • видео или голосовое, без подписи

  • видео или голосовое, без подписи

  • видео или голосовое, без подписи

  • Анонс новых учебных стендов по DE Я стремлюсь найти такие способы обучения технологиям, которые помогают разобраться и понять материал. Сейчас работаю над интерактивными стендами в стиле kodekloud, но для дата инженеров. Это такие лабы на 3-5 часов, где…

  • 11 авг.6368удалён 14 авг.

    видео или голосовое, без подписи

  • 11 авг.618108удалён 14 авг.

    Как записи конференций помогли мне быстрее стать сеньором Читай пост до конца — участвуй в розыгрыше билета на онлайн-участие в конференции SmartData2026! 🔸Мне нравится формат видео-эссе и докладов по играм и кино, поэтому года три назад я решил поискать что-то по своей профессии на youtube. Решил пройтись по Clickhouse, так как технология была на слуху, и всё чаще появлялась в вакансиях и на проектах. Наткнулся на записи SmartData, в которых большинство докладов как раз по теме Data engineering - и понеслось) Дальше я просто включал записи фоном, как подкасты. Пока делаешь какие-то задачи по быту, едешь по городу или просто выполняешь рутинные задачи на работе. 🔸 Вначале я не особо заметил пользу, ну слушал и слушал. Но потом это очень помогло на технических собеседованиях. Когда заходил разговор о сложной задаче, я вспоминал историю из доклада и приводил её как пример. Взять то же масштабирование связки кластеров Kafka & Clickhouse. Срабатывало это двумя путями: • Иногда собеседующий узнавал выступление, и между нами возникало ощущение общего контекста. И тогда мы могли уйти в обсуждение выступления или того, что ещё вместе смотрели. Так быстрее выстраивался более теплый коннект с потенциальным руководителем. • Иногда упоминания каких-то деталей было достаточно для подтверждения, что в теме я разобрался достаточно глубоко. Чужие достижения при этом присваивать не обязательно, достаточно было сказать, что я знаю про такую особенность и понимаю механизм. И вот уже осенью 2023 я впервые устроился в роли Senior DE :) 🔸Сами доклады дали мне конкретику, которую тяжело собрать по обрывкам статей. Например, вот доклады которые мне запомнились: • об особенностях (не)идемпотентной записи в разные движки таблиц, откуда может взяться перекос данных по шардам и партициям • о внутренней работе разреженных индексов и вставке в MergeTree • о том, как на Clickhouse строили DWH, и с какими проблемами столкнулись 🔸С теплотой рекомендую посетить конференцию SmartData в этом году всем заинтересованным. Также у подписчиков моего канала есть уникальная возможность приобрести персональный билет со скидкой 15% по промокоду: RzvDe ❕Пришли в комментарии свою историю, когда доклады конференций помогли тебе на собеседованиях или в работе. Случайным образом выберу победителя и подарю билет на онлайн-участие 23–24 сентября Реклама. ООО «Джуг Ру Груп». ИНН 7801341446

  • 10 авг.1 0791

    видео или голосовое, без подписи

  • 8 авг.1 09275

    Опыт миграции небольшого стека с 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 строк

  • видео или голосовое, без подписи

  • 8 авг.1 07210

    видео или голосовое, без подписи

  • 8 авг.1 0512510

    Анонс новых учебных стендов по DE Я стремлюсь найти такие способы обучения технологиям, которые помогают разобраться и понять материал. Сейчас работаю над интерактивными стендами в стиле kodekloud, но для дата инженеров. Это такие лабы на 3-5 часов, где можно знакомиться с технологиями через практику. Пользователь на платформе сможет создавать конфиги, запускать команды в терминале, обращаться к СУБД через SQL клиент, заходить на UI сервисов и тд. Опыт приближен к техническому взаимодействию с системой, как это бывает на работе. Планирую в августе зарелизить первый стенд по Lakehouse: Trino + Iceberg + S3 - ждите новостей) Закладываю опыт работы в американском стартапе, где ещё в 2024 удалось поработать с Lakehouse

  • 25 июл.1 6916

    Вопрос на подумать-порассуждать. Что делать, если меняется первичный ключ? Например, ты строишь CRM систему на основе телеграмма. И в качестве колонки, которая "однозначно определяет аккаунт", выбираешь телеграм никнейм. Запускаешь систему в прод, всё хорошо работает какое-то время. А потом ты узнаёшь, что некоторые клиенты поменяли свой телеграм никнейм. То есть теперь есть две разных строчки, которые на самом деле один клиент.

  • 30 июн.2 547229

    Как BI-аналитики воспринимают оптимизацию таблиц в СУБД под тяжёлые запросы https://rzvde.pro/clickhouse_query_optimization_demo и такой пересчёт происходит при каждом обращении к СУБД, например Clickhouse: • выбор другого отчётного периода • фильтрация по конкретным значением • изменение полей агрегации (симуляция, реальная база не пострадала)

  • 19 июн.2 5691814

    Дата инженерный опыт работы с кубером 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.

rzv Data Engineering — tgindex