Etcd
是一个分布式、高可用的Key/Value
存储系统,主要用于分享配置与服务发现。
Confd
是一个轻量级的配置管理工具。通过查询Etcd,结合配置模板引擎,保持本地配置最新,同时具备定期探测机制,配置变更自动reload。对应的后端存储可以是etcd,redis、zookeeper等。
以nginx
为例,当docker容器健康运行起来后,会通过接口向etcd
注册相关k/v信息,confd
检测到etcd
的k/v变化后,立即触发程序通过模板形成新的nginx
配置文件,先做离线语法测试,如果没问题就覆盖原配置,进而reload,测试不通过就不覆盖原配置,整个过程是安全可控的。在容器注册到nginx
的upstream后,nginx
会对容器做健康检查发现,如果正常,则分发流量过去。对应的业务流程图如下:
# docker-compose.yml
version: '3'
services:
etcd:
image: quay.io/coreos/etcd:v3.2.20
container_name: etcd
command: /usr/local/bin/etcd
restart: always
networks:
- etcdtest
ports:
# etcd服务启动后提供给外部客户端通信的端口是2379
- "2379:2379"
# etcd服务中成员间的通信端口是2380
- "2380:2380"
environment:
ETCDCTL_API: 2
ETCD_LOGGER: capnslog
# etcd数据的存储目录
ETCD_DATA_DIR: /etcd-data
ETCD_NAME: node1
# 通知集群中其他成员本节点的peer urls,一定要保证从其他member能可访问该地址。
ETCD_INITIAL_ADVERTISE_PEER_URLS: http://0.0.0.0:2380
# 监听的用于节点之间通信的url,可建通多个,集群内部将通过这些url进行数据交互(选举、数据同步),用于监听其他member发送信息的地址
ETCD_LISTEN_PEER_URLS: http://0.0.0.0:2380
# 监听的用于客户端通信的的urls,可以是多个,用于监听etcd客户端发送信息的地址
ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379
# 通知集群中其他成员本节点的client url,一定要保证从客户侧能可访问该地址。
ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379
# #广播给集群内其他成员访问的URL
ETCD_INITIAL_CLUSTER: node1=http://0.0.0.0:2380
networks:
etcdtest:
external: true
etcdctl
是一个命令行客户端,它能提供一些简洁的命令,供用户直接跟etcd服务打交道,而无需基于 HTTP API 方式。
PUT [options]
PUT使用指定的键赋值。如果key已经保存了一个值,它将被覆盖。
RPC: Put
Option
Output
ok
Examples
# 写入一对key-value: /foo-Hello World
etcdctl --endpoints=http://127.0.0.1:2379 put /foo "Hello World"
# OK
GET [options] [range_end]
GET如果给出了range_end,则GET获取键或键范围
RPC: Range
Option
Output
\n\n
Examples
# 查询key为foo的value
etcdctl --endpoints=http://127.0.0.1:2379 get /foo
# /foo
# Hello World
# 查询所有的key
etcdctl --endpoints=http://127.0.0.1:2379 get --from-key ''
# foo
# /testkey
DEL [options] [range_end]
如果给定范围结束,则删除指定的键或键[键,范围结束)范围。
RPC: DeleteRange
Option
Output
如果DEL成功,则打印以十进制删除的键的数目。
Examples
# 删除key为foo的键值对
etcdctl --endpoints=http://127.0.0.1:2379 del foo
# 1
Confd动态更新配置文件架构:
server端调用config-server对应客户端获取配置,和监听配置变更。
Confd的作用流程:
下载confd
的二进制文件,下载地址为:https://github.com/kelseyhightower/confd/releases。
# 下载二进制文件
wget https://github.com/kelseyhightower/confd/releases/download/v0.16.0/confd-0.16.0-linux-amd64
# 重命名二进制文件,并移动到PATH的目录下
mv confd-0.16.0-linux-amd64 /usr/local/bin/confd
# 赋予执行权限
chmod +x /usr/local/bin/confd
# 验证是否安装成功
confd -version
Confd
通过读取后端存储的配置信息来动态更新对应的配置文件,对应的后端存储可以是etcd
,redis
等,其中etcd的v3版本对应的存储后端为etcdv3
。
创建confdir
confdir
是存储模板资源配置和源模板的地方,包含两个目录:
conf.d
:confd的配置文件,主要包含配置的生成逻辑,例如模板源,后端存储对应的keys,命令执行等。templates
:配置模板Template,即基于不同组件的配置,修改为符合 Golang text templates的模板文件。sudo mkdir -p /etc/confd/{conf.d,templates}
插入数据
使用etcd作为confd的后端存储。
etcdctl --endpoints=http://127.0.0.1:2379 put /myapp/database/url db.example.com
etcdctl --endpoints=http://127.0.0.1:2379 put /myapp/database/user rob
创建模板资源配置
模板资源定义在confdir
下的TOML
配置文件中。
必要参数
dest
(string) - 目标文件.keys
(array of strings) - 一组键值对.src
(string) - 配置模板的相对路径.可选参数
gid
(int) - 应该拥有该文件的gid。默认为有效的gid。mode
(string) - 文件的权限模式.uid
(int) - 应该拥有该文件的uid. 默认为有效uid.reload_cmd
(string) - 重新加载配置的命令.check_cmd
(string) - 用于检查配置的命令。 使用{{.src}}
引用渲染的源模板。.prefix
(string) - 作为键前缀的字符串./etc/confd/conf.d/myconfig.toml
[template]
## templates中的模板文件名
src = "myconfig.conf.tmpl"
## 配置文件生成的路径
dest = "/tmp/myconfig.conf"
## 键值对组
keys = [
"/myapp/database/url",
"/myapp/database/user",
]
创建源模板
应符合 Golang text templates的模板文件
/etc/confd/templates/myconfig.conf.tmpl
[myconfig]
database_url = {{getv "/myapp/database/url"}}
database_user = {{getv "/myapp/database/user"}}
生成配置文件
confd支持两种运行方式,即daemon
和onetime
。 在守护程序模式下,confd轮询后端以进行更改,并在必要时更新目标配置文件。
etcd
# confd onetime只生成一次
confd -onetime -backend etcdv3 -node http://127.0.0.1:2379
# confd监听etcd,一旦发现etcd数据变化,则重新生成配置文件
confd -watch -backend etcdv3 -node http://127.0.0.1:2379
# 每隔60秒轮询一次。一旦后端etcd相应的值发生变化就会重新生成相应的配置文件
confd -interval=60 -backend etcdv3 -node http://127.0.0.1:2379
查看生成的配置文件
/tmp/myconfig.conf
[myconfig]
database_url = db.example.com
database_user = rob
在这个例子中,我们将使用etcd作为后端存储,用confd使用一个模板来管理两个nginx配置文件。
etcd存入数据
etcdctl put /myapp/subdomain myapp
etcdctl put /myapp/upstream/app2 "10.0.1.100:80"
etcdctl put /myapp/upstream/app1 "10.0.1.101:80"
etcdctl put /yourapp/subdomain yourapp
etcdctl put /yourapp/upstream/app2 "10.0.1.102:80"
etcdctl put /yourapp/upstream/app1 "10.0.1.103:80"
创建模板资源配置
/etc/confd/conf.d/myapp-nginx.toml
[template]
# 做为键的前缀
prefix = "/myapp"
# templates中的模板文件名
src = "nginx.tmpl"
# 配置文件生成路径
dest = "/tmp/myapp.conf"
# 配置生成配置文件的用户
owner = "nginx"
# 文件的权限模式
mode = "0644"
# 一组键值对
keys = [
"/subdomain",
"/upstream",
]
# 检查nginx配置
check_cmd = "/usr/sbin/nginx -t -c {{.src}}"
# 重新加载配置
reload_cmd = "/usr/sbin/service nginx reload"
/etc/confd/conf.d/yourapp-nginx.toml
[template]
# 做为键的前缀
prefix = "/yourapp"
# templates中的模板文件名
src = "nginx.tmpl"
# 配置文件生成路径
dest = "/tmp/yourapp.conf"
# 配置生成配置文件的用户
owner = "nginx"
# 文件的权限模式
mode = "0644"
# 一组键值对
keys = [
"/subdomain",
"/upstream",
]
# 检查nginx配置
check_cmd = "/usr/sbin/nginx -t -c {{.src}}"
# 重新加载配置
reload_cmd = "/usr/sbin/service nginx reload"
创建源模板
/etc/confd/templates/nginx.tmpl
upstream {{getv "/subdomain"}} {
{{range getvs "/upstream/*"}}
server {{.}};
{{end}}
}
server {
server_name {{getv "/subdomain"}}.example.com;
location / {
proxy_pass http://{{getv "/subdomain"}};
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;
}
}
生成配置文件
生成配置文件
confd -onetime -backend etcdv3 -node http://127.0.0.1:2379
查看配置文件
/tmp/myapp.conf
upstream myapp {
server 10.0.1.100:80;
server 10.0.1.101:80;
}
server {
server_name myapp.example.com;
location / {
proxy_pass http://myapp;
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;
}
}
/tmp/yourapp.conf
upstream yourapp {
server 10.0.1.102:80;
server 10.0.1.103:80;
}
server {
server_name yourapp.example.com;
location / {
proxy_pass http://yourapp;
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;
}
}