DDNS动态域名解析方案对比与实战配置

admin

本文详解DDNS工作原理、主流方案对比,以及在各种场景下的实战配置。

前言

家里的宽带IP是动态的,每次重启光猫IP就变了。想从外面访问家里的服务器,怎么办?

DDNS(Dynamic DNS) 就是解决这个问题的:自动检测IP变化,自动更新域名解析。

今天来聊聊各种DDNS方案的选择和配置。

一、DDNS原理

1.1 动态IP的问题

运营商给家庭宽带分配的通常是动态公网IP:

IP地址会变化(重启、租期到期)

无法用固定IP访问

1.2 DDNS如何解决

复制代码

┌─────────────┐ IP变化 ┌─────────────┐

│ 家庭路由 │ ──检测到──→ │ DDNS客户端 │

│ IP:1.2.3.4 │ │ │

└─────────────┘ └──────┬──────┘

│ 调用API更新

┌─────────────┐

│ DNS服务商 │

│ example.com │

│ → 1.2.3.4 │

└─────────────┘

工作流程:

DDNS客户端定期检测本机公网IP

发现IP变化后,调用DNS服务商API

更新域名的A记录

域名始终指向当前IP

二、DDNS方案对比

2.1 免费DDNS服务

服务

域名

优点

缺点

No-IP

xxx.ddns.net

免费、客户端多

需每月确认

DuckDNS

xxx.duckdns.org

完全免费

域名不好看

Dynu

xxx.dynu.net

免费、功能全

速度一般

FreeDNS

多种子域名

选择多

稳定性一般

2.2 国内DNS服务商

服务商

支持DDNS

API调用

适合场景

阿里云DNS

有官方API

有自己域名

腾讯云DNS

有官方API

有自己域名

Cloudflare

有API

海外访问

华为云DNS

有API

华为云用户

2.3 路由器内置

很多路由器/光猫内置DDNS功能:

花生壳

No-IP

自定义

三、阿里云DDNS配置

3.1 前置条件

有一个域名(托管在阿里云)

创建AccessKey

3.2 获取AccessKey

登录阿里云控制台

右上角头像 → AccessKey管理

创建AccessKey,保存好ID和Secret

3.3 使用Shell脚本

bash

复制代码

#!/bin/bash

# aliyun-ddns.sh

# 配置区

ACCESS_KEY_ID="your_access_key_id"

ACCESS_KEY_SECRET="your_access_key_secret"

DOMAIN="example.com"

SUBDOMAIN="home" # 完整域名是 home.example.com

# 获取当前公网IP

get_current_ip() {

curl -s http://ip.3322.net || \

curl -s http://ifconfig.me || \

curl -s http://ipinfo.io/ip

}

# 获取当前DNS记录

get_dns_ip() {

aliyun alidns DescribeDomainRecords \

--DomainName "$DOMAIN" \

--RRKeyWord "$SUBDOMAIN" \

--Type "A" 2>/dev/null | \

jq -r '.DomainRecords.Record[0].Value'

}

# 更新DNS记录

update_dns() {

local record_id=$1

local ip=$2

aliyun alidns UpdateDomainRecord \

--RecordId "$record_id" \

--RR "$SUBDOMAIN" \

--Type "A" \

--Value "$ip"

}

# 主逻辑

current_ip=$(get_current_ip)

dns_ip=$(get_dns_ip)

if [ "$current_ip" != "$dns_ip" ]; then

echo "$(date): IP changed from $dns_ip to $current_ip"

record_id=$(aliyun alidns DescribeDomainRecords \

--DomainName "$DOMAIN" \

--RRKeyWord "$SUBDOMAIN" | \

jq -r '.DomainRecords.Record[0].RecordId')

update_dns "$record_id" "$current_ip"

echo "DNS updated successfully"

else

echo "$(date): IP unchanged ($current_ip)"

fi

3.4 使用第三方工具(推荐)

ddns-go 是一个优秀的开源DDNS工具:

bash

复制代码

# 下载

wget https://github.com/jeessy2/ddns-go/releases/download/v5.6.0/ddns-go_5.6.0_linux_x86_64.tar.gz

tar xf ddns-go*.tar.gz

# 运行

./ddns-go

# 访问 http://localhost:9876 配置

ddns-go优点:

Web界面配置

支持多种DNS服务商

支持IPv6

支持Webhook通知

四、腾讯云DDNS配置

4.1 获取密钥

登录腾讯云控制台

访问密钥管理

创建API密钥

4.2 Python脚本

python

复制代码

#!/usr/bin/env python3

# tencent_ddns.py

import json

import requests

from tencentcloud.common import credential

from tencentcloud.dnspod.v20210323 import dnspod_client, models

# 配置

SECRET_ID = "your_secret_id"

SECRET_KEY = "your_secret_key"

DOMAIN = "example.com"

SUBDOMAIN = "home"

def get_current_ip():

"""获取当前公网IP"""

services = [

'http://ip.3322.net',

'http://ifconfig.me',

'http://ipinfo.io/ip'

]

for service in services:

try:

resp = requests.get(service, timeout=5)

return resp.text.strip()

except:

continue

return None

def get_dns_record():

"""获取DNS记录"""

cred = credential.Credential(SECRET_ID, SECRET_KEY)

client = dnspod_client.DnspodClient(cred, "")

req = models.DescribeRecordListRequest()

req.Domain = DOMAIN

req.Subdomain = SUBDOMAIN

resp = client.DescribeRecordList(req)

if resp.RecordList:

return resp.RecordList[0]

return None

def update_dns_record(record_id, ip):

"""更新DNS记录"""

cred = credential.Credential(SECRET_ID, SECRET_KEY)

client = dnspod_client.DnspodClient(cred, "")

req = models.ModifyRecordRequest()

req.Domain = DOMAIN

req.RecordId = record_id

req.SubDomain = SUBDOMAIN

req.RecordType = "A"

req.RecordLine = "默认"

req.Value = ip

client.ModifyRecord(req)

print(f"Updated {SUBDOMAIN}.{DOMAIN} to {ip}")

if __name__ == "__main__":

current_ip = get_current_ip()

record = get_dns_record()

if record and record.Value != current_ip:

update_dns_record(record.RecordId, current_ip)

else:

print(f"IP unchanged: {current_ip}")

4.3 安装依赖

bash

复制代码

pip install tencentcloud-sdk-python requests

五、Cloudflare DDNS配置

5.1 获取API Token

登录Cloudflare

My Profile → API Tokens

Create Token → Edit zone DNS

5.2 使用Shell脚本

bash

复制代码

#!/bin/bash

# cloudflare-ddns.sh

# 配置

CF_API_TOKEN="your_api_token"

CF_ZONE_ID="your_zone_id"

CF_RECORD_NAME="home.example.com"

# 获取当前IP

CURRENT_IP=$(curl -s http://ipinfo.io/ip)

# 获取DNS记录ID

RECORD_ID=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records?name=$CF_RECORD_NAME" \

-H "Authorization: Bearer $CF_API_TOKEN" \

-H "Content-Type: application/json" | jq -r '.result[0].id')

# 获取当前DNS记录的IP

DNS_IP=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records/$RECORD_ID" \

-H "Authorization: Bearer $CF_API_TOKEN" \

-H "Content-Type: application/json" | jq -r '.result.content')

# 更新DNS

if [ "$CURRENT_IP" != "$DNS_IP" ]; then

echo "Updating DNS: $DNS_IP -> $CURRENT_IP"

curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records/$RECORD_ID" \

-H "Authorization: Bearer $CF_API_TOKEN" \

-H "Content-Type: application/json" \

--data "{\"type\":\"A\",\"name\":\"$CF_RECORD_NAME\",\"content\":\"$CURRENT_IP\",\"ttl\":120,\"proxied\":false}"

echo "DNS updated"

else

echo "IP unchanged: $CURRENT_IP"

fi

六、定时任务配置

6.1 Cron定时执行

bash

复制代码

# 编辑crontab

crontab -e

# 每5分钟检查一次

*/5 * * * * /path/to/ddns-script.sh >> /var/log/ddns.log 2>&1

6.2 Systemd Timer

bash

复制代码

# /etc/systemd/system/ddns.service

[Unit]

Description=DDNS Update Service

[Service]

Type=oneshot

ExecStart=/path/to/ddns-script.sh

# /etc/systemd/system/ddns.timer

[Unit]

Description=DDNS Update Timer

[Timer]

OnBootSec=1min

OnUnitActiveSec=5min

[Install]

WantedBy=timers.target

bash

复制代码

systemctl enable ddns.timer

systemctl start ddns.timer

七、路由器DDNS配置

7.1 OpenWrt内置DDNS

bash

复制代码

# 安装ddns脚本

opkg update

opkg install ddns-scripts luci-app-ddns

# Web界面配置

# Services → Dynamic DNS

7.2 常见路由器

华硕路由器:

外部网络 → DDNS → 选择服务商配置

小米路由器:

高级设置 → DDNS → 配置账号

TP-Link:

应用管理 → DDNS → 配置

八、没有公网IP怎么办

8.1 检测是否有公网IP

bash

复制代码

# 方法1:比较内外网IP

# 路由器WAN口IP

route -n | grep UG

# 公网IP

curl ip.3322.net

# 如果两个IP不一样,说明没有公网IP

8.2 替代方案

如果运营商没有给公网IP(大内网),DDNS就没用了。替代方案:

向运营商申请公网IP:打客服电话,说要监控/远程办公

使用组网软件:如星空组网,不需要公网IP也能远程访问

安装客户端

登录同一账号

通过虚拟IP访问

内网穿透:frp、ngrok等

九、DDNS + 端口映射

有了DDNS域名,还需要配置端口映射才能访问内网服务:

复制代码

互联网 → home.example.com:8080 → 路由器:8080 → 内网服务器:80

路由器端口映射配置:

外部端口:8080

内部IP:192.168.1.100

内部端口:80

协议:TCP

安全建议:

不要映射22端口到公网

使用非标准端口

配合fail2ban等安全措施

或者用组网软件替代端口映射,更安全

十、总结

DDNS方案选择建议:

场景

推荐方案

快速体验

DuckDNS(完全免费)

有自己域名

ddns-go + 阿里云/腾讯云

海外访问

Cloudflare

路由器方案

OpenWrt ddns-scripts

无公网IP

组网软件(如星空组网)

DDNS虽然能解决动态IP问题,但还要配合端口映射使用,有一定安全风险。如果不想折腾,直接用组网软件可能是更简单的选择。

参考资料

ddns-go: https://github.com/jeessy2/ddns-go

阿里云DNS API: https://help.aliyun.com/document_detail/29739.html

Cloudflare API: https://api.cloudflare.com/

💡 提示:DDNS更新有延迟(取决于TTL设置),建议TTL设置短一些(如120秒)。但太短会增加DNS查询次数。

Copyright © 2088 中国战将网 - 顶级游戏活动平台 All Rights Reserved.
友情链接