Настройка PostgreSQL на VPS для доступа из Docker контейнера

Всем привет, сегодня я хочу рассказать вам о своем опыте постановки PostgreSQL на сервере и какие при этом возникли сложности, а также о том как все это решилось.

А с какой целью?

При разработке нашего проекте мы дошли до того этапа, когда база данных в Docker-контейнере уже слишком ненадежный метод хранения информации, но при этом проект еще недостаточно разросся, чтобы использовать K8s и его возможностей.

Что было сначала

Изначальная конфигурация приложения:

services:
backend:
image: application:latest
ports:
- "127.0.0.1:8000:8000"
env_file:
- ../../.env
links:
- "postgres:db"
depends_on:
- postgres
volumes:
- ../../:/app
postgres:
image: postgres:17.2-alpine3.21
env_file:
- ../../.env
volumes:
- postgres:/var/lib/postgresql/data
healthcheck:
test: [ "CMD-SHELL", "POSTGRES_PASSWORD=postgres pg_isready -U postgres" ]
retries: 10
interval: 5s
timeout: 5s
volumes:
postgres:

В наше приложение данные для подключения к базе данных попадают из .env файла и выглядят следующим образом:

POSTGRES_DB=postgres
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_PORT=5432
POSTGRES_HOST=db

Теперь мы вынесем базу данных и установим её на машину. Для этого:

sudo apt install postgresql

После этого требуется настроить, откуда можно будет получать доступ к базе данных. Для этого переходим в файл /etc/postgresql/*/main/postgresql.conf. Меняем строку listen_addresses и убираем комментарий с неё

listen_addresses = '*'

После этого заходим в дефолтную базу данных template1 для настройки и меняем пароль пользователя postgres:

sudo -u postgres psql template1
ALTER USER postgres with encrypted password 'your_password';

После этого для входа под этим пользователем потребуется вводить пароль. Для этого необходимо изменить конфигурацию в файле /etc/postgresql/*/main/pg_hba.conf . Найти строку, настраивающую администратора, и заменить peer на scram-sha-256:

local   all             postgres                                scram-sha-256

Также следует добавить новую строку для предоставления доступа Docker к базе данных:

host    postgres        postgres        172.18.0.0/16           scram-sha-256

После всех этих преобразований наша база данных настроена и готова к использованию. (В целях демонстрации я не буду создавать отдельного пользователя и все действия будут выполнять пользователем postgres в дефолтной базе данных postgres, чего я настоятельно не рекомендую делать в рабочих проектах).

Однако если мы попробуем сейчас подключиться к нашей базе данных из нашего приложения, ничего не выйдет, так как в данных момент в .env файле хост базы данных стоит db (имя старого контейнера). Можно было бы заменить его на localhost, однако по стандарту Docker-контейнер не имеет доступа к сети машины, на которой он запущен. Для этого изменяем исходную конфигурацию приложения:

services:
backend:
image: application:latest
extra_hosts:
- "host.docker.internal:host-gateway"
ports:
- "127.0.0.1:8000:8000"
env_file:
- ../../.env
volumes:
- ../../:/app

Таким образом, база данных вынесена, а также добавлена настройка extra_hosts для доступа к внешней сети. После этого изменяем .env файл, указав в нём host.docker.internal в качестве хоста.

После этого приложение сможет подключаться к базе данных на локальной машине. Стоит отметить, что в идеальном сценарии следует создавать отдельного пользователя и предоставлять ему доступ, настраивая его для конкретных задач с целью повышения безопасности приложения. Это, тем не менее, не влияет на описанные выше этапы.

Что-то еще?

Если на сервере включён UFW (фаерволл), он может конфликтовать с текущей конфигурацией, не позволяя Docker-сети использовать порт 5432. Для исправления этой ситуации следует добавить новые правила, предварительно полностью открыв порт 5432:

sudo ufw delete deny 5432/tcp

После этого:

sudo ufw allow from 172.17.0.0/16 to any port 5432
sudo ufw allow from 172.18.0.0/16 to any port 5432
sudo ufw deny 5432/tcp

Здесь мы разрешаем доступ к базе данных для подсетей Docker и запрещаем доступ извне.

Резюме

В итоге нам удалось полностью перевести приложение на локальную базу данных, настроить доступ к ней, наладить взаимодействие между базой данных и Docker, а также сделать всё это достаточно безопасно