Перейти к основному содержимому

PostgreSQL в Docker

Инженеру

Раздел 8.11, шаг 7 из 12. Дальше — облако и Kubernetes.


Базовый запуск

Официальный образ — hub.docker.com/_/postgres.

docker run -d \
--name pg-dev \
-e POSTGRES_USER=app \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=appdb \
-p 5432:5432 \
-v pgdata:/var/lib/postgresql/data \
postgres:16-alpine
ПеременнаяСмысл
POSTGRES_USER / POSTGRES_PASSWORDСуперпользователь (default postgres)
POSTGRES_DBБД, создаваемая при init
PGDATAПуть к данным внутри контейнера (default /var/lib/postgresql/data)

Init-скрипты — монтируйте .sql / .sh в /docker-entrypoint-initdb.d/ (выполняются только при первом создании volume).


Volume — обязательное правило

Без named volume или bind-mount данные исчезают при удалении контейнера.

# docker-compose.yml
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data
- ./init:/docker-entrypoint-initdb.d:ro
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
interval: 5s
timeout: 3s
retries: 5
command:
- "postgres"
- "-c"
- "shared_buffers=256MB"
- "-c"
- "log_min_duration_statement=200ms"

volumes:
pgdata:

healthcheck с pg_isready — приложение должно ждать depends_on: condition: service_healthy (Compose v2+).


Сеть и несколько контейнеров

services:
api:
build: .
environment:
DATABASE_URL: postgres://app:${DB_PASSWORD}@db:5432/appdb
depends_on:
db:
condition: service_healthy

db:
image: postgres:16-alpine
# …

pgbouncer:
image: edoburu/pgbouncer
environment:
DATABASE_URL: postgres://app:${DB_PASSWORD}@db:5432/appdb
POOL_MODE: transaction
ports:
- "6432:5432"
depends_on:
db:
condition: service_healthy

Имя сервиса db — DNS внутри compose-сети. Приложение подключается к PgBouncer, Postgres остаётся internal.


Кастомный конфиг

Вариант 1 — command: postgres -c key=val (см. выше).

Вариант 2 — смонтировать файл:

volumes:
- ./postgresql.conf:/etc/postgresql/postgresql.conf:ro
command: postgres -c config_file=/etc/postgresql/postgresql.conf

На production в контейнере часто минимальный конфиг + override через orchestrator.


Типовые ошибки

ОшибкаПоследствиеРешение
Данные в writable layerПотеря при recreateNamed volume
docker-compose down -v в CIУдаление volumeОтдельный volume для prod
Один контейнер = prod БД без бэкапаПотеря данныхшаг 10, managed или Patroni
fsync=off «для скорости»Корruption при crashТолько ephemeral dev
Root в контейнере + широкие права на volumeРиск безопасностиUser namespace, POSTGRES_INITDB_ARGS=--auth-host=scram-sha-256
Контейнер ≠ высокая доступность

Один Docker-контейнер с Postgres подходит для dev/stage и учебных стендов. Production HA — репликация, Patroni или managed-сервис (шаги 8–9).


Образы и версии

  • -alpine — меньше размер, иногда проблемы с локалями и расширениями.
  • Pinningpostgres:16.4-alpine, не latest.
  • Расширения (PostGIS) — образы postgis/postgis или сборка своего Dockerfile:
FROM postgres:16-alpine
RUN apk add --no-cache postgis

Миграция данных в/из контейнера

docker exec -t pg-dev pg_dump -U app -Fc appdb > backup.dump
docker exec -i pg-dev pg_restore -U app -d appdb < backup.dump

Логический dump переносим между версиями (с оговорками major upgrade).


Практика

  1. Поднимите compose с api + postgres + healthcheck; подключитесь через phpPgAdmin или psql.
  2. Убейте контейнер db, пересоздайте — данные должны сохраниться в volume.
  3. Добавьте PgBouncer, прогоните нагрузку через pgbench на 6432.

Связанные материалы


См. также

Другие статьи этого же раздела в боковом меню (как на странице "О разделе").