Создание локальной платформы данных с Kubernetes и Terraform
Чем дольше я работаю инженером по данным (Data Engineer), тем отчётливее понимаю, насколько эта роль пересекается с ролью инженера платформы данных (Data Platform Engineer). Помимо пайплайнов и трансформаций, мы всё чаще берём на себя ответственность за инфраструктуру, инструментарий и опыт разработчика. Именно с этой мыслью написан данный материал — он призван дать практический пример того, как объединить такие инструменты, как Kubernetes, Terraform и DevContainers, чтобы построить локальную платформу данных структурированно и удобно для сопровождения.
Этот пост продолжает предыдущую статью, в которой я рассказывал, как поднять локальный кластер Kubernetes и развернуть приложения вручную с помощью Argo CD. Если вы её ещё не читали — рекомендую начать с неё, поскольку часть концепций и инструментов перейдёт в этот пост.
На этот раз фокус смещается в сторону автоматизации и воспроизводимости. Используя Terraform и DevContainers, ту же конфигурацию можно выразить в виде кода и завернуть в согласованную среду разработки, которая точно воспроизводит рабочие процессы, характерные для продакшена. Хотя реальное продакшен-развёртывание опиралось бы на облачные ресурсы, конфигурации Kubernetes и операционные паттерны в значительной мере остаются теми же.
|
Примечание
|
Я не буду подробно останавливаться на инструментах, которые уже разобраны в предыдущем посте. Вместо этого сосредоточусь на новых дополнениях — DevContainers и Terraform — и, что важнее, объясню, почему они выбраны и какие проблемы решают. |
Проект
Главная цель проекта — построить локальную платформу данных, позволяющую развёртывать инструменты и приложения структурированно, воспроизводимо и с удобным сопровождением. Несмотря на то что всё работает на машине разработчика, установка намеренно спроектирована с оглядкой на продакшен. Инфраструктура, компоненты платформы и приложения чётко разделены, а вся среда может быть воссоздана с нуля из кода.
Чтобы платформа выглядела реалистично, но без лишней сложности, мы развёртываем небольшой, но представительный набор сервисов данных (полный код доступен здесь):
-
Mage.ai — для оркестрации пайплайнов данных
-
PostgreSQL — основная база данных
-
Kafka — для потоковой передачи событий
В основе установки лежит Kubernetes, который обеспечивает единообразную абстракцию для запуска и управления сервисами в локальной, тестовой и продакшен-среде. Кластер Kubernetes создаётся локально с помощью KinD, а Terraform используется для декларативного управления компонентами платформы, работающими поверх него. Подход «инфраструктура как код» делает установку воспроизводимой, версионируемой и простой в развитии по мере роста сложности.
Terraform также отвечает за развёртывание Argo CD, который выступает движком GitOps для платформы. Технически возможно развернуть всё через Terraform, но я предпочитаю использовать каждый инструмент по назначению:
-
Terraform управляет компонентами уровня платформы и начальной конфигурацией (bootstrap)
-
Argo CD следит за тем, чтобы приложения были развёрнуты и синхронизированы с Git
Такое чёткое разделение ответственности исключает путаницу и упрощает понимание системы по мере её роста.
Наконец, DevContainers стандартизируют среду разработки. Заранее описав инструментарий и зависимости, мы избавляемся от проблемы «у меня на машине работает» и упрощаем онбординг для всех, кто приходит в проект.
Эту установку удобно представить в виде пирамиды:
-
Базовый слой: кластер Kubernetes (KinD) и начальная загрузка платформы
-
Средний слой: оркестрация приложений через Argo CD (GitOps)
-
Верхний слой: приложения и сервисы данных (Mage, PostgreSQL, Kafka, пайплайны)
Такая структура повторяет типичный дизайн реальных платформ данных, оставаясь при этом достаточно лёгкой, чтобы работать полностью на локальной машине.
Сборка локальной платформы
Определив цели проекта и принципы проектирования, перейдём к тому, как они отражаются в коде и как платформа собирается от начала до конца. Вместо того чтобы рассматривать структуру и исполнение по отдельности, этот раздел показывает, как расположение файлов в репозитории напрямую поддерживает процесс начальной загрузки платформы.
Проект разбит на небольшое количество директорий верхнего уровня, каждая из которых несёт чётко определённую ответственность. Это позволяет легко ориентироваться в системе и понимать, где должны жить те или иные изменения по мере развития платформы.
.
├── .devcontainer/
├── scripts/
├── infra/
│ ├── terraform/
│ └── kind/
├── apps/
├── argo/
├── Makefile
└── README.md
1. Стандартизация среды разработки (.devcontainer/)
Всё начинается с единообразной среды разработки. Директория .devcontainer/ описывает полностью воспроизводимую установку с помощью DevContainers, гарантируя, что все участники используют одинаковые инструменты, версии и зависимости.
DevContainer включает все инструменты, необходимые для локальной работы с платформой — Terraform, инструменты Kubernetes и KinD — с зафиксированными версиями, чтобы исключить расхождения между машинами. Kubernetes запускается локально с помощью Docker-in-Docker, что позволяет создавать кластер и управлять им полностью изнутри контейнера.
Такой подход устраняет проблему «у меня на машине работает» и гарантирует, что платформу можно поднять из чистого чекаута без каких-либо ручных настроек на хосте.
|
Примечание
|
Запуск Kubernetes через DevContainer и Docker-in-Docker имеет ограничения. Доступ через Ingress в такой конфигурации непрактичен, поэтому сервисы открываются через проброс портов (port forwarding). |
2. Создание кластера Kubernetes (infra/kind/)
После того как среда разработки готова, следующий шаг — создание самого кластера Kubernetes. За это отвечает KinD, а конфигурация кластера описана в файле infra/kind/kind-config.yaml.
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: development
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true,kubernetes.io/os=linux"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
Конфигурация задаёт, как именно должен быть создан локальный кластер: роли узлов, метки и маппинги портов. После создания кластер становится доступен через отдельный контекст kubeconfig, который служит фундаментом для всех последующих шагов.
Важно, что создание кластера намеренно отделено от провизии инфраструктуры. KinD отвечает только за создание кластера, но не за установку чего-либо внутри него.
3. Начальная загрузка управляющего уровня платформы (infra/terraform/)
Когда кластер создан, Terraform берёт на себя установку компонентов уровня платформы, работающих внутри него. Вся конфигурация Terraform хранится в infra/terraform/ и охватывает исключительно платформенные задачи, но не приложения.
В локальной среде разработки Terraform устанавливает Argo CD в кластер с помощью провайдера Helm. Конфигурация провайдера связывает Terraform с кластером Kubernetes, созданным KinD, что позволяет декларативно управлять ресурсами внутри кластера.
На этом зона ответственности Terraform заканчивается. Развёртыванием приложений он не занимается. Вместо этого он подготавливает кластер к передаче управления GitOps.
Такое намеренное сужение области применения упрощает понимание системы и исключает дублирование ответственности между инструментами.
3a. Как Terraform подключается к кластеру
Конфигурация Terraform следует стандартным соглашениям и разбита на несколько файлов, каждый из которых несёт чёткую ответственность.
-
versions.tf— определяет требуемую версию Terraform и ограничения на версии провайдеров (Helm и Kubernetes), обеспечивая единообразие инструментария в разных средах. -
variables.tf— задаёт входные параметры, специфичные для окружения:-
пространство имён (namespace), в которое устанавливается Argo CD
-
путь к kubeconfig внутри DevContainer
-
контекст Kubernetes, созданный KinD
-
-
providers.tf— подключает Terraform к кластеру Kubernetes, настраивая провайдеры Helm и Kubernetes для использования нужного kubeconfig и контекста. -
main.tf— содержит фактическое определение ресурсов платформы. В данном случае Terraform устанавливает Argo CD из официального Helm-чарта, применяя небольшой набор переопределений значений и сохраняя всё под контролем версий.
После того как все эти части собраны вместе, Terraform может использовать локальный кластер KinD как целевую систему и декларативно поднять управляющий уровень платформы.
3b. Как всё это стыкуется
На высоком уровне поток выглядит так:
-
KinD создаёт кластер Kubernetes, используя конфигурацию из
infra/kind/ -
Terraform считывает входные параметры окружения и конфигурацию провайдера
-
Terraform подключается к кластеру через контекст
kind-development -
Terraform устанавливает Argo CD в кластер
-
Argo CD берёт управление и разворачивает всё, что определено в
argo/иapps/
Это ключевая точка передачи управления в платформе:
Terraform загружает управляющий уровень, а GitOps с этого момента берёт на себя полный контроль.
4. Описание приложений через GitOps (argo/ и apps/)
После завершения работы Terraform управление переходит к Argo CD. Директория argo/ содержит манифесты Argo CD, которые определяют, как приложения разворачиваются по принципу GitOps, следуя паттерну «app-of-apps».
Единственное корневое приложение указывает Argo CD на директорию, содержащую по одному манифесту Application на каждый сервис данных. Каждое приложение определено независимо, со своей конфигурацией и жизненным циклом.
Фактическая конфигурация развёртывания этих сервисов хранится в apps/. В зависимости от инструмента используются разные подходы:
-
Helm — значения для таких инструментов, как Mage
-
Kustomize — базы и оверлеи для таких инструментов, как Kafka
Такое разделение позволяет приложениям развиваться независимо, оставаясь полностью декларативными и версионируемыми.
Начиная с этого момента Argo CD непрерывно сверяет желаемое состояние из Git с тем, что реально работает в кластере.
|
Примечание
|
Для таких сервисов, как Kafka, можно использовать более современные и продвинутые способы развёртывания с помощью операторов, например Strimzi. |
5. Оркестрация процесса начальной загрузки (scripts/)
Все перечисленные шаги объединяет небольшой набор вспомогательных скриптов в директории scripts/. Основная точка входа — скрипт начальной загрузки, который оркестрирует полный жизненный цикл:
#!/usr/bin/env bash
set -euo pipefail
# Определяем корень репозитория (директория, где лежит скрипт)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
mkdir -p "${REPO_ROOT}/.kube"
KIND_UP="${REPO_ROOT}/scripts/kind-up.sh"
TF_DIR="${REPO_ROOT}/infra/terraform/environments/dev"
ARGO_BOOTSTRAP="${REPO_ROOT}/argo/app-of-apps.yaml"
if [[ ! -x "${KIND_UP}" ]]; then
echo "ERROR: kind-up script not found or not executable: ${KIND_UP}" >&2
echo "Repo root: ${REPO_ROOT}" >&2
echo "Contents of scripts/:" >&2
ls -la "${REPO_ROOT}/scripts" || true
exit 1
fi
bash "${KIND_UP}" development
pushd "${TF_DIR}" >/dev/null
terraform init
terraform apply -auto-approve
popd >/dev/null
kubectl apply -f "${ARGO_BOOTSTRAP}"
Скрипт последовательно выполняет:
-
Создание (или пересоздание) локального кластера Kubernetes
-
Запуск Terraform для установки компонентов платформы
-
Начальную загрузку Argo CD с корневым приложением
Это даёт единственную повторяемую команду для поднятия всей платформы с нуля, а также устанавливает чёткий порядок выполнения, который впоследствии можно переиспользовать в CI/CD-пайплайнах для валидации, тестирования или провизии окружений.
Благодаря тому, что репозиторий организован вокруг порядка выполнения и чёткого разделения ответственности, платформа остаётся понятной, несмотря на участие множества инструментов. Каждая директория выполняет конкретную роль на конкретном этапе процесса начальной загрузки.
В результате получается локальная платформа данных, повторяющая реальные архитектуры, полностью воспроизводимая и поднимаемая из чистого чекаута с минимальными ручными усилиями.
Запускаем платформу
Когда все компоненты на месте, можно поднять платформу и увидеть её в действии. На этом этапе вся установка управляется из единой точки входа, максимально близкой к тому, как такая платформа в итоге работала бы из CI/CD-пайплайна.
Запуск среды разработки
Сначала откройте репозиторий в DevContainer. Это гарантирует, что все необходимые инструменты (Terraform, CLI для Kubernetes, Helm и др.) доступны и правильно настроены.
После того как DevContainer запущен, все команды выполняются изнутри контейнера.
Одна команда для запуска всего
Имея готовую среду разработки, всю платформу можно поднять единственной командой:
make up
Внутри это тонкая обёртка над scripts/bootstrap.sh, которая оркестрирует описанный выше полный цикл начальной загрузки.
Примерно через минуту платформа и все приложения должны быть развёрнуты.
Это также воспроизводит то, как реальная платформа данных могла бы запускаться в CI/CD, что упрощает переход от этой локальной установки к более продакшен-подобному окружению в будущем.
Доступ к компонентам платформы
Поскольку платформа работает внутри DevContainer с Docker-in-Docker, приложения доступны через проброс портов (port forwarding). В Makefile уже есть вспомогательные команды для наиболее распространённых сервисов.
Чтобы открыть интерфейс Argo CD, выполните:
make argo-ui
Эта команда настраивает проброс портов и выводит:
-
Локальный URL
-
Имя пользователя и пароль
После открытия вы увидите все приложения платформы, развёрнутые Argo CD и управляемые им. Также можно заметить, что некоторые приложения (например, Mage) развёрнуты через Helm — именно так, как задано в соответствующих манифестах Argo CD Application.
Доступ к Mage открывается соответствующей командой Makefile:
make mage-ui
В интерфейсе Mage вы можете:
-
Создавать пайплайны в интерактивном режиме
-
Исследовать существующие рабочие процессы
-
Экспериментировать с паттернами развёртывания (например, пайплайны на базе Docker) — подробнее в этом руководстве
PostgreSQL также можно открыть через проброс портов с помощью соответствующей команды Makefile. После этого можно подключиться к базе данных через клиент, например DBeaver.
Просмотр топиков и сообщений Kafka
С Kafka всё немного иначе. Технически пробросить порт Kafka возможно, но сделать это надёжно требует тщательной настройки advertised listeners и зачастую создаёт больше проблем, чем решает при локальной отладке.
Более простой и надёжный подход — запустить временный отладочный под с тем же образом Kafka и взаимодействовать с кластером изнутри Kubernetes.
kubectl run kafka-debug -n develop \
--image=confluentinc/cp-kafka:7.6.1 \
--rm -it -- bash
Оказавшись внутри контейнера, можно вывести список доступных топиков:
kafka-topics \
--bootstrap-server kafka-svc.develop.svc.cluster.local:9092 \
--list
Чтобы просмотреть сообщения в топике:
kafka-console-consumer \
--bootstrap-server kafka-svc.develop.svc.cluster.local:9092 \
--topic iot-telemetry \ # или любое другое название вашего топика
--from-beginning
Этот подход позволяет убедиться в корректной работе Kafka, не открывая брокеры за пределы кластера.
|
Примечание
|
В этом проекте учётные данные хранятся в открытом виде для простоты. В реальном продакшен-окружении секреты следует хранить в защищённом менеджере секретов (например, в облачных хранилищах ключей) и никогда не коммитить их в репозиторий. |
Демо-пайплайн
Когда платформа поднята и работает, можно проверить, как отдельные инструменты взаимодействуют друг с другом, запустив простой готовый пайплайн данных.
Откройте интерфейс Mage. Демо-пайплайн уже определён и настроен так, чтобы запускаться без дополнительных настроек. Все необходимые переменные окружения параметризованы, так что пайплайн должен заработать сразу.
|
Примечание
|
Для более детального знакомства с тем, как этот пайплайн интегрируется в полноценный CI/CD-процесс, смотрите следующие посты: |
В рамках этого демо мы вручную создадим таблицу в PostgreSQL для хранения входящих телеметрических данных.
Сначала пробросьте сервис PostgreSQL с помощью соответствующей команды Makefile. После этого подключитесь к базе данных через клиент, например DBeaver.
Затем создайте таблицу, выполнив:
CREATE TABLE IF NOT EXISTS telemetry (
device_id TEXT NOT NULL,
ts TIMESTAMPTZ NOT NULL,
energy_usage NUMERIC(5,2),
temperature NUMERIC(4,1),
vibration NUMERIC(4,1),
signal_strength INTEGER,
PRIMARY KEY (device_id, ts)
);
Когда таблица создана, вернитесь в интерфейс Mage и запустите демо-пайплайн вручную (он состоит из streaming_tutorial и lambda_iot_data_generator — нужно запустить оба). Для простоты пайплайн запускается в интерактивном режиме, а не по расписанию.
Пока пайплайн работает, его статус и отдельные шаги можно отслеживать прямо в интерфейсе Mage.
Во время выполнения пайплайнов можно делать запросы к таблице telemetry в PostgreSQL, чтобы убедиться, что данные успешно записываются. Если всё прошло как ожидалось, вы увидите записи, соответствующие выводу пайплайна.
Это простое демо объединяет все ключевые компоненты платформы — сетевое взаимодействие Kubernetes, развёртывание приложений, оркестрацию и хранение данных — без лишней сложности.
В итоге у вас есть полностью рабочая локальная платформа данных, на которой можно экспериментировать с операциями Kubernetes, сетевыми настройками, развёртываниями и рабочими процессами GitOps в реалистичной, но лёгкой среде.
Заключение
В этом посте мы построили локальную платформу данных, воспроизводящую многие паттерны, характерные для реальных продакшен-сред. Используя Kubernetes (через KinD) как исполнительный слой, Terraform для провизии инфраструктуры, Argo CD для развёртывания по принципу GitOps и DevContainers для единообразной среды разработки, мы создали установку, которая воспроизводима, дружественна к автоматизации и легко поддаётся осмыслению.
Цель состояла не в том, чтобы рассмотреть каждый инструмент отдельно, а в том, чтобы показать, как они вписываются в единую целостную платформу. В результате получается локальное окружение, которое можно поднять с нуля, которое развивается через код и чётко разграничивает ответственность между инфраструктурой, компонентами платформы и приложениями.
Для инженеров по данным такая установка помогает перекинуть мост между написанием пайплайнов и владением платформами, на которых они работают. Это практический способ поэкспериментировать с Kubernetes, Terraform и рабочими процессами GitOps без накладных расходов на управление реальной облачной инфраструктурой — при этом используя паттерны, которые напрямую переносятся в продакшен.
Следующие естественные шаги: интеграция CI/CD для автоматизации процесса начальной загрузки, добавление наблюдаемости (observability) для лучшего понимания поведения системы и создание более реалистичных пайплайнов поверх платформы. Именно эти темы я планирую рассмотреть дальше.
Спасибо за чтение!
Надеюсь, это руководство оказалось полезным и вдохновит вас на дальнейшие эксперименты с архитектурами пайплайнов данных.
Обратная связь всегда приветствуется и помогает улучшать будущие материалы.
📦 Код проекта доступен в репозитории на GitHub
🤝 Буду рад связаться с вами в LinkedIn