Как разместить базу данных в Docker-контейнере, не нарушив целостность данных?

Ответ

Основной принцип — отделить данные от контейнера с помощью Docker Volumes или Bind Mounts. Контейнер должен быть эпиhemeral (временным), а данные — persistent (постоянными). В production-среде для Node.js приложений я всегда настраиваю том для данных СУБД.

Способ 1: Named Volumes (рекомендуется для Docker)

# docker-compose.yml для Node.js приложения и PostgreSQL
version: '3.8'
services:
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: strongpassword
    volumes:
      - postgres_data:/var/lib/postgresql/data # Named volume
    networks:
      - app-network

  app:
    build: .
    depends_on:
      - postgres
    environment:
      DATABASE_URL: postgresql://user:strongpassword@postgres:5432/myapp
    networks:
      - app-network

volumes:
  postgres_data: # Volume объявляется здесь

networks:
  app-network:
    driver: bridge

Способ 2: Bind Mounts (для разработки или специфичных путей на хосте)

volumes:
  - ./postgres-data:/var/lib/postgresql/data

Ключевые практики для Node.js окружения:

  1. Используйте .env файлы или секреты Docker для хранения паролей БД, никогда не хардкодьте их.
  2. Настройте регулярные бэкапы самого тома (например, pg_dump в cron-задаче внутри отдельного контейнера).
  3. Для stateful-сервисов в оркестраторах (K8s) используйте PersistentVolumeClaims.
  4. Тестируйте процедуру восстановления из бэкапа на staging-окружении.

Почему это важно: При обновлении образа базы данных или перезапуске контейнера том с данными остается нетронутым. Удаление контейнера postgres не приведет к потере данных, так как они хранятся в volume postgres_data.

Ответ 18+ 🔞

А, ну это же классика, ёпта! Слушай сюда, я тебе сейчас на пальцах объясню, как не наступить на эти грабли, чтобы потом не орать «ёбааать, где моя база?!».

Представь себе: ты накатал своё Node.js приложение, запустил в контейнере, базу туда же запихнул. Всё летает, красота. А потом — бац! — контейнер упал, пересоздался, или ты образ обновил. И тут выясняется, что твоя PostgreSQL, которая внутри контейнера жила, взяла и накрылась медным тазом. Все данные — в пизду. Пользователи, заказы, логи — всё, чих-пых тебя в сраку. Почему? Потому что контейнер — он как одноразовый стаканчик. Выкинул — и всё, что внутри, тоже на свалку.

Так как же быть? А вот как: надо отделить мух от котлет, а данные — от контейнера. Контейнер должен быть временным (ephemeral), а данные — вечными (persistent). Для этого есть два основных способа, и я тебе про оба расскажу, но один — прям золотой стандарт.

Способ 1: Named Volumes (То, что доктор прописал для прода)

Это как снять отдельную квартиру для своих данных. Docker сам её создаст и будет присматривать. Удобно, надёжно, переносимо. Вот смотри, как это в docker-compose.yml для твоего Node.js и Postgres выглядит:

version: '3.8'
services:
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: strongpassword
    volumes:
      - postgres_data:/var/lib/postgresql/data # Вот эта строка — магия! Том с именем.
    networks:
      - app-network

  app:
    build: .
    depends_on:
      - postgres
    environment:
      DATABASE_URL: postgresql://user:strongpassword@postgres:5432/myapp
    networks:
      - app-network

volumes:
  postgres_data: # Объявляем том здесь. Docker: «О, понял, ща сделаю!»

networks:
  app-network:
    driver: bridge

Что происходит? Данные базы живут теперь не в контейнере, а в этом самом томе postgres_data. Удалишь контейнер postgres — том останется. Поднимешь новый — он прицепится к тому же тому, и все твои таблички будут на месте, как ни в чём не бывало. Удобно, правда? Доверия к такому подходу — не ноль ебать, а вполне себе высокое.

Способ 2: Bind Mounts (Для разработки, когда надо ковыряться)

Это как прорубить дыру из контейнера прямо в папку на твоём компьютере. Очень удобно для разработки, потому что видишь файлы сразу, но для продакшна — так себе идея, слишком уж привязано к конкретной машине.

volumes:
  - ./postgres-data:/var/lib/postgresql/data # Связываем папку на хосте с папкой в контейнере.

Закинул так — и всё, что база пишет, летит прямиком в ./postgres-data на твоём диске. Перезапустил контейнер — данные на месте. Но если папку эту случайно удалишь... ну, ты понял, будет тебе хиросима.

А теперь, чувак, главные правила, чтобы не было мучительно больно:

  1. Пароли — в тайне. Никогда, слышишь, НИКОГДА не пихай пароли от базы прямо в код или docker-compose.yml. Это пиздопроебибна идея. Используй .env файлы или, что ещё круче, Docker Secrets. Иначе какой-нибудь полупидор их утянет, и будет тебе веселье.
  2. Бэкапы, блядь! Настроил том — и расслабился? Не, не работает так. Настрой регулярные дампы базы (pg_dump). Можно в отдельном контейнере-кронзадаче. Данные-то живут в томе, но от человеческого фактора или багов в коде они не застрахованы.
  3. Проверяй восстановление. Сделал бэкап — отлично. А теперь на тестовом стенде попробуй из этого бэкапа всё восстановить. Потому что если не проверишь, в момент Х может вылезти какая-нибудь ядрёна вошь, и окажется, что твой бэкап — просто куча бесполезных файлов.
  4. Для Kubernetes — свои заморочки. Если вдруг переползёшь на K8s, там вместо томов будут свои штуки — PersistentVolumeClaims. Принцип тот же, но реализация другая. Голова должна думать, э бошка!

Итог: Используй Named Volumes для прода, Bind Mounts для разработки, если удобно. И помни главное: контейнер с базой — это просто процесс и временная обёртка. Все твои ценные данные должны жить снаружи. Тогда и спать будешь спокойнее, и волнение ебать не будет мучить каждый раз при деплое.