一、背景
随着服务器部署的服务越来越多,今天安装新服务时突然意识到,很多服务都依赖 PostgreSQL 数据库。
并且每个服务都拥有自己的 PostgreSQL 容器,这导致服务器上存在多个数据库实例,占用了较多的内存资源,同时也增加了后续维护和备份的成本。
因此思考了一番打算将原 Docker 中运行的 PostgreSQL 数据通过 dump 导出,然后在宿主机统一安装 PostgreSQL,让 Docker 容器中的各个服务访问宿主机的 PostgreSQL。
这样可以减少重复运行数据库带来的资源浪费,同时方便后续数据库的统一管理。
二、安装 PostgreSQL 以及相关工具
由于 Docker 容器中的环境通常比较精简,缺少部分数据库管理工具,无法直接进行数据备份操作。
因此,先通过 PostgreSQL 官方仓库在宿主机安装 PostgreSQL 以及相关工具。
具体安装方式可以参考 PostgreSQL 官方文档:https://www.postgresql.org/download/
三、备份容器中的数据
由于不同 PostgreSQL 镜像的内容有所区别,部分精简版镜像中可能没有安装 pg_dump 等数据库备份工具,因此需要根据实际情况选择备份方式。
方式一:容器中存在 pg_dump 工具
首先进入 PostgreSQL 容器:
docker exec -it <postgres-container-name> bash检查是否存在 pg_dump:
which pg_dump如果可以正常找到路径,则可以直接进行数据库备份:
pg_dump -U <username> -d <database_name> > backup.sql也可以直接在宿主机执行:
docker exec <postgres-container-name> pg_dump -U <username> -d <database_name> > backup.sql方式二:容器中没有 pg_dump 工具
如果进入容器后发现没有 pg_dump:
which pg_dump没有返回结果,说明当前容器环境缺少 PostgreSQL 客户端工具。
这种情况下,不需要在容器内额外安装工具,可以直接使用宿主机安装的 PostgreSQL 客户端进行备份。
首先确认宿主机存在 pg_dump:
pg_dump --version然后通过容器端口连接 PostgreSQL:
pg_dump -h 127.0.0.1 -p <port> -U <username> -d <database_name> > backup.sql如果 PostgreSQL 容器没有映射端口,也可以通过 Docker 网络连接:
pg_dump -h <container-ip> -p 5432 -U <username> -d <database_name> > backup.sql备份完成后检查文件:
ls -lh backup.sql确认备份文件正常生成。
四、恢复数据到宿主机 PostgreSQL
完成数据备份后,需要将原 Docker 中的 PostgreSQL 数据恢复到宿主机安装的 PostgreSQL 中。
首先需要在宿主机 PostgreSQL 中创建对应的用户和数据库。
创建用户:
createuser -U postgres <username>创建数据库:
createdb -U postgres -O <username> <database_name>恢复备份:
psql -U <username> -d <database_name> < backup.sql恢复完成后,可以登录 PostgreSQL 检查数据:
psql -U <username> -d <database_name>查看数据库表:
\dt确认数据恢复正常。
五、配置 PostgreSQL 访问权限
由于 PostgreSQL 对客户端访问有权限控制,而迁移后 Docker 服务访问数据库的方式发生了变化。
原本 PostgreSQL 运行在 Docker 容器内部,服务之间通过 Docker 网络进行访问。
迁移到宿主机后,Docker 容器需要通过 Docker bridge 网关地址访问宿主机上的 PostgreSQL 服务。
因此需要重新配置 PostgreSQL 的监听地址以及访问权限。
5.1 获取 Docker 网桥地址
Docker 容器访问宿主机网络,需要通过 Docker 网桥的网关地址。
如果使用普通 Docker 容器,可以通过以下命令查看:
docker network inspect bridge查看输出中的:
"Gateway": "172.17.0.1"该地址即为 Docker 容器访问宿主机的网关地址。
如果使用 Docker Compose 创建的自定义网络,可以先查看网络名称:
docker network ls然后执行:
docker network inspect <network-name>查看对应的 Gateway 地址。
例如:
172.18.0.15.2 配置 PostgreSQL 监听地址
主要涉及两个配置文件(以Debian系为例):
postgresql.confpg_hba.conf
默认情况下,PostgreSQL 通常只监听本机地址:
listen_addresses = 'localhost'这样 Docker 容器无法直接访问宿主机 PostgreSQL。
修改配置文件:
vim /etc/postgresql/<version>/main/postgresql.conf找到:
listen_addresses = 'localhost'修改为限制只监听本地地址和 Docker 网桥地址:
listen_addresses = '127.0.0.1,172.17.0.1,172.18.0.1'5.3 配置 pg_hba.conf 允许 Docker 网络访问
修改:
vim /etc/postgresql/<version>/main/pg_hba.conf增加 Docker 网段访问规则:
host all all 172.17.0.0/16 scram-sha-256host all all 172.18.0.0/16 scram-sha-2565.4 重启 PostgreSQL
修改配置后,需要重新加载配置:
systemctl restart postgresql确认 PostgreSQL 是否监听 Docker 地址:
ss -lntp | grep 5432正常情况下可以看到:
LISTEN 0 200 127.0.0.1:5432LISTEN 0 200 172.17.0.1:5432LISTEN 0 200 172.18.0.1:5432完成以上配置后,Docker 容器已经具备访问宿主机 PostgreSQL 的基础条件。
六、仍然无法访问的问题排查
按照上述配置完成 PostgreSQL 监听地址和访问权限设置后,服务仍然无法连接数据库。
但是通过 Docker 容器测试,可以正常 ping 通宿主机地址:
ping 172.18.0.1说明:
- Docker 网络正常
- 容器可以访问宿主机
但是数据库连接仍然失败,因此继续排查 TCP 端口访问情况。
使用 nc 测试 PostgreSQL 端口:
nc -vz 172.18.0.1 5432发现连接一直等待,没有返回结果。
这说明问题并不是 PostgreSQL 配置,而是 TCP 5432 端口访问被阻断。
检查宿主机防火墙:
ufw status verbose发现 ufw 默认拒绝转发流量:
Default: deny (routed)由于 Docker 容器访问宿主机 PostgreSQL 属于 Docker 网桥网络转发流量,因此被 ufw 拦截。
放行 Docker 网段访问 PostgreSQL:
ufw allow from 172.17.0.0/16 to any port 5432 proto tcpufw allow from 172.18.0.0/16 to any port 5432 proto tcp由于 Docker 容器访问宿主机属于转发流量,还需要允许路由转发:
ufw route allow from 172.17.0.0/16 to any port 5432 proto tcpufw route allow from 172.18.0.0/16 to any port 5432 proto tcp重新加载防火墙配置:
ufw reload再次测试:
nc -vz 172.18.0.1 5432如果返回:
172.18.0.1:5432 open说明 Docker 容器已经可以正常访问宿主机 PostgreSQL。
非商业用途可以使用,但必须注明出处;
若有改编需采用相同许可协议发布。