Почти 87% проектов в сфере Big Data терпят крах из-за ненадежной инфраструктуры, превращая амбициозные задачи в кладбища данных. Чтобы не попасть в эту статистику, современный дата-инженер должен пройти путь от ручной сборки Docker-контейнеров до оркестрации пайплайнов в Airflow и dbt. Это руководство по превращению хаоса разрозненных SQL-запросов в масштабируемую систему, где даже ошибки становятся фундаментом для надежного кода.
🛠 Введение в Data Engineering и экосистему Docker 0:00
Фундамент данных: почему индустрии нужны инженеры, а не только аналитики 1:06
Современный мир больших данных сталкивается с парадоксальной ситуацией: несмотря на обилие инструментов, от 85% до 87% Big Data-проектов заканчиваются провалом . Как отмечает эксперт Airbyte Джастин Чоу, основная причина кроется не в отсутствии алгоритмов, а в ненадежной инфраструктуре и низком качестве данных . Долгое время компании ожидали, что обязанности по созданию инфраструктуры возьмут на себя дата-сайентисты, однако это привело к некорректному моделированию, избыточности работы и колоссальной текучке кадров среди специалистов по данным . Именно этот инфраструктурный вакуум и призвана заполнить роль дата-инженера .
Спрос на специалистов, способных обеспечить бесперебойный поток качественных данных, отражается и на уровне компенсации: в США медианная зарплата дата-инженера варьируется от 90 до 150 тысяч долларов в год . Помимо финансовой стороны, роль инженера становится критической в эпоху расцвета генеративного ИИ и LLM, таких как ChatGPT . Дата-инженер — это тот, кто гарантирует, что данные для обучения и работы моделей будут:
- доступны вовремя и без перебоев ;
- безопасны и защищены ;
- отформатированы именно так, как того требуют команды аналитиков и исследователей .
По сути, дата-инженер создает кровеносную систему для бизнеса, позволяя компании принимать решения на основе фактов, а не интуиции, что напрямую влияет на её конкурентоспособность .
Docker как стандарт индустрии: образы, контейнеры и решение проблемы «на моей машине работает» 3:45
Первым и фундаментальным инструментом в арсенале дата-инженера является Docker — платформа с открытым исходным кодом, которая радикально упрощает процесс сборки и запуска приложений . Главная проблема, которую решает Docker, — это классическая дилемма «на моей машине всё работает, а у коллеги — нет» . Вместо того чтобы вручную настраивать окружение, инженер упаковывает приложение вместе со всеми его зависимостями, библиотеками и конфигурациями в изолированный контейнер .
Для глубокого понимания технологии важно различать три ключевых концепта:
- Dockerfile: текстовый файл-инструкция или «чертеж», по которому Docker понимает, как именно нужно собрать приложение .
- Docker Image (Образ): исполняемый пакет, созданный на основе Dockerfile. Он включает в себя код, среду выполнения, системные инструменты и настройки . Важнейшая характеристика образа — его иммутабельность (неизменяемость) . Если вам нужно внести изменения, вы не меняете текущий образ, а создаете новый на основе обновленного Dockerfile .
- Docker Container (Контейнер): это запущенный экземпляр образа . Если образ — это чертеж здания, то контейнер — это само здание, в котором можно жить (запускать процессы) .
Контейнеры полностью изолированы как от хост-системы, так и друг от друга . Это позволяет, например, запустить три разных экземпляра одного и того же приложения на одной машине: вы можете остановить или удалить один контейнер, и это никак не затронет остальные . Такая легковесная виртуализация обеспечивает воспроизводимость окружения на любом этапе — от локальной разработки до облачных серверов AWS или GCP .
Жизненный цикл контейнера: от написания Dockerfile до запуска локального приложения 8:56
Процесс контейнеризации приложения начинается с подготовки рабочего окружения. Для работы необходим Docker Desktop, который уже включает в себя Docker Compose (инструмент, необходимый для более сложных задач, которые будут рассмотрены позже) . Практический процесс сборки можно разобрать на примере простого Node.js приложения .
В корневой директории проекта создается Dockerfile, содержимое которого читается почти как обычный английский текст, но требует строгого порядка команд . Типичный набор инструкций включает:
FROM: выбор базового образа (например,node:18) ;WORKDIR: определение рабочей директории внутри контейнера ;COPYиRUN: копирование файлов и установка необходимых зависимостей (например, черезyarn install) ;EXPOSE: указание порта, который будет использовать приложение (например, 3000) ;CMD: финальная команда для запуска приложения .
После написания инструкций выполняется сборка образа командой docker build -t [имя_образа] ., где флаг -t задает тег (имя), а точка указывает на текущую директорию . Когда образ готов, он появляется в списке доступных в системе, и его можно запустить в работу .
Запуск контейнера осуществляется командой docker run. Важно использовать флаг -d (detached mode), чтобы приложение работало в фоновом режиме, и флаг -p, который пробрасывает порты . Например, маппинг 127.0.0.1:3000:3000 позволяет обращаться к приложению внутри контейнера через локальный браузер по адресу localhost:3000 . Если код приложения меняется, цикл повторяется: старый контейнер удаляется (командой docker rm -f [id]), образ пересобирается, и запускается новый контейнер . Ранее в разговоре Джастин упоминал, что в реальных проектах данные должны сохраняться даже после удаления контейнера, но детально механизмы персистентности и сетевого взаимодействия между несколькими контейнерами (например, приложением и базой данных SQL) будут разобраны в следующих частях курса .
🛠️ Оркестрация и персистентность: работа с Docker Compose 25:08
Магия Docker Compose: один конфиг для всей системы 25:49
Работа с отдельными контейнерами вручную требует ввода длинных команд в терминале, что становится неудобным при усложнении архитектуры . Для решения этой проблемы используется Docker Compose — инструмент, позволяющий описывать и запускать многоконтейнерные приложения с помощью одного конфигурационного файла . Вместо того чтобы запускать отдельно базу данных и отдельно приложение, разработчик создает файл compose.yaml (или docker-compose.yml), который автоматизирует весь процесс .
При написании Compose-файла критически важна структура и отступы, так как формат YAML крайне строг к синтаксису . Основные элементы конфигурации включают в себя:
- Services (Сервисы): Определение контейнеров, которые будут запущены. В рассматриваемом примере это сервис
appна базе Node.js 18 и сервисmysqlвосьмой версии [26:53, 27:59]. - Command: Инструкция, которую должен выполнить контейнер при старте, например,
yarn install && yarn run devдля запуска среды разработки . - Ports: Сопоставление портов хоста и контейнера. В учебном примере приложение открывается на порту 3000 .
- Environment (Переменные окружения): Параметры для подключения к базе данных, такие как хост, имя пользователя, пароль и название БД .
Docker Compose значительно упрощает сетевое взаимодействие: контейнеры, описанные в одном файле, по умолчанию находятся в одной сети и могут легко обращаться друг к другу . Для запуска всей инфраструктуры достаточно выполнить команду docker compose up, а добавление флага -d (detached mode) позволит запустить процессы в фоновом режиме, не блокируя терминал . Если же работу нужно прекратить, команда docker compose down не только остановит, но и корректно удалит созданные контейнеры .
Персистентность данных: как не потерять базу при перезапуске 25:21
Одной из главных проблем контейнеризации является эфемерность: по умолчанию все данные внутри контейнера стираются после его удаления. Чтобы данные в базе данных MySQL или Postgres сохранялись, используются Docker-тома (volumes) . В интерфейсе приложения это подтверждается тем, что задачи, созданные в To-Do приложении, корректно отображаются в базе данных даже после манипуляций с контейнерами .
При использовании Docker Compose тома описываются на двух уровнях. Сначала конкретный сервис (например, MySQL) указывает путь внутри контейнера, который нужно синхронизировать, а затем на верхнем уровне файла объявляется именованный том, такой как todo-mysql-data . Это гарантирует, что даже если разработчик полностью удалит контейнер с базой данных, при следующем запуске информация останется нетронутой . Ранее в курсе уже упоминалось, что использование томов является фундаментом для работы с любыми системами хранения данных в Docker .
Такой подход позволяет создать надежную среду для разработки. Если у вас есть коллеги, им достаточно получить ваш образ и файл Compose, чтобы развернуть идентичную среду со всеми зависимостями и переменными окружения . Это исключает классическую проблему «на моей машине всё работает», позволяя всей команде использовать одинаковые версии инструментов и библиотек .
Подготовка инфраструктуры для работы с SQL 31:02
После освоения основ Docker следующим логическим шагом становится настройка среды для работы с языком структурированных запросов (SQL). Вместо локальной установки тяжеловесных СУБД, таких как PostgreSQL, в современной практике Data Engineering используется запуск базы внутри контейнера . Процесс подготовки «песочницы» начинается с загрузки образа командой docker pull postgres, которая скачивает последнюю версию из Docker Hub .
Для запуска контейнера с Postgres используются специфические переменные окружения, в частности POSTGRES_PASSWORD, без которой база данных не запустится . Автор демонстрирует создание контейнера с именем data-engineering-postgres и паролем secret . Важно понимать разницу между запущенным контейнером и конкретной базой данных внутри него: для создания новой БД используется команда docker exec с утилитой createDB .
Такая установка позволяет мгновенно подключиться к терминалу базы данных через команду psql . На этом этапе завершается инфраструктурная часть: теперь у инженера есть готовая, изолированная среда, где можно создавать таблицы (например, users или films) и наполнять их тестовыми данными [33:45, 40:07]. В дальнейшем обучении этот фундамент Docker позволит переходить к изучению синтаксиса SQL, включая выборку данных через SELECT, обновление записей через UPDATE и агрегацию информации, что является базовыми навыками для любого аналитика или инженера данных [35:01, 37:22, 44:03].
📊 Глава 3. Практика SQL: от базовой выборки к управлению данными 50:23
Базовые операции CRUD: создание и наполнение таблиц 51:19
В работе дата-инженера умение манипулировать данными на базовом уровне предшествует любой сложной аналитике. Основной фокус в этой части курса смещается на практическое применение команд INSERT и SELECT для управления наборами данных . Чтобы продемонстрировать связи между сущностями, инструктор создает две новые таблицы: actors (актеры) с полями actor_id и actor_name, а также связующую таблицу film_actors . Процесс наполнения данными (INSERT) показан на примере сопоставления имен актеров с конкретными фильмами, которые уже есть в базе .
Для проверки корректности вставки используется стандартная выборка SELECT *, которая позволяет мгновенно увидеть структуру: например, что в таблице actors каждому имени присвоен уникальный числовой идентификатор . Важность команды SELECT подчеркивается при работе с таблицей Filmore category, где после выполнения запроса отображается 39 записей, содержащих ID категорий и ID фильмов .
В процессе обучения рассматриваются сценарии изменения состояния базы данных:
- Добавление новых записей для тестирования логики: так в таблицу
filmsвносится вымышленный фильм «Mystical Adventures» . - Работа с пропусками: при добавлении фильма без указания актеров или последующей вставке новых имен, таких как Jane Doe и John Smith .
- Использование команды
SELECT ALL(илиSELECT *) для подтверждения того, что новая запись действительно появилась в самом низу списка под номером 21 .
Эти базовые манипуляции (часть цикла CRUD) критически важны для понимания того, как данные будут вести себя на следующих этапах — при объединении таблиц или их трансформации, о чем пойдет речь в будущих главах .
Псевдонимы (Aliases) и форматирование результатов выборки 52:39
По мере усложнения запросов, когда в выборке участвуют несколько таблиц, SQL-код может стать громоздким. Для решения этой проблемы вводятся псевдонимы (Aliases) . Инструктор демонстрирует, как присвоить таблице films короткое имя f, а таблице actors — a . Это позволяет обращаться к столбцам через точечную нотацию, например f.title или a.actor_name, что значительно упрощает чтение кода .
Особое внимание уделяется оператору AS, который используется для переименования столбцов в итоговой таблице результатов . Хотя технически этот оператор является необязательным и его можно опустить, его использование делает намерения инженера более прозрачными: например, запрос Select title AS name превратит заголовок колонки «title» в «name» в выводе .
Для организации данных в удобном для анализа виде применяется оператор ORDER BY . В примерах курса данные сортируются:
- По числовым значениям: например, по
film_idв возрастающем порядке (1, 2, 3...), чтобы «Inception» (ID 1) всегда шел первым . - По алфавиту: при объединении списков имен актеров и названий фильмов через оператор UNION (подробно изучаемый позже), итоговый результат выстраивается в алфавитном порядке .
Инструктор подчеркивает, что правильное форматирование и использование псевдонимов — это не просто вопрос эстетики, а необходимый навык для подготовки «чистых» таблиц, которые затем будут использоваться командами аналитиков .
Логика фильтрации и подготовка к агрегации данных 1:04:08
Хотя агрегатные функции (такие как COUNT, SUM, AVG, MAX/MIN) и оператор GROUP BY были впервые упомянуты ранее , в данном сегменте акцент делается на логике подготовки данных для таких вычислений. Ключевым инструментом здесь выступает фильтрация через предложение WHERE в сочетании с оператором IN .
Инструктор показывает, как извлечь из базы только те фильмы, в которых снимались конкретные люди, например Леонардо Ди Каприо или Том Хэнкс . Этот запрос возвращает список из трех картин: «Inception», «Forrest Gump» и «Toy Story» . Подобная фильтрация является фундаментом для агрегации: прежде чем посчитать средний бюджет или количество фильмов (COUNT), инженер должен точно определить выборку данных.
В ходе демонстрации также упоминаются важные аспекты работы с данными:
- Использование «мостовых» таблиц (bridge tables), таких как
film_actors, которые содержат только пары ID и служат связующим звеном для фильтрации . - Проверка соответствия данных: например, подтверждение того, что актеру Ди Каприо соответствует
actor_id1, а фильму «Inception» —film_id1 . - Обработка NULL-значений: понимание того, как отсутствие данных о актере влияет на итоговую выборку .
Эти навыки работы с выборкой и условиями подготавливают почву для создания полноценного ETL-скрипта на Python . Ранее в курсе уже затрагивались основы Docker, и теперь эти SQL-знания станут основой для данных, которые будут циркулировать между контейнерами источника и назначения в рамках создаваемого конвейера .
🧩 Объединение данных и сложные выборки: SQL в сердце трансформаций 1:15:14
После того как инфраструктура данных настроена через Docker (о чем подробно говорилось в предыдущих частях), наступает критический момент: данные нужно не просто переместить, а сделать их пригодными для анализа . Простой перенос таблиц «как есть» редко удовлетворяет запросам бизнеса. В этой главе мы разберем, как использовать мощь SQL для объединения разрозненных сущностей и создания сложных выборок, которые превратят сырые строки в структурированные инсайты.
Искусство объединения: JOIN и работа с промежуточными таблицами 1:31:50
Основная задача инженера данных при работе с реляционными базами — это восстановление связей между таблицами. В нашем проекте используются три ключевые сущности: actors (актеры), films (фильмы) и связующая таблица film_actors . Поскольку один актер может сниматься в разных фильмах, а в одном фильме участвует множество актеров, мы имеем дело со связью «многие ко многим», которая реализуется через промежуточную таблицу-связку .
Для извлечения осмысленной информации используются следующие типы соединений:
- INNER JOIN: Позволяет выбрать только те записи, для которых есть соответствие в обеих таблицах. Например, если мы хотим получить список актеров и названий фильмов, в которых они снимались, мы объединяем таблицу
actorsсfilm_actors, а затем сfilms. Если актер не закреплен ни за одним фильмом в базе, он просто не попадет в результат. - LEFT JOIN: Критически важен, когда нам нужно сохранить все записи из «левой» (основной) таблицы, даже если для них нет пары в «правой». Это часто применяется в аналитике для поиска пропусков: например, чтобы найти фильмы, в которых еще не указан актерский состав .
- Алиасы (Aliases): При работе с тремя и более таблицами запросы становятся громоздкими. Использование сокращений (например,
aдляactors,fforfilms,faдляfilm_actors) — это не просто вопрос удобства, а стандарт индустрии, упрощающий чтение логики трансформации .
Как отмечает автор курса, часто аналитики или специалисты по Data Science не хотят писать сложные SQL-запросы самостоятельно . Задача инженера данных — заранее подготовить эти объединения, чтобы в целевой базе (destination database) данные лежали в уже «сшитом» и понятном виде .
Подзапросы и оператор UNION: гибкость в подготовке данных 1:38:47
Когда простых JOIN-ов недостаточно для «морфинга» данных под конкретные нужды бизнеса , в игру вступают подзапросы и операторы объединения результатов.
Подзапросы позволяют изолировать часть логики. Например, мы можем сначала отфильтровать список только «топовых» актеров во внутреннем запросе, а затем уже во внешнем запросе объединять этот результат с таблицей фильмов . Это повышает читаемость кода и позволяет строить многоуровневые трансформации, превращая сырые данные в так называемые «витрины данных».
Оператор UNION незаменим в ситуациях, когда данные об однотипных сущностях приходят из разных источников. В контексте нашего ELT-процесса это может выглядеть так:
- У нас есть таблица актеров из одной системы и аналогичная таблица из другой.
- Чтобы создать единый мастер-список для аналитиков, мы используем
UNION, который «склеивает» результаты двух выборок по вертикали . - Важно помнить, что для работы
UNIONколичество и типы колонок в объединяемых запросах должны строго совпадать .
Эти инструменты позволяют инженеру гибко управлять структурой данных, прежде чем они попадут в руки конечных пользователей .
Инфраструктура для трансформаций: подготовка к dbt 1:31:38
Для автоматизации всех описанных SQL-операций (Joins, Subqueries, Unions) автор предлагает использовать dbt (Data Build Tool) . Это open-source инструмент, который позволяет писать SQL-код в виде моделей. Вместо того чтобы вручную запускать скрипты объединения таблиц, dbt берет на себя управление зависимостями .
Процесс подготовки включает несколько этапов:
- Инициализация проекта: Команда
dbt initсоздает структуру папок, включаяmodels, где будут храниться наши SQL-файлы с джойнами . - Настройка профилей: В файле
profiles.ymlпрописываются параметры подключения к базе данных . Важным нюансом здесь является использование хостаhost.docker.internalдля того, чтобы dbt, запущенный локально, мог «достучаться» до базы внутри Docker-контейнера . - Создание ссылок (Sources): Прежде чем писать сложные JOIN-запросы, необходимо создать базовые модели, которые просто выбирают данные из таблиц
actors,filmsиfilm_actors. Это делается с помощью конструкцииSELECT * FROM {{ source(...) }}.
Такой подход превращает написание SQL-запросов из хаотичного процесса в стройную систему разработки, где каждая трансформация — это отдельная, тестируемая модель . После успешного запуска ELT-скрипта и подтверждения наличия данных в базе через psql , мы готовы к созданию полноценных аналитических моделей.
🛠️ Развертывание ETL-пайплайна: интеграция Python-скрипта и Docker 1:40:19
Проектирование логики трансформации данных 1:42:14
После того как первичные данные были подготовлены, необходимо определить логику их преобразования внутри пайплайна. На этом этапе создаются кастомные модели, которые формулируют и модифицируют сырые данные в таблицы нужного формата . Одной из ключевых задач становится объединение информации о фильмах и актерах в единую структуру, ориентированную на рейтинги .
Для написания чистого и поддерживаемого кода используются обобщенные табличные выражения (CTE — Common Table Expressions) . Это позволяет оборачивать подзапросы в именованные блоки, которые действуют только в рамках одного файла, что значительно упрощает чтение сложных SQL-конструкций . В процессе трансформации применяется оператор CASE (аналог if-else в программировании) для сегментации пользовательских рейтингов: например, оценки выше 4.5 классифицируются как «Excellent», а выше 4.0 — как «Good» .
Технически процесс объединения данных выглядит следующим образом:
- Создается первая CTE для обработки рейтингов и категорий .
- Создается вторая CTE для агрегации имен актеров через запятую для каждого фильма .
- Используется
LEFT JOINдля связки таблиц фильмов, актеров и промежуточных таблиц (bridge tables) по идентификаторуfilm_id. - Финальная выборка объединяет обе CTE в одну итоговую таблицу .
Такой подход позволяет подготовить данные к аналитике еще на этапе загрузки, минимизируя нагрузку на базу при последующих запросах .
Настройка Docker-окружения для ETL-пайплайна 1:48:04
Для автоматизации работы ETL-скрипта требуется правильно настроенная контейнеризация. В файле docker-compose.yaml описываются сервисы, которые будут отвечать за обработку данных . Основным звеном здесь выступает Python-скрипт (ранее упоминавшийся как скрипт на базе subprocess), который выполняет дамп и загрузку между базами Postgres, но теперь к нему добавляется сервисный слой для трансформаций .
Ключевым моментом в конфигурации Docker является управление зависимостями через параметр depends_on . Трансформационная часть пайплайна должна запускаться только после того, как Python-скрипт (elt_script) успешно завершит свою работу . Если запустить процесс обработки на пустой базе, система выдаст ошибку, так как ей не к чему будет обращаться .
Важные аспекты настройки контейнера:
- Образы и версии: Использование специфических образов для работы с Postgres (например,
dbt-postgresверсии 1.4.7) . - Маппинг томов (Volumes): Необходимо сопоставить локальные директории проекта с путями внутри контейнера, чтобы Docker «видел» ваши SQL-файлы и профили подключения .
- Переменные окружения: Установка целевых таргетов (например,
devдля разработки) позволяет гибко переключаться между тестовыми и боевыми средами . - Сетевое взаимодействие: Все компоненты, включая базы данных и скрипты, должны находиться в одной сети (
elt_network), чтобы иметь возможность беспрепятственно обмениваться данными .
При настройке важно избегать опечаток в именах директорий и файлов (например, использование подчеркивания вместо тире), так как Docker чувствителен к путям .
Отладка и верификация данных в целевой базе Postgres 1:54:48
После запуска контейнеров через docker-compose up критически важно убедиться, что данные прошли весь путь от источника до финальной таблицы без потерь . Первым признаком успеха является статус Exited Code 0 у ETL-скрипта, что означает корректное завершение процесса выгрузки и загрузки данных .
Для проверки результатов используется терминал и команда docker exec . Процесс верификации включает:
- Подключение к контейнеру целевой базы данных (destination_postgres) .
- Вход в интерактивную оболочку
psqlпод пользователемpostgres. - Переключение на базу данных
destination_db. - Просмотр списка созданных таблиц с помощью команды
\dt.
Если все этапы выполнены верно, в списке появится новая таблица (например, film_ratings), созданная в результате работы пайплайна . Выполнив простой SQL-запрос SELECT * FROM film_ratings, можно увидеть результат трансформации: объединенные данные о фильмах, списки актеров и присвоенные категории рейтингов . Успешное отображение таких фильмов, как «Inception» с Леонардо Ди Каприо и его категорией рейтинга, подтверждает работоспособность всей логики .
Ранее в разговоре авторы упоминали возможность сделать эти запросы более гибкими. В дальнейшем для улучшения читаемости и повторного использования кода будут применяться более продвинутые методы, такие как макросы и Jinja-шаблоны , а также системы оркестрации вроде Apache Airflow для замены ручного запуска и Cron-задач .
🛠️ Трансформации и логика dbt: от SQL-моделей до динамических макросов 2:05:23
После того как первичные данные загружены в хранилище, наступает этап их преобразования. В современной дата-инженерии для этого чаще всего используется dbt (data build tool) — инструмент, который позволяет описывать логику трансформаций на чистом SQL, сохраняя при этом все преимущества профессиональной разработки: версионирование, тестирование и документацию . В отличие от традиционных ETL-инструментов, dbt реализует подход ELT, где трансформации происходят непосредственно внутри целевой базы данных, используя её вычислительные мощности .
Модели dbt и концепция трансформаций «внутри» базы 2:09:32
Основным строительным блоком в dbt является «модель». По сути, это обычный файл .sql, содержащий инструкцию SELECT . Когда инженер запускает команду dbt run, инструмент автоматически оборачивает этот запрос в конструкцию CREATE TABLE AS... или CREATE VIEW..., создавая готовые объекты в базе данных . Это избавляет разработчика от необходимости вручную писать DDL-коды (Data Definition Language) и следить за порядком создания таблиц.
В рамках проекта трансформации организуются в специальной папке (например, custom_postgres), которая затем монтируется в рабочее окружение . Это позволяет dbt иметь полный доступ к исходным кодам моделей и конфигурационным файлам проекта . Важно понимать, что dbt не перемещает данные между системами; он лишь отправляет SQL-команды в Postgres, заставляя базу данных эффективно преобразовывать уже имеющиеся в ней «сырые» таблицы в аналитические витрины .
Для работы dbt в контейнеризированной среде используется специализированный образ, например ghcr.io/dbt-labs/dbt-postgres версии 1.4.7 . При запуске трансформаций критически важно правильно указать пути к проекту:
--project-dir— путь к папке, где находится файлdbt_project.yml;--profiles-dir— путь к конфигурации подключенийprofiles.yml, который обычно располагается в корневой директории пользователя .
Такое разделение позволяет хранить логику трансформаций отдельно от учетных данных для доступа к базе .
Гибкость кода: Jinja-шаблоны и макросы в dbt 2:16:24
Одной из самых мощных функций dbt является использование движка шаблонов Jinja. Хотя dbt и работает с SQL, Jinja превращает его в подобие языка программирования, позволяя использовать переменные, циклы и условия прямо внутри запросов . Это критично для создания динамических моделей, которые адаптируются к входным данным без переписывания кода вручную.
Макросы в dbt — это переиспользуемые фрагменты кода, аналогичные функциям в Python . Если у инженера есть сложная логика очистки данных или специфический расчет, который повторяется в десяти моделях, он может вынести его в макрос и вызывать одной строкой . Это не только сокращает объем кода, но и радикально упрощает его поддержку: ошибка исправляется в одном месте, а не в десяти .
Применение Jinja также позволяет реализовать механизм ссылок {{ ref('model_name') }}. Вместо того чтобы жестко прописывать имена таблиц в SQL (например, FROM raw_data.users), инженер пишет ссылку на модель . dbt на лету выстраивает граф зависимостей (Lineage Graph), понимая, какие таблицы нужно создать сначала, а какие — после, чтобы данные в финальных отчетах всегда были актуальными . Ранее в разговоре авторы упоминали, что такая автоматизация зависимостей — ключевое отличие dbt от простых SQL-скриптов, запускаемых вручную.
Интеграция и конфигурация dbt в производственном цикле 2:16:32
Для того чтобы dbt успешно взаимодействовал с базой данных в рамках автоматизированного пайплайна, необходимо обеспечить правильное мапирование томов и настройку профилей. В конфигурации системы папка с трансформациями custom_postgres связывается с внутренней директорией /dbt внутри рабочего контейнера . Аналогичным образом пробрасываются и настройки подключений через profiles.yml, чтобы инструмент знал адрес хоста, порт и учетные данные базы данных .
В процессе работы dbt может быть настроен на автоматическое удаление временных контейнеров после выполнения задачи (Auto remove = true), что позволяет экономить системные ресурсы . При этом взаимодействие с Docker-сетью происходит через специальный сокет unix:///var/run/docker.sock, что дает инструментам возможность «общаться» друг с другом внутри изолированной среды .
Основные этапы настройки dbt для продакшена включают:
- Определение сетевого режима (например,
bridge), соответствующего драйверу общей сети проекта . - Монтирование исходного кода моделей в целевой контейнер через
type=bind. - Настройку зависимостей между задачами: сначала должна завершиться загрузка данных (инструментами, описанными в предыдущих главах), и только потом запускается
dbt run.
Такой подход гарантирует, что трансформации всегда работают с полным набором данных, а логика SQL-моделей остается чистой и независимой от инфраструктурного кода .
🐘 Оркестрация процессов через Apache Airflow
Настройка DAG для управления ETL и dbt 2:30:45
Когда основные компоненты ETL-скрипта и dbt-модели готовы, наступает этап их объединения в единый автоматизированный рабочий процесс. Для этого используется Apache Airflow, где логика управления описывается в виде DAG (Directed Acyclic Graph) . В рассматриваемом примере первый оператор отвечает за запуск Python-скрипта, а второй — DockerOperator — создает отдельный контейнер для dbt . Это позволяет изолированно запустить трансформации поверх данных, уже загруженных в целевое хранилище на первом этапе .
Важным архитектурным изменением становится модификация файла docker-compose.yaml. Поскольку теперь запуском задач управляет Airflow, из основного файла конфигурации Docker следует закомментировать сервисы elt_script и dbt . Airflow сам будет инициировать создание нужных контейнеров в нужной последовательности . Перед полноценным запуском всей системы критически важно выполнить команду docker compose up init-airflow . Это инициализирует базу данных Postgres для метаданных Airflow и настраивает необходимые учетные записи . Только после того как контейнеры базы данных и инициализации перейдут в статус выполнения, можно запускать веб-сервер и планировщик (scheduler) .
Отладка и работа с интерфейсом Airflow 2:33:13
Веб-интерфейс Airflow доступен по адресу localhost:8080 . Стандартные учетные данные для входа в рамках данного проекта — airflow / airflow . Часто при первом запуске возникают ошибки импорта, такие как "broken DAG" . В процессе разработки автор сталкивается с типичной опечаткой в названии модулей: вместо operators необходимо использовать providers в пути импорта для Docker-оператора . Правильный путь выглядит как airflow.providers.docker.operators.docker .
После исправления синтаксических ошибок в UI появляется DAG с именем elt_and_dbt . При ручном запуске графа (trigger dag) можно наблюдать за статусом выполнения каждой задачи в реальном времени . Если задача dbt падает с ошибкой, Airflow предоставляет доступ к подробным логам, которые хранятся в его собственной базе данных Postgres . Одной из специфических проблем при использовании Docker внутри Airflow является настройка путей: проект dbt должен быть правильно смонтирован, например, по пути /opt/dbt внутри контейнера . Также следует внимательно следить за тем, чтобы в папке моделей не оставалось лишних примеров (вроде my_second_dbt_model.sql), которые могут вызвать конфликты при компиляции проекта . Успешное выполнение обеих задач в DAG подтверждается зеленым цветом индикаторов в интерфейсе .
Автоматизация среды и переход к новым операторам 2:43:35
Для масштабирования проекта и добавления новых инструментов, таких как Airbyte (платформа для интеграции данных), необходимо расширить возможности контейнера Airflow. В Dockerfile добавляются новые провайдеры через pip install: помимо Docker-провайдера, устанавливаются apache-airflow-providers-http и apache-airflow-providers-airbyte . HTTP-провайдер является обязательной зависимостью для работы с Airbyte . Чтобы упростить процесс запуска множества контейнеров, автор создает вспомогательные bash-скрипты: start.sh и stop.sh . Скрипт запуска автоматизирует инициализацию Airflow, ожидание в 5 секунд и последующий запуск Airbyte из его собственной директории .
В самом коде DAG также происходят изменения. Вместо использования Python-оператора для самописного ETL-скрипта (который ранее обсуждался в контексте извлечения данных), внедряется AirbyteTriggerSyncOperator . Этот оператор позволяет инициировать синхронизацию данных через внешний инструмент, передавая лишь connection_id . Основные параметры оператора включают:
airbyte_conn_id: имя подключения, настроенное внутри Airflow (обычно 'airbyte') ;connection_id: уникальный идентификатор конкретной связки источника и приемника в Airbyte ;asynchronous: параметр, установленный вFalse, чтобы Airflow дожидался завершения задачи перед переходом к dbt .
Подготовка инфраструктуры для внешних инструментов 2:51:30
Интеграция сторонних инструментов в оркестрацию требует понимания того, как контейнеры взаимодействуют друг с другом. Поскольку Airbyte работает в своем наборе Docker-контейнеров, Airflow должен знать, как к ним обратиться . Для этого в настройках подключений Airflow используется адрес host.docker.internal, что позволяет контейнерам «общаться» через хост-машину . При настройке источников данных (например, Source Postgres) важно правильно указывать порты, такие как 5433 для исходной базы, чтобы избежать конфликтов с целевой базой данных .
Хотя Airbyte имеет собственный планировщик, при работе в связке с Airflow его лучше переключить в режим manual . Это гарантирует, что именно Airflow будет выступать «единым источником истины» для расписания всех процессов . Выбор режима синхронизации (например, Full Refresh Overwrite ) также влияет на то, как dbt-модели будут обрабатывать данные на следующем шаге DAG. Таким образом, Airflow связывает разрозненные инструменты — от систем перемещения данных до систем трансформации — в единый, контролируемый поток данных.
🔄 Интеграция с Airbyte: Переход к современному стеку данных 2:55:40
В финальной стадии проекта происходит качественный переход от самописных решений к профессиональным инструментам автоматизации. Если ранее в рамках курса мы создавали ETL-скрипт на Python вручную, то теперь его место занимает Airbyte — платформа с открытым исходным кодом, которая стандартизирует процесс извлечения и загрузки данных. Этот этап превращает разрозненные контейнеры в согласованную экосистему, где каждый инструмент выполняет свою узкоспециализированную задачу.
Синхронизация Airflow и Airbyte: Настройка соединений 2:55:55
Первым шагом к автоматизации является установление связи между оркестратором Airflow и платформой Airbyte. Для этого необходимо получить уникальный идентификатор соединения (Connection ID) непосредственно из интерфейса Airbyte и интегрировать его в код нашего DAG-файла . Это критически важный момент, так как именно через этот ID Airflow будет отдавать команды на запуск конкретных потоков данных.
Однако простого обновления кода недостаточно. Поскольку компоненты системы работают в изолированных Docker-контейнерах, изменения не всегда распространяются мгновенно . Инструктор подчеркивает, что для корректной работы часто требуется перезапуск части инфраструктуры. В данном случае необходимо остановить секцию ELT, сохранив Airbyte запущенным, а затем инициализировать процесс заново с помощью скрипта elt.sh . Это гарантирует, что все переменные окружения и обновленные DAG-файлы будут подтянуты системой корректно .
Настройка самого соединения внутри веб-интерфейса Airflow требует точности в деталях:
- Тип соединения устанавливается как HTTP, а в поле Host прописывается адрес
host.docker.internal. - Порт для доступа к UI Airbyte — 8001 .
- Для авторизации используются стандартные учетные данные: логин
airbyteи парольpassword, что является обязательным требованием безопасности в современных версиях платформы .
Устранение неполадок при развертывании в Docker 2:57:26
Интеграция сложных инструментов часто сопровождается техническими трудностями, связанными с зависимостями и правами доступа. В процессе настройки автор сталкивается с ошибкой импорта оператора airflow.providers.airbyte.operators.airbyte . Проблема кроется в специфике сборки образов: Airflow требует выполнения установки провайдеров от имени определенного пользователя, что создает конфликт при стандартной сборке .
Решение заключается в специфическом алгоритме: сначала система должна войти под пользователем airflow для установки необходимых операторов, а затем переключиться на пользователя root для выполнения DAG . Еще один важный нюанс — кеширование образов в Docker. Если изменения в командах установки не применяются, единственный надежный способ — полностью удалить старые образы веб-сервера и планировщика (scheduler), после чего запустить сборку с нуля . Только после такой «чистой» переустановки DAG появляется в веб-интерфейсе без ошибок .
Запуск финального пайплайна и верификация данных 3:00:29
Когда все настройки завершены, происходит запуск всей цепочки. Airflow триггерит Airbyte, и в интерфейсе последнего можно наблюдать за процессом синхронизации в реальном времени . Успешное завершение этого этапа подтверждается статистикой: в рассматриваемом примере из источника было извлечено 1113 записей . Сразу после этого оркестратор автоматически запускает dbt (о котором рассказывалось в предыдущих главах), чтобы выполнить финальные трансформации .
Проверка результата в целевой базе данных (destination_db) показывает полную картину работы пайплайна:
- Появляются таблицы с префиксом
_airbyte_raw, содержащие «сырые» данные и метаданные платформы . - Присутствуют финальные таблицы, созданные dbt, такие как
specific_movie. - Данные успешно перемещены из Postgres (источник) через Airbyte в целевую БД и трансформированы для анализа .
В итоге проект демонстрирует эволюцию пайплайна: от сложного и хрупкого ручного скрипта в начале обучения к чистому и масштабируемому решению на базе Modern Data Stack . Использование Airbyte и Airflow позволяет инженеру сосредоточиться на логике данных, а не на написании кода для обработки API или сетевых протоколов .