背景

在家搭建了一个服务器,如果需要从外网访问就需要一个IP地址,而我用的是电信网络,IP地址是随时会变的,在应用中无法使用。

这时候就需要一个网址和一个动态DNS来解决这个问题,应用中使用网址,然后DNS只要更新到最新的IP地址就可以正常访问了。

本来花生壳可以完美的解决这个问题,但是他们的服务太差所以不想用,还好现在市面上的云服务器平台基本都有DNS服务而且有完整的API可以调用,这样就能程序化的修改DNS地址了。

下面的介绍是以阿里云的DNS为例(下面的代码中也有DNSPOD的解决方案)。

思路

其实整个思路很简单,

  1. 获取当前的IP,如果当前IP和记录的IP不一致,就执行更新程序
  2. 使用记录列表接口获取所有的A记录(只有A记录才有自动更新的必要),另外,为了区分不需要更新的列表,对于阿里云需要把记录的备注设置成指定值,这个要放在配置文件中,详情看下文
  3. 把所有符合要求的记录用修改记录接口更新掉
  4. 把这个IP记下来以便下次使用

因为所有的接口基本都需要提供登录凭证,所以肯定需要一个私有配置来记录。因此,这里把上面这4个步骤写成一个库,然后配置传入的过程和IP记录的位置就自己动动手吧,这样也比较灵活。

最后再把它加入到定时任务每分钟执行一次就好了。

获取当前IP

因为我们的IP会变动,但是一天内变动的频率其实不大,一般一个V4地址的租期都是1天左右,所以没有必要每次都去调用阿里云的接口。

所以只需要每次查询一次IP,然后和上一次修改的IP对比,如果不一致的情况下再去做下一步的操作。

网上有很多能查到当前IP的接口,要求就是越简单越好,我用的是ip-api.com/json,没有什么限制,而且可以直接返回json格式,这对PHP来说是最好的格式了。

API接口调通

首先找到API的文档,阿里云的在这里

主要用到的是解析管理接口里面的获取解析记录列表接口修改解析记录接口

阿里云提供了PHP的SDK,但是很多不需要的东西,如果就为了这2个接口就要把那一整个SDK下载下来实在是有点大材小用,所以决定自己做。

阿里云的接口其余部分都很简单,唯独签名机制部分十分复杂,这里吐槽一下阿里云的签名机制,真的是又臭又长,又没有什么意义,并不会因为这么复杂而增加安全性,而且文档看起来很详细,实则很烂,完全找不到重点。

实际代码

完成以后的库放在了Github上。

然后主要的过程在这里用代码列出,首先把这个库加入到composer里面,由于没有放在package里面,所以要用Git的方式取出,需要在composer.json里面加入这个,

1
2
3
4
5
6
7
8
9
"require": {
"orzorc/update-dnspod-a-record": "dev-master"
},
"repositories": [
{
"type": "git",
"url": "https://github.com/ahcheqiu/update-dnspod-a-record.git"
}
]

然后,写一个配置文件,aliyun.php,这里就需要设置用来区分不需要更新的记录的备注,

1
2
3
4
5
6
7
8
9
10
11
return [
// aliyun控制台申请的access key和secret
'accessKey' => '',
'accessSecret' => '',
// 需要更新的domain
'domain' => 'orzorc.com',
// 更新的记录需要在备注里设置成
'remark' => 'ddns',
'endPoint' => 'https://alidns.aliyuncs.com/',
'currentIPFile' => __DIR__ . DIRECTORY_SEPARATOR . 'current.ip'
];

最后,添加一个入口文件,update.php,

1
2
3
4
5
6
7
$aliyun = new \OrzOrc\DDnsUpdate\Aliyun(__DIR__);
try {
$count = $aliyun->runUpdate();
echo "更新了{$count}条记录\n";
} catch (Exception $e) {
echo $e->getMessage() . "\n";
}

如果需要使用其他平台的,只需要写一个相应的更新类即可,不记录了。

这样就差不多完成了主流程了。

部署

因为我的入口程序放在了Gitlab上,所以我倾向于使用CI的方式将它部署到我的服务器上,如果不需要的话就可以跳过。

由于每分钟都会更新,而且composer update的速度比较慢,所以这里使用修改软链的方式把代码更新上去。

CI文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
deploy:
script:
# 先更新composer
- composer update --no-dev
# 移除上上个版本的
- rm -rf $APP_FOLDER/previous/
# 复制一份上个版本的
- mkdir -p $APP_FOLDER/new
- cp -r $APP_FOLDER/new $APP_FOLDER/previous
# 把latest指向previous
- rm -f $APP_FOLDER/latest
- ln -s $APP_FOLDER/previous $APP_FOLDER/latest
- rm -rf $APP_FOLDER/new/
# 把这个版本的移动过来
- mkdir -p $APP_FOLDER/new
- cp -rf ./* $APP_FOLDER/new/
# 把latest指向这个版本
- rm -f $APP_FOLDER/latest
- ln -s $APP_FOLDER/new $APP_FOLDER/latest
# 给记录IP的文件添加所有人可写的权限
- chmod a+w $APP_FOLDER/latest/current.ip
only:
- master

这样可以随时保持2个版本的东西,随时回退,不过好像其实没有什么必要,_(°ω°」∠)_

定时任务

把输出定向到一个日志文件就好,方便看为什么出问题了。

1
php -f $APP_FOLDER/latest/update.php >> /var/log/updateAliyun.log

总结

这样就可以使用固定的网址访问相关的服务了,节省了一大笔的服务器的支出,重要的是除了无法使用80端口,还不会被要求备案。