Nginx结合Consul-Template(二)
一、Consul KV
#要先来了解consul的重要功能KV
1.1 先简单了解下KV
简单了解
Consul的KV存储是一个简单而强大键值存储系统,也是Consul的核心功能,但是请注意它是一个简单的KV存储。
Consul KV允许用户存储索引对象它被广泛用于分布式系统中的配置管理和共享数据。
Consul KV数据存储位于server上,但可以由任何agent(client或server)访问。本地集成的RPC功能允许客户端向服务器转发请求,包括读取和写入键/值。
KV存储可以通过consul KV CLI子命令、HTTP API和consul UI访问。如果需要限制访问,请启用并配置acl。
数据存储本身位于Consul服务器的数据目录中。为了确保在完全中断的情况下不会丢失数据,请使用consul快照特性来备份数据。
Objects对Consul来说是不透明的,这意味着存储在key/value条目中的对象类型没有限制。object的主要限制是大小,最大限制是512 KB。
Consul的KV存储提供了类似文件系统的键值存储模型,其中每个键都是一个路径,类似于/path/to/key,而每个键对应的值可以是任意字节序列。
consul-template的官网文档:https://developer.hashicorp.com/consul/tutorials/network-automation/consul-template-load-balancing?utm_source=docs
Consul KV 存储
Consul的KV存储是基于Raft算法实现的。Consul将KV存储看作一个状态机,每个节点都维护一个本地的状态机和日志。当客户端向任何一个节点发送写操作请求时,该节点会将操作转发给leader,
leader将操作应用到状态机和日志中,并将结果通知所有follower。当大多数节点确认了某个操作时,该操作被认为是已提交的,leader将操作应用到状态机中,并将结果返回给客户端。
Consul的KV存储支持多种操作,包括读取、写入、更新和删除。每个键值对都由一个唯一的key标识,并且可以关联一个可选的value。
Consul的KV存储支持版本控制,每个key都可以存储多个版本的value,客户端可以选择读取特定版本的value。
1.2 跟着官网的例子学习一下key/value操作
向KV存储中添加数据
# consul kv put redis/config/minconns 1 #插入key是redis/config/minconns,value是1,没有此key就是创建,有此key就是更新
Success! Data written to: redis/config/minconns
# consul kv put redis/config/maxconns 25 #插入key是redis/config/maxconns,value是25
# consul kv put -flags=42 redis/config/users/admin zaphod #该命令的flags值为42。key支持设置64位整数标志值,该标志值不用于Consul内部,但可用于客户端向KV对添加元数据。
查询KV存储数据
#consul kv get redis/config/minconns #查询key是redis/config/minconns的值
# consul kv get -detailed redis/config/users/admin
CreateIndex 473907 Flags 42 #如果put的时候不设置flags这里默认是0 Key redis/config/users/admin LockIndex 0 ModifyIndex 473907 Session - Value zaphod
# consul kv get -recurse #要列出存储中的所有键,结果按字典顺序返回。
redis/config/maxconns:25 redis/config/minconns:1 redis/config/users/admin:zaphod redis/config/users/admin1:zaphod
# consul kv get -keys redis/config/ #这是查询redis/config下面都有什么key,跟上面命令不同,只显示此层信息,如果是目录比如users就显示redis/config/users/
#consul kv get -keys #默认四列出根目录下的所有keys
下面让我们查看一下web ui上面的显示效果:
#注意上面web页面如果想创建目录而不是key的话,就这样test/,就是在后面加/就表示创建目录,么有/就是创建key就要输入value了。
删除数据
要删除KV存储中某个键的值,使用consul KV delete命令。
# consul kv delete redis/config/minconns
# consul kv delete -recurse redis/config/users #-recurse选项删除所有带有users前缀的键,如果你的config目录下有users和users1两个目录,这个命令就会把两个
目录都删除掉,如果你只想删除掉users这个目录就可以执行# consul kv delete -recurse redis/config/users/命令
博文来自:www.51niux.com
1.3 http操作key/value
创建kv
# curl --request PUT --data "admin 123456" http://127.0.0.1:8500/v1/kv/redis01/config/user
# curl --request PUT --data "123456" http://127.0.0.1:8500/v1/kv/redis01/config/user/admin
#从上面的例子可以看到如果一个目录下面是可以同时存在同名的key的但是要是两种类型
# curl --request PUT --data "123456" http://127.0.0.1:8500/v1/kv/redis01/config/user/admin?flags=6666 #如果要添加其他参数这样添加
true
#让我们看看还能带上哪些其他参数?
dc (string: "") - 指定要查询的数据中心。这将默认为正在查询的agent的数据中心。 flags (int: 0) - 指定一个介于0和(2^64)-1之间的无符号值来存储键。API消费者可以为他们的应用程序选择任何方式来使用这个字段。 cas (int: 0) - 指定使用检查与设置操作。这对于更复杂的同步原语来说是非常有用的构建块。如果索引为0,那么Consul只会在键不存在的情况下才会放入该键。如果索引不为零,则仅在索引匹配该键的ModifyIndex时才设置该键。 acquire (string: "") - 提供用于锁获取操作的会话ID。 release (string: "") - 提供一个会话ID用于释放操作。这在与?acquire=配对时很有用,因为它允许客户端产生锁。这将使LockIndex保持不变,但将清除该键的关联会话。该密钥必须由此会话持有才能解锁。
#那会不会像service一样添加key的consul节点关闭了,key就跟着消失了呢,答案是不会的,除非执行delete删除操作。
Read Key
如果给定路径中不存在键,则返回404而不是200响应。
如果递归或键查询参数为真,将返回一个键数组。如果ACL策略配置的原因响应数组不包含结果,则响应header中包含X-Consul-Results-Filtered-By-ACLs: true
# curl http://127.0.0.1:8500/v1/kv/redis01/config/user/admin
[{"LockIndex":0,"Key":"redis01/config/user/admin","Flags":6666,"Value":"MTIzNDU2","CreateIndex":476187,"ModifyIndex":477574}] #CreateIndex表示条目创建时间的内部索引值 #ModifyIndex是最后一个修改此键的索引 #LockIndex是在锁中成功获取该键的次数。如果锁被持有,会话键提供拥有该锁的会话 #Key就是条目的完整路径 #Flags是一个不透明的无符号整数,可以附加到每个条目。 #Value是一个base64编码的数据块。
#可以看到value值并未明文而是base64加密的需要解密一下
# echo "MTIzNDU2"|base64 -d
123456
下面是一些查询条件:
dc (string: "") recurse (bool: false) - 指定查找是否应该递归并将key视为前缀而不是文字匹配。 raw (bool: false) - 指定响应只是键的原始值,没有任何编码或元数据。 keys (bool: false) -指 定只返回键(不返回值或元数据)。指定此参数意味着递归。 separator (string: "") - 指定用于递归键查找的分隔符的字符串。此选项仅在与keys参数配对时使用,以限制返回的键的前缀,仅限给定的分隔符。
# curl http://127.0.0.1:8500/v1/kv/redis01/config/user/admin?raw #可以看到只是单纯的显示value
123456
# curl http://127.0.0.1:8500/v1/kv/redis01/config/?keys #可以看到是显示指定目录下所有的keys,以数组的形式展示
["redis01/config/user","redis01/config/user/admin","redis01/config/user/admin1","redis01/config/user1/admin1"]
# curl http://127.0.0.1:8500/v1/kv/redis01/config?recurse=true #显示所有key的详细信息,跟config?recurse一个效果
# curl http://127.0.0.1:8500/v1/kv/redis01/config/user/admin?keys #注意这是获取admin为前缀的key列表并非admin的key
Delete Key
# curl --request DELETE http://127.0.0.1:8500/v1/kv/redis01/config/user/admin #这是删除为redis01/config/user/admin的key
true
# curl --requDELETE http://127.0.0.1:8500/v1/kv/redis01/config/user/admin?recurse #这是删除admin为前缀开头的key
二、先跟着官网结构简单学习下consul-template
2.1 安装
官网地址:https://github.com/hashicorp/consul-template
# wget https://releases.hashicorp.com/consul-template/0.39.1/consul-template_0.39.1_linux_amd64.zip
# unzip consul-template_0.39.1_linux_amd64.zip
#mv consul-template /usr/local/bin/
# consul-template -v
consul-template v0.39.1 (cc8f954)
举个简单的例子:
# cat /tmp/in.tpl #定义一个foo的key
{{ key "foo" }}
# consul-template -template "in.tpl:out.txt" -once #-once是执行一次,你会发现这个进程一直在监听等待,如果foo有值的话再次执行得话进程就不会再监测卡主了
# /opt/soft/consul/consul kv put foo bar #给foo赋值bar,然后会发现的进程会执行完毕
# cat /tmp/out.txt #会看到key名称是foo会把获取的值写入到out.txt文件中
bar
# consul-template -template "in.tpl:out.txt" #如果不加-once会怎么样?那么这个进程会一直监测着foo这个key,一旦值发生变化就会更新out.txt文件可以试下
2.2 配置Consul模板
Consul Template可以使用命令行标志和配置文件进行配置。
命令行标志
# consul-template -h
-config=<path> #设置磁盘上配置文件或文件夹的路径。这可能是指定多次以加载多个文件或文件夹。如果有多个给定值后,它们从左向右合并,CLI参数取最高优先级。 -consul-addr=<address> #设置Consul实例的地址 -consul-auth=<username[:password]> #设置consul通信的基本身份验证用户名和密码 -consul-retry #与Consul通信失败时使用重试逻辑 -consul-retry-attempts=<int> #重试失败通信时使用的尝试次数 -consul-retry-backoff=<duration> #用于回退持续时间的基数。这个数字将是每次重试都会成倍增加 -consul-retry-max-backoff=<duration> #重试回退持续时间的最大限制。默认为一分钟。0表示无穷。回退将呈指数增长,直到给定值 -consul-ssl #在连接到Consul时使用SSL -consul-ssl-ca-cert=<string> #根据此CA证书文件列表验证服务器证书 -consul-ssl-ca-path=<string> #设置要用于TLS验证的CA的路径 -consul-ssl-cert=<string> #要发送到服务器的SSL客户端证书 -consul-ssl-key=<string> #用于客户端认证密钥交换的SSL/TLS私钥 -consul-ssl-server-name=<string> #设置验证TLS时要使用的服务器的名称 -consul-ssl-verify #通过SSL连接时验证证书 -consul-token=<token> #设置Consul API令牌 -consul-token-file=<path> #将路径设置为包含Consul API令牌的文件 -consul-transport-dial-keep-alive=<duration> #设置用于keep-alive的时间 -consul-transport-dial-timeout=<duration> #设置为建立连接而等待的时间 -consul-transport-disable-keep-alives #禁用keep-alive(这会影响性能) -consul-transport-max-idle-conns-per-host=<int> #设置每台主机允许的最大空闲连接数 -consul-transport-tls-handshake-timeout=<duration> #设置握手超时时间 -dedup #启用重复数据删除模式—当有多个实例时,可以减少Consul上的负载 -default-left-delimiter #模板的默认左分隔符 -default-right-delimiter #模板的默认右分隔符 -dry #将生成的模板打印到标准输出,而不是呈现 -exec=<command> #启用exec模式,使其作为类似管理程序的进程运行 -exec-kill-signal=<signal> #在优雅地终止进程时发送的信号 -exec-kill-timeout=<duration> #强制杀死进程之前的等待时间 -exec-reload-signal=<signal> #重新加载时发送的信号 -exec-splay=<duration> #发送信号前的等待时间 -exec-env-pristine #子进程不继承父进程的环境变量 -exec-env-custom #子进程附加的自定义环境变量 -exec-env-allowlist #暴漏给子进程的环境变量列表 -exec-env-denylist #禁止暴漏给子进程的环境变量列表 -kill-signal=<signal> #优雅关闭进程的信号 -log-level=<level> #设置日志级别,取值为debug,info,warn,err -max-stale=<duration> #设置最大过期时间 -once #不要将该进程作为守护进程运行 -parse-only #不要处理模板仅解析它们的结构 -pid-file=<path> #pid文件的路径 -reload-signal=<signal> #监听到信号重新加载配置 -retry=<duration> #如果Consul在与API通信时返回错误,则需要等待的时间 -syslog #将输出发送到syslog,而不是标准错误和标准输出。 -syslog-facility=<facility> #设置syslog日志应该记录的功能 -syslog-name=<name> #设置将出现在syslog中的应用程序的名称 -template=<template> #在磁盘上以‘in:out(:command)’的格式添加一个要监视的新模板 -template-error-fatal=<bool> #控制模板错误是否会导致consul-template立即退出。这将覆盖每个模板的设置 ...... 关于vault的就不列了 ...... -wait=<duration> #设置在编写模板(并触发命令)之前等待的‘min(:max)’时间 -v, -version #打印版本
# consul-template -template "in.tpl:out.txt" -exec "echo 'haha'" #先来一个例子,这里我们in.tpl里面定义了一个footest得key,注意这有点一次执行的意思,如果这是个新key没有value值的话,这个进程会一直等待直到有value出现,然后输出haha,如果这个key有value值的话就立马执行一次打印haha进程也就结束了。
# consul-template -template "in.tpl:out.txt:/bin/bash -c 'echo haha'" #这样进程会是常驻状态
# consul-template -template "in.tpl:out.txt:/bin/bash -c 'service nginx1 restart||true'" #如果你希望就算命令执行错误,进程还依旧运行,而不是非0退出的话
2.3 配置文件
配置文件是用HashiCorp配置语言编写,使用带有-config标志来使用配置文件。可以多次指定此参数以加载多个配置文件。最右侧的配置具有最高优先级。如果提供了目录的路径,则给定目录中的所有文件将按词法顺序递归合并。CLI上指定的命令优先于配置文件。
先来一个最简单的例子
#cat /tmp/config.hcl
consul { address = "127.0.0.1:8500" } template { contents = "{{key \"hello\"}}" destination = "/tmp/out1.txt" exec { command = "cat /tmp/out1.txt" } }
# consul-template -config "/tmp/config.hcl" #使用配置文件,进程也是一直监听着这个key,如果又变化就会更新文件打印内容到屏幕,只有value变化的时候才会触发
下面是官网的一个配置文件详解:
Consul Template部分:
下面这些选项是配置Consul Template的顶级值。它们不是必需的,并且在省略时将回退到默认值。
#这表示Consul Template的配置部分的开始。本节中包含的所有值都属于Vault。 reload_signal = "SIGHUP" kill_signal = "SIGINT" max_stale = "10m" block_query_wait = "60s" log_level = "warn" template_error_fatal = true err_on_failed_lookup = true wait { min = "5s" max = "10s" }
要启用下面这些功能,请在配置文件中声明值
pid_file = "/path/to/pid" syslog { #这将启用syslog日志记录。 enabled = true #这是要进行日志记录的syslog设施的名称 facility = "LOCAL5" } #这个块定义了记录到文件的配置 log_file { #如果指定了路径,则启用该特性。 path = "/var/log/something.log" #可以写入日志文件的字节数 log_rotate_bytes = 1024000 #默认情况下是24h小时轮转一次 log_rotate_duration = "3h" #日志的数量.默认为0(不删除任何文件)。设置为-1表示在创建新日志文件时丢弃旧的日志文件 log_rotate_max_files = 10 }
Consul部分:
通过声明Consul块,使Consul Template能够与Consul连接。
下面表示Consul配置部分的开始。本节中包含的所有值都与Consul有关。
consul { #此块指定与请求一起传递的基本身份验证信息。 auth { enabled = true username = "test" password = "test" } address = "127.0.0.1:8500" #这是一个用于读/写的Consul Enterprise名称空间,这是一个测试功能 namespace = "" #这是连接到Consul时要使用的ACL令牌.如果没有在Consul集群上启用acl,则不需要设置此选项。 token = "" #可以使用此选项指定包含令牌的文件的路径 token_file = "" #这将控制从Consul返回错误时的重试行为。Consul Template具有高度容错性,这意味着它在遇到故障时不会退出 retry { #这启用了重试。默认情况下,重试是启用的,所以这是多余的。 enabled = true #这指定在放弃之前尝试的次数。将其设置为零将实现无限次重试。 attempts = 12 #这是重试尝试之间的基本睡眠时间。每次重试休眠的时间比这个基数长2个指数。对于5次重试,睡眠时间将是:250ms、500ms、15秒、25秒,然后是4s。 backoff = "250ms" #这是重试尝试之间的最大睡眠时间。当max_backoff设置为零时,重试尝试之间的指数睡眠没有上限。 #如果max_backoff设置为10s, backoff设置为1s,则睡眠时间将为:1s, 2s, 4s, 8s, 10s, 10s,… max_backoff = "1m" } #该块配置tcp连接选项 transport { #这控制在空闲状态下两个keepalive传输之间的持续时间 dial_keep_alive = "10s" #这控制了重试之间的tcp超时。 dial_timeout = "10s" #这允许禁用保持活动连接 disable_keep_alives = true #最大空闲连接的数量 max_idle_conns_per_host = 100 #tls与consul握手的超时 tls_handshake_timeout = "30s" } #此块配置用于连接到Consul服务器的SSL选项。 ssl { #这将启用SSL。指定SSL的任何选项也将启用它。 enabled = true #这将启用SSL对等验证。默认值为"true",将检查全局CA链,以确保给定的证书有效。如果使用的是尚未添加的自签名证书对于CA链,可能需要禁用SSL验证 verify = false #这是用于身份验证的证书的路径。 cert = "/path/to/client/cert" key = "/path/to/client/key" #这是用作CA的证书颁发机构的路径。这对于自签名证书或使用自己内部证书颁发机构(CA)的组织非常有用。 ca_cert = "/path/to/ca" #这是指向PEM编码的CA证书文件目录的路径。 ca_path = "path/to/certs/" #这将设置用于验证的SNI服务器名称。 server_name = "my-server.com" } }
Templates部分:
模板块定义了模板的配置。与其他块不同,这个块可以被指定多次来配置多个模板。也可以直接通过CLI配置模板。
template { #这是磁盘上用作输入模板的源文件。这通常被称为“Consul Template模板”。如果不使用'contents'选项,则需要此选项。 source = "/path/on/disk/to/template.ctmpl" #这是源模板将在其中呈现的磁盘上的目标路径。如果父目录不存在,Consul Template将尝试创建它们,除非create_dest_dirs为false。 destination = "/path/on/disk/where/template/will/render.txt" #此选项告诉Consul Template,如果目标路径的父目录不存在,则创建它们。默认值为true。 create_dest_dirs = true #这个选项允许在配置文件中嵌入模板的内容,而不是为模板文件提供“源”路径。这对于短模板很有用。此选项与‘source’选项互斥。 contents = "{{ keyOrDefault \"service/redis/maxconns@east-aws\" \"5\" }}" #在访问不存在的结构体或映射field/key时出现错误退出。当访问不存在的字段时,默认行为将打印“<no value>” error_on_missing_key = false #控制模板中的错误是否会导致consul-template立即退出。 error_fatal = true #目标文件的文件权限 perms = 0600 #创建文件的用户和用户组,如果不指定,Consul Template将保留现有文件的所有权。 user = 1000 group = 1000 #写入新模版之前会创建一份备份,它只保留一个备份 backup = true #这些是要在模板中使用的分隔符。默认值是“{{”和“}}” left_delimiter = "{{" right_delimiter = "}}" #这些是模板中不允许使用的函数。如果模板包含这些函数之一,它将退出并显示错误。 function_denylist = [] #沙盒路径 sandbox_path = "" #新模版到执行命令之间等待的最小和最大时间 wait { min = "2s" max = "10s" } #这是可选的exec块,用于在呈现模板时提供要运行的命令。该命令只会在生成的模板发生更改时运行。 exec { command = ["restart", "service", "foo"] timeout = "30s" } #为了向后兼容,模板块还支持裸command和command_timeout设置。 command = ["restart", "service", "foo"] command_timeout = "60s" }
Modes部分:
使用以下选项配置Consul Template以在各种模式下运行。
Once Mode(一次模式):
将Consul Template配置为只执行每个模板一次,并使用标志退出-once或在配置文件中退出。
once = true
De-Duplication Mode(重复数据删除模式):
此块定义了在重复数据删除模式下运行Consul Template的配置。
deduplicate { # 这将启用重复数据删除模式。指定任何其他选项也会启用 # 这将启用重复数据删除模式。指定任何其他选项也会启用 enabled = true #这是Consul的KV存储路径的前缀,重复数据删除模板将在这里被预渲染和存储。 prefix = "consul-template/dedup/"}
Exec Mode(执行模式):
此块定义了在执行模式下运行Consul Template的配置。
exec { #这是作为子进程执行的命令。每个Consul Template进程只能有一个命令。 command = ["/usr/bin/app"] #等待命令完成的最大时间。默认情况下,该值为0,表示永久等待 timeout = "0" #这是在终止命令之前等待的随机显示。默认值为0(无等待),但大型集群应考虑设置一个splay值,以防止所有子进程在数据更改时同时重新加载。 splay = "5s" env { #这指定了子进程是否不应继承父进程的环境。默认情况下,子节点将具有对父节点环境变量的完全访问权限。 pristine = false #这将以如下所示的形式指定额外的自定义环境变量,以注入子进程的运行时环境。 custom = ["PATH=$PATH:/etc/myapp/bin"] #这将指定一个环境变量列表,以独占地包含在向子进程公开的环境变量列表中。 allowlist = ["CONSUL_*"] #这将指定一个环境变量列表,以便在向子进程公开的环境变量列表中独占禁止。如果指定,则任何与给定模式匹配的环境变量都不会公开给子进程,即使它们在allowlist中。 denylist = ["VAULT_*"] } #这定义了当监视的模板发生更改时将发送给子进程的信号。只有在进程启动后才会发送信号,并且只有在所有依赖模板至少渲染一次后才会启动进程。 reload_signal = "" #这定义了当Consul Template正常关闭时发送给子进程的信号。应用程序应该开始优雅的清理。 kill_signal = "SIGINT" #这定义了当Consul Template退出时等待子进程优雅终止所需的时间。默认是30s kill_timeout = "2s" }
2.4 Reload Configuration and Templates
虽然运行Consul Template有多种方式,但最常见的模式是将Consul Template作为系统服务运行。当Consul Template首次启动时,它从磁盘读取所有配置文件和模板,并将它们加载到内存中。从那时起,如果不重新加载,对磁盘上文件的更改不会传播到正在运行的进程。
如果要更新配置或模板,只需将HUP发送到正在运行的Consul Template进程,Consul Template将从磁盘重新加载所有配置和模板。
上面的解释很好理解,就是当你用consul-template加载一个配置文件启动后,类似于Nginx,配置就都加载到内存里面了,这时候如果你改变了比如config.hcl里面的配置,比如监听的key从hello变成了hellotest,或者输出的文件从out1.txt变成了out2.txt,改完之后是不生效的,需要重启consul-template进程或者重新加载。
# kill -HUP "consul-template的对应pid进程号" #就能重新加载配置文件了
2.5 模版语言(翻译官网内容,可忽略)
#我们将都有哪些参数简单了解一下:
Consul Template解析以Go模板格式编写的文件。如果您不熟悉语法,请阅读Go的文档和示例。除了go提供的模板函数外,Consul template还提供以下函数:
API Functions:
API函数与远程API调用交互,与Consul和Vault等外部服务通信
caLeaf #查询Consul以获取代表单个服务的leaf certificate caRoots #查询Consul以获取所有连接的受信任证书颁发机构(CA)根证书 connect #根据可连接服务的运行状况查询Consul datacenters #查询Consul目录中的所有数据中心 exportedServices #查询Consul以获取给定分区中的所有导出服务 file #读取并输出磁盘上本地文件的内容。如果无法读取文件,则会发生错误。当文件更改时,Consul Template将获取更改并重新渲染模板 key #查询Consul以获取给定键路径处的值。如果密钥不存在,Consul Template将阻止渲染,直到密钥存在。为了避免阻塞,请使用keyOrDefault或keyExists keyExists #查询Consul以获取给定键路径处的值。如果密钥存在,返回true否则返回false。与key不同如果key不存在,此函数将不会阻塞。这对于控制流量非常有用 keyOrDefault #查询Consul以获取给定键路径处的值。如果密钥不存在,则将使用默认值。与key不同,如果key不存在,此函数将不会阻塞 ls #查询Consul以获取给定关键路径上的所有顶级kv对 safeLs #与ls相同,但如果KV前缀查询返回空白/空数据,则拒绝呈现模板。这对于渲染由consul模板填充的关键任务文件特别有用。 node #在Consul中查询目录中的节点 nodes #查询目录中所有节点的Consul partitions #查询所有分区的Consul secret #在给定路径上查询Vault中的密钥 secrets #查询Vault以获取给定路径上的机密列表。并非所有端点都支持列表 pkiCert #使用实例查询PKI证书的存储库。它返回用字符串编码的证书PEM。它返回PKI证书、CA和密钥作为字段:Cert、CA和Key。 service #根据Consul的健康状况查询服务 services #向Consul查询目录中的所有服务 tree #查询Consul给定关键路径上所有kv对 safeTree #与tree相同,但如果KV前缀查询返回空白/空数据,则拒绝渲染模板。这对于渲染由consul模板填充的关键任务文件特别有用。
Scratch:
scratchpad在模板上下文中可用,用于存储临时数据或计算。暂存数据不会在模板之间共享,也不会在调用之间缓存。
scratch.Key #如果在指定key处的scratchpad中存在数据,则返回一个布尔值。即使该key处的数据为nil,这仍然返回true scratch.Get #返回scratchpad中指定键处的值。如果数据不存在,则返回nil scratch.Set #在给定key处保存给定值。如果该键上已经存在数据,则会被覆盖 scratch.SetX #这与Set的行为完全相同,但如果值已存在,则不会覆盖 scratch.MapSet #将值保存在映射中的指定key中。如果该键处已经存在数据,则覆盖该键 scratch.MapSetX #这与MapSet的行为完全相同,但如果值已经存在,则不会覆盖 scratch.MapValues #返回指定映射中所有值的排序列表(按key)
Helper Functions(辅助函数):
与API函数不同,helper函数不查询远程服务。这些函数对于解析数据、格式化数据、执行数学运算等都很有用
base64Decode #接受base64编码的字符串并返回解码结果,如果给定的字符串不是有效的base64字符串,则返回错误 base64Encode #接受一个字符串并返回一个base64编码的字符串 base64URLDecode #接受一个base64编码的url安全字符串并返回解码的结果,如果给定的字符串不是一个有效的base64 url安全字符串,则返回一个错误 base64URLEncode #接受字符串并返回base-64编码的url安全字符串 byKey #接受从tree调用返回的对列表,并创建一个映射,根据它们的顶级目录对它们进行分组 byTag #获取服务或服务函数返回的服务列表,并创建按标记分组服务的映射 byMeta #接受service返回的服务列表,并返回按ServiceMeta值对服务进行分组的映射。多个服务元键可以作为逗号分隔的字符串传递 contains #确定指针是否在可迭代元素内 containsAll #如果所有针头都在一个可迭代元素中,则返回true,否则返回false。如果指针列表为空,则返回true containsAny #如果指针位于可迭代元素中,则返回true,否则返回false。如果指针列表为空,则返回false containsNone #如果可迭代元素中没有针头则返回true,否则返回false。如果指针列表为空,则返回true containsNotAll #如果某个指针不在可迭代元素中,则返回true,否则返回false。如果指针列表为空,则返回false env #读取当前进程可访问的给定环境变量 mustEnv #读取当前进程可访问的给定环境变量。如果变量未定义或为空,则替换将失败并出现错误 envOrDefault #读取当前进程可访问的给定环境变量。如果找到了环境变量,将使用该变量的值。这包括空值。否则,将使用默认值 executeTemplate #执行并返回已定义的模板 explode #从tree或ls调用中获取结果,并将其转换为用于解析/遍历的深嵌套映射 explodeMap #使用与explosion相同的逻辑,获取map的值并将其转换为用于解析/遍历的深嵌套映射 indent #通过在每行前添加N个空格来缩进一段文本 in #确定指针是否在可迭代元素内 loop #接受不同的参数,并根据这些参数改变其行为。如果给loop一个整数,它将返回一个从0开始的程序,循环到但不包括给定的整数 join #将给定的字符串列表视为管道,并将它们连接到提供的字符串上 mergeMap #获取explode和一个explode参数的结果,然后合并这两个映射。参数的源不会被管道映射覆盖 mergeMapWithOverride #获取explode和一个explode参数的结果,然后合并这两个映射。参数的源将被管道映射覆盖 trimSpace #接受提供的输入并修剪所有空格、制表符和换行符 trim #接受提供的输入并裁剪所有前导和尾部的unicode trimPrefix #接受提供的输入并修剪前导前缀字符串 parseBool #接收给定的字符串并将其解析为布尔值 parseFloat #获取给定的字符串并将其解析为以10为底的float64 parseInt #获取给定的字符串并将其解析为以10为基数的int64 parseJSON #接受给定的输入(通常是key的值),并将结果解析为JSON parseUint #获取给定的字符串并将其解析为以10为基数的int64 parseYAML #接受给定的输入(通常是key的值)并将结果解析为YAML plugin #获取插件名称和可选有效负载,并执行Consul Template插件 regexMatch #将参数视为正则表达式,如果与给定字符串匹配,则返回true,否则返回false regexReplaceAll #将参数视为正则表达式,并将正则表达式的所有出现替换为给定的字符串 replaceAll #将参数视为字符串,并将给定字符串的所有出现替换为给定字符串 sha256Hex #将参数视为字符串并计算sha256_hex值 md5sum #将字符串输入作为参数,并返回输入的十六进制编码的md5哈希 hmacSHA256Hex #将key和消息作为字符串输入。返回具有给定参数的十六进制编码的HMAC-SHA256哈希 split #在提供的分隔符上拆分给定字符串 splitToMap #在提供的分隔符上拆分给定字符串,并将每个结果项拆分为key和value timestamp #以字符串(UTC)形式返回当前时间戳。如果没有给出参数,结果是当前RFC3339时间戳 toJSON #从tree或ls调用获取结果并将其转换为JSON对象 toJSONPretty #从tree或ls调用中获取结果,并将其转换为打印精美的JSON对象,缩进两个空格 toUnescapedJSON #从tree或ls调用中获取结果,并将其转换为JSON对象,而不进行HTML转义。当处理数据库连接字符串或包含查询参数的uri时,这个函数会派上用场 toUnescapedJSONPretty #从tree或ls调用中获取结果,并将其转换为打印精美的JSON对象,不使用HTML转义,缩进两个空格 toLower #将参数作为字符串接受,并将其转换为小写 toTitle #将参数作为字符串接受,并将其转换为标题大小写 toTOML #从tree或ls调用中获取结果并将其转换为TOML对象 toUpper #将参数作为字符串接受,并将其转换为大写 toYAML #从tree或ls调用中获取结果,并将其转换为打印精美的YAML对象,缩进两个空格 sockaddr #接受引号转义的模板字符串作为参数,并将其传递给hashicorp/go-sockaddr模板引擎 writeToFile #将内容写入具有权限、用户名(或UID)、组名(或GID)和可选标志的文件,以选择追加模式或添加换行符
Sprig Functions
Consul-template提供了在模板中访问Sprig库的功能。要在模板中使用sprrig函数,请在函数名前加上sprig_。Sprig函数的完整列表可以在Sprig函数文档中找到
Math Functions(数学函数)
以下函数可用于浮点数和整数值
add #返回两个值的和 subtract #返回第二个值与第一个值的差值 multiply #返回两个值的乘积 divide #返回第二个值与第一个值的除法 modulo #返回第二个值与第一个值的模 minimum #返回两个值的最小值 maximum #返回两个值的最大值
内容太多了,直接参照官网吧:https://github.com/hashicorp/consul-template/blob/main/docs/templating-language.md
博文来自:www.51niux.com
2.6 Multi-phase Execution(多阶段执行)
Consul Template对模板进行n次遍历评估,在每次遍历中累积依赖项。由于嵌套依赖,这是必需的,例如:
{{ range services }} {{ range service .Name }} {{ .Address }} {{ end }}{{ end }}
在第一次传递期间,Consul Template不知道Consul中的任何服务,因此它必须执行查询。当这些结果返回时,将使用该结果对内循环进行求值,从而可能创建更多查询和监视。下面这种方式仍然会将依赖项添加到监视列表中,但不会计算内部if,从而避免了索引外错误。
{{ with service "foo" }} {{ with index . 0 }} {{ .Node }}{{ end }}{{ end }}
三、nginx结合consul-template实现配置文件动态更新
3.1 使用示例例子了解一下
# cat test.ctmpl
{{range services}} {{$name := .Name}} {{$service := service .Name}} upstream {{$name}} { zone upstream-{{$name}} 64k; {{range $service}}server {{.Address}}:{{.Port}} max_fails=3 fail_timeout=60 weight=1; {{else}}server 127.0.0.1:65535; # force a 502{{end}} } {{end}}
# consul-template -template "/tmp/test.ctmpl:/tmp/out.conf" -exec "echo 'I GET'" #可见执行成功了
I GET
# cat out.conf #我们看下产生的内容,就能理解上面模版中的意思了
upstream consul { zone upstream-consul 64k; server 192.168.1.164:8300 max_fails=3 fail_timeout=60 weight=1; server 192.168.1.165:8300 max_fails=3 fail_timeout=60 weight=1; server 192.168.1.166:8300 max_fails=3 fail_timeout=60 weight=1; } upstream redis01 { zone upstream-redis01 64k; server 192.168.1.165:6379 max_fails=3 fail_timeout=60 weight=1; } upstream redis02 { zone upstream-redis02 64k; server 192.168.1.164:6379 max_fails=3 fail_timeout=60 weight=1; }
#如果你想取消息中其他的内容可以通过获取service的信息或者通过下面的方式看一下,比如:
# cat /tmp/test.ctmpl #注意spew_sdump是记录消息到指定的文件,spew_dump则是将消息打印到屏幕而不会写入到指定的文件
{{range services}} {{$name := .Name}} {{$service := service .Name}} {{range $service}} {{- spew_sdump $service -}} {{end}} {{range $service}} {{- spew_dump $name .Address .Port -}} {{end}} {{end}}
#然后再稍微复杂一点,你想让nginx的location直接就是service得name,就可以像下面那样:
# cat /tmp/test2.ctmpl
upstream {{$name}} { zone upstream-{{$name}} 64k; {{range $service}}server {{.Address}}:{{.Port}} max_fails=3 fail_timeout=60 weight=1; {{else}}server 127.0.0.1:65535; # force a 502{{end}} } {{end}} server { listen 80 default_server; location / { root /usr/local/nginx/html/; index index.html; } {{range services}} {{$name := .Name}} location /{{$name}} { proxy_pass http://{{$name}}; } {{end}} }
# cat /tmp/test2.ctmpl #如果你只想针对某个service进行监听,那就是把name定义成固定值了,如下面的示例
{{$name := "redis01" }} {{$service := service $name}} upstream {{$name}} { zone upstream-{{$name}} 64k; {{range $service}}server {{.Address}}:{{.Port}} max_fails=3 fail_timeout=60 weight=1; {{else}}server 127.0.0.1:65535; # force a 502{{end}} }
3.2 k/v获取演示一下
先来一个最简单了获取指定key的value:
# cat /tmp/kv_test1.tpl #获取key是redis/config/users/admin1的值
{{ key "redis/config/users/admin1" }}
# consul-template -template "/tmp/kv_test1.tpl:/tmp/kv_out.txt" -once
# cat /tmp/kv_out.txt #看下输出结果
zaphod
再来一个稍微复杂的,遍历一个目录下面有哪些key:
# cat /tmp/kv_test1.tpl #注意只会遍历key,哪些目录不会显示
{{range tree "offline/"}}{{.}} {{end}}
# consul-template -template "/tmp/kv_test1.tpl:/tmp/kv_out.txt" -once
# cat /tmp/kv_out.txt
{offline/upstream/grafana/192.168.1.164 upstream/grafana/192.168.1.164 3000 798755 798755 0 0 } {offline/upstream/grafana/192.168.1.165 upstream/grafana/192.168.1.165 80 798759 798759 0 0 } {offline/upstream/grafana1/192.168.1.197 upstream/grafana1/192.168.1.197 30001 811350 811350 0 0 } {offline/upstream/grafana1/192.168.1.198 upstream/grafana1/192.168.1.198 30002 811354 811354 0 0 } {offline/upstream_name/grafana upstream_name/grafana 811424 811424 0 0 } {offline/upstream_name/grafana1 upstream_name/grafana1 811427 811427 0 0 }
# cat /tmp/kv_test1.tpl #假设我们已经到最深的目录了,只需要把里面的key和value渲染出来就可以了
{{range ls "offline/upstream/grafana"}}{{.Key}}:{{.Value}} {{end}}
# consul-template -template "/tmp/kv_test1.tpl:/tmp/kv_out.txt" -once
# cat /tmp/kv_out.txt
192.168.1.164:3000 192.168.1.165:80
现在我们封装一下:
这就是一个比较贴合实际的场景了,比如你有多个集群,当然每个集群都有多个IP,你希望nginx的后端upstream以集群命名,然后集群名自动和后端关联,这样是不是就实现了动态变更的效果了,那么怎么实现一个nginx-template进程监听众多的集群key的变化呢,就是下面的例子了:
#cat /tmp/grafana_upstream.tpl #注意这里使用了printf将变量拼接,不然不太好实现字符串和变量的拼接
{{range tree "offline/upstream_name"}}{{ $server_name := .Key}} {{ $dir := "offline/upstream" }} {{$key := printf "%s/%s" $dir $server_name }} upstream {{$server_name}}_pool { {{range ls $key}} server {{.Key}}:{{.Value}} max_fails=3 fail_timeout=60 weight=1;{{else}}server 127.0.0.1:65535 down; {{end}} }{{end}}
# consul-template -template "grafana_upstream.tpl:/tmp/grafana_upstream.conf" -once
# cat /tmp/grafana_upstream.conf #看下产生的效果
upstream grafana_pool { server 192.168.1.164:3000 max_fails=3 fail_timeout=60 weight=1; server 192.168.1.165:80 max_fails=3 fail_timeout=60 weight=1; } upstream grafana1_pool { server 192.168.1.197:30001 max_fails=3 fail_timeout=60 weight=1; server 192.168.1.198:30002 max_fails=3 fail_timeout=60 weight=1; }
#这时候你可以把-noce参数去掉,然后去修改grafana和grafana1下面的value,然后你再看看/tmp/grafana_upstream.conf里面的内容也跟着变化了。
博文来自:www.51niux.com
3.3 做个关于nginx的试验
#上面可以看到文件是可以随时变化的,我们知道nginx是每次启动的时候将配置文件的信息加载到了内存里面,所以单纯的配置文件发生变更,其实nginx是不生效的,我们来验证一下。
首先创建两个grafan域名的配置文件.比如grafana.test.com引用grafana_pool做后端,同理grafana1.test.com使用grafana1_pool做后端。
然后启动一下template进程监控一下指定的k/v变化:
# consul-template --consul-addr 192.168.1.164:8500 --template "/tmp/kv_test1.tpl:/opt/soft/nginx/conf.d/upstream.conf" --log-level=info #加上log-level=info有变化就会输出到屏幕,如下图:
#你刷新web页面会发现虽然配置文件变化了,但是nginx域名指向的后端是没变化的,所以启动命令还需要变化一下:
# consul-template --consul-addr 192.168.1.164:8500 --template "/tmp/kv_test1.tpl:/opt/soft/nginx/conf.d/upstream.conf:/opt/soft/nginx/sbin/nginx -s reload" --log-level=info #就是让文件发生变化直接nginx重新加载配置文件,注意啊,这里又有一个新的问题,如果你upstream.conf文件修改的有问题,也就是nginx -t会报错,这时候你直接reload的话nginx报错就会直接退出整个template进程了。你说那么我修改成reload || true这种形式不就好了,不管命令怎么报错我template进程都不会退出,但是虽然进程不退出了,但是你nginx没办法reload就导致了你的修改一直不生效。所以还不如有问题template直接退出,然后对template进程做监控,一旦template进程退出了就报警出来然后迅速排查问题。
如果你想多个upstream后端配置文件启动得话:
# cat /tmp/grafana1.tpl
{{ $server_name := "grafana"}} {{ $key := "offline/upstream/grafana" }} upstream {{$server_name}}_pool { {{range ls $key}} server {{.Key}}:{{.Value}} max_fails=3 fail_timeout=60 weight=1;{{else}}server 127.0.0.1:65535 down; {{end}} }
# cat /tmp/grafana1.tpl
{{ $server_name := "grafana1"}} {{ $key := "offline/upstream/grafana1" }} upstream {{$server_name}}_pool { {{range ls $key}} server {{.Key}}:{{.Value}} max_fails=3 fail_timeout=60 weight=1;{{else}}server 127.0.0.1:65535 down; {{end}} }
# consul-template --consul-addr 192.168.1.164:8500 --template "/tmp/grafana.tpl:/opt/soft/nginx/conf.d/grafana_upstream.conf:/opt/soft/nginx/sbin/nginx -s reload" --template "/tmp/grafana1.tpl:/opt/soft/nginx/conf.d/grafana1_upstream.conf:/opt/soft/nginx/sbin/nginx -s reload" --log-level=info
#上图为修改了value值之后的一个进程输出
3.4 使用配置文件启动template进程
#紧接上文,上面我们如果想生成多个文件都用的cli命令行传参的方式,我们换种方式把要监听并创建的配置文件都生成在配置文件中,这样我们的进程命令就不会太长
# cat grafana_upstream.hcl #为了方便查看输出,我命令就是直接输出,可以看到配置文件中可以指定多个template的,syslog表示日志打到message日志没必要加
wait { min = "3s" max = "9s" } syslog { enabled = true facility = "LOCAL5" } log_level = "info" consul { address = "192.168.1.164:8500" } template { source = "/tmp/grafana.tpl" destination = "/tmp/grafana_upstream.conf" command = "echo aaa" } template { source = "/tmp/grafana1.tpl" destination = "/tmp/grafana_upstream1.conf" command = "echo bbb" }
# consul-template -config=/tmp/grafana_upstream.hcl #可以尝试修改下这两个监听的key查看一下,下面是测试输出