背景介绍
把网站托管在树莓派上后如果家里停电或是宽带故障,会造成网站中断。本文提供一个免费的解决方案(前提是你需要有自己的一个域名,并由DNSPod解析)
DNSPod
首先需要在DNSPod里设置好需要failover的域名CNAME:比如hugozhu.myalert.info
其中默认
指向pi.myalert.info
, 这是一个域名的A Record,会由运行在树莓派上的脚本来更新动态IP,国外
则指向github。当停电时我们需要自动把`默认`这条纪录修改成github。
使用下面命令获得相应CNAME的domain_id:
curl -k https://dnsapi.cn/Domain.List -d "login_email=xxx&login_password=xxx"
使用下面命令获得相应CNAME的record_id:
curl -k https://dnsapi.cn/Record.List -d "login_email=xxx&login_password=xxx&domain_id=xxx"
Google App Engine
切换DNS脚本
package dnspod
import (
"io/ioutil"
"net/http"
"net/url"
"strings"
)
const (
login_email = "<your_login_email>"
login_password = "<your_login_password>"
format = "json"
domain_id = "<domain_id>"
record_id = "<record_id>"
sub_domain = "<your_subdomain>"
record_type = "CNAME"
record_line = "默认"
ttl = "600"
)
func Update(client *http.Client, cname string) string {
body := url.Values{
"login_email": {login_email},
"login_password": {login_password},
"format": {format},
"domain_id": {domain_id},
"record_id": {record_id},
"sub_domain": {sub_domain},
"record_type": {record_type},
"record_line": {record_line},
"value": {cname},
"ttl": {ttl},
}
req, err := http.NewRequest("POST", "https://dnsapi.cn/Record.Modify", strings.NewReader(body.Encode()))
req.Header.Set("Accept", "text/json")
req.Header.Set("Content-type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
return err.Error()
}
defer resp.Body.Close()
bytes, _ := ioutil.ReadAll(resp.Body)
return string(bytes)
}
检测接口
部署一个web应用到Google App Engine上,该应用接受树莓派上的一个URL(注意这里不应该用需failver的域名),并请求该域名以检查网站是否正常。这里也可以使用监控宝来监控,但只有付费专业版才支持出错后回调URL。
func ping(w http.ResponseWriter, r *http.Request) {
url := r.FormValue("url")
c := appengine.NewContext(r)
client := urlfetch.Client(c)
ok, body := url_ok(client, url)
item, _ := memcache.Get(c, "site_fails")
if ok {
if item != nil {
//switch back to pi
dnspod.Update(client, "pi.myalert.info.")
memcache.Delete(c, "site_fails")
}
} else {
if item != nil {
//previously failed, switch to github
dnspod.Update(client, "hugozhu.github.com.")
value, _ := strconv.Atoi(string(item.Value))
value++
item.Value = []byte(strconv.Itoa(value))
memcache.Set(c, item)
} else {
//first time failed
item = &memcache.Item{
Key: "site_fails",
Value: []byte("0"),
}
memcache.Set(c, item)
}
}
w.Header().Set("Content-type", "text/plain")
fmt.Fprintf(w, "%s", body)
}
func url_ok(client *http.Client, url string) (bool, string) {
resp, err := client.Get(url)
ok := false
body := ""
if err != nil {
body = err.Error()
} else {
bytes, _ := ioutil.ReadAll(resp.Body)
body = string(bytes)
resp.Body.Close()
ok = resp.StatusCode == 200
}
return ok, body
}
定时任务
在gae项目里增加一个文件:cron.yaml,内容如下,这样gae会自动每隔15分钟检查一次,如果连续发现两次不可用,就会切换到Github,恢复后则会切换回来。
cron:
- description: check pi if it's still alive
url: /ping
schedule: every 15 minutes
参考链接
- http://hugozhu.myalert.info/2013/02/27/在Pi和Github上搭建自己的个人博客.html
- https://github.com/hugozhu/gae-rpi-webapp