Fork me on GitHub

背景介绍

把网站托管在树莓派上后如果家里停电或是宽带故障,会造成网站中断。本文提供一个免费的解决方案(前提是你需要有自己的一个域名,并由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

参考链接

  1. http://hugozhu.myalert.info/2013/02/27/在Pi和Github上搭建自己的个人博客.html
  2. https://github.com/hugozhu/gae-rpi-webapp


blog comments powered by Disqus

Published

2013-04-22

Categories


Tags