Python实现阿里云动态域名解析

摘要:前段时间买了个树莓派,打电信客服开通了外网ip,相当于在家里搭了一个云服务器。今天有时间记录一下当时的搭建过程。

开通外网IP地址

我家用的电信宽带,拨打电话10000,找人工客服,就说开通外网IP业务,等待办理成功即可。

办理成功后,便可通过外网IP访问到你的天翼网关。(注意:80、443端口会被屏蔽,其它端口可以正常使用。)

再到阿里云配置一下域名解析,即可通过域名访问。操作方法见文档

天翼网关端口映射

输入超级管理员的账号密码,注意不是普通用户的账户密码。

如果没改过的话,账号:telecomadmin 密码:nE7jA%5m

如果密码不对,那就只能问电信工作人员了。

登录成功之后,进入【应用】-【高级NAT配置】-【虚拟服务器设置】,添加端口映射规则。即将你外网ip的端口映射到内网ip的端口。

这里两种情况:

  1. 如果家里装了路由器,所有设备(手机、电脑、树莓派)都是连的路由器,这里就映射到路由器的IP,然后再在路由器设置一次端口映射。

  2. 如果家里没装路由器,设备直接连接的天翼网关,那么这里就直接映射设备的ip。注意这里要给设备设置为固定ip地址。

路由器设置IP与MAC绑定

路由器端口转发

最终转发效果

graph LR
用户 --> http://domain.com:10000 --> 天翼网关10000端口 --> TPLink路由器10000端口 --> 树莓派80端口

动态域名解析(DDNS)

这一套搞好之后,刚开始用着还比较舒服,用了一段时间后,问题暴露出来了。电信的外网IP是不固定的,隔一段时间就会变。虽然我可以上阿里云控制台,去修改域名对应的ip地址,但是手动去改是很烦的。能不能实现自动修改呢?答案是肯定的。

阿里云提供了修改域名解析的APISDK。我们可以根据这些自己写程序来实现动态域名解析。

思路大概就是,定时(每隔15分钟)查询一下当前的外网IP,看是否发生了变化,如果变化了,则调用阿里云API修改域名解析。

废话少说,python代码如下,只需加入系统定时任务即可。

1
2
3
4
root@raspberrypi:~ # crontab -e

# 每隔15分钟检查一次公网ip是否发生变化
0,15,30,45 * * * * /opt/alidns/main.py
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#!/usr/bin/env python
#-*- coding: utf-8 -*-

import os
import json
import logging
from urllib2 import urlopen
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ClientException
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkalidns.request.v20150109 import DescribeDomainRecordsRequest
from aliyunsdkalidns.request.v20150109 import UpdateDomainRecordRequest
import sys

reload(sys)
sys.setdefaultencoding('utf8')

logging.basicConfig(level=logging.INFO,format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s',filename='/opt/alidns/log/alidns.log')

class DnsHandler:
# 从阿里云开发者后台获取Access_Key_Id和Access_Key_Secret
access_key_id = " "
access_key_secret = " "

# 填入自己的域名
domain_name = "domain.com"
# 填入二级域名的RR值
rr_keyword = "test"

# 解析记录类型,一般为A记录
record_type = "A"

# 用于储存解析记录的文件名
file_name = "/opt/alidns/ip_addr.txt"

client = None
record = None
current_ip = ''

# 初始化,获取client实例
def __init__(self):
self.client = AcsClient(
self.access_key_id,
self.access_key_secret
)
self.record = self.get_record()
self.current_ip = self.get_current_ip()

# 如果公网IP发生变化,则自动修改阿里云解析记录
def reset(self):
if self.current_ip <> self.get_record_value():
self.update_record(self.current_ip)
else:
logging.info('ip 地址未发生变化:' + self.current_ip)

# 获取阿里云域名解析完整记录,并使用文件缓存
def get_record(self):
if os.path.isfile(self.file_name) :
file_handler = open(self.file_name, 'r')
r = file_handler.read()
file_handler.close()
else :
request = DescribeDomainRecordsRequest.DescribeDomainRecordsRequest()
request.set_PageSize(10)
request.set_action_name("DescribeDomainRecords")
request.set_DomainName(self.domain_name)
request.set_RRKeyWord(self.rr_keyword)
request.set_TypeKeyWord(self.record_type)
r = self.client.do_action_with_exception(request)
file_handler = open(self.file_name, 'w')
file_handler.write(r)
file_handler.close()
return json.loads(r)

# 获取阿里云域名解析记录ID
def get_record_id(self) :
return self.record["DomainRecords"]["Record"][0]["RecordId"]

# 获取当前域名解析记录
def get_record_value(self) :
return self.record["DomainRecords"]["Record"][0]["Value"]

# 修改阿里云解析记录
def update_record(self, value):
request = UpdateDomainRecordRequest.UpdateDomainRecordRequest()
request.set_action_name("UpdateDomainRecord")
request.set_RecordId(self.get_record_id())
request.set_Type(self.record_type)
request.set_RR(self.rr_keyword)
request.set_Value(value)
self.client.do_action_with_exception(request)
os.remove(self.file_name)
logging.info('ip地址变化,更新DNS解析:' + value);

# 获取当前公网IP
def get_current_ip(self):
currentip = json.load(urlopen('https://ipv4.jsonip.com'))['ip']
logging.debug('currentip:' + currentip)
return currentip

# 实例化类并启动更新程序
dns = DnsHandler()
dns.reset()