commit фиксирует изменения в локальном репозитории, push отправляет коммиты в удалённый. Ветка это указатель на коммит: изменения делаются в отдельной ветке от main, проверяются в merge request и вливаются. Повседневные команды: status, add, commit, pull, push, checkout/switch, branch, merge, log, diff, stash. rebase переносит коммиты ветки поверх свежего main, давая линейную историю без merge-коммитов; rebase -i склеивает и правит коммиты перед MR. Правило: не делать rebase уже опубликованных общих веток.
Пайплайн на merge request: линтеры и форматирование (ruff/flake8, black, sqlfluff), юнит-тесты на mock-данных, проверка DAG на импорт, сборка образа или артефакта; при слиянии в main деплой на стенд, прогон интеграционных проверок, затем прод. Секреты не в репозитории: переменные CI с маскированием, Vault или секрет-менеджер облака, в Airflow Connections с секрет-бэкендом; в коде только имена секретов. Код базы (таблицы, процедуры) тоже под версиями: миграции (Liquibase, dbt, Flyway, drizzle) применяются пайплайном, а не руками в проде.
DDL и процедуры в репозитории как миграции с номерами, ревью через merge request, автоматическое применение на тестовом стенде, затем в прод в окно с откатом (обратная миграция или снимок). Данные витрин не деплоятся, пересчитываются пайплайном после изменения схемы. Для dbt-проектов это dbt run из CI по окружениям.
Docker упаковывает код с зависимостями в образ: один и тот же образ для локальной разработки, CI и прода, воспроизводимое окружение для Airflow-воркеров, Spark-драйверов, сервисов загрузки; docker compose поднимает локальный стенд из базы, Airflow и MinIO. Kubernetes запускает эти образы в кластере: KubernetesPodOperator выполняет задачу в изолированном поде с нужными ресурсами, Spark on Kubernetes заменяет YARN, сервисы масштабируются и перезапускаются автоматически. Инженер данных обычно пишет Dockerfile и манифесты для своих задач, а кластер поддерживает DevOps.
Монолит это одно приложение и один деплой: проще разрабатывать и отлаживать, но сложнее масштабировать части независимо. Микросервисы делят систему на независимые сервисы с отдельными деплоями и командами: гибкость и масштабирование ценой сети, согласованности данных и инфраструктуры. Для платформы данных обычно модульный монолит пайплайнов в одном репозитории с общими библиотеками и отдельные сервисы только для того, что живёт постоянно (API выдачи данных, стриминговые консьюмеры). Ответ «зависит от команды и объёма» с аргументами ценится выше, чем модное слово.
Shell уместен для обвязки: запуск и цепочки утилит (psql, spark-submit, aws s3), проверки файлов, ротация логов, сборка и деплой, cron там, где нет оркестратора. Ускорение приходит от инструментов, которые shell вызывает: COPY вместо построчной вставки, параллельная выгрузка через xargs -P или parallel, потоковая обработка через конвейеры без промежуточных файлов. Логику данных в shell не пишут: нет тестов, типов и обработки ошибок, поэтому длиннее 50 строк это уже Python.
Метрики и дашборды: Prometheus + Grafana (нагрузка кластера, лаг Kafka, длительность задач Airflow через StatsD), логи: Loki или ELK, алерты в Telegram/Slack/почту из Grafana и из Airflow (on_failure_callback). Для данных отдельный слой: таблица результатов проверок качества и свежести, из неё метрики в тот же Grafana. Важно разделять алерты «упала задача» (инженер) и «данные не сходятся» (владелец данных).
Горизонтальное масштабирование воркеров Airflow и исполнителей Spark (динамическая аллокация), репликация в Kafka (фактор 3) и в HDFS, зеркала сегментов и резервный мастер Greenplum, реплики ClickHouse через ReplicatedMergeTree и ZooKeeper/Keeper, реплика Postgres для чтения. На уровне пайплайнов: ретраи с паузой, идемпотентные задачи, таймауты, очереди с пулами, чтобы падение одного источника не останавливало остальные. Отказоустойчивость начинается с того, что перезапуск любой задачи безопасен.