13 分钟
Consul 服务发现
Version: 1.31.1
概述
Consul 被官方定义为多网络工具,提供功能齐全的服务网格解决方案。Consul 提供了一种软件驱动的路由和分段方法。它还带来了额外的好处,例如故障处理、重试和网络可观察性。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建完整的服务网格并实现零信任安全。
最早 Consul 的就是一个高可用的服务注册和发现的注册中心,近些年来,Consul 引入了服务网格(service mesh)。从其官方网站来开,官方希望 Consul 可以提供一整套服务网格的解决方案。
本文,主要从使用者的角度,来介绍 Consul 服务发现相关的特性的基本使用以及安装和部署。其他关于 kv 存储、服务网格等特性,本文暂不涉及。此外,文本也不重点介绍 Consul 底层的高可用的原理和算法。
快速开始
单机运行
由于 Consul 是 Go 实现的,而具有 Go 优秀的跨平台特性,因此在任何平台安装和运行 Consul 非常容易,只需从其 官方下载站点 ,下载相应平台的 zip 包,解压后即可直接运行 ./consul
命令即可运行(或加入 PATH
),关于下载,更多参见:Install Consul。
cd ~/Downloads
unzip consul_1.13.1_darwin_amd64.zip
sudo mv consul /usr/local/bin
安装完成后,通过如下命令,以 dev 模式运行:
consul agent -dev -node machine
核心输出如下:
==> Starting Consul agent...
Version: '1.13.1'
Build Date: '2022-08-11 19:07:00 +0000 UTC'
Node ID: 'd4f7830e-00e8-9405-fa38-ec873c85b295'
Node name: 'machine'
Datacenter: 'dc1' (Segment: '<all>')
Server: true (Bootstrap: false)
Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, gRPC: 8502, DNS: 8600)
Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false, Auto-Encrypt-TLS: false
- Node Name:是 Agent 的唯一名称。默认情况下,这是机器的主机名,但您可以使用
-node
标志对其进行自定义。 - Datacenter:配置代理运行的数据中心。对于单 DC 配置,代理将默认为
dc1
,您可以使用 -datacenter 配置。 Consul 对多个数据中心具有一流的支持,但配置每个节点以报告其数据中心可提高代理效率。 - Server:表明 Agent 是在 Server 还是 Client 模式下运行。在服务器模式下运行代理需要额外的开销。这是因为它们参与了共识仲裁、存储集群状态并处理查询。服务器也可能处于 “bootstrap” 模式,这使服务器能够选举自己作为 Raft 领导者。多个服务器不能处于引导模式,因为它会使集群处于不一致的状态。
- Client Addr:这是用于 Client 连接的接口的地址。这包括 HTTP 和 DNS 接口的端口。默认情况下,这仅绑定到 localhost。如果更改此地址或端口,则在运行 consul members 等命令时指定
-http-addr
以指示如何访问代理。其他应用程序也可以使用 HTTP 地址和端口来控制 Consul。 - Cluster Addr:这是用于集群中 Consul Agent 之间通信的地址和端口集。并不要求集群中的所有 Consul Agent 都必须使用相同的端口,但所有其他节点必须可以访问此地址。
注意:如果使用 systemd 运行 Consul 且配置了 -join 时,service 配置文件需配置 Type=notify。
该模式将启用单个 Server Agent,并打印 Debug 日志,不能在真实生产环境使用,如何在生产模式部署,参见:下文。
此时,通过浏览器打开 http://127.0.0.1:8500 即可打开 Consul 的 WebUI。
下文将,通过 http://localhost:8500 端口的 HTTP API 注册和查询服务。需要注意的是:
- 注册和取消服务,需使用
/v1/agent
接口。 - 发现服务,需使用
/v1/catalog
和/v1/health
接口。
基本使用
本部分主要介绍如何通过 HTTP API 进行 Consul 的服务注册和发现的基本使用方法。
假设我们有两个服务,分别是 test-service-1
和 test-service-2
。
test-service-1
包含两个实例,一个是健康的 (9000
),一个是未启动的 (9001
)。test-service-2
有两个健康实例 (9010
和 9011
),使用 nc 命令模拟这两个服务。
while true; do echo -e "HTTP/1.1 200 OK\n\ntest-service-1 (instance1): $(date)" | nc -l 9000; if [ $? -ne 0 ]; then break; fi; done
while true; do echo -e "HTTP/1.1 200 OK\n\ntest-service-2 (instance1): $(date)" | nc -l 9010; if [ $? -ne 0 ]; then break; fi; done
while true; do echo -e "HTTP/1.1 200 OK\n\ntest-service-2 (instance2): $(date)" | nc -l 9011; if [ $? -ne 0 ]; then break; fi; done
注册服务
# 注册第 1 个服务的第 1 个实例
curl \
--request PUT \
--data '
{
"ID": "test-service-1-instance-1",
"Name": "test-service-1",
"Address": "127.0.0.1",
"Port": 9000,
"Check": {
"HTTP": "http://127.0.0.1:9000",
"Interval": "10s"
}
}
' \
http://localhost:8500/v1/agent/service/register
# 注册第 1 个服务的第 2 个实例
curl \
--request PUT \
--data '
{
"ID": "test-service-1-instance-2",
"Name": "test-service-1",
"Address": "127.0.0.1",
"Port": 9001,
"Check": {
"HTTP": "http://127.0.0.1:9001",
"Interval": "10s"
}
}
' \
http://localhost:8500/v1/agent/service/register
# 注册第 2 个服务的第 1 个实例
curl \
--request PUT \
--data '
{
"ID": "test-service-2-instance-1",
"Name": "test-service-2",
"Address": "127.0.0.1",
"Port": 9010,
"Check": {
"HTTP": "http://127.0.0.1:9010",
"Interval": "10s"
}
}
' \
http://localhost:8500/v1/agent/service/register
# 注册第 2 个服务的第 2 个实例
curl \
--request PUT \
--data '
{
"ID": "test-service-2-instance-2",
"Name": "test-service-2",
"Address": "127.0.0.1",
"Port": 9011,
"Check": {
"HTTP": "http://127.0.0.1:9011",
"Interval": "10s"
}
}
' \
http://localhost:8500/v1/agent/service/register
发现服务
方式 1:通过 DNS
只返回健康的实例。
dig @127.0.0.1 -p 8600 test-service-1.service.consul SRV
输出如下:
; <<>> DiG 9.10.6 <<>> @127.0.0.1 -p 8600 test-service-1.service.consul SRV
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 9930
;; 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: 4096
;; QUESTION SECTION:
;test-service-1.service.consul. IN SRV
;; ANSWER SECTION:
test-service-1.service.consul. 0 IN SRV 1 1 9000 7f000001.addr.dc1.consul.
;; ADDITIONAL SECTION:
7f000001.addr.dc1.consul. 0 IN A 127.0.0.1
machine.node.dc1.consul. 0 IN TXT "consul-network-segment="
;; Query time: 0 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Sun Aug 21 14:58:43 CST 2022
;; MSG SIZE rcvd: 167
方式 2:catalog HTTP API
返回所有实例(细节参见:List Nodes for Service | Filtering)。
curl http://localhost:8500/v1/catalog/service/test-service-1
输出部分内容如下所示:
[
{
"ID": "3033c057-b976-28bb-1666-02fbf3dd00d2",
"Node": "machine",
"Address": "127.0.0.1",
"Datacenter": "dc1",
"ServiceID": "test-service-1-instance-1",
"ServiceName": "test-service-1",
"ServiceTags": [],
"ServicePort": 9000,
// ...
},
{
"ID": "3033c057-b976-28bb-1666-02fbf3dd00d2",
"Node": "machine",
"Address": "127.0.0.1",
"Datacenter": "dc1",
"ServiceID": "test-service-1-instance-2",
"ServiceName": "test-service-1",
"ServiceTags": [],
"ServicePort": 9001,
// ...
}
]
方式 3:health HTTP API
返回所有实例,并可以获取到健康检查的状态(细节参见:List Nodes for Service | Filtering)。
curl http://127.0.0.1:8500/v1/health/service/test-service-1
输出部分内容如下所示:
[
{
"Node": {
"ID": "3033c057-b976-28bb-1666-02fbf3dd00d2",
"Node": "machine",
"Address": "127.0.0.1",
"Datacenter": "dc1",
// ...
},
"Service": {
"ID": "test-service-1-instance-1",
"Service": "test-service-1",
"Tags": [],
"Address": "127.0.0.1",
"Port": 9000,
},
"Checks": [
{
"Node": "machine",
"CheckID": "serfHealth",
"Name": "Serf Health Status",
"Status": "passing",
// ...
},
{
"Node": "machine",
"CheckID": "service:test-service-1-instance-1",
"Name": "Service 'test-service-1' check",
"Status": "passing",
// ...
}
]
},
{
"Node": {
"ID": "3033c057-b976-28bb-1666-02fbf3dd00d2",
"Node": "machine",
"Address": "127.0.0.1",
"Datacenter": "dc1",
// ...
},
"Service": {
"ID": "test-service-1-instance-2",
"Service": "test-service-1",
"Tags": [],
"Address": "127.0.0.1",
"Port": 9001,
},
"Checks": [
{
"Node": "machine",
"CheckID": "serfHealth",
"Name": "Serf Health Status",
"Status": "passing",
// ...
},
{
"Node": "machine",
"CheckID": "service:test-service-1-instance-2",
"Name": "Service 'test-service-1' check",
"Status": "critical",
// ...
}
]
}
]
取消注册
curl --request PUT http://127.0.0.1:8500/v1/agent/service/deregister/test-service-1-instance-1
curl --request PUT http://127.0.0.1:8500/v1/agent/service/deregister/test-service-1-instance-2
curl --request PUT http://127.0.0.1:8500/v1/agent/service/deregister/test-service-2-instance-1
curl --request PUT http://127.0.0.1:8500/v1/agent/service/deregister/test-service-2-instance-2
安装和部署
参考: 数据中心部署
架构和说明
参考: What is Consul? | 部署架构
Consul 是一个分布式系统,在 Consul 的概念中,一个 Consul 集群被称为数据中心 (datacenter)。
一个 Consul 数据中心由多个节点(被称为 Agent)组成,这些节点可以部署在物理机、虚拟机、或容器中。这些 Agent 可以分为如下两类:
- 3~5 台 Server Agent,包括一个 Leader 和多个 Follower,部署独立的性能强劲的机器上。
- 0~n 台 Client Agent,部署在每一台需要部署业务应用的机器上。
在 Consul 中,Server 还是 Client 只是 consul 这个二进制文件 agent 子命令的一个模式。区别在于:
- Server Agent
- 跟踪可用服务、它们的 IP 地址以及它们当前的运行状况和状态。
- 跟踪可用节点、它们的 IP 地址以及它们当前的运行状况和状态。
- 构建一个了解服务和节点可用性的服务目录 (DNS)。
- 维护和更新 K/V 存储。
- 采用 gossip protocol 协议向所有 Agent 传达更新。
- 对通过该 Agent 注册的服务进行健康检查。
- Client Agent
- 将请求通过 RPC 转发到 Server Agent,以及一些缓存策略。
- 对通过该 Agent 注册的服务进行健康检查。
也就是说,Client Agent 的健康检查和 Server Agent 能力是相同的,其他的 API 请求都会转发到 ServerAgent 中。
在 API 层面。在使用者看来,不需要区分 Client 和 Server,不管连到 Client 还是 Server,都可以使用 Consul 的全部的功能。
另外,一个只有 Server 的 Consul 集群也是可以正常工作的,但是 Consul 的推荐架构是为每个服务所在的节点 (如 k8s node) 部署一个 Client Agent 原因在于(来自:网络):
- Agent 可以减轻 Server 的健康检查的压力。
- 对应用层隐藏 Server Agent 的分布式复杂性:应用层只需要知道服务发现的地址永远是 localhost:8500。
- 当 Server Agent 故障时, Client Agent 可以利用缓存继续提供服务。
- Client Agent 的缓存机制,极大提高了集群的吞吐和性能。
因此一个推荐的单数据中心的 Consul 集群的架构如下图所示(图片来源:一篇文章了解Consul服务发现实现原理):
Consul Agent 会暴露很多个服务地址,可以分为两类 Client 和 Cluster,默认端口为(详见:Required Ports):
- Client (给应用程序使用)
- 8500(tcp): HTTP api 和 WebUI
- 8600(tcp/udp): DNS 服务
- 8501(tcp): HTTPS(默认不开启)
- 8502(tcp): gRPC API(默认不开启)
- Cluster (集群 Agent 使用,通过 serf 库实现)
- 8300(tcp): Server RPC 地址。
- 8301(tcp/udp):集群内部 Client & Server Agent 间相互通讯协调的端口。
- 8302(tcp/udp):跨集群(跨数据中心) 的 Server Agent 间相互通讯协调的端口。
consul agent 的配置可以通过 命令行参数 (consul agent --help
) 和 配置文件。本部分仅介绍部分常用的命令行参数:
参数 | 默认值 | 说明 |
---|---|---|
-datacenter=<value> | dc1 | 同一个集群的数据中心名应该是一致的 |
-server | agent 模式是否为 server | |
-bootstrap-expect=<value> | server 模式有效,当 server agent 达到该数值后,集群开始引导启动,需要注意的是,这个集群中所有 server agent 的该参数的值必须一致 | |
-node=<value> | 主机的 hostname | 此节点的名称。 在集群中必须是唯一的。 |
-bind=<value> | 0.0.0.0 | Cluster 端口绑定的地址,可以通过 go-sockaddr 语法运行时获取。如果为默认值,通告地址(指的是别的节点访问本节点时的 ip 地址)将为该机器的私有地址,因此如果有多个私有地址将报错 |
-retry-join | 需要加入的集群中的其他节点的地址,会一致重试直到成功,可以通过 go-sockaddr 语法运行时获取,支持域名,可以指定多次。 | |
-data-dir=<value> | 数据持久化目录,需要保证在 agent 挂掉数据仍然是持久化。 client 和 server 模式都需要,client 用来实现停止后重新注册服务。 | |
-client=<value> | 127.0.0.1 | Client 端口绑定的地址,可以通过 go-sockaddr 语法运行时获取。 |
-ui | 是否启用 webui | |
-domain | consul. | 通过 DNS 做服务发现时的域名。 |
手动部署
上文 快速开始 - 单机运行 介绍了如何启动一个开发模式(单 Server Agent 节点)的 Consul 集群。本部分,介绍如何手动部署一个 Consul 集群。集群规划如下:
- 3 台机器:搭建一个包含 3 个 Server Agent 节点的 Consul 集群。
- 2 台机器:模拟用于部署服务的节点,在这两台机器上部署 Client Agent 进程。
需要特别说明的是,Consul 所有 Agent 节点必须是相互可通的同一内网。
简单起见,使用 Docker 容器模拟这 5 台机器(参见:官方镜像说明,配置文件内容可以通过 CONSUL_LOCAL_CONFIG
配置)。
创建网络
docker network create consul-cluster
部署 Server Agent
# 启动第 1 个 server
docker run --network=consul-cluster --hostname=consul-server-1 --name=consul-server-1 -d \
consul agent -server -bootstrap-expect=3 -retry-join=consul-server-1 -retry-join=consul-server-2 -retry-join=consul-server-3
# 启动第 2 个 server
docker run --network=consul-cluster --hostname=consul-server-2 --name=consul-server-2 -d \
consul agent -server -bootstrap-expect=3 -retry-join=consul-server-1 -retry-join=consul-server-2 -retry-join=consul-server-3
# 启动第 3 个 server
docker run --network=consul-cluster --hostname=consul-server-3 --name=consul-server-3 -d \
consul agent -server -bootstrap-expect=3 -retry-join=consul-server-1 -retry-join=consul-server-2 -retry-join=consul-server-3
注意:
- 可以添加
-client 0.0.0.0 -ui
简单起见使用默认值。 - 生产环境一定要指定
-data-dir
持久化数据。
部署 Client Agent
# 启动第 1 个 client (为了方便观察,启用 webui、并将 http api 和 DNS 暴露到宿主机)
docker run --network=consul-cluster --hostname=consul-client-1 --name=consul-client-1 -p 8500:8500 -p 8600:8600 -d \
consul agent -retry-join=consul-server-1 -retry-join=consul-server-2 -retry-join=consul-server-3 -client=0.0.0.0 -ui
# 启动第 2 个 client
docker run --network=consul-cluster --hostname=consul-client-2 --name=consul-client-2 -d \
consul agent -retry-join=consul-server-1 -retry-join=consul-server-2 -retry-join=consul-server-3
观察测试
- 打开 client 1 的 webui ( http://localhost:8500 ) 可以看到
- Services 面板: 3 个 server 组成了 consul 服务。
- Node 面板: 5 个 agent 全部健康。
在 client 1 上注册一个测试服务。
# 不健康的服务 curl \ --request PUT \ --data ' { "ID": "test-service-1-instance-1", "Name": "test-service-1", "Address": "127.0.0.1", "Port": 9000, "Check": { "HTTP": "http://127.0.0.1:9000", "Interval": "10s" } } ' \ http://localhost:8500/v1/agent/service/register # 健康的服务(不启用健康检查) curl \ --request PUT \ --data ' { "ID": "test-service-1-instance-2", "Name": "test-service-1", "Address": "127.0.0.1", "Port": 9001 } ' \ http://localhost:8500/v1/agent/service/register
服务发现
- 通过 DNS:
dig @127.0.0.1 +tcp -p 8600 test-service-1.service.consul SRV
(注意+tcp
),可以返回 9001 的服务。 - 通过 Catalog API:
curl http://localhost:8500/v1/catalog/service/test-service-1
,返回全部两个服务。 - 通过 Health API:
curl http://localhost:8500/v1/health/service/test-service-1
,返回全部两个服务以及状态。
- 通过 DNS:
服务发现 (在其他 client 中)
- 进入 client2
docker exec -it consul-client-2 sh
- 通过 Catalog API:
curl http://localhost:8500/v1/catalog/service/test-service-1
,返回全部两个服务。 - 通过 Health API:
curl http://localhost:8500/v1/health/service/test-service-1
,返回全部两个服务以及状态。 - 通过 Service Local Health API:
curl http://localhost:8500/v1/agent/health/service/name/test-service-1
找不到,可以看出该 API 只能查找到注册到当前 Node 上的服务。
- 进入 client2
Client Agent 正常退出
- 停止 client1
docker stop consul-client-1
- 进入 client2
docker exec -it consul-client-2 sh
- 通过 Health API:
curl http://localhost:8500/v1/health/service/test-service-1
,观察状态可知,服务已经被取消注册了。 - 重新启动 client1
docker stop consul-client-1
- 进入 client2
docker exec -it consul-client-2 sh
- 通过 Health API:
curl http://localhost:8500/v1/health/service/test-service-1
,观察状态可知,服务已经又被重新注册了。
- 停止 client1
Client Agent 异常退出
- 停止 client1
docker kill -9 consul-client-1
- 进入 client2
docker exec -it consul-client-2 sh
- 通过 Health API:
curl http://localhost:8500/v1/health/service/test-service-1
,观察状态可知,服务仍然存在,但是 Checks 状态可以看出 Node 退出了。 - 重新启动 client1
docker start consul-client-1
- 进入 client2
docker exec -it consul-client-2 sh
- 通过 Health API:
curl http://localhost:8500/v1/health/service/test-service-1
,观察状态可知,服务已经又被重新注册了。
- 停止 client1
Server Agent (Leader) 异常退出
- 停止 server1 (从 webui 中找到 leader):
docker kill consul-server-1
- 打开 webui,经过一段时间后,可以看出 Consul 集群 2 个 server 仍然正常工作
- 重新启动 server2:
docker start consul-server-1
- 打开 webui,经过一段时间后,可以看出 Consul 集群 3 个 server 仍然正常工作
- 停止 server1 (从 webui 中找到 leader):
Server Agent (Leader) 退出
- 停止 server2 (从 webui 中找到 leader):
docker stop consul-server-2
- 打开 webui 可以看出 Consul 集群 2 个 server 仍然正常工作
- 重新启动 server2:
docker start consul-server-2
- 打开 webui 可以看出 Consul 集群 3 个 server 仍然正常工作
- 停止 server2 (从 webui 中找到 leader):
Server Agent (follower) 退出
- 停止 server3 (从 webui 中找到非 leader):
docker stop consul-server-3
- 打开 webui 可以看出 Consul 集群 2 个 server 仍然正常工作
- 重新启动 server2:
docker start consul-server-3
- 打开 webui 可以看出 Consul 集群 3 个 server 仍然正常工作
- 停止 server3 (从 webui 中找到非 leader):
清理现场
docker rm -f consul-client-1 consul-client-2 consul-server-1 consul-server-2 consul-server-3
云原生部署
在官方的将 Consul 部署到 Kubernetes 的文档中,重点介绍的是 Service Mesh 相关的教程。
本部分,不会介绍 Service Mesh 相关的内容,而介绍如何在 Kubernetes 部署一套仅提供服务发现注册中心能力的 Consul 集群。可能的规划如下:
- 使用 Kubernetes StatefulSet 部署具有 3~5 个 Consul Server Agent 的 Consul 集群。
- 该 Consul 集群的 Client 的部署有如下两种选择:
- (推荐) 使用 Kubernetes DaemonSet 为 Kubernete 集群的每个节点,部署 Consul Client Agent。
- 如果没有 Kubernetes 集群 DaemonSet 的权限,则可以使用 Kubernetes StatefulSet 部署一个 Consul Client Agent 集群,并通过 Kubernetes 的 Service (type=Cluster) 提供服务。
需要使用 Consul 服务注册和发现能力的 Pod,针对如上 Consul 集群 Client 的部署方式的不同,有不同的使用方式:
DaemonSet:
- (推荐)挂载宿主机的文件(Consul Client Agent 的配置文件添加
addresses { http = "0.0.0.0 unix:///var/run/consul/socket/http.sock"}
)。- 挂载宿主机
/var/run/consul/socket
目录 - 导出环境变量
CONSUL_HTTP_ADDR=unix:///var/run/consul/socket/http.sock
- 挂载宿主机
使用 host ip。
env: - name: HOST_IP valueFrom: fieldRef: apiVersion: v1 fieldPath: status.hostIP - name: CONSUL_HTTP_ADDR value: http://$(HOST_IP):8500
- (推荐)挂载宿主机的文件(Consul Client Agent 的配置文件添加
StatefulSet + Service (type=Cluster):导出环境变量
CONSUL_HTTP_ADDR=http://$ConsulClientAgentService.$Namespace.svc.cluster.local
。
本部分示例选择:以 DaemonSet 的方式部署 Consul Client Agent,通过挂载宿主机文件 unix daemon socket 文件的方式给其他 pod 提供服务。
执行 kubectl apply -f consul.yaml
,其中 consul.yaml
内容如下:
# Namespace: consul
apiVersion: v1
kind: Namespace
metadata:
name: consul
---
# Headless Service: consul-server
# 为 StatefulSet 的 consul-server 准备
apiVersion: v1
kind: Service
metadata:
name: consul-server
namespace: consul
spec:
clusterIP: None
selector:
app: consul-server
ports:
- name: http
port: 8500
targetPort: 8500
- name: dns-udp
protocol: "UDP"
port: 8600
targetPort: 8600
- name: dns-tcp
protocol: "TCP"
port: 8600
targetPort: 8600
- name: server
port: 8300
targetPort: 8300
- name: serflan-tcp
protocol: "TCP"
port: 8301
targetPort: 8301
- name: serflan-udp
protocol: "UDP"
port: 8301
targetPort: 8301
- name: serfwan-tcp
protocol: "TCP"
port: 8302
targetPort: 8302
- name: serfwan-udp
protocol: "UDP"
port: 8302
targetPort: 8302
---
# StatefulSet: consul-server
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: consul-server
namespace: consul
spec:
selector:
matchLabels:
app: consul-server
serviceName: "consul-server"
replicas: 3 # TODO: 节点数目根据情况而定
template:
metadata:
labels:
app: consul-server
spec:
containers:
- name: consul-server
image: "consul:1.13.1"
args:
- "agent"
- "-server"
- "-bootstrap-expect=3" # TODO: 节点数目根据情况而定
# https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/statefulset/#stable-network-id
# url 为 $podName-{0..N-1}.$(serviceName).$(namespace).svc.cluster.local
- "-retry-join=consul-server-0.consul-server.consul.svc.cluster.local"
- "-retry-join=consul-server-1.consul-server.consul.svc.cluster.local"
- "-retry-join=consul-server-2.consul-server.consul.svc.cluster.local" # TODO: 节点数目根据情况而定
# TODO: 其他参数根据自身情况修改
volumeMounts:
- name: consul-server-data
mountPath: /consul/data
volumeClaimTemplates:
- metadata:
name: consul-server-data
namespace: consul
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "my-storage-class" # TODO: 根据自身情况修改
resources:
requests:
storage: 5Gi # TODO: 根据自身情况修改
---
# DaemonSet: consul-client
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: consul-client
namespace: consul
spec:
selector:
matchLabels:
name: consul-client
template:
metadata:
labels:
name: consul-client
spec:
initContainers:
- name: init-mount
image: "consul:1.13.1"
command: ['/bin/sh', '-c', 'chown -R consul:consul /var/run/consul/socket']
volumeMounts:
- name: consul-client-socket
mountPath: /var/run/consul/socket
containers:
- name: consul-client
image: "consul:1.13.1"
args:
- "agent"
- "-retry-join=consul-server-0.consul-server.consul.svc.cluster.local"
- "-retry-join=consul-server-1.consul-server.consul.svc.cluster.local"
- "-retry-join=consul-server-2.consul-server.consul.svc.cluster.local"
# TODO: 其他参数根据自身情况修改
- "-ui"
env:
- name: CONSUL_LOCAL_CONFIG
value: '{ "addresses": { "http": "0.0.0.0 unix:///var/run/consul/socket/http.sock" } }'
volumeMounts:
- name: consul-client-data
mountPath: /consul/data
- name: consul-client-socket
mountPath: /var/run/consul/socket
volumes:
- name: consul-client-data
hostPath:
path: /var/run/consul/data
type: DirectoryOrCreate
- name: consul-client-socket
hostPath:
path: /var/run/consul/socket
type: DirectoryOrCreate
通过 kubectl port-forward pods/consul-client-4mtdb 8500:8500 -n consul
将 consul-client
的 8500 端口进行端口转发,并打开 WebUI http://localhost:8500
,观察集群情况。
执行 kubectl create -f test-consul-pod.yaml
创建测试 pod,其中 test-consul-pod.yaml
内容如下:
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
volumes:
- name: consul-client-socket
hostPath:
path: /var/run/consul/socket
containers:
- name: busybox
image: busybox:1.28.4
command: [ "sleep", "100000000"]
imagePullPolicy: IfNotPresent
env:
- name: CONSUL_HTTP_ADDR
value: unix:///var/run/consul/socket/http.sock
volumeMounts:
- name: consul-client-socket
mountPath: /var/run/consul/socket
restartPolicy: Always
启动测试服务:
kubectl exec -it busybox -- /bin/sh
while true; do echo -e "HTTP/1.1 200 OK\n\ntest-service-1 (instance1): $(date)" | nc -l -p 9000; if [ $? -ne 0 ]; then break; fi; done
注册测试服务:
kubectl exec -it busybox -- /bin/sh
ip addr
# 将如下的 127.0.0.1 替换为 ip addr 的输出
curl \
--request PUT \
--unix-socket ${CONSUL_HTTP_ADDR#*//} \
--data '
{
"ID": "test-service-1-instance-1",
"Name": "test-service-1",
"Address": "127.0.0.1",
"Port": 9000,
"Check": {
"HTTP": "http://127.0.0.1:9000",
"Interval": "10s"
}
}
' \
http://localhost:8500/v1/agent/service/register
通过 WebUI 观察:http://localhost:8500/ui/dc1/services 。