consul爱的初体验
Consul是HashiCorp研发的一款服务发现与服务治理工具,常用于分布式系统、微服务和混合云环境。服务发现,指服务启动后向Consul注册自己,其他服务通过DNS或HTTP API查询可用实例报告服务位置。服务治理,在分布式微服务架构中,对大量独立服务进行统一管理、控制和优化的一整套机制与方法。
- 服务注册与发现
- 配置与策略管理,内置的KV数据库可以作为运行时的轻量级配置中心功能
- 健康检查,支持TCP|HTTP|Script|TTL心跳的健康检查,监控服务状态
- 服务通信与路由,基于标签的选路机制(相同服务的不同版本),基于名称的选路机制(web-v${x})
- 容错与稳定性治理,服务崩溃策略(超时,重试,熔断,限流,降级)
- 可观测性,提供服务的指标(Metrics),日志(Logs),链路追踪(Tracing)
- 安全与访问控制,支持服务身份验证,零信任通信
consul运行模式: [应用服务] → [Consul客户端agent] → [Consul服务集群]
1.安装与配置
与其他hashicorp产品一样,consul的安装十分简单,官方网站基本针对每种操作系统都有详细的介绍。 以ubuntu系统举例,导入hashicorp的公钥,增加官方源后,软件管理工具即可快捷安装
wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(grep -oP '(?<=UBUNTU_CODENAME=).*' /etc/os-release || lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install consul
consul运行于两种模式的一种,客户端与服务器
服务器配置
# consul.hcl
datacenter = "Datacenter-0"
data_dir = "/opt/consul" # 数据存放位置,低版本该配置不生效,默认存放在/var/lib/consul中
client_addr = "0.0.0.0"
ui_config{
enabled = true # 内置ui界面
}
ports {
grpc_tls = 8502
}
server = true
bind_addr = "192.168.5.225" # consul服务器集群通信地址,一般多3-5server端同时运行,提供高可用服务
bootstrap_expect=1
connect {
enabled = true
ca_provider = "consul"
}
telemetry {
prometheus_retention_time = "72h"
disable_hostname = true
}
acl = {
enabled = true # 开启访问控制,控制运行连接的consul客户端
default_policy = "deny" # 默认禁止
down_policy = "extend-cache" # 客户端与服务器连接断开,允许读取本地缓存
tokens = {
default = "d0c24029-fcf5-e83d-7876-acc18ac247f0" # 允许查询
}
}
consul acl bootstrap # 创建超级权限token
export CONSUL_HTTP_TOKEN=b9e0ee65-70d7-6586-677a-f545dcb5d2b0 (简化操作,下面操作遇到权限问题,都使用根权限)
# bootstrap-token,(root-token)无限权限
# agent-token,供agent的server或client在集群里注册自身、注册服务、更新sidecar配置时使用
# policy-token,即普通token,限定权限,例如某个namespace、KV、服务或connect路由的读写权限
# 在创建具体token时,需要提前创建token的策略,即在定义token的使用范围
# agent-policy.hcl, 该策略允许所有节点可被注册和访问,允许所有服务可读
node_prefix "" {
policy = "write"
}
service_prefix "" {
policy = "write"
}
consul acl policy create -name "agent-token-policy" -description "Agent Token Policy" -rules @agent-policy.hcl
consul acl token create -description "Agent Token" -policy-name "agent-token-policy"
客户端配置
# client.hcl
datacenter = "Datacenter-0"
bind_addr = "192.168.5.170"
client_addr = "0.0.0.0"
ports {
grpc_tls = 8502 # 启用Connect服务网格前,务必在Agent配置中开启8502gRPC 端口
}
server = false
retry_join = ["192.168.5.225"] # server端的地址
connect {
enabled = true
}
telemetry {
prometheus_retention_time = "72h"
disable_hostname = true
}
acl = {
enabled = true
default_policy = "deny"
down_policy = "extend-cache"
tokens = {
agent = "595feab1-3613-58F2-D1DA-cc96ea8c7c6e"
default = "d0c24029-fcf5-e83d-7876-acc18ac247f0"
}
}
2.基本命令
- 服务注册与发现
consul支持,配置文件注册(传统运维场景),HTTP API动态注册(应用自动程序)
# /etc/consul.d/gitlab.json
{
"service": {
"name": "gitlab",
"address": "10.10.0.21",
"port": 443,
"token": "fcbe30bd-35e1-b21d-0632-cd0d51d4d98b",
"tags": ["v2", "canary"],
"check": {
"http": "https://gitlab.diyao.me/-/health",
"interval": "10s",
"timeout": "5s",
"tls_skip_verify": false
},
"weights": {
"passing": 9, # 服务正常,权重9
"warning": 1 # 过载情况,权重1
}
}
}
consul services register ./gitlab.json
consul reload
# 配置文件gitlab.conf
curl --request PUT --data ${gitlab.conf} http://127.0.0.1:8500/v1/agent/service/register
dig @127.0.0.1 -p 8600 gitlab.service.consul SRV # 查询dns的srv记录
; <<>> DiG 9.18.30-0ubuntu0.20.04.2-Ubuntu <<>> @127.0.0.1 -p 8600 gitlab.service.consul SRV
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2205
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 3
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;gitlab.service.consul. IN SRV
;; ANSWER SECTION:
gitlab.service.consul. 0 IN SRV 1 1 443 0a0a0015.addr.datacenter-0.consul.
;; ADDITIONAL SECTION:
0a0a0015.addr.datacenter-0.consul. 0 IN A 10.10.0.21
peter-ThinkPad-Edge-E530.node.datacenter-0.consul. 0 IN TXT "consul-network-segment="
;; Query time: 3 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1) (UDP)
;; WHEN: Mon Feb 09 10:10:00 CST 2026
;; MSG SIZE rcvd: 185
curl -s http://127.0.0.1:8500/v1/health/service/gitlab?passing=true | jq .
[
{
"Node": {
"ID": "3e4b7df7-397b-106d-c7b8-a768b15258f7",
"Node": "peter-ThinkPad-Edge-E530",
"Address": "192.168.5.170",
"Datacenter": "datacenter-0",
"TaggedAddresses": {
"lan": "192.168.5.170",
"wan": "192.168.5.170"
},
"Meta": {
"consul-network-segment": ""
},
"CreateIndex": 3393,
"ModifyIndex": 7033
},
"Service": {
"ID": "gitlab",
"Service": "gitlab",
"Tags": [],
"Address": "10.10.0.21",
"Meta": null,
"Port": 443,
"Weights": {
"Passing": 9,
"Warning": 1
},
"EnableTagOverride": false,
"ProxyDestination": "",
"Proxy": {},
"Connect": {},
"CreateIndex": 21502,
"ModifyIndex": 21874
},
"Checks": [
{
"Node": "peter-ThinkPad-Edge-E530",
"CheckID": "serfHealth",
"Name": "Serf Health Status",
"Status": "passing",
"Notes": "",
"Output": "Agent alive and reachable",
"ServiceID": "",
"ServiceName": "",
"ServiceTags": [],
"Definition": {},
"CreateIndex": 3396,
"ModifyIndex": 19076
},
{
"Node": "peter-ThinkPad-Edge-E530",
"CheckID": "service:gitlab",
"Name": "Service 'gitlab' check",
"Status": "passing",
"Notes": "",
"Output": "HTTP GET https://gitlab.diyao.me/-/health: 200 OK Output: GitLab OK",
"ServiceID": "gitlab",
"ServiceName": "gitlab",
"ServiceTags": [],
"Definition": {},
"CreateIndex": 21676,
"ModifyIndex": 21877
}
]
}
]
- 动态配置中心
consul内置一个分布式的键值对Key/Value存储系统,非常适合存放数据库连接串、功能开关等
consul kv put config/gitlab/max_upload_size "100MB"
consul kv get config/gitlab/max_upload_size

- 应用无感知, consul提供的template守护进程,监视远端配置变化(KV存储),远程值变更,自动下载并重写本地config.conf,然后执行service reload重启进程
# gitlab.conf.ctmpl
upstream gitlab_servers {
{{ range service "gitlab" }}
server {{ .Address }}:{{ .Port }}; # 自动循环填入所有健康的 gitlab 地址
{{ else }}
server 127.0.0.1:65535; # 如果没服务,防止报错的备用地址
{{ end }}
}
server {
listen 80;
client_max_body_size {{ key "config/gitlab/max_upload_size" }};
location / {
proxy_pass http://gitlab_servers;
}
}
# consult-template的运行配置
consul-template -config "/etc/consul-template/config.hcl"
consul {
address = "127.0.0.1:8500"
}
template {
source = "/etc/consul-template/templates/gitlab.conf.ctmpl"
destination = "/etc/nginx/conf.d/gitlab.conf"
command = "systemctl reload nginx"
}
- 应用原生支持,应用启动时,直接通过网络请求Consul API
config = consul.get_kv("config/gitlab/database")
db.connect(config.url)
- 服务通信与路由
Consul不仅能告诉你服务在哪,还能通过标签Tags实现精细化的流量控制。例如,在注册GitLab服务时增加了v2标签,其他服务便可通过v2.gitlab.service.consul 精准访问该版本
利用预定义查询,编写一套路由剧本,可以在服务发现层直接完成智能故障切换与降级
{
"Name": "gitlab-smart-router",
"Service": {
"Service": "gitlab",
"Tags": ["v2"],
"OnlyPassing": true,
"Failover": {
"NearestN": 0, # 禁止跨数据中心寻址,如果设置成2表示,延时最小的2个数据中心中所有可用地址
"Selectors": [
{ "Tag": "v1" }, # 优先返回v1标签服务
{ "Untagged": true } # 兜底选择
]
}
}
}
curl -q http://127.0.0.1:8500/v1/query/gitlab-smart-router/execute | jq .
dig @127.0.0.1 -p 8600 gitlab-smart-router.query.consul # 注意域名是query.consul
consul的强大之处在于它让流量调度变得可编程。通过服务注册时的Weights权重参数,可以轻松实现金丝雀发布。例如,为v2版本设置较低权重,即可实现小比例的风险试错与性能验证
进一步地,利用Tags标签+自定义路由功能,consul能化身为精准的流量控制器。通过给实例打上地域或身份标签如region-bj或user-vip,可以实现更高级的灰度发布,定向引导VIP用户或特定地域流量访问指定版本的服务,从而在业务层面实现精准控流
upstream gitlab_prod {
{{ range service "gitlab" }}
server {{ .Address }}:{{ .Port }};
{{ end }}
}
upstream gitlab_gray {
{{ range service "vip-only.gitlab" }}
server {{ .Address }}:{{ .Port }};
{{ end }}
}
# 通过http首部,X-User-Role字段,设置backend变量
map $http_x_user_role $backend {
default gitlab_prod;
VIP gitlab_gray;
}
server {
listen 80;
location / {
proxy_pass http://$backend;
}
}
3.高级用法
connect是consul提供的服务网格能力扩展(也称作service mesh),用于实现服务之间的安全通信mTLS(双向认证),并在服务发现之上引入以sidecar代理为核心的服务级路由与流量控制机制。 前文尚未涉及的服务容错与稳定性治理、安全与访问控制等高级特性,均构建于connect所提供的服务网格能力之上。 通过统一接管服务间通信链路,consul得以在不侵入业务代码的前提下,实现对服务行为的集中治理。
下面以一个简单的echo服务为例,演示Consul Connect的基本使用方式及其在服务间通信中的作用。
# /etc/consul.d/echo.json
{
"service": {
"name": "echo",
"port": 58888,
"check": {
"tcp": "127.0.0.1:58888",
"interval": "10s",
"timeout": "1s"
},
"connect": {
"sidecar_service": {
}
}
}
}
socat TCP-LISTEN:58888,reuseaddr,fork SYSTEM:'cat'
-
consul内置的L4层代理
通过在服务端创建echo服务的sidecar,其对外监听一个由自动分配的动态端口(21000+),对内其连接echo服务指定的本地58888端口,并自动获取TLS证书双向认证,接管mTLS加密流量
在客户端一侧启动一个sidecar服务,在本地伪造一个目标服务,声明当前代理的身份web应用的同时,指定上游服务与本地端口的映射关系echo:9000本地客户端(telnet localhost:9000) -> consule-client-sidecar(认证加密) -> consule-server-sidecar(认证解密) -> echo服务(58888端口)
# 服务端
consul connect proxy -sidecar-for echo # 创建echo-sidecar-proxy服务
# 客户端
consul connect proxy -service web -upstream echo:9000 # web标明身份,告诉consul当前是web流量在访问echo服务
-
基于envoy的应用层代理
注意在使用envoy作为流量代理插件时,需要在配置文件中设置verify_incoming和verify_outgoing选项关闭
# 服务端
# 客户端
consul connect envoy -sidecar-for echo
consul connect proxy -service web -upstream echo:9000 # 客户侧继续使用内置代理插件
# 客户端侧注册web服务,使用envoy作为外部代理工具
# web.hcl
service {
name = "web"
connect {
sidecar_service {
proxy {
upstreams {
destination_name = "echo"
local_bind_port = 9000
}
}
}
}
}
consul services register web.hcl
consul connect envoy --sidecar-for web
- 安全与访问控制
consul通过访问控制意图Intentions来限制服务间访问,实现零信任安全通信。服务身份验证,只有拥有合法服务身份的应用才能访问目标服务。 服务间通信,consul通过对服务调用方和服务提供方认证(mTLS),以及服务数据的全流量加密,保证数据安全
# 访问控制
consul intention create -allow web echo
consul intention create -deny "*" echo
# 命令行实际创建service-intentions类型的配置条目
# 现代化的管理方式是使用consul config write ./echo-intentions.hcl
# echo-intentions.hcl
{
"kind": "service-intentions",
"name": "echo",
"sources": [
{
"name": "web",
"action": "allow"
}
]
}
# 查询
consul intention check web echo
- 容错与稳定性治理
consul本身提供的健康检查、服务发现、预编译查询,可作为容错决策基础。 同时consul原生支持的特性,consul-template会自动感知服务,在发现服务不可用时,自动更新nginx配置,在nginx配置文件中删除不可用节点,也可以视作服务的简单熔断(被动剔除)。以及前面提到的预编译查询语句,也可以达到基础的服务降级效果。
但一般情况下,大型应用场景,consul使用envoy或其他第三方代理服务提供的高级熔断、重试、限流、超时等功能,更好的达到主动式服务稳定性治理目的。
# proxy-defaults.hcl
# 定义全局代理配置默认值
# 由于echo服务使用的tcp协议,当前配置要求envoy将代理流量当做http协议解析
# envoy会构造http-400(bad request)响应
kind = "proxy-defaults"
name = "global"
config {
protocol = "http"
envoy_prometheus_bind_addr = "0.0.0.0:9102"
}
accesslogs {
enabled = true
}
consul config write ./proxy-defaults.hcl
# echo-service.hcl
# 定义特定服务在网格中的全局行为, 比如下面定义echo服务,定义服务协议为tcp
kind = "service-defaults"
name = "echo"
protocol = "tcp"
consul config write ./echo-service.hcl
# 定义web服务的相关限制,比如这里限制web服务到echo服务的最大连接数量2
kind = "service-defaults"
name = "web"
protocol = "tcp"
meta = {
purpose = "test"
}
upstreamconfig = {
overrides = {
name = "echo"
limits = {
maxconnections = 2
maxpendingrequests = 2 # tcp协议无效
}
}
}
下面介绍一下使用consul的配置项进行灰度测试的样例,例子中我们希望80%的访问继续使用稳定版本v1,20%的版本使用开发版v2
创建服务
流量方向:
- 客户端浏览器访问192.168.5.225:10000
- 主机192.168.5.225(consul-server机器),consul-envoy-sidecar(client:10000)
- 主机192.168.5.46(consul-client机器),consul-envoy-sidecar(端口内部协商)
- hugo进程主机(192.168.5.46:1313)
# hugo.hcl
service = {
name = "hugo"
port = 1313
token = "38123619-dc6f-7ce6-3857-43c829f40d61"
check = {
http = "http://127.0.0.1:1313"
interval = "10s"
timeout = "1s"
}
meta = {
version = "v1"
}
connect { sidecar_service {} }
}
consul services register hugo.hcl
consul connect envoy -bootstrap -admin-bind=127.0.0.1:9010 -sidecar-for hugo > bootstrap.json
envoy -c bootstrap.json
# 客户端侧注册hugo-proxy服务,使用envoy作为外部代理工具
# hugo-proxy.hcl
service {
name = "hugo-proxy"
connect {
sidecar_service {
proxy {
upstreams {
destination_name = "hugo"
local_bind_port = 10000
local_bind_address = "0.0.0.0"
}
}
}
}
}
consul connect envoy --sidecar-for hugo-proxy -admin-bind=127.0.0.1:9010
curl http://192.168.5.225:10000
服务分组
针对注册的hugo服务,默认将其划入v1组,服务元信息标明版本信息的,划入对应的组别
# hugo-service-resolver.hcl
kind = "service-resolver"
name = "hugo"
default_subset = "v1"
Subsets = {
v1 = {
Filter = "Service.Meta.version == v1" # service和meta必须大写
}
v2 = {
Filter = "Service.Meta.version == v2"
}
}
划分流量
发往hugo服务的流量,按照权重随机分配,v1组别处理80%,v2组别处理20%
# hugo-service-splitter.hcl
kind = "service-splitter"
name = "hugo"
splits = [
{
weight = 80
service_subset = "v1"
},
{
weight = 20
service_subset = "v2"
}
]
利用consul connect的高级功能,可以轻松的实现服务区分,比如下面的配置项,实现vip用户的优先处理 在请求体首部设置X-User-Role:vip或者请求参数中包含x-user-role=vip的请求会路由至服务的vip子集处理,默认使用hugo服务的稳定版本
# hugo-service-router.hcl
kind = "service-router"
name = "hugo"
routes = [
{
match {
http {
header = [
{
name = "X-User-Role"
exact = "vip"
},
]
}
}
destination {
service = "hugo"
servicesubset = "vip"
}
},
{
match {
http {
queryparam = [
{
name = "x-user-role"
exact = "vip"
},
]
}
}
destination {
service = "hugo"
servicesubset = "vip"
}
},
{
destination {
service = "hugo"
retrynnconnectfailure = true
retryon = ["reset","5xx", "gateway-error"]
}
}
]
- 服务可观测性
指标
- 数据面,指标需要通过proxy-defaults的全局配置项,envoy_prometheus_bind_addr参数,发送至监控系统
- 管理面,通过向consul进程发送user1信号,同时监控
journalctl -u consul -f命令输出,可以观测consul agent运行的相关指标数据
Feb 12 05:43:04 resources consul[151659]: [2026-02-12 05:42:10 +0000 UTC][G] 'consul.runtime.total_gc_pause_ns': 220032864.000
Feb 12 05:43:04 resources consul[151659]: [2026-02-12 05:42:10 +0000 UTC][G] 'consul.session_ttl.active': 0.000
Feb 12 05:43:04 resources consul[151659]: [2026-02-12 05:42:10 +0000 UTC][G] 'consul.state.config_entries.datacenter-0.terminating-gateway': 0.000
Feb 12 05:43:04 resources consul[151659]: [2026-02-12 05:42:10 +0000 UTC][G] 'consul.state.config_entries.datacenter-0.exported-services': 0.000
Feb 12 05:43:04 resources consul[151659]: [2026-02-12 05:42:10 +0000 UTC][G] 'consul.state.config_entries.datacenter-0.service-intentions': 2.000
Feb 12 05:43:04 resources consul[151659]: [2026-02-12 05:42:10 +0000 UTC][G] 'consul.runtime.alloc_bytes': 47405864.000
# 省略
......
日志
journalctl -u consul -f命令输出consul的程序运行日志- 在proxy-defaults的全局配置项中配置accesslogs参数,记录envoy处理的每一次请求或连接,包括来源、目标、状态、延迟等信息(服务网格里的流量日志,类似apache中access.log)
consul的服务网格中,链路跟踪记录一次请求跨多个服务的调用路径,分析系统行为和依赖关系,帮助你分析延迟、错误和性能瓶颈。envoy作为sidecar代理,可以自动收集每个请求的trace数据,将这些trace发送到外部追踪系统,如Jaeger,Zipkin,Lightstep。