家里闲置着一台老款的 Mac mini Server, 跑 OS X 越来越慢, 索性装上了 Cent OS 7, 变成了一台家庭服务器, 装上了 Plex 媒体服务器和 Transmission 下载服务, 同时, 也装上了 NginxMysqlMongoDBRedis 等, 可以调试代码, 甚至担当一些小型项目的服务器
不过, 只在家庭内网使用, 功能太有限, 于是接下来面临的一个问题就是内网穿透使用过花生壳和花生棒, 服务相当不稳定, 而且种种受限, 每要多加一个端口就要多花钱, 安全性也有问题
其实想想, 内网穿透的最大难题无非就是家里是动态公网 IP, 每变换一次公网 IP, 需要重新解析一次域名而阿里云等大型的云服务商, 目前都已经实现了域名解析管理的 API 接口, 而且基本都是立即就可生效
所以我的思路就是, 系统运行一个定期执行的程序, 每隔一段时间扫描一下最新的公网 IP, 如果发现最新的公网 IP 与域名解析到的 IP 地址不一致, 就通过阿里云 API 自动更新解析设置即可这样的花费不过每年一个域名的费用, 最贵也就几十块钱
具体实现步骤如下:
1. 阿里云设置
首先, 要确定一个准备用于外网访问的域名, 并将此域名转入到阿里云的云解析服务来解析如图所示, 添加需要管理的域名
image
转入后, 在解析设置中, 设置一下 A 记录解析, 解析的 IP 地址可以填当前的公网 IP 如果不知道自己的公网 IP, 在 CentOS 系统下, 可以输入使用以下命令获取当前的公网 IP
curl ifconfig.me
获取公网 IP 后, 在阿里云云解析中设置完 A 记录解析
image
在阿里云账户管理后台, 点击右上角的账户头像, 然后点击 accesskeys, 或者直接登陆 https://ak-console.aliyun.com, 获取阿里云的 AccessKeyID 和 AccessKeySecret
最后一个需要获取的是区域 ID(RegionID), 这个主要是针对 ECS 管理的, 云解析 DNS 似乎并不需要如果同时购买了阿里云的 ECS 服务, 则填入 ECS 服务的区域如果没有, 随便填一个应该就行区域 ID 具体值可参考阿里云的帮助文档: https://help.aliyun.com/document_detail/40654.html
image
2. 路由器设置
阿里云的设置完成后, 需要对路由器设置端口映射, 使外网对公网 IP 的端口访问能转发到内网服务器的相应端口绝大部分的路由器都支持端口映射
常见的服务端口包括, 用于 web 访问的 80 端口 SSH 远程管理的 22 端口 Mysql 数据库的 3306 端口 Transmission 下载服务管理的 9091 端口和 Plex 媒体服务的 32400 端口等等不用花生壳的好处就是没有端口数量限制, 想设置多少就可以设置多少
当然, 在设置端口映射之前, 应确保服务器的内网 IP 已经设置为静态 IP, 而不是 DHCP 动态获取
image
3. 服务器设置
服务器端安装好想要使用的各种服务后, 别忘了在防火墙中开启相应的端口, 在 CentOS 7 中, 防火墙永久开启端口的命令是:
firewall-cmd --add-port=80/tcp --permanent
开启之后别忘了重新载入防火墙的设置以使其生效, 命令如下:
firewall-cmd --reload
4. 自动更新域名解析程序
准备工作都做好了, 接下来就是通过程序检测公网 IP, 并在公网 IP 发生变化时, 及时更新阿里云的域名解析
这个程序是用 Python 写的, 先使用 Python 的包管理工具 pip 下载安装阿里云的 Python SDK 如果没有安装 pip, 则先安装 pip:
yum install pip
安装好 pip 后, 安装阿里云的 Python 核心 SDK 以及云解析 SDK:
- pip install aliyun-python-sdk-core
- pip install aliyun-python-sdk-alidns
导入项目所需要的包, 如果缺少则使用 pip 安装:
- import os
- import json
- 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
完整代码如下:
- #!/usr/bin/env python
- # coding= utf-8
- import os
- import json
- 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
- class DnsHandler:
- # 从阿里云开发者后台获取区域 IDAccess_Key_Id 和 Access_Key_Secret
- region_id = "cn-qingdao"
- access_key_id = ""access_key_secret =""
- # 填入自己的域名
- domain_name = ""
- # 填入二级域名的 RR 值
- rr_keyword = ""
- # 解析记录类型, 一般为 A 记录
- record_type = "A"
- # 用于储存解析记录的文件名
- file_name = ".ip_addr"
- client = None
- record = None
- current_ip = ''
- # 初始化, 获取 client 实例
- def __init__(self):
- self.client = AcsClient(
- self.access_key_id,
- self.access_key_secret,
- self.region_id
- )
- self.record = self.get_record()
- self.current_ip = self.get_current_ip()
- # 如果公网 IP 发生变化, 则自动修改阿里云解析记录
- def reset(self):
- if self.current_ip <> self.get_record_value():
- print self.update_record(self.current_ip)
- self.get_record()
- # 获取阿里云域名解析完整记录, 并使用文件缓存
- 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)
- return self.client.do_action_with_exception(request)
- # 获取当前公网 IP
- def get_current_ip(self):
- return json.load(urlopen('http://jsonip.com'))['ip']
- # 实例化类并启动更新程序
- dns = DnsHandler()
- dns.reset()
将以上代码保存为 dns.py 文件, 并赋予执行权限:
chmod +x dns.py
5. 设置定时运行
CentOS 内置有强大的计划任务工具 Crontab, 如果系统里没有则先使用 yum 安装:
yum install crontabs
首先, 设置执行用户的环境变量, 比如, 我们使用 root 用户来执行这一程序, 则先在用户目录下建立. profile 文件, 或者在已有的. profile 文件下加入如下一行, 以使得可以使用 VI 来编辑 cron 文件:
EDITOR=vi; export EDITOR
建立 mycron 文件, 加入如下内容:
*/10 * * * * /root/ddns/dns.py
这意味着每 10 分钟执行一次任务, 即扫描公网 IP, 若与阿里云解析不一致, 则修改阿里云解析
然后, 提交 crontab 任务:
crontab mycron
好了, 大功告成, 接下来, 程序会每隔 10 分钟自动扫描公网 IP, 然后自动更新阿里云的解析, 速度稳定性和安全性都远胜于第三方的 DDNS 服务
来源: http://www.jianshu.com/p/291a3a5283ac