数据库装好之后,接下来一定会有这个需求:不想每次都 SSH 上去敲 SQL。
想看一眼表结构、改个字段、导出一份数据、跑条临时查询 —— 这些事用命令行做不是不行,但效率差得远。一个网页版的管理面板能省掉大量时间。
但这里有个前提必须先说清楚:数据库管理面板是全互联网被攻击得最狠的目标之一。phpMyAdmin 历史上有过大量漏洞,扫描器无时无刻不在全网找 /phpmyadmin 这个路径。把它随手开到公网,等于给数据库开了一道没有门卫的侧门。
这篇讲三个主流面板怎么选、怎么装,以及怎么安全地访问它们 —— 最后这点比装软件本身重要得多。
| phpMyAdmin | Adminer | CloudBeaver | |
|---|---|---|---|
| 支持的数据库 | MySQL / MariaDB | 8 种(MySQL、PG、SQLite、Mongo 等) | 30+ 种 |
| 镜像体积 | ~180MB | ~30MB | ~500MB |
| 内存占用 | 中等 | ~20MB | 300MB-1GB(JVM) |
| 启动速度 | 秒级 | 秒级 | 30-60 秒 |
| 界面 | 传统,功能细 | 极简,一页搞定 | 现代 IDE 风格 |
| SQL 编辑器 | 有,带补全 | 纯文本框 | 完整 IDE,自动补全 |
| ER 图 / 可视化 | 有设计器 | ❌ | ✅ |
| 多用户与权限 | 无 | 无 | ✅ 内置用户体系 |
| 导出/导入 | 强,支持大文件分块 | 基础 | 强 |
怎么选:
- 只管理 MySQL/MariaDB → phpMyAdmin。功能最细,导出导入最成熟
- 想省资源,或者要连多种数据库 → Adminer。30MB 镜像、20MB 内存,秒开
- 团队共用,或者需要 ER 图和完整 IDE 体验 → CloudBeaver
它们可以共存。 实际部署里很常见的组合是:Adminer 常备(轻,随手查点东西),CloudBeaver 或 phpMyAdmin 按需启停。
这是本文最该认真看的部分。
不要把面板端口直接暴露在公网,也不要指望"改个复杂路径别人就找不到" —— 路径混淆能挡住自动扫描,挡不住有心人,而且你迟早会在某个地方泄露这个路径。
按安全性从高到低,三种做法:
1. SSH 隧道(最推荐,个人用)
面板只监听 127.0.0.1,从公网完全访问不到。用的时候在本地开一条隧道:
ssh -L 8080:127.0.0.1:8080 user@你的服务器
然后本地浏览器打开 http://127.0.0.1:8080 就行。所有流量走 SSH 加密,服务器上不需要开任何额外端口,也不需要有公网域名。
这是个人用户的最优解 —— 零攻击面,零证书配置。
2. 反代 + HTTPS + 认证 + IP 白名单
需要多人访问、或者想在任何设备上打开就用这个:
- 反代加 HTTPS(必须)
- 加一层 Basic Auth 或 Cloudflare Access
- 再用防火墙限制来源 IP
3. 直接暴露端口
不要这么做。如果非要,至少确认数据库本身只允许本机连接、面板有强密码、并且开了 fail2ban。
下面三个方案我都按"监听 127.0.0.1"来配置。
phpMyAdmin 要能连到数据库,两个容器必须在同一个 Docker 网络里。
如果你之前用站内这篇 MySQL 8.4 部署指南装的数据库,最省事的做法是把 phpMyAdmin 加到同一个 compose 文件里,复用已有的网络:
services:
phpmyadmin:
image: phpmyadmin/phpmyadmin:latest
container_name: phpmyadmin
restart: unless-stopped
environment:
PMA_HOST: mysql
PMA_PORT: 3306
PMA_ABSOLUTE_URI: https://db.example.com/
UPLOAD_LIMIT: 300M
ports:
- "127.0.0.1:8080:80"
PMA_HOST 填数据库容器的服务名(compose 里定义的那个名字),不是 IP。同一个 compose 项目里的服务会自动加入同一个网络,直接能用服务名互相解析。
UPLOAD_LIMIT 决定能导入多大的 SQL 文件,默认偏小,导入大备份的时候会被卡住。
PostgreSQL 的话,用 pgAdmin 代替,思路一样,配置换成 PGADMIN_DEFAULT_EMAIL 和 PGADMIN_DEFAULT_PASSWORD。
第一次连不上,先查 MySQL 的用户授权。 MySQL 的权限是按来源主机区分的,你之前可能只建了 'user'@'localhost'。从容器连过来时来源是容器网络的 IP,需要单独授权:
CREATE USER 'appuser'@'%' IDENTIFIED BY '强密码';
GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'%';
FLUSH PRIVILEGES;
别用 root 连面板。 建一个只在特定库上有权限的账号 —— 万一面板被攻破,损失能控制在一个库内。
Adminer 是单文件 PHP 应用,一条命令搞定:
docker run -d \
--name adminer \
--restart unless-stopped \
-p 127.0.0.1:8081:8080 \
--network 你的数据库网络名 \
adminer
它没有任何配置文件,打开就是登录页,填数据库地址、账号密码就能进。
"数据库地址"填什么? 如果 Adminer 和数据库在同一网络,填数据库的服务名(比如 mysql);如果是宿主机上跑的数据库,填 host.docker.internal(Linux 上需要额外加 --add-host=host.docker.internal:host-gateway)。
Adminer 的定位是"够用就好" —— 它不做花哨的界面,但查询、导出、改表结构这些核心操作一样不少。资源占用低到可以忽略,适合常驻。
如果想要更好看的主题,Adminer 支持换 CSS 皮肤和插件,把文件挂进容器即可。
CloudBeaver 是 DBeaver 的网页版,支持 30 多种数据库,界面是完整的 IDE —— 有自动补全、ER 图、按用户保存的连接配置。
services:
cloudbeaver:
image: dbeaver/cloudbeaver:latest
container_name: cloudbeaver
restart: unless-stopped
ports:
- "127.0.0.1:8978:8978"
volumes:
- ./workspace:/opt/cloudbeaver/workspace
environment:
- CB_SERVER_MEMORY=1024m
几个注意点:
workspace 目录必须挂出来。 你配的所有数据库连接、保存的查询、用户账号都在里面,容器重建就没了,而且要定期备份。
它是 JVM 应用,内存吃得比另外两个多得多。 空载 300-500MB,跑查询时能到 1GB。1 核 2G 的机器跑它要留够余量,别和别的重服务挤在一起。CB_SERVER_MEMORY 可以限制 JVM 堆大小。
启动慢。 JVM 初始化要 30-60 秒,第一次打开会停在初始化页,别以为是挂了。
第一次访问会引导你创建管理员账号,之后可以在面板里给团队成员建子账号、分配不同的数据库权限 —— 这是它相对另外两个最明显的优势。
上面三个面板主要面向 SQL 数据库。如果你跑的是站内讲过的 Redis 8 或 MongoDB 8,情况不太一样。
Redis 的可视化需求通常不高。 它多用于缓存和队列,日常操作就是 GET、SET、KEYS 这几个命令,SSH 上去用 redis-cli 反而更快。真要看内存分布、慢查询分析,官方有个 RedisInsight(redis/redisinsight 镜像,端口 5540),功能挺全,但属于"用得上再装"的工具。
MongoDB 有个轻量的 Mongo Express(mongo-express 镜像),界面朴素但够用,能浏览集合、执行查询、编辑文档。它的配置方式和 phpMyAdmin 类似 —— 需要连到 MongoDB 容器所在的网络,通过环境变量指定连接地址。
注意 Redis 和 MongoDB 默认都不该暴露端口。 Redis 未授权访问是经典的入侵入口(写 SSH 公钥、写定时任务),MongoDB 早期版本默认无认证也是重灾区。装面板的时候顺手确认一下这两个服务的 ports 有没有写 127.0.0.1: 前缀。
CloudBeaver 也支持 MongoDB 和 Redis,如果你懒得装一堆工具,一个 CloudBeaver 全连上是最省事的做法。
需要从外部访问的话,用 Caddy 加 HTTPS 和认证:
db.example.com {
basicauth {
admin $2a$14$你的密码哈希
}
reverse_proxy 127.0.0.1:8080
}
phpMyAdmin 在反代下有个经典坑。 它靠 $_SERVER['HTTP_HOST'] 这类变量拼 URL,反代之后这些变量反映的是容器内部的地址(http://localhost:8080),结果就是:
- 登录后跳转到 localhost,页面打不开
- CSS/JS 加载失败,界面变成裸 HTML
- 提交表单报 CSRF 错误
解法是设 PMA_ABSOLUTE_URI 成对外的完整地址。关于结尾斜杠,网上说法不一致 —— 有的说必须带,有的说必须不带。稳妥做法是先按带斜杠配(https://db.example.com/),不行再去掉试试。Caddy 默认会正确转发 Host 和 X-Forwarded-* 头,所以主要就是这一个变量的事。
用 Nginx 的话还要额外确认几项,完整写法看这篇 Caddy 反向代理指南,Nginx 的话对照着改。
CloudBeaver 和 Adminer 没有这个问题,它们对反代的支持更省心。
面板监听 127.0.0.1 之后,防火墙其实没什么要开的。但要确认两件事:
数据库端口没被暴露。 检查一下 compose 文件里数据库的 ports,如果写的是 "3306:3306"(没带 127.0.0.1: 前缀),那就是全网可达的。改成 "127.0.0.1:3306:3306"。
Docker 会绕过 UFW。 这是 Docker 的已知行为 —— 用 -p 3306:3306 发布的端口,即使 UFW 里 deny 了也照样能访问。要彻底解决得改 DOCKER_OPTS 或者在 DOCKER-USER 链里加规则。站内这篇 UFW 防火墙配置里讲过这个坑。
如果走 SSH 隧道,上面两条就不用操心了 —— 因为压根没有端口是公开的。
一个原则:面板用的数据库账号,权限刚好够用就行。
具体的做法:
- 给面板单独建一个账号,别用 root
- 只授予需要管理的库,不要给全局权限
- 生产库给只读账号,需要写入的库才给写权限
- 用完的面板及时停掉(
docker compose stop phpmyadmin)
phpMyAdmin 和 Adminer 本身没有账号体系,安全性完全依赖数据库账号 + 外层访问控制。CloudBeaver 有自己的用户管理,可以给每个人分配独立的数据库凭据 —— 团队场景下这点很重要,避免所有人共用一个高权限账号。
三个面板里,只有 CloudBeaver 需要单独备份(workspace 目录)。phpMyAdmin 和 Adminer 是无状态的,配置都在环境变量里,把 compose 文件存好就行。
cd ~/cloudbeaver
docker compose stop
tar czf ~/cloudbeaver-$(date +%F).tar.gz workspace docker-compose.yml
docker compose start
不过要记住:面板的配置丢了不心疼,数据库本身的数据才是命根子。 面板只是看数据的窗口。数据库备份参考 MySQL 部署那篇里的方案。
phpMyAdmin 报 "Cannot log in to the MySQL server":三种可能 —— PMA_HOST 填错了(要填服务名不是 IP)、两个容器不在同一网络、或者 MySQL 没授权这个来源。逐个排查。
反代后界面变成没样式的纯文本:PMA_ABSOLUTE_URI 没设或者设错了。试一下加/去结尾斜杠。
Adminer 连不上宿主机上的数据库:容器里的 127.0.0.1 指的是容器自己。要用 host.docker.internal,或者把数据库也放进 Docker。
CloudBeaver 打开一直转圈:JVM 还在启动,等 30-60 秒。如果超过两分钟,看 docker compose logs,多半是内存不够被 OOM 杀了。
导入大 SQL 文件失败:phpMyAdmin 调大 UPLOAD_LIMIT,同时注意 PHP 的执行时间和内存限制。几 GB 的备份建议直接用 mysql 命令行导入,比面板可靠。
面板 CPU 一直很高:有人在扫你的路径。如果端口是公开的,赶紧关掉或者加 IP 白名单。
数据库面板本质上是个"方便但不安全"的工具 —— 它的存在就是为了绕过命令行直接碰数据,这也意味着一旦被突破,攻击者拿到的是完整的数据访问权。
所以我的建议很简单:面板只监听本地,用 SSH 隧道访问。个人用户没有任何理由把它开到公网,多打一条命令的成本,换来的是把攻击面降为零。
需要多人协作的话,再上反代 + HTTPS + 认证 + CloudBeaver 的用户体系,把权限分清楚。
延伸阅读:
