导读:本期聚焦于行者创作的《如何让Ubuntu服务器通过DDNS保持固定域名指向动态IP?》,敬请观看详情。家庭宽带或云服务器使用动态IP时,每次重新拨号地址都会变化,绑定域名的A记录如果不及时更新,远程访问就会失败。DDNS的核心原理是让运行在Ubuntu上的客户端定期检测本机出口IP,并调用DNS服务商的API自动覆盖解析记录,让固定域名始终指向最新的动态地址。本文不依赖第三方客户端套件,直接使用curl与Cloudflare API完成更新,并配合systemd timer实现周期执行。操作流程涵盖API令牌申请、Zone ID与Record ID获取、更新脚本编写、定时任务配置以及日志排查。相比ddclient等工具,这种方式更透明可控,也便于针对国内网络环境调整IP获取源。完成配置后,无论宽带IP如何变化,域名解析都能在几分钟内自动同步,适用于家庭NAS、远程桌面、自建网站等场景。

通过域名访问一台Ubuntu主机时,如果接入的是普通家庭宽带,运营商分配的公网IP通常是动态变化的。每次光猫或路由器重新拨号,IP都可能改变,导致之前设置的DNS A记录失效。要解决这个问题,需要引入DDNS(动态域名解析)机制,让主机在IP变化后主动通知DNS服务商更新解析记录。下面将围绕Ubuntu环境,逐步展示如何用Cloudflare提供的API实现一个轻量、可靠的DDNS更新方案。

如何让Ubuntu服务器通过DDNS保持固定域名指向动态IP?

一、DDNS的工作逻辑与方案选型

传统的DNS解析记录是静态的,一旦添加,通常不会自动变更。对于拥有固定公网IP的服务器,手动设置一次A记录就能长期使用;但家庭宽带的公网IP并不固定,运营商可能每隔一段时间重新分配地址,甚至每天变化多次。如果每次都要登录DNS管理后台手动修改,既麻烦又容易遗漏。

DDNS的思路是在目标主机上运行一个客户端,由它周期性地获取本机出口IP,并与上一次成功更新的IP进行对比。如果发现地址发生变化,就调用DNS服务商提供的API,把域名对应的A记录内容覆盖为新IP。为了减少不必要的API调用,客户端通常会缓存上次IP,仅在变化时发起更新请求。整个过程自动完成,用户无需干预。

在Ubuntu上实现DDNS有多种方式。ddclient是比较老牌的通用客户端,支持多种DNS服务商,但配置文件较为繁琐,而且部分插件更新滞后。自己编写脚本调用Cloudflare API则更简单直观,只需要curl和jq两个命令即可完成,适合希望了解底层细节或需要定制行为的用户。本文选择自定义脚本加systemd timer的组合,既保证轻量,也方便排查问题。

二、准备Cloudflare API凭据与编写更新脚本

开始之前,确保域名已经托管在Cloudflare上,并且已经为需要动态更新的子域名(例如home.ipipp.com)创建了一条A记录。A记录可以随便填一个IP地址,后续脚本会自动覆盖。接下来登录Cloudflare控制台,进入“我的个人资料”下的“API令牌”页面,创建一个新的API令牌。权限选择“区域- DNS-编辑”,区域资源限定为对应域名。这样即使令牌泄露,攻击者也仅能修改该域名的解析记录,不会影响其他服务。

创建完成后会显示一次令牌明文,务必及时保存。随后进入域名的概述页面,右侧可以看到Zone ID。在DNS管理页面,点击对应A记录,可以获取Record ID,它是一串十六进制标识符。将这三个值记录下来,后续写入脚本。为了安全,也可以将它们放入独立的配置文件中,并通过权限设置限制读取。

接下来安装必要的工具:curl用于发起HTTP请求,jq用于解析JSON响应。在Ubuntu上执行sudo apt update && sudo apt install curl jq -y即可。然后创建脚本文件/usr/local/bin/update_ddns.sh,内容如下。脚本首先通过多个IP查询接口依次尝试获取出口IP,这样可以避免单一服务不可用导致更新失败。获取成功后与缓存文件中的旧IP比较,只有发生变化才调用Cloudflare API。API响应中的success字段用来判断更新是否成功,成功后将新IP写入缓存。

更新脚本示例:

#!/bin/bash
# Cloudflare DDNS 更新脚本
API_TOKEN="填写你的API令牌"
ZONE_ID="填写Zone ID"
RECORD_ID="填写Record ID"
DOMAIN="home.ipipp.com"

get_ip() {
    for url in "https://api.ipify.org" "https://ifconfig.me/ip" "https://icanhazip.com"; do
        IP=$(curl -s --connect-timeout 5 "$url")
        if [[ -n "$IP" ]]; then
            if [[ "$IP" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
                echo "$IP"
                return 0
            fi
        fi
    done
    return 1
}

IP=$(get_ip)
if [[ -z "$IP" ]]; then
    echo "无法获取公网IP"
    exit 1
fi

CACHE_FILE="/tmp/ddns_ip_cache"
if [[ -f "$CACHE_FILE" ]]; then
    OLD_IP=$(cat "$CACHE_FILE")
else
    OLD_IP=""
fi

if [[ "$IP" == "$OLD_IP" ]]; then
    echo "IP未变化,跳过更新: $IP"
    exit 0
fi

RESPONSE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \
     -H "Authorization: Bearer $API_TOKEN" \
     -H "Content-Type: application/json" \
     --data "{\"type\":\"A\",\"name\":\"$DOMAIN\",\"content\":\"$IP\",\"ttl\":120,\"proxied\":false}")

if echo "$RESPONSE" | grep -q '"success":true'; then
    echo "$IP" > "$CACHE_FILE"
    echo "DDNS更新成功: $IP"
else
    echo "DDNS更新失败: $RESPONSE"
    exit 1
fi

脚本中的get_ip函数依次尝试三个公网IP查询接口,避免单一服务不可用;proxied参数设为false表示不经过Cloudflare代理,让域名直接解析到真实IP,适合远程桌面、SSH等非HTTP服务。保存后执行chmod +x /usr/local/bin/update_ddns.sh,再手动运行一次验证。如果输出“DDNS更新成功: 你的IP”,说明凭据和网络均正常。

三、配置systemd定时任务实现自动更新

手动执行脚本只能解决一次更新,要让域名持续跟随公网IP变化,需要借助Ubuntu的systemd定时器。systemd timer比传统cron更加灵活,可以指定开机后首次运行时间以及后续执行间隔,并且服务日志统一由journald管理,排查问题更方便。

首先创建服务单元文件/etc/systemd/system/ddns-update.service,将脚本注册为一个一次性服务。内容中指定Type=oneshot,表示任务执行完成后进程退出,不会驻留后台。After=network-online.target确保网络就绪后再执行,避免开机时因网络未连接导致IP获取失败。然后创建定时器单元/etc/systemd/system/ddns-update.timer,设置开机后2分钟首次触发,之后每5分钟运行一次。这个频率对于大多数动态IP场景足够,同时不会对Cloudflare API造成过大压力。

两个配置文件示例:

[Unit]
Description=Update DDNS for Cloudflare
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/update_ddns.sh
User=root
[Unit]
Description=Run DDNS update every 5 minutes

[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
Unit=ddns-update.service

[Install]
WantedBy=timers.target

配置文件保存后,执行以下命令重新加载systemd并启用定时器:sudo systemctl daemon-reload、sudo systemctl enable --now ddns-update.timer。通过systemctl status ddns-update.timer可以查看定时器是否处于活跃状态,systemctl list-timers ddns-update.timer会显示下一次触发时间。手动测试时可以运行sudo systemctl start ddns-update.service,并用journalctl -u ddns-update.service -f实时查看日志输出。

为了验证整个链路是否正常,可以先删除/tmp/ddns_ip_cache缓存文件,再强制启动一次服务,观察Cloudflare控制台中的A记录是否更新为当前公网IP。如果IP没有变化,脚本会输出“IP未变化”,这同样说明检测逻辑有效。

四、常见问题排查与安全建议

配置过程中最容易遇到的问题是脚本无法获取正确的出口IP。如果Ubuntu主机位于路由器后面,通过NAT访问互联网,那么curl查询到的是路由器的公网IP,而不是内网地址,这正是我们需要的出口IP。但如果主机同时配置了代理环境变量,curl可能会走代理导致获取到错误的地址,可以在脚本中显式使用--noproxy参数或清除相关环境变量。

另一个常见原因是API令牌权限不足。Cloudflare令牌必须包含对应区域的DNS编辑权限,如果只给了读取权限,更新时会返回403错误。脚本中可以通过grep -q '"success":true'检测响应,如果失败,响应体中会包含具体的错误消息,通常与令牌或Zone ID、Record ID不匹配有关。建议先将脚本中的变量替换为实际值,手动执行一次,观察完整JSON输出。

安全问题同样值得关注。API令牌和Zone ID等敏感信息不应硬编码在脚本中,可以考虑将脚本权限设置为700,仅root可读写。如果多人使用同一台主机,更推荐将敏感值写入独立的环境文件,例如/etc/default/ddns-update,并在systemd服务中通过EnvironmentFile加载。此外,Cloudflare的API令牌支持IP白名单和过期时间,适当加以限制能进一步降低泄露风险。

TTL设置方面,如果IP变化频繁,可以将TTL调低到120秒甚至60秒,但过低的TTL会增加解析延迟和DNS查询负载。对于家庭宽带,通常120秒已经足够在IP变化后几分钟内完成同步。如果使用Cloudflare代理(proxied设为true),则面向公网的是Cloudflare的CDN IP,无法直接用于SSH等非HTTP协议,需要根据实际服务类型选择是否开启代理。

UbuntuDDNS动态域名解析修改时间:2026-10-01 18:20:48

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/1001/64345.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。