confd模板语法详解

目的

本文详细介绍了confd模板的语法和结构。并且在结尾给出示例演示,帮助你充分理解好消化。

模板源

模板源以TOML编写并已 .toml 作为后缀的来定义的。默认情况下,模板源存储在/etc/confd/conf.d 目录下。

必要参数

  • dest (字符串) - 目标文件。
  • keys (字符串数组) - 键数组。
  • src (字符串) - 配置模板的相对路径 。

可选参数

  • gid (int) - 应该拥有该文件的gid。默认为有效的gid。
  • mode (字符串) - 文件的权限模式。
  • uid (int) - 应该拥有该文件的uid。默认为有效的uid。
  • reload_cmd (字符串) - 重新加载配置的命令。
  • check_cmd (字符串)- 检查配置的命令。
  • prefix (字符串) - 键前缀的字符串。

注意点

使用该 reload_cmd 功能时,命令自行退出非常重要。reload命令不由confd管理,并将阻止配置运行直到它退出。

例子

1
2
3
4
5
6
7
8
9
10
11
[template]
src = "nginx.conf.tmpl"
dest = "/etc/nginx/nginx.conf"
uid = 0
gid = 0
mode = "0644"
keys = [
"/nginx",
]
check_cmd = "/usr/sbin/nginx -t -c {{.src}}"
reload_cmd = "/usr/sbin/service nginx restart"

模板定义单个应用程序配置模板。默认情况下,模板存储在/etc/confd/templates 目录下。

模板是用Go编写的 模板格式

模板函数

map

创建接口和字符串的键值映射

1
2
3
4
5
{{$endpoint := map "name" "elasticsearch" "private_port" 9200 "public_port" 443}}

name: {{index $endpoint "name"}}
private-port: {{index $endpoint "private_port"}}
public-port: {{index $endpoint "public_port"}}

如果您是子模板并且想要向其传递多个值,则特别有用。

base

path.Base函数的别名 。

1
2
3
4
{{with get "/key"}}
key: {{base .Key}}
value: {{.Value}}
{{end}}

exists

判断键是否存在。如果找不到键,则返回false。

1
2
3
{{if exists "/key"}}
value: {{getv "/key"}}
{{end}}

get

返回键与其键匹配的键值对。如果未找到键,则返回错误。

1
2
3
4
{{with get "/key"}}
key: {{.Key}}
value: {{.Value}}
{{end}}

gets

返回与其key匹配所有键值对,如果未找到键,则返回错误。

1
2
3
4
{{range gets "/*"}}
key: {{.Key}}
value: {{.Value}}
{{end}}

getv

返回与其键或可选的默认值匹配的字符串,如果未找到键且未给出默认值,则返回错误。

1
value: {{getv "/key"}}

getv默认值

1
value: {{getv "/key" "default_value"}}

getvs

返回与其键匹配所有值的字符串,如果未找到密钥,则返回错误。

1
2
3
{{range getvs "/*"}}
value: {{.}}
{{end}}

getenv

返回在os.Getenv 中检索由键命名的环境变量的值。如果变量不存在,该值将为空。(可选)您可以提供一个默认值,如果该键不存在,将返回该值。

1
2
export HOSTNAME=`hostname`
hostname: {{getenv "HOSTNAME"}}

getenv默认值

1
ipaddr: {{getenv "HOST_IP" "127.0.0.1"}}

datetime

time.Now的别名

1
2
3
Generated by confd {{datetime}}
输出:
Generated by confd 2015-01-23 13:34:56.093250283 -0800 PST
1
2
3
Generated by confd {{datetime.Format "Jan 2, 2006 at 3:04pm (MST)"}}
输出:
Generated by confd Jan 23, 2015 at 1:34pm (EST)

更多用法,请参阅官方时间用法

split

包装器 strings.Split。分隔输入的字符串并返回一个子字符串切片。

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

toUpper

strings.ToUpper的 别名 返回大写字符串。

1
key: {{toUpper "value"}}

toLower

strings.ToLower的 别名 。返回小写字符串。

1
key: {{toLower "Value"}}

json

返回map[string]interface{}形式的json值。

lookupSRV

net.LookupSRV 包装器 。通过组合net.SRV结构的所有字段按字母顺序对SRV记录进行排序,以减少不必要的配置重新加载。

1
2
3
4
5
6
{{range lookupSRV "mail" "tcp" "example.com"}}
target: {{.Target}}
port: {{.Port}}
priority: {{.Priority}}
weight: {{.Weight}}
{{end}}

etcd添加键值

1
2
etcdctl set /services/zookeeper/host1 '{"Id":"host1", "IP":"192.168.10.11"}'
etcdctl set /services/zookeeper/host2 '{"Id":"host2", "IP":"192.168.10.12"}'

创建模板源

1
2
3
4
5
6
[template]
src = "services.conf.tmpl"
dest = "/tmp/services.conf"
keys = [
"/services/zookeeper/"
]

创建模板

1
2
3
4
5
{{range gets "/services/zookeeper/*"}}
{{$data := json .Value}}
id: {{$data.Id}}
ip: {{$data.IP}}
{{end}}

map遍历

一旦解析了JSON,就可以使用普通的Go模板函数遍历它 index

更高级的结构,如下所示:

1
2
3
4
5
6
{
"animals": [
{"type": "dog", "name": "Fido"},
{"type": "cat", "name": "Misse"}
]
}

它可以像这样遍历:

1
2
3
4
5
6
{{$data := json (getv "/test/data/")}}
type: {{ (index $data.animals 1).type }}
name: {{ (index $data.animals 1).name }}
{{range $data.animals}}
{{.name}}
{{end}}

jsonArray

从接口返回json数组,例如: [“a”, “b”, “c”]`。

1
2
3
{{range jsonArray (getv "/services/data/")}}
val: {{.}}
{{end}}

ls

返回匹配路径的所有子键,字符串等。如果找不到路径,则返回空列表。

1
2
3
{{range ls "/deis/services"}}
value: {{.}}
{{end}}

lsdir

返回匹配路径的所有子键,字符串等。注意它只返回也有子键的子键。如果找不到路径,则返回空列表。

1
2
3
{{range lsdir "/deis/services"}}
value: {{.}}
{{end}}

dir

返回制定键的父目录。

1
2
3
{{with dir "/services/data/url"}}
dir: {{.}}
{{end}}

join

strings.Join 函数的别名 。

1
2
{{$services := getvs "/services/elasticsearch/*"}}
services: {{join $services ","}}

replace

strings.place 函数的别名 。

1
2
{{$backend := getv "/services/backend/nginx"}}
backend = {{replace $backend "-" "_" -1}}

lookupIP

net.LookupIP 函数的包装器 。包装器还按字母顺序排序IP地址。这一点至关重要,因为在动态环境中,DNS服务器通常会混淆链接到域名的地址。这将导致不必要的配置重新加载。

1
2
3
{{range lookupIP "some.host.local"}}
server {{.}};
{{end}}

用法

简单实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
etcdctl set /nginx/domain 'example.com'
etcdctl set /nginx/root '/var/www/example_dotcom'
etcdctl set /nginx/worker_processes '2'
etcdctl set /app/upstream/app1 "10.0.1.100:80"
etcdctl set /app/upstream/app2 "10.0.1.101:80"

cat /etc/confd/templates/nginx.conf.tmpl

worker_processes {{getv "/nginx/worker_processes"}};

upstream app {
{{range getvs "/app/upstream/*"}}
server {{.}};
{{end}}
}

server {
listen 80;
server_name www.{{getv "/nginx/domain"}};
access_log /var/log/nginx/{{getv "/nginx/domain"}}.access.log;
error_log /var/log/nginx/{{getv "/nginx/domain"}}.log;

location / {
root {{getv "/nginx/root"}};
index index.html index.htm;
proxy_pass http://app;
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;
}
}

输出: /etc/nginx/nginx.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
worker_processes 2;

upstream app {
server 10.0.1.100:80;
server 10.0.1.101:80;
}

server {
listen 80;
server_name www.example.com;
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;

location / {
root /var/www/example_dotcom;
index index.html index.htm;
proxy_pass http://app;
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;
}
}

复杂的例子

此示例显示如何使用模板函数的组合来执行嵌套迭代。

到etcd添加键

1
2
3
4
5
6
etcdctl mkdir /services/web/cust1/
etcdctl mkdir /services/web/cust2/
etcdctl set /services/web/cust1/2 '{"IP": "10.0.0.2"}'
etcdctl set /services/web/cust2/2 '{"IP": "10.0.0.4"}'
etcdctl set /services/web/cust2/1 '{"IP": "10.0.0.3"}'
etcdctl set /services/web/cust1/1 '{"IP": "10.0.0.1"}'

创建模板源

1
2
3
4
5
6
[template]
src = "services.conf.tmpl"
dest = "/tmp/services.conf"
keys = [
"/services/web"
]

创建模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{{range $dir := lsdir "/services/web"}}
upstream {{base $dir}} {
{{$custdir := printf "/services/web/%s/*" $dir}}{{range gets $custdir}}
server {{$data := json .Value}}{{$data.IP}}:80;
{{end}}
}

server {
server_name {{base $dir}}.example.com;
location / {
proxy_pass {{base $dir}};
}
}
{{end}}

输出:/tmp/services.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
upstream cust1 {
server 10.0.0.1:80;
server 10.0.0.2:80;
}

server {
server_name cust1.example.com;
location / {
proxy_pass cust1;
}
}

upstream cust2 {
server 10.0.0.3:80;
server 10.0.0.4:80;
}

server {
server_name cust2.example.com;
location / {
proxy_pass cust2;
}
}
-------------本文结束感谢您的阅读-------------

本文标题:confd模板语法详解

文章作者:豌豆多多

发布时间:2019年06月26日 - 10:06

最后更新:2020年08月04日 - 14:08

原始链接:https://wandouduoduo.github.io/articles/9d4187fa.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

原创技术分享,您的支持将鼓励我继续创作