起因是因为要在家里搭建一个服务器,但因为家用宽带的ip是动态更新的,如果在学校使用当IP更新后我相当于丢失了这个服务器。 至于市面上的众多DDNS软件之所以不用,一是因为个人不喜欢到处注册,其实是因为纯粹的无聊。疫情期间过于无聊,就打算造个轮子来玩玩。
前言 使用方法
python 版本:3.71 python <FileName> <accessKeyId> <accessSecret> <DomainName>
DDNS是指动态域名解析服务,将用户的IP地址解析到对应的域名
为什么要用DDNS
家用宽带没有固定的公网IP
不想并且也无法每天手动更新IP
需要远程使用
准备工作
拥有一个公网IP,不然解析了也没啥用
拥有一个域名,并且是在阿里云购买的
查看阿里云的SDK
阿里云SDK 一个是阿里云的核心SDK库,一个是阿里云的域名库,阿里云DNS SDK库,阿里云SDK平台
阿里云核心SDK库:pip install aliyun-python-sdk-core
阿里云域名SDK库:pip aliyun-python-sdk-domain
阿里云DNS SDK库:pip install aliyun-python-sdk-alidns
实现思路
获取本地公网IP(local+ip)
获取云端解析IP(cloud_ip)
判断两个IP是否一致
公网IP发生变化(不一致)时,更新域名解析记录
设计缺陷
accessKeyId需要运行是作为参数输入,如果服务器宕机重启,有可能导致无法运行,或者泄露accessKeyId
当刷新时间过短,会消耗网络资源(每次获取解析的IP都是通过阿里云接口获取,并非本地保存)
程序效率低下,很多多余操作。
详细步骤 获取本地公网IP(local_ip) 这里我通过网络上面的外网IP的API获取,我目前只使用了一个,计划以后添加。 另一个实现思路是类似百度搜索IP,然后提取网页内容。
1 2 3 4 5 6 7 8 9 10 from urllib import requestimport timedef get_internet_ip (): myip_html = request.urlopen('http://www.3322.org/dyndns/getip' ) myip = myip_html.read() ip = str (myip, encoding='utf-8' ).replace("\n" , "" ) return ip
解析记录IP查询(cloud_ip)
获取accessKeyId和accessSecret 通过阿里云控制台获取,但一般建议使用RAM角色来进行权限的控制,这样accessKeyId和accessSecret就无法进行其他操作。由于是个人使用,所以我选择直接使用。 获取到accessKeyId和accessSecret之后,填入对应的函数中,初始化即可:
通过 get_cloud_ip()
函数,填入你的accessKeyId和accessSecret以及你要解析的域名(DomainName) 这个函数会返回很多东西,其中就包括我们需要的IP记录。 这里我们可以选择提取我们需要的IP记录,或者直接修改,但我感觉这样不太优雅,所以我这里选择在程序中进行修改。
修改域名解析记录所需和函数 get_cloud_ip()
查询的内容和格式都是一样的 所以我们可以对里面需要的项来进行修改,避免重新构建。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from aliyunsdkcore.client import AcsClientfrom aliyunsdkcore.acs_exception.exceptions import ClientExceptionfrom aliyunsdkcore.acs_exception.exceptions import ServerExceptionfrom aliyunsdkalidns.request.v20150109.DescribeDomainRecordsRequest import DescribeDomainRecordsRequestdef get_cloud_ip (accessKeyId, accessSecret, DomainName ): client = AcsClient(accessKeyId, accessSecret, 'cn-hangzhou' ) request = DescribeDomainRecordsRequest() request.set_accept_format('json' ) request.set_DomainName(DomainName) response = client.do_action_with_exception(request) return response
判断本地IP(locol_ip)和解析记录的IP(cloud_ip)是否相同
当两者相同时,等待下次刷新。 这里使用sleep函数实现简单的暂停,感觉这是个笨方法。
当两者不相同时,运行更新函数 update_ip()
更新解析记录。 由于返回的解析记录中,含有多个记录,而我目前只需要修改主机记录为 WWW
的IP记录即可,所以我们提取该主机记录出来进行修改。 修改完后,更新标记 check_update_ip
为真值。
当更新标记 check_update_ip
为真时,运行更新函数 update_ip()
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 my_cloud_data = get_cloud_ip.get_cloud_ip(accessKeyId, accessSecret, DomainName) my_cloud_data = json.loads(my_cloud_data) my_cloud_date = my_cloud_data['DomainRecords' ]['Record' ] for Record in my_cloud_date: if Record['RR' ] == 'www' : cloud_ip = Record['Value' ] if cloud_ip != My_ip: Record['Value' ] = My_ip new_cloud_date = Record print ('[INFO] 更新IP中....' ) check_update_ip = True print (new_cloud_date) elif cloud_ip == My_ip: print ('[INFO] 等待更新....' ) check_update_ip = False if check_update_ip == True : update_ip.update_ip(accessKeyId, accessSecret, new_cloud_date) write_log(new_cloud_date['Value' ]) print ("[INFO] 更新成功... " ) else : time.sleep(3600 ) print ("INFO checking..." )
更新解析记录 这里使用的是阿里云官方的示例代码,阿里云云解析DNS接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from aliyunsdkcore.client import AcsClientfrom aliyunsdkcore.acs_exception.exceptions import ClientExceptionfrom aliyunsdkcore.acs_exception.exceptions import ServerExceptionfrom aliyunsdkalidns.request.v20150109.UpdateDomainRecordRequest import UpdateDomainRecordRequestdef update_ip (accessKeyId, accessSecret, new_cloud_date ): client = AcsClient(accessKeyId, accessSecret, 'cn-hangzhou' ) request = UpdateDomainRecordRequest() request.set_accept_format('json' ) request.set_RecordId(new_cloud_date["RecordId" ]) request.set_RR(new_cloud_date["RR" ]) request.set_Type(new_cloud_date["Type" ]) request.set_Value(new_cloud_date["Value" ]) response = client.do_action_with_exception(request) print (str (response, encoding='utf-8' )) return response
总结
每次更新IP后可以把IP保存为解析记录的IP,下次直接判断两者即可,不用每次都调用查询API。
多看官方API文档。