Merge branch 'fix/greenplum-pxf'

This commit is contained in:
2026-01-09 14:03:57 +03:00
6 changed files with 225 additions and 48 deletions
+3 -2
View File
@@ -22,9 +22,10 @@ stop:
docker compose -f docker-compose.yml stop
down:
docker compose -f docker-compose.yml down -v
docker compose -f docker-compose.yml down
clean: down
clean:
docker compose -f docker-compose.yml down -v
airflow-init:
docker compose -f docker-compose.yml run --rm airflow-init
+11 -6
View File
@@ -82,11 +82,12 @@ CSV-файлы после выполнения DAG остаются в дире
Если после изменений что‑то «сломалось»:
```bash
make down # Остановить и стереть данные в контейнерах
make down # Остановить и удалить контейнеры/сети (volumes сохраняются)
make up
```
Это помогает, когда Greenplum не стартует из‑за «грязной» остановки и внутренних файлов.
Если проблема связана с «грязной» остановкой и данными в томах (например, Greenplum не стартует),
используйте полный reset: `make clean && make up` (данные в Docker-томах будут потеряны).
---
@@ -150,10 +151,10 @@ make gp-psql # Подключение к Greenplum
- базовые конфиги PXF копируются в `PXF_BASE/conf` (если их ещё нет);
- создаются каталоги `PXF_BASE/run` и `PXF_BASE/logs`;
- `CREATE EXTENSION pxf` выполняется автоматически, когда Greenplum становится доступен.
- `CREATE EXTENSION pxf` выполняется автоматически, когда Greenplum становится доступен (с ретраями).
Healthcheck сервиса `greenplum` учитывает не только готовность Greenplum, но и запуск PXF
это нужно, чтобы Airflow не стартовал раньше PXF.
Healthcheck сервиса `greenplum` учитывает не только готовность Greenplum, но и запуск PXF,
а также наличие `extension pxf`это нужно, чтобы Airflow не стартовал раньше PXF.
Сборка и запуск:
@@ -240,6 +241,9 @@ make bookings-psql # Подключиться к демобазе bookings
# Проверка данных
make logs # Следить за логами Airflow
# Логи задач Airflow сохраняются в Docker-томе `airflow_logs`
# и переживают `docker compose down`/`up` (удаляются при `docker compose down -v` / `make clean`).
# Контроль генерации bookings
docker compose -f docker-compose.yml exec bookings-db bash -lc 'PGPASSWORD="$POSTGRES_PASSWORD" psql -U "$POSTGRES_USER" -d demo -c "SELECT busy();"'
# busy() = t — генерация ещё идёт; f — завершена. При необходимости можно вызвать CALL abort(); и запустить генерацию заново.
@@ -409,11 +413,12 @@ load_bookings_to_stg = PostgresOperator(
| Проблема | Решение |
|----------|---------|
| Airflow UI не открывается | Дождитесь сообщения `Listening at: http://0.0.0.0:8080` в логах (`make logs`) |
| `database "demo" does not exist` в bookings‑DAG | Вы сделали полный reset с удалением томов (`docker compose down -v` / `make clean`), поэтому демобаза bookings не установлена. Запустите `make bookings-init` и повторите DAG. |
| Ошибка подключения к Greenplum | Убедитесь, что контейнер `greenplum` стал статусом `healthy` (проверьте `docker compose ps`) |
| Не открывается порт 8080/5433/5434/5435 | Проверьте, что эти порты не заняты локальными сервисами; при необходимости остановите их или измените порты в `.env`/`docker-compose.yml` |
| Нет файла в `./data` после запуска DAG | Проверьте логи задачи `generate_csv`, убедитесь, что `CSV_DIR` смонтирован в docker-compose |
| Команда `make` не найдена | Используйте полные команды `docker compose` или установите make |
| Greenplum не стартует/падает при старте | Выполните `make down`, затем `make up` (очищает тома и поднимает заново, включая авто‑инициализацию Airflow) |
| Greenplum не стартует/падает при старте | Попробуйте `make down && make up`. Если не помогло — полный reset: `make clean && make up` (удалит тома). |
| `protocol "pxf" does not exist` | Перезапустите `greenplum` и повторите `bookings_stg_ddl`/`make ddl-gp` — расширение `pxf` создаётся автоматически при старте контейнера. |
| PXF не отвечает (Connection refused к порту 5888) | Проверьте `pxf cluster status` в контейнере `greenplum` и перезапустите сервис `greenplum`. |
| PXF не подхватывает изменения конфигов | Пересоберите образ (`make build`) и перезапустите `greenplum`. Для принудительной перезаписи файлов задайте `PXF_SEED_OVERWRITE=1`. |
+4 -3
View File
@@ -37,14 +37,15 @@
- Проверить, что все 5 задач Success и логи содержат `Проверка пройдена`.
- DAG `bookings_to_gp_stage` (полная проверка цепочки bookings → Greenplum STG):
- предварительно выполнить один раз: `make bookings-init` (инициализация демо‑БД bookings) и `make ddl-gp` (создаёт `stg.bookings_ext` и `stg.bookings` в Greenplum);
- предварительно выполнить один раз: `make bookings-init` (установка демобазы `demo` в контейнере `bookings-db`) и `make ddl-gp` (создаёт `stg.bookings_ext` и `stg.bookings` в Greenplum);
- важно: DAG `bookings_stg_ddl` **не** создаёт базу `demo` в `bookings-db`; если вы делали `docker compose down -v` / `make clean`, `make bookings-init` обязателен;
- включить DAG `bookings_to_gp_stage` и запустить `Trigger DAG`;
- убедиться, что все задачи (`generate_bookings_day`, `load_bookings_to_stg`, `check_row_counts`, `finish_summary`) завершились со статусом Success;
- при желании проверить данные: в `bookings-db` появился новый день, а в Greenplum в `stg.bookings` — строки с актуальным `batch_id` (см. пример запросов в разделе 5).
- (опционально, для менторов/разработчиков) Smoke-тест DAG через Airflow CLI без UI:
- `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` внутри контейнера).
- `docker compose -f docker-compose.yml exec 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 airflow-webserver airflow dags trigger bookings_to_gp_stage` — создаёт реальный запуск DAG (логи и статус можно смотреть либо через UI, либо командой `airflow tasks list`/`airflow tasks logs` внутри контейнера).
## 5. Проверка данных в Greenplum
- `make gp-psql` — запустить psql в контейнере от имени `gpadmin`.
+8 -3
View File
@@ -10,6 +10,7 @@ x-airflow-common-volumes: &airflow-volumes
- ./airflow/dags:/opt/airflow/dags
- ./sql:/sql:ro
- airflow_data:/opt/airflow/data
- airflow_logs:/opt/airflow/logs
x-airflow-common-depends: &airflow-depends
pgmeta:
@@ -86,7 +87,7 @@ services:
healthcheck:
# Ждём не только доступность GPDB, но и готовность PXF,
# чтобы Airflow не стартовал раньше PXF.
test: [ "CMD-SHELL", "/usr/local/greenplum-db/bin/pg_isready -h 127.0.0.1 -p 5432 -U ${GP_USER:-gpadmin} -d ${GP_DB:-gp_dwh} && su - ${GP_USER:-gpadmin} -c '/usr/local/pxf/bin/pxf cluster status' >/dev/null 2>&1" ]
test: [ "CMD-SHELL", "/usr/local/greenplum-db/bin/pg_isready -h 127.0.0.1 -p 5432 -U ${GP_USER:-gpadmin} -d ${GP_DB:-gp_dwh} && PGPASSWORD=${GP_PASSWORD:-gpadmin} /usr/local/greenplum-db/bin/psql -h 127.0.0.1 -p 5432 -U ${GP_USER:-gpadmin} -d ${GP_DB:-gp_dwh} -t -A -c \"SELECT 1 FROM pg_extension WHERE extname='pxf';\" | grep -q 1 && su - ${GP_USER:-gpadmin} -c '/usr/local/pxf/bin/pxf cluster status' >/dev/null 2>&1" ]
interval: 10s
timeout: 5s
retries: 30
@@ -101,13 +102,14 @@ services:
env_file: .env
environment:
<<: *airflow-env
command: airflow webserver
command: bash -lc "rm -f /opt/airflow/airflow-webserver.pid && exec airflow webserver"
ports:
- "8080:8080"
volumes:
- ./airflow/dags:/opt/airflow/dags
- ./sql:/sql:ro
- airflow_data:/opt/airflow/data
- airflow_logs:/opt/airflow/logs
- ./airflow/requirements.txt:/opt/airflow/requirements.txt
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
@@ -132,6 +134,7 @@ services:
- ./airflow/dags:/opt/airflow/dags
- ./sql:/sql:ro
- airflow_data:/opt/airflow/data
- airflow_logs:/opt/airflow/logs
- ./airflow/requirements.txt:/opt/airflow/requirements.txt
depends_on:
<<: *airflow-depends
@@ -150,10 +153,11 @@ services:
- ./airflow/dags:/opt/airflow/dags
- ./sql:/sql:ro
- airflow_data:/opt/airflow/data
- airflow_logs:/opt/airflow/logs
command: >
bash -lc "
set -e;
mkdir -p /opt/airflow/data && chown -R airflow:root /opt/airflow/data;
mkdir -p /opt/airflow/data /opt/airflow/logs && chown -R airflow:root /opt/airflow/data /opt/airflow/logs;
# Дожидаемся готовности БД ретрая миграции
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;
@@ -170,3 +174,4 @@ volumes:
bookings_data:
greenplum_data:
airflow_data:
airflow_logs:
+73 -27
View File
@@ -101,13 +101,13 @@
- причина: файлы уже лежат в `PXF_BASE` на томе, а seed из образа по умолчанию не перетирает их;
- решение: `make build` + restart `greenplum` + (при необходимости) `PXF_SEED_OVERWRITE=1`.
## 9. Известная проблема: `protocol "pxf" does not exist` на «холодном старте»
## 9. Известная проблема: `protocol "pxf" does not exist` на «холодном старте» (исправлено)
Иногда (и это воспроизводится в `./scripts/e2e_smoke.sh`) при первом `make ddl-gp` можно получить:
Раньше (воспроизводилось в `./scripts/e2e_smoke.sh`) при первом `make ddl-gp` можно было получить:
`ERROR: protocol "pxf" does not exist`
### Почему так происходит
### Почему так происходило
В базовом `/start_gpdb.sh` из образа Greenplum создание расширения `pxf` связано с проверкой
файла `${PXF_BASE}/conf/pxf-env.sh`:
@@ -116,35 +116,26 @@
`CREATE EXTENSION IF NOT EXISTS pxf`;
- если `pxf-env.sh` **уже существует**, этот блок **пропускается**, и расширение может не появиться.
При этом наш ensure‑скрипт `pxf/init/10_pxf_bookings.sh` копирует `pxf-env.sh` в `${PXF_BASE}`
ещё до запуска Greenplum, из‑за чего базовый скрипт считает PXF “уже настроенным” и
пропускает создание расширения.
При этом наш ensure‑скрипт `pxf/init/10_pxf_bookings.sh` копировал `pxf-env.sh` в `${PXF_BASE}`
ещё до запуска Greenplum, из‑за чего базовый скрипт считал PXF “уже настроенным” и
пропускал создание расширения.
Отдельно `/start_greenplum_with_pxf.sh` пытается выполнить `CREATE EXTENSION IF NOT EXISTS pxf`
в фоне, но это может “не попасть” в окно готовности базы (гонка при старте), поэтому после старта:
- PXF может быть “running” (`pxf cluster status`), но
- `protocol pxf` в базе ещё не создан.
### Что изменили
### Как проверить
- создание `extension pxf` вынесено в `start_greenplum_with_pxf.sh` и обёрнуто ретраями;
- `pxf-env.sh` по‑прежнему копируется в `PXF_BASE`, чтобы `/start_gpdb.sh` не пытался выполнять
`pxf cluster prepare` на непустом `PXF_BASE`;
- healthcheck `greenplum` ждёт не только PXF, но и наличие `extension pxf`.
- добавлен экспорт `PGPASSWORD` для `pxf cluster start`, чтобы `docker compose stop/start`
не ломал запуск из‑за `password authentication failed` для `gpadmin`.
- `docker compose exec greenplum bash -lc "su - gpadmin -c '/usr/local/greenplum-db/bin/psql -d gp_dwh -t -A -c \"SELECT extname FROM pg_extension WHERE extname = ''pxf'';\"'"`
### Если ошибка всё ещё возникает
Если команда ничего не вернула — расширение не создано.
1) Пересоберите образ и перезапустите контейнер `greenplum`:
`make build && make down && make up`
### Временный workaround (ручной)
1) Создать расширение:
`docker compose exec greenplum bash -lc "su - gpadmin -c '/usr/local/greenplum-db/bin/psql -d gp_dwh -c \"CREATE EXTENSION IF NOT EXISTS pxf;\"'"`
2) Повторить DDL:
`make ddl-gp`
### Что чинить в коде (идея фикса)
Сделать создание расширения `pxf` независимым от наличия `pxf-env.sh`:
- либо убрать/изменить условие в `/start_gpdb.sh` (в образе),
- либо перестать копировать `pxf-env.sh` в ensure‑скрипте и дать базовому скрипту создать его,
- либо сделать retry‑логику `CREATE EXTENSION pxf` надёжной (повторять попытки и проверять результат).
2) Проверьте наличие extension:
`docker compose exec greenplum bash -lc "su - gpadmin -c '/usr/local/greenplum-db/bin/psql -d gp_dwh -t -A -c \"SELECT extname FROM pg_extension WHERE extname = ''pxf'';\"'"`
## 8. Связанные файлы
@@ -153,3 +144,58 @@
- `pxf/init/10_pxf_bookings.sh` (ensure‑логика)
- `pxf/init/start_greenplum_with_pxf.sh` (старт контейнера)
- `README.md` (раздел «Greenplum + PXF: свой образ»)
## 10. Известная проблема: после `docker compose stop/start` Greenplum может упасть (auth для PXF)
### Симптом
После `docker compose stop`, затем `docker compose start` контейнер `greenplum` иногда уходит в `Exited (1)`.
В логах видно, что GPDB поднялся, но упал на старте PXF:
- `INFO - pxf cluster start`
- `ERROR: Could not connect to GPDB`
- `FATAL: password authentication failed for user "gpadmin"`
### Текущее понимание причины (почему это “иногда”)
1) При старте GPDB образ `woblerr/greenplum` генерирует/дописывает `pg_hba.conf` на persistent volume.
2) В `pg_hba.conf` присутствует trust‑правило для **конкретного IP** контейнера в docker‑сети
(пример из диагностики: `host all gpadmin 172.21.0.2/32 trust`).
3) После `docker compose stop/start` Docker может выдать контейнеру **другой IP** (например, `172.21.0.3`).
Тогда trust‑правило больше не подходит, и подключение начинает идти по `md5`.
4) `pxf cluster start` подключается к GPDB по TCP на `host=gpdbsne` (hostname контейнера),
то есть попадает именно в `pg_hba.conf` (а не в localauth).
5) В результате при “не совпавшем IP” получаем `md5` + пароль (возможно пустой/не тот) → падение на `28P01`.
Эта проблема выглядит флапающей, потому что IP после `stop/start` иногда совпадает с захардкоженным trust‑/32,
а иногда нет.
### Как подтвердить при следующем воспроизведении
1) Посмотреть логи `greenplum`:
`docker compose logs --tail=200 greenplum`
2) Найти реальный IP клиента в master‑логах GPDB (на томе):
`Password does not match ...` обычно содержит адрес вида `172.21.0.X`.
3) Сравнить его с trust‑строкой в `pg_hba.conf` на томе:
`/data/master/gpseg-1/pg_hba.conf`
Если IP в ошибке (например, `172.21.0.3`) **не** совпадает с trust‑/32 (например, `172.21.0.2/32`) —
это почти наверняка корень падения.
### Что с этим делать дальше (варианты решения, без реализации здесь)
Основная цель — убрать зависимость от “случайного IP после stop/start”:
- заставить `pxf cluster start` подключаться к GPDB через `127.0.0.1` (тогда работает существующий trust на localhost);
- или перестать добавлять в `pg_hba.conf` trust на конкретный `172.21.0.2/32` и заменить на более стабильное правило
(например, на подсеть docker‑сети или на `samehost`);
- или закрепить IP контейнера в compose (static IP), чтобы он не “плавал”;
- или отказаться от `stop/start` в пользу сценария, который не меняет сетевое окружение (но это хуже для UX студентов).
### Что реализовано
- В `pxf/init/start_greenplum_with_pxf.sh` добавлен шаг, который на каждом старте
обеспечивает в `pg_hba.conf` trust‑правило `host all gpadmin samehost trust`
(вставка перед `host all all 0.0.0.0/0 md5`), и делает `pg_ctl reload`, если GPDB уже запущен.
+125 -6
View File
@@ -1,9 +1,105 @@
#!/usr/bin/env bash
set -euo pipefail
# Этот скрипт — "обёртка" над стандартным стартом Greenplum (exec /start_gpdb.sh),
# которая добавляет устойчивый старт PXF для учебного стенда.
#
# Задачи скрипта (почему он нужен):
# 1) Подготовить PXF_BASE на persistent volume (через ensure-скрипт).
# 2) Обеспечить стабильную аутентификацию PXF → GPDB после `docker compose stop/start`
# (не зависеть от «плавающего» IP контейнера в docker-сети).
# 3) Гарантировать наличие расширения `pxf` в БД GPDB (иначе внешние таблицы падают с
# `ERROR: protocol "pxf" does not exist`).
#
# Важно: это учебный стенд, поэтому мы сознательно выбираем простые и надёжные решения
# (например, trust для samehost), а не «боевой» hardened security.
ensure_script="/opt/pxf-scripts/ensure_pxf_bookings.sh"
# PXF CLI использует libpq и при `pxf cluster start` подключается к GPDB как к обычному Postgres.
# После `docker compose stop/start` может внезапно потребоваться пароль (см. ensure_pg_hba_trust ниже),
# поэтому экспортируем PGPASSWORD заранее: это уменьшает "флап" и делает поведение воспроизводимым.
if [ -z "${PGPASSWORD:-}" ]; then
export PGPASSWORD="${GREENPLUM_PASSWORD:-gpadmin}"
fi
ensure_pg_hba_trust() {
# После `docker compose stop/start` Docker может выдать контейнеру другой IP.
# У базового образа Greenplum встречается trust-правило на конкретный /32 (старый IP),
# и тогда аутентификация по TCP начинает идти через md5 → PXF падает на `28P01`.
#
# Решение: добавить правило `host all gpadmin samehost trust`, которое срабатывает для
# подключений "с этого же контейнера" (PXF запускается рядом с master).
# Это максимально простое и стабильное правило для учебного стенда.
local gp_user="${GREENPLUM_USER:-gpadmin}"
local data_dir="${GREENPLUM_DATA_DIRECTORY:-/data}"
local attempts=60
local pg_hba=""
local trust_line="host all ${gp_user} samehost trust"
find_pg_hba() {
local candidate
for candidate in "${data_dir}/master"/*/pg_hba.conf; do
if [ -f "${candidate}" ]; then
echo "${candidate}"
return 0
fi
done
return 1
}
for _ in $(seq 1 "${attempts}"); do
pg_hba="$(find_pg_hba || true)"
if [ -n "${pg_hba}" ]; then
break
fi
sleep 2
done
if [ -z "${pg_hba}" ]; then
echo "WARN - pg_hba.conf не найден, пропускаем trust для ${gp_user}"
return 0
fi
if grep -Eq "^[[:space:]]*host[[:space:]]+all[[:space:]]+${gp_user}[[:space:]]+samehost[[:space:]]+trust" "${pg_hba}"; then
echo "INFO - pg_hba.conf уже содержит trust для ${gp_user} samehost"
return 0
fi
# Вставляем trust-правило перед самым "общим" md5-правилом (0.0.0.0/0),
# чтобы samehost гарантированно матчился раньше.
awk -v trust_line="${trust_line}" '
BEGIN { added = 0 }
$0 ~ /^[[:space:]]*host[[:space:]]+all[[:space:]]+all[[:space:]]+0\.0\.0\.0\/0[[:space:]]+md5/ && added == 0 {
print trust_line
added = 1
}
{ print }
END {
if (added == 0) {
print trust_line
}
}
' "${pg_hba}" > "${pg_hba}.tmp" && mv "${pg_hba}.tmp" "${pg_hba}"
echo "INFO - pg_hba.conf: добавлен trust для ${gp_user} samehost"
local master_dir="${pg_hba%/pg_hba.conf}"
# Если GPDB уже поднялся, достаточно reload, чтобы новое правило применилось без рестарта.
if /usr/local/greenplum-db/bin/pg_ctl -D "${master_dir}" status >/dev/null 2>&1; then
if ! /usr/local/greenplum-db/bin/pg_ctl -D "${master_dir}" reload >/dev/null 2>&1; then
echo "WARN - не удалось перезагрузить pg_hba.conf (pg_ctl reload)"
fi
fi
}
ensure_pxf_extension() {
# В базовом /start_gpdb.sh создание `CREATE EXTENSION pxf` зависит от наличия
# `${PXF_BASE}/conf/pxf-env.sh`. В нашем стенде pxf-env.sh может быть уже создан
# ensure-скриптом (PXF_BASE на томе), и тогда /start_gpdb.sh пропускает extension.
#
# Чтобы внешние таблицы через PXF работали после любого рестарта, создаём extension сами,
# но только когда GPDB начнёт принимать подключения (с ретраями).
local gp_user="${GREENPLUM_USER:-gpadmin}"
local gp_db="${GREENPLUM_DATABASE_NAME:-gp_dwh}"
local gp_password="${GREENPLUM_PASSWORD:-gpadmin}"
@@ -13,30 +109,53 @@ ensure_pxf_extension() {
return 0
fi
for _ in $(seq 1 "${attempts}"); do
extension_exists() {
local result
# Подключаемся к localhost: это "локальный" путь и на нём обычно уже есть trust.
result=$(PGPASSWORD="${gp_password}" /usr/local/greenplum-db/bin/psql \
-h 127.0.0.1 -p 5432 -U "${gp_user}" -d "${gp_db}" \
-t -A -c "SELECT 1 FROM pg_extension WHERE extname='pxf';" 2>/dev/null || true)
[ "${result}" = "1" ]
}
for attempt in $(seq 1 "${attempts}"); do
# Ждём, пока master начнёт принимать подключения.
if /usr/local/greenplum-db/bin/pg_isready \
-h 127.0.0.1 -p 5432 -U "${gp_user}" -d "${gp_db}" >/dev/null 2>&1; then
if extension_exists; then
echo "INFO - extension pxf уже создано"
return 0
fi
if PGPASSWORD="${gp_password}" /usr/local/greenplum-db/bin/psql \
-h 127.0.0.1 -p 5432 -U "${gp_user}" -d "${gp_db}" \
-v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS pxf;" >/dev/null; then
-v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS pxf;" >/dev/null 2>&1; then
if extension_exists; then
echo "INFO - extension pxf готово"
else
echo "WARN - не удалось создать extension pxf"
fi
return 0
fi
fi
echo "WARN - попытка ${attempt}/${attempts}: не удалось создать extension pxf"
fi
sleep 2
done
echo "WARN - Greenplum не готов, пропускаем CREATE EXTENSION pxf"
echo "WARN - Greenplum не готов или extension pxf не создано"
}
# 1) Подготовка PXF_BASE (копирование seed, конфигов и т.д.).
if [ -f "${ensure_script}" ]; then
"${ensure_script}"
else
echo "WARN - не найден ensure-скрипт PXF: ${ensure_script}"
fi
# 2) Дальше запускаем две "подстраховки" параллельно, чтобы не замедлять старт контейнера:
# - правка pg_hba.conf (как только он появится на томе);
# - создание extension pxf (как только GPDB начнёт отвечать).
ensure_pg_hba_trust &
ensure_pxf_extension &
# 3) Стартуем GPDB "как обычно". Важно использовать exec, чтобы сигналы Docker
# (stop/restart) корректно приходили в основной процесс entrypoint.
exec /start_gpdb.sh