项目地址:https://github.com/shubaocai/sbc-dnsmgr | MIT License

如果你的域名散在阿里云、腾讯云、Cloudflare、华为云好几家,改一条解析要挨个登录控制台,那 dnsmgr(彩虹聚合DNS管理系统) 就是干这个的:一个面板管所有平台的解析。

先说清楚一个容易误解的点:dnsmgr 自己不做 DNS 解析。它不是 BIND / PowerDNS 那种域名服务器,而是替你调用各家云厂商的 API 去改记录、申请和部署证书。所以它只是「管」解析,真正的解析还是在各家的 DNS 服务器上。

这篇记录一下怎么用一条命令把它跑起来,以及过程中踩到的几个坑。


它到底能干什么

能力 说白了就是
聚合管理 一个面板里管 20 多家平台:阿里云、腾讯云 DNSPod、华为云、百度云、火山引擎、京东云、青云、西部数码、宝塔、Cloudflare、AWS Route 53、Namesilo、Spaceship、PowerDNS、Technitium、HE.net、dynv6、GoEdge、DNSLA 等
多用户 给每个用户分配不同的域名权限,可以开 API 和免登录链接,方便 IDC 系统对接 —— 说白了能当小型的 DNS 代管服务用
容灾切换 支持 ping / tcp / http(s) 检测,站点挂了自动暂停或切换解析,恢复了再切回来,并推通知
定时切换 到点自动改 / 暂停 / 删除解析记录
CF 优选 IP 自动拉取 Cloudflare 优选 IP 并写回解析
SSL 证书 从 Let's Encrypt 等渠道申请,再自动部署到面板 / 云厂商 / 服务器(宝塔、1Panel、K8s、群晖、FTP、SSH……几十种目标)
通知 邮件、微信公众号、Telegram、钉钉、飞书、企业微信、QQ 机器人

同类面板也有几个,dnsmgr 的优势是更新勤、平台覆盖多、证书自动部署的目标特别全。

官方部署方式的两个坑

上游 README 给了 docker run 和 docker-compose 两种,但它给的卷挂法有坑:

# 上游示例(有坑)
docker run --name dnsmgr -dit -p 8081:80 -v /var/dnsmgr:/app/www netcccyun/dnsmgr

问题在这个镜像的 entrypoint:它用 cp -a /usr/src/www /app/ 把程序铺进 /app/www。而你把卷挂在 /app/www(那是已存在的挂载点),cp 会把它套一层变成 /app/www/www,结果 nginx 的 root /app/www/public 找不到入口文件,页面直接 404。

第二个坑是:面板的自动化功能(容灾切换、定时切换、CF 优选、证书自动续期)依赖常驻进程 + cron。这个镜像内部已经带了 supervisord 和 crond,用官方 docker run 其实也有,但如果你在虚拟主机 / 宝塔上手动部署,就只有 PHP-CGI,没有常驻进程,这些功能基本只能手动点。

所以我把部署过程写成了一个脚本,一次性把环境检查、Docker 安装、数据库初始化、健康检查都处理掉,也顺手避开了上面这个卷挂载的坑(改成挂 /app)。

脚本做了什么

步骤 内容
1/7 检查环境 校验 root、系统与架构、磁盘;内存不足 4GB 且没有 swap 时自动加 2GB swap;检查端口占用
2/7 安装 Docker 依次探测 5 个 Docker 软件源(腾讯云内网 / 腾讯云公网 / 阿里云 / 中科大 / 官方),gpg 和 dists/<codename>/Release 都取得到才用;装完自动配镜像加速、限制容器日志大小
3/7 生成配置 随机生成两个密码(openssl rand -hex 16),写 docker-compose.yml 与 .credentials(都是 600)
4/7 拉镜像 优先从华为云 SWR 拉(国内快),失败自动退回 Docker Hub
5/7 起数据库 MySQL 5.7 + 健康检查,自动建库建用户,并实测该账号能不能登录
6/7 起面板 面板容器 + 健康检查,校验程序是否真的落在 /app/www/public/index.php
7/7 汇总 把面板地址、目录、凭据文件,以及向导里要填的数据库信息打在屏幕上

其中「国内网络」那一段是花时间最多的地方,和之前处理其它项目时踩的坑一样:

  • download.docker.com 经常 TLS 握手被重置 → 改成 5 个源逐个探测,能取到 GPG 和 Release 才采用
  • 容器里没有 IPv6,deb.debian.org 返回 AAAA 会让 apt 卡死 → 强制 IPv4
  • 镜像站首选国内(华为云 SWR 比 Docker Hub 稳得多)

三条命令跑起来

curl -fsSL -o /tmp/dnsmgr.sh https://raw.githubusercontent.com/shubaocai/sbc-dnsmgr/main/install_dnsmgr.sh && sudo bash /tmp/dnsmgr.sh

raw.githubusercontent.com 时通时不通的话,换 jsDelivr:

curl -fsSL -o /tmp/dnsmgr.sh https://cdn.jsdelivr.net/gh/shubaocai/sbc-dnsmgr@main/install_dnsmgr.sh && sudo bash /tmp/dnsmgr.sh

或者 clone 下来跑:

git clone https://github.com/shubaocai/sbc-dnsmgr.git
cd sbc-dnsmgr
sudo bash install_dnsmgr.sh

跑完屏幕上会直接把要用的信息列成两块,重点信息在最底部:

  ▸ 安装向导:  http://<你的公网IP>:8081/
    项目目录:  /opt/dnsmgr
    挂载目录:  /opt/dnsmgr/app(程序与配置)、/opt/dnsmgr/mysql(数据库)
    凭据文件:  /opt/dnsmgr/.credentials(600,那串密码也在里面)

  ▸ 向导里这样填(其它留默认):
      数据库主机:dnsmgr-mysql    端口:3306
      数据库名:  sbc_dnsmgr
      数据库用户:sbc_dnsmgr
      数据库密码:<脚本随机生成>
      管理员账号 / 密码:自己设,装完立刻开二次验证(TOTP)

装完要填的字段

浏览器打开 http://<你的公网IP>:8081/,会跳到安装页。这里有个必踩的坑:

字段 填什么
数据库主机 dnsmgr-mysql —— ⚠️ 不能填 localhost 或 127.0.0.1
数据库端口 3306
数据库名 / 用户 sbc_dnsmgr / sbc_dnsmgr
数据库密码 脚本最后打印的那串(也在 /opt/dnsmgr/.credentials)
表前缀 默认 dnsmgr_,不用改
管理员 自己设,装完立刻开 TOTP

为什么主机要填 dnsmgr-mysql?因为面板和数据库是两个容器,靠 Docker 内部的服务名互相找。填 localhost 时,PHP 找的是面板容器自己,那里当然没有 MySQL —— 报错就是一句干巴巴的「数据库连接失败」。我自己第一次就填错在这里。

实测数据

项目 数值
测试环境 Ubuntu 22.04.5 LTS / x86_64 / 2 vCPU / 1.9GB 内存 / 50GB 磁盘
从零到面板可访问 约 2~3 分钟(拉镜像约 240MB 占大头)
内存占用 面板容器约 84MB + MySQL 约 194MB,整机约 550MB
磁盘占用 整套(Docker + 镜像 + 数据库)约 3.4GB
重复执行 幂等:检测到已安装会复用原数据库密码,不会把密码改掉、数据不受影响

2GB 内存的小机器完全够用 —— 脚本还会在内存不足 4GB 且没有 swap 时自动补 2GB swap,避免 MySQL + Swoole 常驻任务把内存吃紧。

装完立刻做三件事

  1. 先加一个「域名账户」:选平台、填那家的 API 密钥。这一步不做的话,「域名管理」页面会报 500(Call to a member function getDomainList() on false)—— 这是上游没做空值判断的小 bug,只要面板里还没任何账户、它就拿空账户去查域名列表。加一个账户就好了。
  2. 开启 TOTP 二次验证,并改掉安装时的初始密码。
  3. 加 HTTPS 反代 + 限制来源 IP(见下面「安全」)。

几个实测会遇到的正常现象

现象 说明
容器日志里 dmtask 一直重启(exit status 255) 正常。安装向导走完前读不到 .env,supervisord 会一直重启它;装完自动稳定
服务器上用公网 IP 打不开自己的面板 云服务器常有回环限制,用 curl -I http://127.0.0.1:8081/ 自测;从外部能不能访问要用另一台机器测
浏览器打不开面板 先查安全组有没有放行 8081,这是最常见原因
向导提示「当前已经安装成功,如果需要重新安装,请手动删除根目录 .env 文件」 已经装过了。重装要删 /opt/dnsmgr/app/www/.env,或者 docker compose down -v 后重跑
/fpm-ping 从宿主机访问返回 403 设计如此,镜像只允许容器内的 127.0.0.1 访问探针

升级 / 备份 / 卸载

# 升级到最新镜像(自动拉新镜像、覆盖程序文件,数据库和 .env 保留)
bash install_dnsmgr.sh --upgrade

# 备份数据库
docker exec dnsmgr-mysql sh -c "exec mysqldump -uroot -p$(awk -F': ' '/^数据库 root 密码/{print $2}' /opt/dnsmgr/.credentials) --databases sbc_dnsmgr" > sbc_dnsmgr-$(date +%F).sql

# 备份程序与配置(含 .env、证书、runtime)
tar czf dnsmgr-app-$(date +%F).tar.gz -C /opt/dnsmgr app

# 停止 / 卸载
cd /opt/dnsmgr && docker compose down   # 停服,数据保留
rm -rf /opt/dnsmgr                      # 连数据一起删

上游更新挺勤(基本几个月一个版本),建议升级前先备份数据库。

关于安全,得说重一点

这个面板里会保存各家云平台的 API 密钥 —— 那基本等于你域名的最高权限;它还能通过 SSH / FTP 往服务器上部署证书。也就是说,它本身就是一个高价值目标:

  • 一定要上 HTTPS(Nginx / Caddy 反代 + Let's Encrypt),别长期裸 HTTP 暴露在公网
  • 强密码 + 开启 TOTP 二次验证
  • 尽量在安全组里只放行你的固定 IP,或者干脆只在内网访问
  • 容器里的 MySQL 默认没有对宿主机暴露端口,这是有意的,别随手加 ports: 3306:3306

相关链接

装的过程中卡在哪一步,把屏幕上的提示原样发出来就行 —— 脚本的每一步都会把「在做什么、结果如何」打出来,失败时也会给下一步的建议(比如换哪个源、换哪个端口)。