昨天打电话跟电信申请了公网ip,竟然真的成功了,过程也很顺利。拿到的公网ip一般是动态的,所以需要配置DDNS(动态域名解析),将动态的ip映射成域名,外网访问时只需通过输入域名,而无需关心ip是否变动。可以看出,DDNS需要设备在内网处理(来获取出网口ip),并且需要一个域名。
尽管我有域名和树莓派可以实现独立DDNS,第一选择还是采用路由器自带的花生壳域名服务,因为便捷,很可惜没有成功,转而采用自己实现的方法。域名沿用之前购买的腾讯云服务的DNSPOD,跟AI交流了一下,整理了以下这段python代码,在树莓派上运行,即可实现DDNS。
Tip:有了公网 IP 搭配 DDNS,就能在外网随时随地访问家里的各类服务。比如在外远程登录家里的设备做运维,访问 NAS 调取家里存的照片、电影与文档,跑个人博客、私人笔记服务、下载机、智能家居控制后台,在外操控 Home‑Assistant 管理全屋 IoT 设备,搭建私人网盘替代公共云盘,还可以做远程监控、内网穿透备选、自建游戏服务器。以前实现我的Home‑Assistant公网访问需要依赖公网的服务器和Frp,现在可以更直接简洁了。
具体步骤为:
- 在账号中心获取腾讯云 API 密钥(SecretID和SecretKey)
- 在云解析DNS控制台添加解析记录,ip可以先随便填一个,比如8.8.8.8。
- 在树莓派执行命令
pip install tencentcloud-sdk-python - 新建python文件,粘贴并运行以下代码。
from tencentcloud.common import credential
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.profile.http_profile import HttpProfile
from tencentcloud.dnspod.v20210323 import dnspod_client, models
import requests
from datetime import datetime
SECRET_ID = "AKID***********"
SECRET_KEY = "***************"
DOMAIN = "colorfulife.site" #你的域名
SUBDOMAIN = "anyword" #与前文设置的解析记录对应
RECORD_TYPE = "A"
RECORD_LINE = "默认"
def log(msg):
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {msg}")
def get_public_ip():
"""获取本机公网IPv4,多接口容错"""
urls = [
"https://api.ipify.org",
"https://ipv4.icanhazip.com",
"https://ifconfig.me/ip"
]
for url in urls:
try:
ip = requests.get(url, timeout=5).text.strip()
if ip.count(".") == 3:
return ip
except Exception:
continue
raise Exception("无法获取公网IP")
def main():
cred = credential.Credential(SECRET_ID, SECRET_KEY)
httpProfile = HttpProfile()
httpProfile.endpoint = "dnspod.tencentcloudapi.com"
clientProfile = ClientProfile()
clientProfile.httpProfile = httpProfile
client = dnspod_client.DnspodClient(cred, "", clientProfile)
# 1 获取公网IP
try:
new_ip = get_public_ip()
except Exception as e:
log(f"获取公网IP失败: {e}")
return
log(f"检测公网IP: {new_ip}")
# 2 查询DNS解析记录
try:
req = models.DescribeRecordListRequest()
req.Domain = DOMAIN
resp = client.DescribeRecordList(req)
except Exception as e:
log(f"查询DNS记录失败: {e}")
return
record = None
for r in resp.RecordList:
if r.Name == SUBDOMAIN and r.Type == RECORD_TYPE:
record = r
break
if record is None:
log(f"未找到解析记录 {SUBDOMAIN}.{DOMAIN} {RECORD_TYPE}")
return
log(f"DNS当前记录IP: {record.Value}")
# 3 IP相同直接退出,不同才执行修改
if record.Value == new_ip:
log("IP未发生变化,无需更新")
return
log(f"IP不一致,准备更新为 {new_ip}")
# 4 修改解析记录
try:
req = models.ModifyRecordRequest()
req.Domain = DOMAIN
req.RecordId = record.RecordId
req.SubDomain = SUBDOMAIN
req.RecordType = RECORD_TYPE
req.RecordLine = RECORD_LINE
req.Value = new_ip
modify_resp = client.ModifyRecord(req)
log("DNS记录更新成功")
log(modify_resp.to_json_string())
except Exception as e:
log(f"更新DNS记录失败: {e}")
if __name__ == "__main__":
main()
- 最后设置定时执行 crontab -e 每五分钟执行一次
*/5 * * * * /usr/bin/python3 /home/pi/ddns_tencent_final.py >> /home/pi/ddns.log 2>&1