现在电信、联通等运营商,可以给家用网络和小型商用网络分配动态公网IP。相比于NAT之后的内网IP,这种动态公网IP的场景,在一定程度上允许用户对外提供服务,例如NAS存储、远程监控、web网站等。但是由于IP是动态变化的,所以需要配合动态域名解析(DDNS),才能保证稳定的可用性。

目前,国内外也有一些免费提供DDNS服务的厂商,但是这些服务往往只能解析到服务商提供的特定范围内的域名,无法配合用户自己的独立域名做解析。

而要把动态公网IP解析到自己注册的独立域名,可以利用域名服务商提供的API,结合脚本代码和定时任务,将动态公网IP的变化,及时更新到域名解析记录上。

所需环境

  1. 具备动态公网IP的网络环境:

    可以查看光猫或路由器wan口的IP是否为公网IP,如果没有公网IP,根据个人经验,电信和联通的网络,可以用监控、NAS存储等服务需要公网IP为理由,找客服或者负责片区的工程师尝试申请公网IP,有一定的几率申请下来

  2. 已经注册好的域名,并且域名服务商提供API接口:

    我用的是百度云注册的域名,百度云官方提供了域名服务API,但是默认不开放,需要下工单申请

  3. 能够24小时开机执行定时任务的终端:

    一般可以是openwrt系统的路由器、树莓派、NAS设备等,我是直接用的QNAP的NAS存储服务器。

设计思路

  1. 网上有许多用于获取公网IP的web服务站点,用脚本请求特定站点,获取到本机的公网IP。
  2. 调用域名服务API,获取到设定域名的解析记录。如果设定域名还没有解析记录,直接调用API创建一条指向本机公网IP的A记录(如果是IPv6地址,创建AAAA记录)。
  3. 如果设定域名已有域名解析记录,读取记录的rdata值(域名指向的IP地址),并和本机公网IP作比较,如果一致,退出脚本,如果不一致,则说明本机的公网IP已经发生了变化,调用域名服务API,更新解析记录,指向本机当前的公网IP。
  4. 将实现上述内容的脚本设定为定时任务,定时频率可以参考自己网络的IP变化频率,如果不确定的话可以设定为每5分钟执行一次。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/python
# -*- coding: UTF-8 -*-
from json import load
from urllib2 import urlopen
import BaiduSDK

def diffIp(domain):
#获取本机实时公网IP
my_ip = load(urlopen('http://jsonip.com'))['ip']

#调用百度云的域名sdk,获取域名对应的解析记录ID和解析IP地址
rid,rdata = BaiduSDK.getRecord(domain)

#SDK调用失败
if rdata == "error":
print 'Failed to connect Dns service'
return False
#当前域名没有解析记录,使用当前主机公网IP,创建一条新的解析记录
elif rdata == "none":
print 'No such DNS record, creating a new one'
BaiduSDK.addRecord(domain,my_ip)
return True
#公网IP发生了变化,和DNS解析记录的IP地址不同,更新解析记录
elif rdata != my_ip:
print "The ip has changed: %s -> %s" % (rdata,my_ip)
BaiduSDK.updateRecord(domain,my_ip,rid)
return True
#公网IP没发生变化,不需要更新DNS
else:
print "Not change: %s" % my_ip
return True

if __name__ == '__main__':
diffIp('example.domain.com')

其中BaiduSDK是我自己实现的调用百度云域名服务API进行解析记录查询、添加和修改操作的python代码。

完整代码:GitHub