


Чтобы приложение и PostgreSQL работали через Docker Compose, им нужны согласованные настройки подключения, общая сеть и отдельное место для данных. В примере приложение записывает факт запуска в таблицу и выводит число записей. Затем мы пересоздадим контейнеры и проверим, что база сохранилась.
Это локальное упражнение Mentoring.digital, а не конфигурация для публикации в интернете. Здесь нет внешних портов и рабочих секретов. Конфигурация разобрана по документации Docker и PostgreSQL; фактический запуск Docker в редакционной среде не выполнен, поэтому команды ниже сопровождаются ожидаемыми результатами, а не выданным за тест отчётом.
Нужен работающий Docker Engine с Compose v2 или Docker Desktop с Linux-контейнерами. Проверьте docker version и docker compose version. В новой папке создайте compose.yaml:
name: mentoring-compose-lab
services:
db:
image: postgres:17
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD: local-study-only
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 2s
timeout: 3s
retries: 20
app:
build: .
environment:
PGHOST: db
PGDATABASE: app
PGUSER: app
PGPASSWORD: local-study-only
depends_on:
db:
condition: service_healthy
volumes:
pgdata:
Сервис db использует PostgreSQL 17. Для этой версии официального образа каталог данных в примере /var/lib/postgresql/data. У образов PostgreSQL 18 и новее схема каталогов отличается, поэтому нельзя просто заменить номер и считать перенос данных выполненным.
Файл Dockerfile собирает небольшое Python-приложение:
FROM python:3.12-slim
WORKDIR /app
RUN pip install --no-cache-dir "psycopg[binary]==3.2.3"
COPY app.py .
CMD ["python", "app.py"]
Версия драйвера закреплена для этого учебного примера. Это не заявление о последней версии. Теги python:3.12-slim и postgres:17 могут получать обновления. Для воспроизведения точных байтов образов в командной работе дополнительно фиксируют digest и хранят зависимости в lock-файле.
Файл app.py:
import os
import psycopg
with psycopg.connect(
host=os.environ["PGHOST"],
dbname=os.environ["PGDATABASE"],
user=os.environ["PGUSER"],
password=os.environ["PGPASSWORD"],
) as conn:
with conn.cursor() as cur:
cur.execute("""CREATE TABLE IF NOT EXISTS visits (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
created_at timestamptz NOT NULL DEFAULT now()
)""")
cur.execute("INSERT INTO visits DEFAULT VALUES")
cur.execute("SELECT count(*) FROM visits")
print(f"Saved runs: {cur.fetchone()[0]}")
Контекст соединения фиксирует успешную транзакцию и закрывает соединение. При исключении изменения этой транзакции откатываются. Программа выполняет одну операцию и завершается: это ожидаемо, у неё нет веб-сервера и фонового цикла.
docker compose config --quiet
docker compose up -d --build
docker compose ps -a
docker compose logs app
docker compose exec db psql -U app -d app -c "SELECT count(*) FROM visits;"
Проверка config обнаруживает ошибки структуры и подстановок, но не доказывает, что база доступна. После первого успешного запуска на новом томе приложение должно вывести Saved runs: 1, а SELECT вернуть 1. Если том уже содержит учебные записи, число будет больше.
Состояние Exited (0) у app здесь нормально: работа выполнена. Ненулевой код требует чтения логов. Не добавляйте бесконечный sleep только ради зелёного статуса контейнера.
Compose создаёт сеть проекта и позволяет сервисам находить друг друга по именам. Из app база доступна как db на внутреннем порту 5432. Адрес localhost внутри app указывает на сам контейнер приложения. Он не становится адресом соседнего PostgreSQL.
В примере нет ports: приложение общается с базой по внутренней сети, а проверка выполняется через compose exec. Если понадобится клиент на вашем компьютере, добавьте к db привязку 127.0.0.1:55432:5432 и подключайтесь с компьютера к 127.0.0.1:55432. В приложении адрес db:5432 при этом сохраняется.
Запустите приложение ещё раз как отдельную короткую задачу:
docker compose run --rm app
docker compose exec db psql -U app -d app -c "SELECT count(*) FROM visits;"
docker compose down
docker compose up -d
docker compose logs app
При исходном пустом томе после первого дополнительного запуска ожидается 2, после down и нового up приложение добавит третью запись. Именованный том pgdata сохраняется при обычном down и подключается к новому контейнеру базы того же Compose-проекта.
Не добавляйте к down параметр -v для устранения ошибки подключения. Он удаляет тома проекта и данные из них. Сохранение тома также не является резервной копией. Если изменить имя проекта или имя тома, можно подключить другое хранилище и ошибочно решить, что прежние данные исчезли.
| Симптом | Проверка |
|---|---|
| Имя db не разрешается | Приложение запущено в той же Compose-сети, адрес не перепутан с хостом |
| Connection refused | База запущена и принимает подключения; смотрите docker compose logs db |
| Password authentication failed | Учётные данные совпадают с уже созданным кластером |
| После изменения POSTGRES_PASSWORD пароль остался прежним | Переменные инициализации не перенастраивают существующую базу автоматически |
| Приложение завершилось | Посмотрите код выхода и логи: одноразовая программа вправе завершаться |
| После пересоздания ноль записей | Проверьте имя проекта, том и каталог монтирования |
Условие service_healthy упорядочивает первоначальный запуск по healthcheck. Оно не заменяет обработку обрывов соединения в долго работающем приложении и не гарантирует существование всех таблиц. Для полноценного сервиса нужны миграции, повторные подключения и отдельные проверки готовности под его задачи.
Запишите версии инструментов, три файла, команды запуска и ожидаемый рост счётчика. Укажите, что пароль учебный, данные хранятся в именованном томе, а удаление контейнеров и удаление данных являются разными операциями. Не переносите этот пароль и роль владельца базы в рабочую систему.
Для следующего шага на курсах Docker с практикой можно разобрать окружение вашего приложения. Наставник по Docker поможет проверить связи сервисов, хранение данных и воспроизводимость запуска по README.