DDNS 简介
DDNS(Dynamic Domain Name Server),它的作用是将用户的动态IP绑定到一个域名上去。
这样就算你的服务器IP发生了变化,用户仍然可以使用你的域名找到你的服务器。
阿里云提供了一套API,可以让你用编程的方式实现 DDNS,但是需要你的域名是在阿里云上申请的。
一些可能用到的库
pip install aliyun-python-sdk-core pip install aliyun-python-sdk-alidns pip install pyyaml
获取和缓存 IP 地址
获取 IP 之后最好再把它缓存在一个文件中。
之所以需要缓存是因为阿里云更新两条一样的IP时会报错,我们可以提前缓存,然后下次调用更新服务之前借用缓存的内容,判断当前的 IP 是否无变化。
定义一个 IPManager
类,可以获取本机的 公网IP
地址,并使用文件进行缓存。
from urllib.request import urlopen import logging class IPManager: def __init__(self, file_cache=".ipbuffer"): self.ip = "" self.file_cache = file_cache def get_current_ip(self): with urlopen('http://www.3322.org/dyndns/getip') as response: self.ip = str(response.read(), encoding='utf-8').replace("\n", "") logging.info("current ip: " + self.ip) return self.ip def sync_cache(self): with open(self.file_cache, "w") as f: f.write(self.ip) logging.info("sync cache ip: " + self.ip) def get_cache(self): with open(self.file_cache, "r") as f: old_ip = f.read() logging.info("get cache ip: " + self.ip) return old_ip
程序默认使用 .ipbuffer
文件存储 IP,我觉得我们还需要先创建这个文件,不然运行的时候可能会报错。
可以使用下面的函数检查和创建一个文件,支持递归创建:
import os def check_file(filename): dirname = os.path.dirname(filename) if not os.path.exists(dirname) and dirname != "": os.makedirs(dirname) with open(filename, "w") as f: f.write("")
def main(): ip_manager = IPManager() current_ip = ip_manager.get_current_ip() if ip_manager.get_cache() == current_ip: return ip_manager.sync_cache()
这个程序可以 获取IP 并且在 IP无缓存 或者 IP更新 的时候更新缓存。
获取 accessKeyId 和 accessKeySecret
创建连接阿里云的客户端
from aliyunsdkcore.client import AcsClient profile = { "accessKeyId": "xxx", "accessKeySecret": "xxx", "regionId": "cn-hangzhou" } client = AcsClient(profile["accessKeyId"], profile["accessKeySecret"], profile["regionId"])
把上一步的 accessKeyId
、accessKeySecret
填进去。
我们需要借助 client.do_action_with_exception
这个函数来发送请求到阿里云。
域名解析记录查询
之所以需要加一步域名解析记录查询是为了校验我们的域名是否已经被其他的 IP 绑定了。
from aliyunsdkalidns.request.v20150109.DescribeDomainRecordsRequest import DescribeDomainRecordsRequest import json import logging def describe_domain_records(client, record_type, subdomain): logging.info("域名解析记录查询") request = DescribeDomainRecordsRequest() request.set_accept_format('json') request.set_Type(record_type) request.set_DomainName(subdomain) response = client.do_action_with_exception(request) response = str(response, encoding='utf-8') result = json.loads(response) logging.debug(result) return result
client
是上一步创建的客户端。
record_type
比较复杂,简单来说是 DNS域名解析
的解析类型。我们这里使用 A记录
就好了。
{% note info %}
常见的 DNS解析类型
A: 将主机名(或域名)指向一个 IPv4 地址
AAAA: 将主机名(或域名)指向一个 IPv6 地址
CNAME: 将域名指向另一个域名
{% endnote %}
subdomain
填你的域名就好了。
describe_domain_records(client, "A", "tuenity.xyz")
添加域名解析记录
from aliyunsdkalidns.request.v20150109.AddDomainRecordRequest import AddDomainRecordRequest import logging import json def add_record(client, priority, ttl, record_type, value, rr, domain_name): logging.info("添加域名解析记录") request = AddDomainRecordRequest() request.set_accept_format('json') request.set_Priority(priority) request.set_TTL(ttl) request.set_Value(value) request.set_Type(record_type) request.set_RR(rr) request.set_DomainName(domain_name) response = client.do_action_with_exception(request) response = str(response, encoding='utf-8') result = json.loads(response) logging.debug(result) return result
priority
告诉域名解析服务,按照 priority
从小到大的顺序对记录搜索,搜索到匹配的记录后,就停止搜索 priority
值更大的记录,对于拥有相同 priority
的记录将通过 weight
再次选择
。
虽然阿里云并不提供 weight
的设置接口,但是你要知道它是个什么东西。
对于拥有相同 priority
的多条记录,weight
给出了选择某条记录的几率,值越大,被选中的概率就越大,合理的取值范围为 0-65535
。
ttl
( Time to live ),当用户在访问一个域名的时候,并不是每次都需要去解析一遍的,DNS服务器
会在用户当地的递归DNS服务器
上缓存一次,在 ttl
的时间长度内失效。一般设置 “600”。
record_type
同上一步。
value
就是你的 IP地址
。
rr
,阿里云的 rr
是主机头的意思,一般设置 “www”。
domain_name
就是你的域名。
更新域名解析记录
from aliyunsdkalidns.request.v20150109.UpdateDomainRecordRequest import UpdateDomainRecordRequest import logging import json def update_record(client, priority, ttl, record_type, value, rr, record_id): logging.info("更新域名解析记录") request = UpdateDomainRecordRequest() request.set_accept_format('json') request.set_Priority(priority) request.set_TTL(ttl) request.set_Value(value) request.set_Type(record_type) request.set_RR(rr) request.set_RecordId(record_id) response = client.do_action_with_exception(request) response = str(response, encoding='utf-8') logging.debug(response) return response
和上一步的函数接口几乎一摸一样,不过多解释了。
需要注意,不一样的是 record_id
。这个需要 describe_domain_records
函数的返回值。
des_result = describe_domain_records(client, "A", "tuenity.xyz")
使用 des_result["TotalCount"]
就可以查处现在有多少条记录绑定在这个域名上了。
如果没有,我们就需要调用 add_record
,否则就调用 update_record
。
record_id
可以通过 des_result["DomainRecords"]["Record"][0]["RecordId"]
获取。
改造、封装建议
- 使用 yaml 来作为配置文件
- 使用 python 自带的日志 logging
- 把查询、更新、添加域名解析记录封装成一个类
获取完整的代码
原文链接:https://blog.csdn.net/qq_33384402/article/details/109272008?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168449620216800180620806%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=168449620216800180620806&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~times_rank-12-109272008-null-null.blog_rank_default&utm_term=NAS%E3%80%81%E7%BE%A4%E6%99%96%E3%80%81%E9%98%BF%E9%87%8C%E4%BA%91%E3%80%81%E5%9F%9F%E5%90%8D%E8%A7%A3%E6%9E%90%E3%80%81%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F%E3%80%81ipv6%E3%80%81ddns%E3%80%81%E8%BD%BB%E9%87%8F%E7%BA%A7%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8%E3%80%81%E9%93%81%E5%A8%81%E9%A9%AC%E3%80%81%E5%A8%81%E8%81%94%E9%80%9A%E3%80%81DSM%E3%80%81DSM6.0%E3%80%81%E7%BE%A4%E6%99%96nas%E3%80%81%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8%E3%80%81%E8%9C%97%E7%89%9B%E6%98%9F%E9%99%85%E3%80%81%E9%BB%91%E7%BE%A4%E6%99%96%E3%80%81docker%E3%80%81%E5%AE%B9%E5%99%A8%E9%95%9C%E5%83%8F%E3%80%81%E5%9F%9F%E5%90%8D%E6%B3%A8%E5%86%8C%E3%80%81%E5%AE%9D%E5%A1%94%E3%80%81%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86%E3%80%81nginx%E3%80%81frp%E3%80%81%E5%8A%A8%E6%80%81%E5%9F%9F%E5%90%8D%E8%A7%A3%E6%9E%90