1. 项目概述与需求分析
先说下我的实际场景。家里宽带拨号能拿到公网 IPv6 地址,但运营商每隔几天就会重新下发一次前缀,地址一变,我通过域名远程访问家里 NAS、监控、软路由的计划就全废了。手动去域名控制台改 AAAA 记录显然不现实,于是就需要一个能在地址变化时自动把域名解析记录更新掉的工具。DDNS-GO 就是这么一类基于 Go 写的动态解析工具,它自带网页管理界面,支持阿里云、腾讯云、DNSPod、Cloudflare 等主流 DNS 服务商,最关键的是对 IPv6 的动态解析支持非常完整。
这套方案适合谁?一种是你手头有一台装了宝塔面板的 Linux 服务器,想在上面多跑一个 DDNS 服务;另一种是你家里有 IPv6 环境、想通过域名随时访问内网设备,但又不愿意为了一个动态解析单独去装一堆依赖环境。DDNS-GO 本身的镜像只有几十 MB,跑起来占用内存通常在 30MB 以内,放到宝塔面板里用 Docker 管理,既不污染宿主机环境,升级和回滚也干净利落。如果你正在找一种“服务器上稳定跑一个 IPv6 动态解析服务”的办法,这篇文章应该能直接帮你少走很多弯路。
为什么我会专门选宝塔面板 + Docker 这套组合,而不是直接在宿主机上裸装程序?一个很重要的原因是隔离性。DDNS-GO 虽然只是一个单二进制文件,但如果你服务器上还跑着网站、数据库、其他业务脚本,多装一个程序就多一分依赖冲突的风险。用 Docker 把它的运行环境完全隔离起来,数据目录单独挂载到宿主机,以后不管是升级镜像、迁移服务器,还是排查主机上的问题,都能把这个服务当做一个独立的黑盒来对待,不影响其他任何业务。
2. 部署前的环境准备
2.1 前置条件清单
在动手之前,先把下面这些条件核对一遍。缺了任何一项,后面都会卡在某个莫名其妙的位置。
- 一台已经装好宝塔面板的 Linux 服务器,主流系统比如 CentOS 7/8、Ubuntu 20.04/22.04、Debian 11/12 都可以。
- 宝塔面板能正常登录,服务器可以正常访问外网。
- 一个域名,并且该域名的 DNS 托管在你准备使用的服务商那里。比如你要用阿里云解析,那域名 NS 记录必须指向阿里云。
- 目标解析 IPv6 的域名,所在服务商必须支持 AAAA 记录。这一点现在主流平台都没问题,但早期注册的一些冷门域名商可能不完善。
- 你的网络出口确实有公网 IPv6 地址。如果只是内网设备有 IPv6,但路由没有做前缀下发,那后面拿到的地址也是无效的。
- 服务器上预留一个数据目录,我习惯用
/opt/ddns-go,这个目录后续会挂载进容器,专门存放 DDNS-GO 的配置。 - 准备一个可用的 DNS 服务商 API 密钥。这一步可以等容器跑起来之后再操作,但不提前准备的话,后面配置时会卡住。
这里要特别提醒一句:如果你的服务器本身没有公网 IPv6,那这个教程就只能用于 IPv4 动态解析,或者你需要先去服务器厂商的控制台给实例开通 IPv6 再回来继续。很多轻量云服务器默认是不分配 IPv6 的,这是我实测踩过最开头的一个坑。
2.2 在宝塔面板里把 Docker 环境装好
宝塔面板安装 Docker 非常简单,路径是:宝塔面板后台 → 左侧菜单“软件商店” → 搜索“Docker管理器” → 点击安装。安装过程一般一两分钟,面板会自动把 Docker 服务启动起来。
装完之后,我建议你先在面板的“终端”里执行一下 docker version,确认输出正常。如果终端里能正常返回 Client 和 Server 两段信息,说明 Docker 已经准备好了。有些朋友会遇到面板显示安装成功,但终端 docker 命令找不到的情况,这种多数是 PATH 问题,重开一次终端或者重新登录面板就能解决。
如果你用的是比较老的宝塔版本,软件商店里找不到 Docker管理器,可以直接走命令行安装。SSH 登录服务器后执行:
curl -fsSL https://get.docker.com | sh systemctl enable docker systemctl start docker安装脚本下载速度慢的话,可以在执行脚本前配置一下国内镜像加速。加速配置写入/etc/docker/daemon.json,里面填入 registry-mirrors 地址,然后重启 Docker。这一步是为了解决后续拉取镜像慢的问题,如果没有这方面困扰可以跳过。
2.3 确认服务器的 IPv6 网络是否可用
这一步很重要,尤其是你想做 IPv6 动态解析的话,必须先确认服务器本身具备获取公网 IPv6 的能力。在终端里执行:
ip -6 addr show观察输出结果。你会在网卡下看到各种 IPv6 地址,重点关注带有scope global的地址,那个才是真正的公网 IPv6 地址。如果只有fe80::开头的 link-local 地址,说明这台机器没有拿到公网 IPv6,需要去服务器厂商或路由器上处理。
接着验证出网 IPv6 是否通畅,执行:
curl -6 -m 5 https://api6.ipify.org如果返回一串 IPv6 地址,说明服务器的出网 IPv6 是通的。这一步非常关键,因为 DDNS-GO 获取公网 IPv6 的常用方式之一就是请求这种外部接口,如果服务器本身没有 IPv6 出口,这个请求就会失败。
顺带说一点:如果你的宽带环境是 PPPoE 拨号,光猫拨号后会从运营商拿到一个 IPv6 前缀,但这个前缀过一段时间会重新分配,所以你每次看到的全局 IPv6 地址可能完全不同。这正是我们需要 DDNS 的原因。
3. 利用 Docker 部署 DDNS-GO 的完整过程
3.1 镜像选择与版本策略
DDNS-GO 的 Docker 镜像官方仓库是jeessy/ddns-go,直接在宝塔终端里拉取即可。我这里建议你拉取时指定一个具体版本而不是直接使用 latest,因为 latest 在社区更新时可能会引入配置格式或行为上的变化,固定版本可以有效降低升级带来的意外风险。当然,第一次部署直接拉 latest 也没问题,等跑通之后再根据实际需要锁定版本就行。
拉取镜像的命令很简单:
docker pull jeessy/ddns-go:latest镜像体积非常小,几秒钟就能拉完。如果你发现拉取速度特别慢,大概率是没配镜像加速,回到 2.2 里把加速配置补上再拉就快了。
3.2 创建容器的关键参数与命令
DDNS-GO 默认监听 9876 端口,所以容器创建的核心就是把 9876 端口映射出来,同时把配置目录挂载到宿主机。这里我直接推荐你使用 host 网络模式,命令如下:
mkdir -p /opt/ddns-go docker run -d \ --name ddns-go \ --restart=always \ --network host \ -v /opt/ddns-go:/root \ jeessy/ddns-go:latest如果你对端口隔离有要求,也可以退回到 bridge 模式,命令改成这样:
docker run -d \ --name ddns-go \ --restart=always \ -p 9876:9876 \ -v /opt/ddns-go:/root \ jeessy/ddns-go:latest创建完成后,用docker ps查看容器状态,如果显示 Up,说明已经跑起来了。然后直接访问http://服务器IP:9876,第一次打开会要求你设置管理员用户名和密码。
3.3 为什么我强烈推荐 host 网络模式
这里展开说一下网络模式的选择逻辑,因为这是我实际踩过最深的一个坑。bridge 模式下,Docker 会给容器创建一个独立的虚拟网卡eth0,而这个网卡拿到的 IPv6 地址通常是 docker0 网桥的内网地址,或者是基于容器 MAC 生成的临时地址,跟宿主机物理网卡上的公网 IPv6 完全不是一回事。DDNS-GO 在“通过网卡获取 IP”模式下,很容易读到这个错误的地址,导致解析记录被覆盖成一个不可达的内网 IPv6。
host 网络模式让容器直接共享宿主机的网络栈,容器内看到的网卡、地址和宿主机完全一致。DDNS-GO 读取到的网卡信息就是物理网卡上的真实地址,准确性大大提升。代价就是 9876 端口没有经过 Docker 的隔离,直接暴露在宿主机上,需要你自己在宝塔防火墙里做好访问控制。这个取舍对我来说非常划算。
接下来是数据目录挂载。容器内的/root目录就是 DDNS-GO 存放配置的位置,把它挂载到宿主机的/opt/ddns-go之后,你的所有配置、日志都落在宿主机磁盘上。以后不管容器怎么删怎么重建,只要继续挂载同一个目录,配置就自动恢复,这点非常省心。挂载目录的权限如果出现异常,可以执行chmod -R 755 /opt/ddns-go来修正。
4. 配置 IPv6 动态解析的核心操作
4.1 登录 DDNS-GO 后台的基本配置
DDNS-GO 的后台界面非常简洁,整体逻辑清晰。登录后会看到几个主要配置区域:DNS 服务商、获取 IP 方式、Domains 列表。我建议按从左到右的顺序依次配置,不容易漏。
先选择你的 DNS 服务商。如果你用的是阿里云,就选“阿里云”;腾讯云解析或 DNSPod 选“腾讯云”;海外域名比较多的话选“Cloudflare”。选择之后会弹出 AccessKey ID 和 AccessKey Secret 两个输入框,把你提前准备号的 API 密钥填进去。填完之后可以先点一下“测试”,看到提示连接成功后,再继续配置域名。
这里有一个安全心得:API 密钥一定不要图省事直接拿主账号的 AccessKey。我自己的做法是单独创建一个 RAM 子用户,只授权 DNS 解析相关的权限,然后把子用户的密钥给 DDNS-GO 使用。这样即使密钥泄露,别人也只能操作你的 DNS 解析,影响面小得多。
4.2 以阿里云为例申请 API 密钥
在阿里云控制台里,进入“访问控制 RAM” → “用户” → “创建用户”。创建时需要勾选“OpenAPI 调用访问”,创建成功后会生成 AccessKey ID 和 AccessKey Secret,Secret 只会完整显示一次,一定要立刻保存。
创建好用户后,给这个用户添加权限策略,搜索AliyunDNSFullAccess,然后授权给该用户。这个权限策略只管云解析 DNS,不会波及服务器、数据库等其他资源。其他服务商的流程大同小异:腾讯云是“访问管理 CAM”里创建子用户并授权QcloudDNSPodFullAccess,华为云也是类似的“统一身份认证服务 IAM”流程。核心原则就是最小权限,够用就行。
密钥拿到之后,回到 DDNS-GO 后台填入。如果出现 API 报错,优先检查权限是否已经生效,以及 AccessKey 是否复制完整。我刚部署时经常因为 Secret 末尾多了一个空格导致测试失败,检查方法就是在输入框里全选删除重新粘贴。
4.3 IPv6 地址获取方式的选择与参数填法
这应该是全篇最关键的一步了。DDNS-GO 的“获取 IP 方式”提供了多种途径,最常见的两种是“通过网卡获取”和“通过接口 URL 获取”。
如果你的服务器本身就有公网 IPv6 地址,比如我这种把 DDNS-GO 直接部署在云服务器或者家里的高性能小主机上的情况,推荐用“通过网卡获取”:
- 访问 IPv4:根据需求选,不是本教程重点。
- 访问 IPv6:这里要重点操作。勾选“启用”或者“优先 IPv6”。
- 网卡名称:填写你服务器物理网卡的名字,比如
eth0、ens18、enp1s0等。网卡名称可以通过ip link show查看。
优先 IPv6 的意思是,当同时存在 IPv4 和 IPv6 地址时,优先把 IPv6 更新到 AAAA 记录里,IPv4 还是更新 A 记录。填好之后,DDNS-GO 会在每次检查时读取你指定的那块网卡上的全局 IPv6 地址,然后调用 DNS 服务商 API 完成更新。
如果你的服务器场景比较特殊,比如没有 IPv6 网卡,但出口确实能拿到公网 IPv6,那就改用“通过接口 URL 获取”。这种方式是让 DDNS-GO 主动请求一个外部接口,从接口返回的文本中解析出公网 IPv6。常用的接口有:
https://api6.ipify.orghttps://ifconfig.co/iphttps://ip.sb
填法就是在输入框中完整粘贴接口地址,确保该接口返回的是纯 IPv6 地址字符串,不要有额外 HTML 内容。选哪种方式没有绝对的对错,关键在于匹配你当前的网络形态。我的判断经验是:只要机器上有全局 IPv6,网卡方式最稳;只有出口有 IPv6、机器本身没有的,才考虑接口 URL 方式。
配置完获取方式之后,在 Domains 列表里填你要解析的域名,格式如home.example.com。可以同时配置 IPv4 和 IPv6 两个解析框,分别填入不同域名,也可以同一个域名同时勾选 A 和 AAAA。保存之后 DDNS-GO 会立刻执行一次检查,并把解析记录更新到当前 IP 上。
我个人的习惯是在这个阶段把“IPv6 解析”单独使用一个子域名,比如v6home.example.com,跟 IPv4 的解析分开,排查问题的时候更清晰。
4.4 同步更新多个域名与通知配置
DDNS-GO 支持在 Domains 列表里一次配置多个域名,每个域名可以独立指定是更新 A 记录还是 AAAA 记录,甚至可以不同域名使用不同的获取 IP 方式。如果你有多台需要远程访问的设备,可以给每台设备分配一个子域名,然后在同一个 DDNS-GO 实例里统一管理,不用每台设备都单独部署一套。
另外,后台还提供了 Webhook 通知功能。更新成功或失败时,可以触发一个 HTTP 请求,把结果推送到企业微信、钉钉、Telegram 这类消息渠道。我个人不太爱折腾通知,但如果你对解析状态的时效性要求高,建议花几分钟配一下。实时掌握是否更新成功,比事后翻日志舒服得多。不过要注意 Webhook 地址本身也是敏感信息,不要把带密钥的完整地址随意往公开代码里贴。
5. 验证效果与日常运维
5.1 手动触发一次解析更新
配置完成后,在 DDNS-GO 后台点“立即检查”按钮,或者直接点击保存配置,都会触发一次更新动作。此时观察页面提示,如果显示更新成功,可以到命令行验证一下解析是否生效。
nslookup home.example.com ping6 home.example.com如果返回的 IPv6 地址跟你服务器当前的公网 IPv6 一致,说明整条链路已经通了。如果不一致,先检查 DNS 缓存,nslookup时指定 DNS 服务器为223.5.5.5或用dig @1.1.1.1绕过本地缓存,排除缓存干扰再看结果。
别忘了 AAAA 记录的 TTL。有些 DNS 服务商默认 TTL 为 600 秒甚至更长,刚更新完立即查询可能还是旧值,等 TTL 过期后再验证才准确。这一点很容易让人误以为更新失败,其实只是还没生效。
5.2 日志分析与更新频率设置
DDNS-GO 容器运行期间的日志可以通过docker logs ddns-go --tail 50查看。日志内容会记录每次检查得到的外部 IP、要更新的域名、调用 API 返回的结果等信息。如果某次更新没有成功,日志里通常会有明确报错,比如鉴权失败、域名不存在、记录冲突等。
DDNS-GO 默认每隔 5 分钟检查一次 IP 是否变化。只有 IP 发生变化时才会调用 DNS 服务商 API,IP 没变就保持静默,这个设计很合理,既能及时响应地址变化,又不会因为频繁请求而触发服务商的 API 限流。如果你希望提高时效性,缩短检查间隔不是问题,但我个人认为 5 分钟已经足够,毕竟域名解析还有 TTL 缓存时间,你检查得再频繁,下游生效也需要时间。
顺带提一个容易被忽略的运维点:定期确认容器配置目录还在正常写入。你可以执行ls -l /opt/ddns-go看看目录里的配置文件和日志是否在持续更新。一旦发现目录不可写或者文件异常,优先检查磁盘空间和目录权限。遇到磁盘已满导致容器无法写入配置的情况,处理方式是清掉不必要的日志和临时文件,然后把 ddns-go 容器重启一次恢复状态。
5.3 与宝塔防火墙、云厂商安全组的配合
DDNS-GO 跑起来之后,9876 端口就成了需要认真对待的入口。如果你用的是容器默认端口且没有做额外保护,至少要做两件事:
- 在宝塔面板的安全 → 防火墙中放行 9876 端口,否则外部访问不了管理后台。
- 如果你的服务器是云厂商的实例,还要到云控制台的安全组里同步放行。这一步经常被遗漏,表现为“面板里放了端口,但外网还是不通”。
但放行不等于裸奔。DDNS-GO 管理后台虽然有登录密码,但既然暴露在公网上,密码就必须设置得足够复杂,否则很容易被暴力尝试。更稳妥的做法是:不在公网直接暴露 9876 端口,改用宝塔面板的反向代理功能,只开放一个特定路径访问,并叠加 IP 白名单限制。家庭场景里如果只是偶尔用,甚至可以只在需要时通过 SSH 隧道访问管理后台,平时保持端口关闭。
这个经验也适用于通过 IPv6 远程访问家里其他设备的场景。很多人遇到“拿到公网 IPv6 但外面还是访问不了”的问题,很大概率不是 DNS 没解析对,而是目标设备自身或者上游路由器的 IPv6 防火墙没有放行对应端口。IPv6 环境没有 NAT 给你兜底,设备上的防火墙规则反而更加重要,务必逐个检查。
6. 常见问题排查与避坑实录
6.1 容器内看不到 IPv6 地址怎么办
现象是 DDNS-GO 日志提示 IPv6 获取失败,或者获取到的 IPv6 是fe80::开头的内网地址。排查思路按顺序来:
- 先在宿主机执行
ip -6 addr show,确认宿主机本身有没有全局 IPv6 地址。没有的话,先解决宿主机网络问题。 - 检查容器网络模式。如果你用的是 bridge 模式,容器内看到的
eth0可能没有全局 IPv6,或者有一个不代表真实出口的地址。解决办法是删除容器并改用 host 模式重新创建。 - 确认 DDNS-GO 里填写的网卡名称正确。网卡名写错是新手最容易犯的错,一个字母不对就获取失败。
这里我插一句:如果宿主机没有 IPv6 地址,但你的路由器或光猫是公网拨号获取 IPv6,且服务器在局域网内,那种场景不要用网卡方式,直接改用接口 URL 方式,让 DDNS-GO 从外部接口拿公网地址,反而简单可靠。
6.2 解析记录迟迟不更新,优先查哪几项
遇到配置完一直不更新,先别急着怀疑程序坏了。我一般按这个顺序排查:
- 看后台日志。如果日志显示“IP 未变化,无需更新”,说明程序工作正常,只是网络出口的 IPv6 确实没有变过。
- 确认域名的 DNS 确实托管在当前配置的服务商上。很多域名虽然在服务商 A 注册,但 NS 记录在服务商 B,你在服务商 A 里配置再多也不会生效。
- 检查 API 密钥的权限是否完整。阿里云的
AliyunDNSFullAccess需要覆盖到目标域名所在的资源组,如果域名不在默认资源组,可能需要额外配置。 - 确认域名和历史解析记录之间没有冲突。比如该域名已经存在一条手动添加的 CNAME 记录,再添加 AAAA 记录时服务商可能会拒绝新记录。
- 排除 DNS 缓存干扰。查询时指定可信 DNS 服务器,并且等待 TTL 过期后再验证。
还有一个常见因素:你有多台设备同时用同一个 AccessKey 更新同一个域名。这种情况会产生相互覆盖,每台设备都以为自己更新成功了,但最终生效的往往是最后调用 API 的那一个。如果确实有多台设备需要接入,一定要分域名管理,避免共用同一个解析目标。
6.3 换宽带、换主机后如何快速恢复
家里换了宽带、机房换了机器,IPv6 地址通常会发生整段变化。这时候 DDNS-GO 会在下一次检查周期自动把解析更新到新地址,理论上什么都不用做。但如果你发现长时间没更新,最多手动点一下“立即检查”强制刷新。
如果主机彻底换了,需要重新部署容器,操作也很简单。只要宿主机上挂载的/opt/ddns-go数据目录还在,重新执行原来的docker run命令,把同一个目录挂载进去,配置和日志都会自动回来。这里再次体现数据目录独立挂载的价值,重装容器基本是无感的。如果没有保留旧目录,那就重新走一遍第 4 章的配置步骤,五分钟左右也能搞定。
另外,我建议在换主机前顺手备份一下/opt/ddns-go目录。这个目录不大,打包起来就几百 KB,放到对象存储或者本地留一份,以后任何迁移都能快速恢复。
6.4 几个让我少走弯路的经验细节
最后分享几个零散但很实用的经验。第一,镜像尽量锁定一个明确版本,而不是一直跟着 latest 跑。升级时先备份数据目录,再拉新版本镜像重建容器,出问题还能回滚。第二,密钥管理一定要走最小权限,定期更换 AccessKey,尤其是怀疑密钥有泄露风险时,直接到云控制台禁用并重新生成。第三,域名解析记录更新后,测试时记得刷新本地 DNS 缓存。Windows 上ipconfig /flushdns,Linux 上通常重启 systemd-resolved 或者直接换 DNS 查询源。第四,如果你打算把 DDNS-GO 的管理后台长期暴露在公网上,强烈建议在宝塔面板里设置访问限制,或者在云安全组里只放行你自己的 IP 段,避免被暴力扫描盯上。
整套流程跑下来,我最大的感受是:DDNS-GO 本身非常稳定,真正容易出问题的往往不是程序,而是部署环境里的网络模式和权限配置。只要你先把宿主机 IPv6 网络确认好、容器采用 host 模式、API 密钥权限给准确,IPv6 动态解析基本就是一次配置、长期省心的状态。我现在家里那台主机已经连续跑了半年,中间运营商重新分配过好几次 IPv6 前缀,域名解析每次都自动跟着更新,再也没有手动去改过 AAAA 记录。如果你也想摆脱“地址一变就失联”的窘境,这套宝塔面板 + Docker + DDNS-GO 的方案可以直接照抄。