概述
家裡閑置着一台老款的Mac mini Server,跑OS X越來越慢,索性裝上了Cent OS 7,變成了一台家庭伺服器,裝上了Plex媒體伺服器和Transmission下載下傳服務,同時,也裝上了Nginx、Mysql、MongoDB、Redis等,可以調試代碼,甚至擔當一些小型項目的伺服器。
不過,隻在家庭内網使用,功能太有限,于是接下來面臨的一個問題就是内網穿透。使用過花生殼和花生棒,服務相當不穩定,而且種種受限,每要多加一個端口就要多花錢,安全性也有問題。
其實想想,内網穿透的最大難題無非就是家裡是動态公網IP,每變換一次公網IP,需要重新解析一次域名。而阿裡雲等大型的雲服務商,目前都已經實作了域名解析管理的API接口,而且基本都是立即就可生效 。
是以我的思路就是,系統運作一個定期執行的程式,每隔一段時間掃描一下最新的公網IP,如果發現最新的公網IP與域名解析到的IP位址不一緻,就通過阿裡雲API自動更新解析設定即可。這樣的花費不過每年一個域名的費用,最貴也就幾十塊錢。
具體實作步驟
-
阿裡雲設定
首先,要确定一個準備用于外網通路的域名,并将此域名轉入到阿裡雲的雲解析服務來解析。如圖所示,添加需要管理的域名。

轉入後,在解析設定中,設定一下A記錄解析,解析的IP位址可以填目前的公網IP。如果不知道自己的公網IP,在CentOS系統下,可以輸入使用以下指令擷取目前的公網IP。
> curl ifconfig.me
擷取公網IP後,在阿裡雲雲解析中設定完A記錄解析。
在阿裡雲賬戶管理背景,點選右上角的賬戶頭像,然後點選accesskeys,或者直接登陸
https://ak-console.aliyun.com,擷取阿裡雲的AccessKeyID和AccessKeySecret。
-
路由器設定
阿裡雲的設定完成後,需要對路由器設定端口映射,使外網對公網IP的端口通路能轉發到内網伺服器的相應端口。絕大部分的路由器都支援端口映射。
常見的服務端口包括,用于WEB通路的80端口、SSH遠端管理的22端口、Mysql資料庫的3306端口、Transmission下載下傳服務管理的9091端口和Plex媒體服務的32400端口等等。不用花生殼的好處就是沒有端口數量限制,想設定多少就可以設定多少。
當然,在設定端口映射之前,應確定伺服器的内網IP已經設定為靜态IP,而不是DHCP動态擷取。
-
伺服器設定
伺服器端安裝好想要使用的各種服務後,别忘了在防火牆中開啟相應的端口,在CentOS 7中,防火牆永久開啟端口的指令是:
> firewall-cmd --add-port=80/tcp --permanent
開啟之後别忘了重新載入防火牆的設定以使其生效,指令如下:
> firewall-cmd --reload
-
自動更新域名解析程式
準備工作都做好了,接下來就是通過程式檢測公網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:
# 從阿裡雲開發者背景擷取Access_Key_Id和Access_Key_Secret
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
-
設定定時運作
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服務。
參考文獻
版權
版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享3.0許可證)
原創作者 [email protected] 發表于阿裡雲·雲栖社群:
https://yq.aliyun.com/users/y4epujtm5wye6掃碼關注我,線上與我溝通、咨詢
轉載請保留原文連結以及版權資訊