雲計算

利用阿里雲 OpenAPI 以及 DNS 雲解析自建 DDNS 動態域名解析服務

概述

家裡閒置著一臺老款的Mac mini Server,跑OS X越來越慢,索性裝上了Cent OS 7,變成了一臺家庭服務器,裝上了Plex媒體服務器和Transmission下載服務,同時,也裝上了Nginx、Mysql、MongoDB、Redis等,可以調試代碼,甚至擔當一些小型項目的服務器。
不過,只在家庭內網使用,功能太有限,於是接下來面臨的一個問題就是內網穿透。使用過花生殼和花生棒,服務相當不穩定,而且種種受限,每要多加一個端口就要多花錢,安全性也有問題。
其實想想,內網穿透的最大難題無非就是家裡是動態公網IP,每變換一次公網IP,需要重新解析一次域名。而阿里雲等大型的雲服務商,目前都已經實現了域名解析管理的API接口,而且基本都是立即就可生效 。
所以我的思路就是,系統運行一個定期執行的程序,每隔一段時間掃描一下最新的公網IP,如果發現最新的公網IP與域名解析到的IP地址不一致,就通過阿里雲API自動更新解析設置即可。這樣的花費不過每年一個域名的費用,最貴也就幾十塊錢。

具體實現步驟

  1. 阿里雲設置
    首先,要確定一個準備用於外網訪問的域名,並將此域名轉入到阿里雲的雲解析服務來解析。如圖所示,添加需要管理的域名。

阿里云云解析服務

轉入後,在解析設置中,設置一下A記錄解析,解析的IP地址可以填當前的公網IP。如果不知道自己的公網IP,在CentOS系統下,可以輸入使用以下命令獲取當前的公網IP。

> curl ifconfig.me

獲取公網IP後,在阿里云云解析中設置完A記錄解析。
設置A記錄解析

在阿里雲賬戶管理後臺,點擊右上角的賬戶頭像,然後點擊accesskeys,或者直接登陸 https://ak-console.aliyun.com,獲取阿里雲的AccessKeyID和AccessKeySecret。

  1. 路由器設置
    阿里雲的設置完成後,需要對路由器設置端口映射,使外網對公網IP的端口訪問能轉發到內網服務器的相應端口。絕大部分的路由器都支持端口映射。

常見的服務端口包括,用於WEB訪問的80端口、SSH遠程管理的22端口、Mysql數據庫的3306端口、Transmission下載服務管理的9091端口和Plex媒體服務的32400端口等等。不用花生殼的好處就是沒有端口數量限制,想設置多少就可以設置多少。
當然,在設置端口映射之前,應確保服務器的內網IP已經設置為靜態IP,而不是DHCP動態獲取。

  1. 服務器設置
    服務器端安裝好想要使用的各種服務後,別忘了在防火牆中開啟相應的端口,在CentOS 7中,防火牆永久開啟端口的命令是:
> firewall-cmd --add-port=80/tcp --permanent

開啟之後別忘了重新載入防火牆的設置以使其生效,命令如下:

> firewall-cmd --reload
  1. 自動更新域名解析程序
    準備工作都做好了,接下來就是通過程序檢測公網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
  1. 設置定時運行
    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

掃碼關注我,在線與我溝通、諮詢
image

轉載請保留原文鏈接以及版權信息

Leave a Reply

Your email address will not be published. Required fields are marked *