Navicat 如何连接 Docker 里的数据库?

预计阅读时间:10 分钟

这是「把自己部署到互联网上」系列的第 34 篇。

上一篇复盘了从 HTTPS 到对象存储,再到 Docker 部署和数据库初始化。博客具备上线条件后,很快会遇到一个实际维护问题:我想用 Navicat 看一下 Docker 里的数据库,应该怎么连?

这个需求很常见。比如确认表是否创建成功,检查迁移有没有执行,看管理员账号是否初始化,排查某条数据为什么没写进去,导出部分数据,或者临时执行一条 SQL。NavicatDataGripDBeaver 这类工具确实方便。

但前提必须明确:不要为了让数据库工具连上,就直接把数据库端口暴露到公网

MySQL 服务在哪儿?

先看 Docker 里的数据库在哪里。如果博客用 Docker Compose 部署,数据库通常是一个单独服务:

services:
  app:
    ...

  db:
    image: mysql:8.0

应用容器连接数据库时,通常写:

DB_HOST=db
DB_PORT=3306

这是 Docker 内部网络通信。

应用容器和数据库容器在同一个 Compose 网络里,所以应用能通过 db:3306 找到数据库。

但你的本地电脑不在这个 Docker 网络里,Navicat 不能直接用 db 这个主机名连接。

也就是说,容器内部能访问,不代表本地电脑能访问。

要让本地工具连接 Docker 数据库,需要额外设计访问路径。

最不推荐的方式,是直接把数据库端口映射到宿主机公网:

ports:
  - "3306:3306"

然后在云平台安全组里放行 3306,本地 Navicat 连接 服务器公网 IP:3306

这确实能连,但风险很高。

数据库端口一旦暴露公网,就可能被扫描、爆破和探测。

如果密码弱、账号权限大或数据库版本有漏洞,风险会直接放大。

服务器里最常见的问题,就是临时开放后忘记关闭。

SSH 隧道建立

我的原则是:数据库默认不暴露公网。本地工具连接优先使用 SSH 隧道。

SSH 隧道的思路是:Navicat 不直接连公网数据库端口,而是先通过 SSH 连接服务器,再把本地端口转发到服务器内部数据库端口。路径是:

Navicat 本地端口
  -> SSH 隧道
  -> 服务器
  -> Docker 数据库

这样公网只保留 SSH 管理入口,数据库端口不直接暴露。

比较实用的做法,是把数据库端口只绑定到服务器本地地址。例如 Compose 里写:

db:
  image: mysql:8.4
  restart: always
  container_name: db
  command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
  env_file:
    - .env
  ports:
    - "127.0.0.1:3306:3306"
  volumes:
    - /data/blog/mysql:/var/lib/mysql

这表示把数据库容器的 3306 映射到宿主机的 127.0.0.1:3306

服务器本机可以访问,但公网不能直接访问。

修改 Compose 后启动数据库服务:

docker compose up -d --force-recreate db

确认监听:

sudo ss -lntp | grep 3306

如果看到 127.0.0.1:3306,说明只监听本地地址。

然后在本地电脑建立 SSH 隧道。假设服务器 IP 是 1.2.3.4,SSH 用户是 coduty,服务器本机数据库端口是 13306,本地也使用 13306

ssh -L 3306:127.0.0.1:3306 coduty@1.2.3.4

这表示把本地 127.0.0.1:13306 转发到服务器上的 127.0.0.1:3306。Navicat 里填写:

Host: 127.0.0.1
Port: 3306
User: 数据库用户名
Password: 数据库密码
Database: 数据库名

注意,这里填的是本地地址和本地端口,不是服务器公网 IP 的数据库端口。

SSH 隧道负责把流量转到服务器内部。

数据库工具连接

Navicat 也可以直接配置 SSH 隧道。通常要填两部分。数据库信息里,Host 是服务器视角下的数据库地址。如果数据库端口绑定到服务器本机,就填:

Host: 127.0.0.1
Port: 13306

SSH 信息里,填写服务器公网 IP、SSH 端口、SSH 用户,以及密码或私钥。这里最容易混的是数据库 Host:使用 Navicat 内置 SSH 时,它连接的是 SSH 登录到服务器后的地址;使用命令行 ssh -L 时,Navicat 连接的是你本机的 127.0.0.1:本地端口

如果数据库没有映射到宿主机,只存在 Docker 内部网络里,Navicat 内置 SSH 通常不能直接访问 db:3306,因为 db 是 Docker 网络里的服务名,不是宿主机能解析的主机名。

这时要么绑定到服务器本地地址,要么直接进容器操作。

简单检查

简单检查其实不一定要 Navicat 或者其他工具,直接进入容器命令行操作。

可以直接进入数据库容器:

docker compose exec db mysql -u blog_user -p 这里是密码

如果是 PostgreSQL:

docker compose exec db psql -U blog_user -d 这里是密码

这种方式不需要开放任何端口。缺点是交互体验不如图形化工具,但用于简单查看表、确认数据、执行少量 SQL,足够安全直接。

如果确实必须临时开放公网端口,至少要做到:

  • 只允许自己的固定公网 IP 访问,不对 0.0.0.0/0 开放
  • 使用低权限数据库账号和强密码
  • 用完关闭安全组规则
  • 删除或恢复 Compose 端口映射
  • 检查数据库日志

配置完后,要确认数据库没有暴露公网。先看 Docker 映射:

docker ps

如果看到:

0.0.0.0:3306->3306/tcp

就要警惕,说明端口绑定到了所有网卡。如果看到:

127.0.0.1:13306->3306/tcp

说明只绑定本机地址,相对更安全。

再看系统监听:

sudo ss -lntp | grep 3306
sudo ss -lntp | grep 3306

同时检查云平台安全组和系统防火墙,确认没有把数据库端口对公网开放。

如果没有建立 SSH 隧道,却能直接连服务器公网 IP 的数据库端口,说明数据库可能已经暴露,需要立刻修改服务端口或密码。

常见问题

先确认 SSH 隧道是否还在运行,本地端口是否写对,服务器本机端口是否监听,数据库容器是否运行。

如果错误是 access denied,通常是账号密码或权限问题,而不是网络不通。

能连上但看不到数据库,说明账号权限不足

这不一定是坏事。远程维护工具不应该默认使用 root 账号。更稳的方式是创建专门维护账号,只授予必要数据库的必要权限。

SSH 隧道断开后 Navicat 也断开,这是正常现象

如果通过命令行建立隧道,终端关闭后隧道就没了。可以保持终端打开,也可以使用 Navicat 内置 SSH。对个人博客维护来说,临时打开,用完关闭,通常更安全。

我的推荐操作顺序是:

  1. 简单查询直接进容器
  2. 需要图形化工具时,用 SSH 隧道
  3. 尽量不直接开放公网数据库端口
  4. 若临时开放,必须限制来源 IP,用完关闭

小结:

Navicat 如何连接 Docker 里的数据库?最稳的答案不是开放 3306,而是让数据库端口只在服务器内部可达,本地通过 SSH 隧道访问。这样既能满足维护需求,又不会把数据库变成公网入口。

下一篇继续写博客上线后的几个真实问题:防爬、静态资源和发文流程。数据库能维护只是一步,长期运营博客还要处理访问、资源和内容发布。


本文由 楸木 原创,转载请注明出处。

相关推荐

发现更多