confd笔记

好记性不如烂笔头。

昨天完成了项目的 etcd + confd + openresty 的反代部分,在这里记录一下。这东西学起来还是有点时间的,文档也说的不明白,没个example可以看。

etcd 部分

单点etcd启动:

docker volume create etcd_data
docker run -dti --network host \
	-v etcd_data:/default.etcd \
	-e ETCD_LISTEN_PEER_URLS=http://10.170.1.31:2380 \
	-e ETCD_LISTEN_CLIENT_URLS=http://10.170.1.31:2379 \
	-e ETCD_ADVERTISE_CLIENT_URLS=http://10.170.1.31:2379 \
	quay.io/coreos/etcd:latest

然后这个 quay.io/coreos/etcd:latest 镜像隔着我国比较远,所以不如用我的镜像 wrfly/etcd pull 的时候会快一点。 (2017-3-12 version: latest)

confd 配置部分

confd启动的时候需要一些配置:

backend = "etcd"
confdir = "/etc/confd"
log-level = "info"
watch = true
noop = false
prefix = "/"
scheme = "http"
node = [
	"1.2.3.4:2379",
	"4.3.2.1:2379",
	"1.4.2.3:2379",
]

node 部分是 etcd 的地址,如果你的 backend 不是 etcd 的话,那就是你用的数据库的地址。具体情况具体分析。

启动 confd 的时候可以指定一些参数,比起 etcd,有一点不好,就是不能通过读环境变量的方式来传入,所以要通过命令行传递:

全部说明: Documents

有几个参数说明一下(针对etcd):

  • watch = true : 这里是通过 watch 来检测 etcd 中数据变化的,就跟通过 curl http://etcd:2379/v2/keys/xxx/xxx?watch=true 一样。这样就不用通过时间间隔来抓数据了,所以通过watch配置的文件都是 real-time 的。

  • -basic-auth-password 讲真这两个参数我没用过,但是以后一定要用的。 所以在这里 TODO 一下。

confd 配置模板

[template]
src = "nginx.http.tmpl"
dest = "/etc/nginx/conf.d/http.conf"
keys = [
    "/",
]
check_cmd = "nginx -t -c /etc/nginx/nginx.conf"
reload_cmd = "nginx -s reload"

来看一下这个配置模板文件,src 指的是模板文件的地址,就是依据那个模板生成我们的配置文件;dest则就是生成的配置文件存放的位置了。

keys 是 keys (array of strings) - An array of keys. 我也不知道具体能起什么效果,可能模板中需要?

全部文档 -> link

模板语法

展示一下最终成果:

```bash
{{range $challengeID := lsdir "/challenges"}}{{ $challengeRoot := printf "/challenges/%s" $challengeID }}{{ $urlKey := printf "%s/url" $challengeRoot }}{{ $servicesRoot := printf "%s/services" $challengeRoot }}
{{range $service := lsdir $servicesRoot}}{{ $pubK := printf "%s/%s/pub" $servicesRoot $service }}{{ $tgtK := printf "%s/%s/tgt" $servicesRoot $service }}{{$pubV := getv $pubK}}{{$tgtV := getv $tgtK}}{{if ne $pubV "0" }}
upstream {{base $challengeID}}_{{base $service}} { {{range getvs "/nodes/*"}}
    server {{.}}:{{$pubV}};{{end}}
}
server {
    server_name {{getv $urlKey}}{{getenv "FC_DOMAIN"}};
    listen {{$tgtV}};
    location / {
        proxy_pass       {{base $challengeID}}_{{base $service}};
        proxy_redirect    off;
        proxy_set_header  Host             $host;
        proxy_set_header  X-Real-IP        $remote_addr;
        proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;
    }
}
{{end}}{{end}}{{end}}
```

然后 pretty print 一下:

{{range $challengeID := lsdir "/challenges"}}
    {{ $challengeRoot := printf "/challenges/%s" $challengeID }}
    {{ $urlKey := printf "%s/url" $challengeRoot }}
    {{ $servicesRoot := printf "%s/services" $challengeRoot }}

    {{range $service := lsdir $servicesRoot}}
        {{ $pubK := printf "%s/%s/pub" $servicesRoot $service }}
        {{ $tgtK := printf "%s/%s/tgt" $servicesRoot $service }}
        {{ $pubV := getv $pubK }}
        {{ $tgtV := getv $tgtK }}
        
        {{if ne $pubV "0" }}
            upstream {{ base $challengeID }}_{{ base $service }} { 
                {{range getvs "/nodes/*"}}
                server {{.}}:{{$pubV}};
                {{end}}
            }
            server {
                server_name {{getv $urlKey}}{{getenv "DOMAIN"}};
                listen {{$tgtV}};
                location / {
                    proxy_pass  {{base $challengeID}}_{{base $service}};
                }
            }
        {{end}}
    {{end}}
{{end}}

然后说明一下 etcd 中的键值结构:

/challenges/xx-xx-xx/url = www.baidu.com

/challenges/xx-xx-xx/service/nginx
/challenges/xx-xx-xx/service/nginx/pub = 30000
/challenges/xx-xx-xx/service/nginx/tgt = 80

/challenges/xx-xx-xx/service/ubuntu
/challenges/xx-xx-xx/service/ubuntu/pub = 0
/challenges/xx-xx-xx/service/ubuntu/tgt = 0

/nodes/
/nodes/a = 192.168.1.101
/nodes/b = 192.168.1.102

然后说一下语法,看完语法之后,再回头看就会明白多了。

首先是 range, 以 {{ range $var }} 开头, 以 {{end}} 结尾,最近相邻配对。其中里面的 $var 可以是 ls 或者 lsdir 一个可以list的对象,或者 $var 就是一个 list,类似于 golang 里面的for i,v := range variable

然后在 range 和 end 中间,我们就可以搞点事情了。

首先是赋值,注意到上面的 printf 了吗,printf 接受两个参数,第一个是格式,第二个是变量,然后用 := 赋值给一个变量。

还忘记说上面的 lsdir: lsdir 会返回这个 dir 下面所有的带有 subdir的 dir 的 name。而 ls 则是返回相反的东西,即返回 dir 下面所有 key 的 value。

然后就是 getv, getv 接受的参数是一个 key, 也就是上面 /challenge/xxx-xxx-xxx/url 这样的键,然后返回键的值。

与之对应的是 getvs, 顾名思义,其返回 匹配的 key 的全部值,如果没有值用来返回,则会报错。

还有 getgets,与上面的区别是, get 会同时返回 Key 和 Value, 而getv则只是返回了 Key 的Value。gets 同样。

接着是 if 这个东西,它也是需要以 {{end}} 来表示结尾的。中间是判断一个 bool 值,比如上面的 ne $pubV "0"。 其中 ne 的意思是不相等,同样的还有 eq, gt, lt 等等等等。具体看这里:HERE

然后是getenv,这个就是读取一个环境变量。

其他

举例: {{ $upstream := replace (base $challengeID) "-" "" -1 }}

这行定义了一个 $upstream 的变量,它将 {{base $challengeID}} 的结果作为 replace 的第一个参数(因为外面已经有{{ }}了,所以里面要用括号),然后replace接收的第二个参数是要替换的字符,第三个参数是替换为的字符,第四个参数则是替换的数量,-1代表全部替换。

还有这个 split 也是对字符进行操作的:

{{ $url := split (getv "/deis/service") ":" }}
    host: {{index $url 0}}
    port: {{index $url 1}}

(OK,中间被其他事情打断了,导致想不起要说什么了,就这样吧。)

官方文档 -> link

comments powered by Disqus