From 4cb11b4769978ec1d617aafeaec65a8b6e84e443 Mon Sep 17 00:00:00 2001 From: Dmitry Dementev Date: Sat, 13 Dec 2025 22:21:41 +0300 Subject: [PATCH 1/2] feat: use custom docker image for airflow to avoid runtime pip install --- Dockerfile | 4 ++++ Makefile | 5 ++++- docker-compose.yml | 26 ++++++++++++-------------- 3 files changed, 20 insertions(+), 15 deletions(-) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fd508b9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +FROM apache/airflow:2.9.2 + +COPY airflow/requirements.txt /requirements.txt +RUN pip install --no-cache-dir -r /requirements.txt diff --git a/Makefile b/Makefile index 4ea395d..ea563ea 100644 --- a/Makefile +++ b/Makefile @@ -9,12 +9,15 @@ BOOKINGS_INIT_DAYS ?= 1 .PHONY: up stop down clean airflow-init logs gp-psql ddl-gp \ bookings-clone-demodb bookings-init bookings-psql bookings-generate-day \ - dev-setup dev-sync dev-lock test lint fmt clean-venv + dev-setup dev-sync dev-lock test lint fmt clean-venv build SHELL := /bin/bash up: docker compose -f docker-compose.yml up -d +build: + docker compose -f docker-compose.yml build + stop: docker compose -f docker-compose.yml stop diff --git a/docker-compose.yml b/docker-compose.yml index e149ea7..a60b225 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,7 +14,7 @@ services: volumes: - pgmeta:/var/lib/postgresql/data healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${PG_USER} -d ${PG_DB}"] + test: [ "CMD-SHELL", "pg_isready -U ${PG_USER} -d ${PG_DB}" ] interval: 5s timeout: 5s retries: 20 @@ -35,13 +35,13 @@ services: # В этот каталог будет монтироваться генератор demodb - ./bookings:/bookings:ro healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${BOOKINGS_DB_USER} -d ${BOOKINGS_DB_NAME}"] + test: [ "CMD-SHELL", "pg_isready -U ${BOOKINGS_DB_USER} -d ${BOOKINGS_DB_NAME}" ] interval: 5s timeout: 5s retries: 20 greenplum: - image: woblerr/greenplum:6.27.1 # based on https://github.com/woblerr/docker-greenplum + image: woblerr/greenplum:6.27.1 # based on https://github.com/woblerr/docker-greenplum # container_name: gp_single # hostname: gpdbsne environment: @@ -61,13 +61,14 @@ services: - greenplum_data:/data # Простая проверка доступности: psql откликается healthcheck: - test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -p 5432 -U ${GP_USER:-gpadmin} -d ${GP_DB:-gp_dwh} || echo 1"] + test: [ "CMD-SHELL", "pg_isready -h 127.0.0.1 -p 5432 -U ${GP_USER:-gpadmin} -d ${GP_DB:-gp_dwh} || echo 1" ] interval: 10s timeout: 5s retries: 30 airflow-webserver: - image: apache/airflow:2.9.2 + build: . + image: airflow-custom:latest container_name: gp_airflow_web env_file: .env environment: @@ -77,9 +78,7 @@ services: AIRFLOW__WEBSERVER__SECRET_KEY: ${AIRFLOW__WEBSERVER__SECRET_KEY} AIRFLOW_CONN_GREENPLUM_CONN: postgresql://${GP_USER}:${GP_PASSWORD}@greenplum:${GP_PORT:-5432}/${GP_DB} AIRFLOW_CONN_BOOKINGS_DB: postgresql://${BOOKINGS_DB_USER}:${BOOKINGS_DB_PASSWORD}@bookings-db:5432/demo - command: > - bash -lc "pip install --no-cache-dir -r /opt/airflow/requirements.txt && - airflow webserver" + command: airflow webserver ports: - "8080:8080" volumes: @@ -96,7 +95,8 @@ services: condition: service_completed_successfully airflow-scheduler: - image: apache/airflow:2.9.2 + build: . + image: airflow-custom:latest container_name: gp_airflow_sch env_file: .env environment: @@ -106,9 +106,7 @@ services: AIRFLOW__WEBSERVER__SECRET_KEY: ${AIRFLOW__WEBSERVER__SECRET_KEY} AIRFLOW_CONN_GREENPLUM_CONN: postgresql://${GP_USER}:${GP_PASSWORD}@greenplum:${GP_PORT:-5432}/${GP_DB} AIRFLOW_CONN_BOOKINGS_DB: postgresql://${BOOKINGS_DB_USER}:${BOOKINGS_DB_PASSWORD}@bookings-db:5432/demo - command: > - bash -lc "pip install --no-cache-dir -r /opt/airflow/requirements.txt && - airflow scheduler" + command: airflow scheduler volumes: - ./airflow/dags:/opt/airflow/dags - ./airflow/requirements.txt:/opt/airflow/requirements.txt @@ -123,7 +121,8 @@ services: condition: service_completed_successfully airflow-init: - image: apache/airflow:2.9.2 + build: . + image: airflow-custom:latest # container_name: gp_airflow_init user: "0" env_file: .env @@ -143,7 +142,6 @@ services: bash -lc " set -e; mkdir -p /opt/airflow/data && chown -R airflow:root /opt/airflow/data; - su -s /bin/bash airflow -c \"PATH='/home/airflow/.local/bin:$${PATH}' pip install --no-cache-dir -r /opt/airflow/requirements.txt\"; # Дожидаемся готовности БД ретрая миграции for i in {1..30}; do su -s /bin/bash airflow -c \"PATH='/home/airflow/.local/bin:$${PATH}' airflow db migrate\" && break || echo 'waiting for pgmeta' && sleep 3; From 1173e0e53fd61aa486f12ce053d97b471ab9a6a8 Mon Sep 17 00:00:00 2001 From: Dmitry Dementev Date: Tue, 6 Jan 2026 23:20:41 +0300 Subject: [PATCH 2/2] =?UTF-8?q?=D0=9E=D1=82=D0=BB=D0=B0=D0=B4=D0=BA=D0=B0?= =?UTF-8?q?=20airflow=20dockerfile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 4 - Dockerfile.airflow | 19 ++ TESTING.md | 4 +- docker-compose.yml | 85 +++--- plans/dockerfile-improvements.md | 489 +++++++++++++++++++++++++++++++ 5 files changed, 556 insertions(+), 45 deletions(-) delete mode 100644 Dockerfile create mode 100644 Dockerfile.airflow create mode 100644 plans/dockerfile-improvements.md diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index fd508b9..0000000 --- a/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM apache/airflow:2.9.2 - -COPY airflow/requirements.txt /requirements.txt -RUN pip install --no-cache-dir -r /requirements.txt diff --git a/Dockerfile.airflow b/Dockerfile.airflow new file mode 100644 index 0000000..c2c6865 --- /dev/null +++ b/Dockerfile.airflow @@ -0,0 +1,19 @@ +# Apache Airflow с дополнительными зависимостями для Greenplum +FROM apache/airflow:2.9.2 + +LABEL maintainer="your-email@example.com" +LABEL description="Airflow with Greenplum and Pandas dependencies" +LABEL version="1.0" + +# Копируем requirements в стандартное расположение +COPY airflow/requirements.txt /opt/airflow/requirements.txt + +# Устанавливаем зависимости +RUN pip install --no-cache-dir -r /opt/airflow/requirements.txt \ + && pip check \ + && rm -rf /tmp/pip-* + +# Переключаемся на пользователя airflow +USER airflow + +WORKDIR /opt/airflow diff --git a/TESTING.md b/TESTING.md index 52d84ce..4249521 100644 --- a/TESTING.md +++ b/TESTING.md @@ -43,8 +43,8 @@ - при желании проверить данные: в `bookings-db` появился новый день, а в Greenplum в `stg.bookings` — строки с актуальным `batch_id` (см. пример запросов в разделе 5). - (опционально, для менторов/разработчиков) Smoke-тест DAG через Airflow CLI без UI: - - `docker compose -f docker-compose.yml exec gp_airflow_web airflow dags test bookings_to_gp_stage 2024-01-01` — прогоняет `bookings_to_gp_stage` целиком в «off-line» режиме; - - `docker compose -f docker-compose.yml exec gp_airflow_web airflow dags trigger bookings_to_gp_stage` — создаёт реальный запуск DAG (логи и статус можно смотреть либо через UI, либо командой `airflow tasks list`/`airflow tasks logs` внутри контейнера). + - `docker compose -f docker-compose.yml exec gp_airflow_webserver airflow dags test bookings_to_gp_stage 2024-01-01` — прогоняет `bookings_to_gp_stage` целиком в «off-line» режиме; + - `docker compose -f docker-compose.yml exec gp_airflow_webserver airflow dags trigger bookings_to_gp_stage` — создаёт реальный запуск DAG (логи и статус можно смотреть либо через UI, либо командой `airflow tasks list`/`airflow tasks logs` внутри контейнера). ## 5. Проверка данных в Greenplum - `make gp-psql` — запустить psql в контейнере от имени `gpadmin`. diff --git a/docker-compose.yml b/docker-compose.yml index a60b225..d5d266a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,24 @@ +x-airflow-common-env: &airflow-env + TZ: ${TZ:-Europe/Moscow} + AIRFLOW__CORE__LOAD_EXAMPLES: "False" + AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: postgresql+psycopg2://${PG_USER}:${PG_PASSWORD}@pgmeta:5432/${PG_DB} + AIRFLOW__WEBSERVER__SECRET_KEY: ${AIRFLOW__WEBSERVER__SECRET_KEY} + AIRFLOW_CONN_GREENPLUM_CONN: postgresql://${GP_USER}:${GP_PASSWORD}@greenplum:${GP_PORT:-5432}/${GP_DB} + AIRFLOW_CONN_BOOKINGS_DB: postgresql://${BOOKINGS_DB_USER}:${BOOKINGS_DB_PASSWORD}@bookings-db:5432/demo + +x-airflow-common-volumes: &airflow-volumes + - ./airflow/dags:/opt/airflow/dags + - ./sql:/sql:ro + - airflow_data:/opt/airflow/data + +x-airflow-common-depends: &airflow-depends + pgmeta: + condition: service_healthy + greenplum: + condition: service_healthy + airflow-init: + condition: service_completed_successfully + services: # Postgres только для Airflow метаданных pgmeta: @@ -67,75 +88,61 @@ services: retries: 30 airflow-webserver: - build: . + build: + context: . + dockerfile: Dockerfile.airflow image: airflow-custom:latest - container_name: gp_airflow_web + container_name: gp_airflow_webserver env_file: .env environment: - TZ: ${TZ:-Europe/Moscow} - AIRFLOW__CORE__LOAD_EXAMPLES: "False" - AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: postgresql+psycopg2://${PG_USER}:${PG_PASSWORD}@pgmeta:5432/${PG_DB} - AIRFLOW__WEBSERVER__SECRET_KEY: ${AIRFLOW__WEBSERVER__SECRET_KEY} - AIRFLOW_CONN_GREENPLUM_CONN: postgresql://${GP_USER}:${GP_PASSWORD}@greenplum:${GP_PORT:-5432}/${GP_DB} - AIRFLOW_CONN_BOOKINGS_DB: postgresql://${BOOKINGS_DB_USER}:${BOOKINGS_DB_PASSWORD}@bookings-db:5432/demo + <<: *airflow-env command: airflow webserver ports: - "8080:8080" volumes: - ./airflow/dags:/opt/airflow/dags - - ./airflow/requirements.txt:/opt/airflow/requirements.txt - ./sql:/sql:ro - airflow_data:/opt/airflow/data + - ./airflow/requirements.txt:/opt/airflow/requirements.txt + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 40s depends_on: - pgmeta: - condition: service_healthy - greenplum: - condition: service_healthy - airflow-init: - condition: service_completed_successfully + <<: *airflow-depends airflow-scheduler: - build: . + build: + context: . + dockerfile: Dockerfile.airflow image: airflow-custom:latest - container_name: gp_airflow_sch + container_name: gp_airflow_scheduler env_file: .env environment: - TZ: ${TZ:-Europe/Moscow} - AIRFLOW__CORE__LOAD_EXAMPLES: "False" - AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: postgresql+psycopg2://${PG_USER}:${PG_PASSWORD}@pgmeta:5432/${PG_DB} - AIRFLOW__WEBSERVER__SECRET_KEY: ${AIRFLOW__WEBSERVER__SECRET_KEY} - AIRFLOW_CONN_GREENPLUM_CONN: postgresql://${GP_USER}:${GP_PASSWORD}@greenplum:${GP_PORT:-5432}/${GP_DB} - AIRFLOW_CONN_BOOKINGS_DB: postgresql://${BOOKINGS_DB_USER}:${BOOKINGS_DB_PASSWORD}@bookings-db:5432/demo + <<: *airflow-env command: airflow scheduler volumes: - ./airflow/dags:/opt/airflow/dags - - ./airflow/requirements.txt:/opt/airflow/requirements.txt - ./sql:/sql:ro - airflow_data:/opt/airflow/data + - ./airflow/requirements.txt:/opt/airflow/requirements.txt depends_on: - pgmeta: - condition: service_healthy - greenplum: - condition: service_healthy - airflow-init: - condition: service_completed_successfully + <<: *airflow-depends airflow-init: - build: . + build: + context: . + dockerfile: Dockerfile.airflow image: airflow-custom:latest - # container_name: gp_airflow_init + # Имя контейнера закомментировано (одноразовый сервис) user: "0" env_file: .env environment: - TZ: ${TZ:-Europe/Moscow} - AIRFLOW__CORE__LOAD_EXAMPLES: "False" - AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: postgresql+psycopg2://${PG_USER}:${PG_PASSWORD}@pgmeta:5432/${PG_DB} - AIRFLOW__WEBSERVER__SECRET_KEY: ${AIRFLOW__WEBSERVER__SECRET_KEY} - AIRFLOW_CONN_GREENPLUM_CONN: postgresql://${GP_USER}:${GP_PASSWORD}@greenplum:${GP_PORT:-5432}/${GP_DB} - AIRFLOW_CONN_BOOKINGS_DB: postgresql://${BOOKINGS_DB_USER}:${BOOKINGS_DB_PASSWORD}@bookings-db:5432/demo + <<: *airflow-env volumes: - ./airflow/dags:/opt/airflow/dags - - ./airflow/requirements.txt:/opt/airflow/requirements.txt - ./sql:/sql:ro - airflow_data:/opt/airflow/data command: > diff --git a/plans/dockerfile-improvements.md b/plans/dockerfile-improvements.md new file mode 100644 index 0000000..f117053 --- /dev/null +++ b/plans/dockerfile-improvements.md @@ -0,0 +1,489 @@ +# План улучшения Dockerfile и docker-compose.yml + +## Обзор + +Документ описывает план улучшения Dockerfile для Airflow и его интеграции с docker-compose.yml на основе анализа best practices. + +## Согласованные изменения + +✅ Переименовать `Dockerfile` → `Dockerfile.airflow` +✅ Обновить `build: .` → `build: { context: ., dockerfile: Dockerfile.airflow }` +✅ Добавить YAML anchors для устранения дублирования конфигурации +✅ Добавить healthcheck для airflow-webserver +✅ Исправить расположение requirements.txt в Dockerfile +✅ Добавить LABEL в Dockerfile +✅ Добавить `USER airflow` после установки зависимостей +✅ Добавить проверку `pip check` +✅ Переименовать контейнеры (`gp_airflow_web` → `gp_airflow_webserver`, `gp_airflow_sch` → `gp_airflow_scheduler`) + +--- + +## Часть 1: Изменения в Dockerfile + +### Текущее состояние (Dockerfile) +```dockerfile +FROM apache/airflow:2.9.2 + +COPY airflow/requirements.txt /requirements.txt +RUN pip install --no-cache-dir -r /requirements.txt +``` + +### Новое состояние (Dockerfile.airflow) +```dockerfile +# Apache Airflow с дополнительными зависимостями для Greenplum +FROM apache/airflow:2.9.2 + +LABEL maintainer="your-email@example.com" +LABEL description="Airflow with Greenplum and Pandas dependencies" +LABEL version="1.0" + +# Копируем requirements в стандартное расположение +COPY airflow/requirements.txt /opt/airflow/requirements.txt + +# Устанавливаем зависимости +RUN pip install --no-cache-dir -r /opt/airflow/requirements.txt \ + && pip check \ + && rm -rf /tmp/pip-* + +# Переключаемся на пользователя airflow +USER airflow + +WORKDIR /opt/airflow +``` + +### Обоснование изменений: + +1. **LABEL** - стандартная практика для документирования образов +2. **`/opt/airflow/requirements.txt`** - стандартное расположение для Airflow +3. **`pip check`** - проверка совместимости установленных пакетов +4. **`USER airflow`** - безопасность и соответствие best practices +5. **`WORKDIR /opt/airflow`** - явное указание рабочей директории + +--- + +## Часть 2: Изменения в docker-compose.yml + +### Основные изменения: + +1. **Добавить YAML anchors** для устранения дублирования +2. **Обновить build** для всех Airflow сервисов +3. **Добавить healthcheck** для airflow-webserver +4. **Добавить комментарии** для пояснения сокращений в именах контейнеров + +### Структура YAML anchors: + +```yaml +x-airflow-common-env: &airflow-env + TZ: ${TZ:-Europe/Moscow} + AIRFLOW__CORE__LOAD_EXAMPLES: "False" + AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: postgresql+psycopg2://${PG_USER}:${PG_PASSWORD}@pgmeta:5432/${PG_DB} + AIRFLOW__WEBSERVER__SECRET_KEY: ${AIRFLOW__WEBSERVER__SECRET_KEY} + AIRFLOW_CONN_GREENPLUM_CONN: postgresql://${GP_USER}:${GP_PASSWORD}@greenplum:${GP_PORT:-5432}/${GP_DB} + AIRFLOW_CONN_BOOKINGS_DB: postgresql://${BOOKINGS_DB_USER}:${BOOKINGS_DB_PASSWORD}@bookings-db:5432/demo + +x-airflow-common-volumes: &airflow-volumes + - ./airflow/dags:/opt/airflow/dags + - ./sql:/sql:ro + - airflow_data:/opt/airflow/data + +x-airflow-common-depends: &airflow-depends + pgmeta: + condition: service_healthy + greenplum: + condition: service_healthy + airflow-init: + condition: service_completed_successfully +``` + +### Изменения для airflow-webserver: + +```yaml +airflow-webserver: + build: + context: . + dockerfile: Dockerfile.airflow + image: airflow-custom:latest + container_name: gp_airflow_webserver + env_file: .env + environment: + <<: *airflow-env + command: airflow webserver + ports: + - "8080:8080" + volumes: + <<: *airflow-volumes + # requirements.txt монтируется как volume для удобства разработки + # При изменении зависимостей не требуется пересборка образа + - ./airflow/requirements.txt:/opt/airflow/requirements.txt + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 40s + depends_on: + <<: *airflow-depends +``` + +### Изменения для airflow-scheduler: + +```yaml +airflow-scheduler: + build: + context: . + dockerfile: Dockerfile.airflow + image: airflow-custom:latest + container_name: gp_airflow_scheduler + env_file: .env + environment: + <<: *airflow-env + command: airflow scheduler + volumes: + <<: *airflow-volumes + - ./airflow/requirements.txt:/opt/airflow/requirements.txt + depends_on: + <<: *airflow-depends +``` + +### Изменения для airflow-init: + +```yaml +airflow-init: + build: + context: . + dockerfile: Dockerfile.airflow + image: airflow-custom:latest + # Имя контейнера закомментировано (одноразовый сервис) + user: "0" + env_file: .env + environment: + <<: *airflow-env + volumes: + <<: *airflow-volumes + command: > + bash -lc " + set -e; + mkdir -p /opt/airflow/data && chown -R airflow:root /opt/airflow/data; + for i in {1..30}; do + su -s /bin/bash airflow -c \"PATH='/home/airflow/.local/bin:$${PATH}' airflow db migrate\" && break || echo 'waiting for pgmeta' && sleep 3; + done; + su -s /bin/bash airflow -c \"PATH='/home/airflow/.local/bin:$${PATH}' airflow users create --username ${AIRFLOW_USER} --password ${AIRFLOW_PASSWORD} --firstname Admin --lastname User --role Admin --email admin@example.org\" || true + " + depends_on: + pgmeta: + condition: service_healthy +``` + +--- + +## Часть 3: Влияние переименования контейнеров + +### Важно: Переименование контейнеров + +При переименовании контейнеров (`gp_airflow_web` → `gp_airflow_webserver`, `gp_airflow_sch` → `gp_airflow_scheduler`) необходимо учитывать: + +1. **Имена сервисов в docker-compose.yml остаются без изменений** + - `airflow-webserver`, `airflow-scheduler`, `airflow-init` - это имена сервисов + - Они используются в командах `docker-compose logs`, `docker-compose restart` и т.д. + - Эти имена НЕ меняются + +2. **Имена контейнеров меняются** + - `gp_airflow_web` → `gp_airflow_webserver` + - `gp_airflow_sch` → `gp_airflow_scheduler` + - Они используются в командах `docker exec`, `docker inspect`, `docker logs` + +3. **Проверка использования старых имён контейнеров** + ```bash + # Поиск упоминаний старых имён в скриптах + grep -r "gp_airflow_web" . + grep -r "gp_airflow_sch" . + ``` + +4. **Обновление Makefile (если используется)** + - Проверить команды, которые используют старые имена контейнеров + - Пример: `make logs` может использовать `docker-compose logs airflow-webserver` (это корректно) + +5. **Обновление документации** + - Обновить все упоминания имён контейнеров в README.md, TESTING.md + - Обновить примеры команд в документации + +--- + +## Часть 4: План тестирования изменений + +### Этап 1: Подготовка окружения + +1. **Остановить текущие контейнеры** + ```bash + make down + ``` + +2. **Удалить старые образы (опционально)** + ```bash + docker rmi airflow-custom:latest + ``` + +3. **Проверить наличие .env файла** + ```bash + ls -la .env + # Если отсутствует, скопировать из .env.example + cp .env.example .env + ``` + +### Этап 2: Сборка новых образов + +1. **Собрать образы с новым Dockerfile** + ```bash + docker-compose build + ``` + +2. **Проверить успешность сборки** + ```bash + docker images | grep airflow-custom + ``` + +3. **Проверить наличие LABEL** + ```bash + docker inspect airflow-custom:latest | grep -A 10 "Labels" + ``` + +### Этап 3: Запуск сервисов + +1. **Запустить весь стек** + ```bash + make up + ``` + +2. **Проверить статус контейнеров** + ```bash + docker-compose ps + ``` + +3. **Проверить логи инициализации** + ```bash + docker-compose logs airflow-init + ``` + +### Этап 4: Проверка healthcheck + +1. **Проверить healthcheck для airflow-webserver** + ```bash + docker inspect gp_airflow_webserver | grep -A 20 "Health" + ``` + +2. **Дождаться healthy статуса** + ```bash + watch -n 2 'docker inspect --format="{{.State.Health.Status}}" gp_airflow_webserver' + ``` + +### Этап 5: Функциональное тестирование + +1. **Проверить доступ к Airflow UI** + - Открыть http://localhost:8080 + - Проверить авторизацию (использовать креды из .env) + - Проверить наличие DAG'ов в списке + +2. **Проверить запуск DAG'ов** + - Выбрать любой DAG (например, `csv_to_greenplum`) + - Запустить вручную через UI + - Проверить успешность выполнения задач + +3. **Проверить подключения к БД** + - Проверить Admin → Connections + - Убедиться, что `greenplum_conn` и `bookings_db` доступны + - Проверить Test Connection для каждого подключения + +4. **Проверить логи scheduler** + ```bash + docker-compose logs airflow-scheduler | tail -50 + ``` + +### Этап 6: Проверка использования старых имён контейнеров + +1. **Поиск упоминаний старых имён в проекте** + ```bash + grep -r "gp_airflow_web" . --exclude-dir=.git --exclude-dir=__pycache__ + grep -r "gp_airflow_sch" . --exclude-dir=.git --exclude-dir=__pycache__ + ``` + +2. **Проверка Makefile** + ```bash + grep -E "gp_airflow_web|gp_airflow_sch" Makefile + ``` + +3. **Проверка документации** + ```bash + grep -r "gp_airflow_web" README.md TESTING.md AGENTS.md docs/ + grep -r "gp_airflow_sch" README.md TESTING.md AGENTS.md docs/ + ``` + +4. **Обновление найденных упоминаний** + - Заменить `gp_airflow_web` на `gp_airflow_webserver` + - Заменить `gp_airflow_sch` на `gp_airflow_scheduler` + - Обновить примеры команд в документации + +### Этап 7: Тестирование требований + +1. **Проверить установленные пакеты** + ```bash + docker exec gp_airflow_webserver pip list | grep -E "psycopg2|pandas" + ``` + +2. **Проверить совместимость пакетов** + ```bash + docker exec gp_airflow_webserver pip check + ``` + +3. **Проверить пользователя внутри контейнера** + ```bash + docker exec gp_airflow_webserver whoami + # Ожидаемый результат: airflow + ``` + +### Этап 8: Тестирование изменений requirements.txt + +1. **Добавить тестовый пакет в requirements.txt** + ```bash + echo "requests==2.31.0" >> airflow/requirements.txt + ``` + +2. **Перезапустить контейнеры** + ```bash + docker-compose restart airflow-webserver airflow-scheduler + ``` + +3. **Проверить установку пакета** + ```bash + docker exec gp_airflow_webserver pip list | grep requests + ``` + +4. **Удалить тестовый пакет** + ```bash + # Вернуть исходный requirements.txt + git checkout airflow/requirements.txt + ``` + +### Этап 9: Регрессионное тестирование + +1. **Запустить существующие тесты** + ```bash + make test + ``` + +2. **Проверить smoke-тесты DAG** + ```bash + uv run pytest tests/test_dags_smoke.py -v + ``` + +3. **Проверить тесты helpers** + ```bash + uv run pytest tests/test_greenplum_helpers.py -v + ``` + +### Этап 10: Проверка после перезапуска + +1. **Полный перезапуск стека** + ```bash + make down + make up + ``` + +2. **Проверить сохранность данных** + - Проверить наличие DAG'ов в UI + - Проверить историю запусков DAG'ов + - Проверить подключения к БД + +3. **Проверить логи на наличие ошибок** + ```bash + docker-compose logs airflow-webserver | grep -i error + docker-compose logs airflow-scheduler | grep -i error + # Обратите внимание: имена сервисов не изменились, только имена контейнеров + ``` + +--- + +## Часть 5: Критерии успеха + +### Функциональные требования: + +- ✅ Все контейнеры успешно запускаются +- ✅ Airflow UI доступен на http://localhost:8080 +- ✅ Авторизация работает корректно +- ✅ Все DAG'ы отображаются в UI +- ✅ DAG'и успешно выполняются +- ✅ Подключения к Greenplum и bookings-db работают +- ✅ Healthcheck для airflow-webserver работает корректно + +### Технические требования: + +- ✅ Образ собирается без ошибок +- ✅ LABEL присутствуют в образе +- ✅ Пакеты устанавливаются от пользователя airflow +- ✅ `pip check` не возвращает ошибок +- ✅ requirements.txt монтируется как volume +- ✅ YAML anchors работают корректно +- ✅ Нет дублирования конфигурации + +### Требования к совместимости: + +- ✅ Существующие тесты проходят успешно +- ✅ Данные сохраняются после перезапуска +- ✅ История запусков DAG'ов сохраняется +- ✅ Подключения к БД работают как раньше + +--- + +## Часть 6: Откат изменений + +Если изменения вызывают проблемы, план отката: + +1. **Остановить контейнеры** + ```bash + make down + ``` + +2. **Вернуть старые файлы** + ```bash + git checkout Dockerfile docker-compose.yml + ``` + +3. **Удалить новый образ** + ```bash + docker rmi airflow-custom:latest + ``` + +4. **Перезапустить стек** + ```bash + make up + ``` + +--- + +## Часть 7: Документация + +После успешного внедрения изменений необходимо обновить: + +1. **README.md** - добавить информацию о новом Dockerfile +2. **AGENTS.md** - обновить инструкции для агентов +3. **Комментарии в docker-compose.yml** - добавить пояснения к YAML anchors + +--- + +## Резюме + +План включает: +- 2 файла для изменения: `Dockerfile` → `Dockerfile.airflow`, `docker-compose.yml` +- 10 этапов тестирования +- 12 функциональных критериев успеха +- План отката на случай проблем +- Переименование контейнеров для улучшения читаемости +- Проверка использования старых имён контейнеров в проекте + +### Важные замечания: + +1. **Имена сервисов НЕ меняются** - `airflow-webserver`, `airflow-scheduler`, `airflow-init` +2. **Имена контейнеров меняются** - `gp_airflow_web` → `gp_airflow_webserver`, `gp_airflow_sch` → `gp_airflow_scheduler` +3. **Необходимо проверить** использование старых имён контейнеров в скриптах и документации +4. **Makefile команды** используют имена сервисов, поэтому они продолжат работать без изменений + +Все изменения следуют best practices для Docker и Airflow, сохраняют обратную совместимость и не требуют изменений в DAG'ах.