Spring Cloud 使用Consul Service Mesh

1,729 阅读6分钟

序言

Spring Cloud在当前这个时间点来说基本是java 微服务开发的标准框架了,但是几年前Service Mesh架构的提出也吸引了很多人的实践。对于一些项目制交付模式的大型软件公司来说,像Spring Cloud这种侵入式的框架,哪怕是改动一个版本号对于企业都是一件非常麻烦的事情。因此我认为Service Mesh架构对这类公司更有使用价值。之所以写这篇文章,也是站在现实的角度,探索一种能尽量降低修改成本的演进方案。

先上k8s还是先上Service Mesh

之前看过阿里分享的Service Mesh落地经验,他们对于谁先上有过一番分析。之所以会有这个问题是因为当时阿里的技术选型是istio,它的部署依赖了k8s。从虚拟机运行环境切换到k8s涉及到巨大的成本,毕竟学习k8s还是一件相对困难的事情。不过在今天这个时间节点上,Service Mesh的技术选型istio不再是唯一。著名开源软件Consul在大约2-3年前开始对Service mesh提供支持,沉淀至今已经差不多可以摘果子尝试一下它了。而使用Consul,则不必再纠结要不要先上k8s,因为它支持虚拟机环境运行。

不过值得注意的时,尽管consul支持虚拟机,但是k8s下通过consul实现Service Mesh要比虚拟机容易得多。毕竟k8s将硬件软件化的特点能解决以前很多软件难以解决的问题。

实践

技术背景

假设咱们当前的软件架构是使用了Spring Cloud的微服务架构,并且运行在虚拟机中。Spring Cloud支持多种服务注册中心,早期只有eureka,不过现在国内用consul做服务注册中心的也不少。这里就选择consul作为服务注册中心吧。选择consul还有一个额外的考虑是本文的主题与consul有关。再一个就是如果想参考本文方案的话,将注册中心切换成consul对Spring Cloud来说是一件比较简单的事情。

目标

以尽量低的改动成本将以上技术栈实现的系统迁移到Service Mesh架构,在虚拟机中部署。按已有经验(如华为分享的ServiceMesh落地案例)来说,往往要将SpringCloud退化至Spring Boot才能上。其中最主要原因是要去掉Spring Cloud服务寻址,将其退化成IP端口直接通信并且IP指向本地。istio及consul在k8s下都提供了一种流量劫持的手段来将通信转到本地,但是对Spring Cloud这种寻址来说还是没法直接起作用。我们可以用很简单的一条iptables命令将本地发出的通往某个远程IP端口的请求重定向至本地,但是前提是我们得知道所有要重定向的远程IP端口,而这跟部署有关并且随着系统部署规模伸缩而变化的,实操起来基本不具备可行性。

而本文则是采用了另一种思路就是通过扩展spring cloud框架来适配service mesh,使其在程序层面实现流量重定向。

Service Mesh技术选型

Consul,前文已经提及了。sidecar则选用envoy,这点与istio相同。本文所用consul版本为v1.10.1,envoy版本为1.18.3。监控则选用prometheus 2.29.2

部署架构

如图所示,准备至少3台Linux虚拟机服务器。其中一台部署consul server,剩下两台部署consul client和envoy,以及标识为client、server两个微服。

7c44a96cf0d4ad9b687ee1388ea3fe8cf6e7a58aa717c906d61eb6590b798a34.jpg

为了下文讲述方便,将这三台虚拟机IP分配一下:

consul server所在虚机IP:10.19.215.45 client微服务所在虚机IP:10.19.215.69,以下简称该服务器为虚机A server微服务所在虚机IP:10.19.215.62,以下简称该服务器为虚机B

部署consul 集群

部署consul server

为了敲命令方便咱们把consul二进制文件放到/usr/bin目录下。后续其他服务器也一样。

现在准备一下consul server的配置文件server.hcl:

//server节点专属配置
server = true
bootstrap_expect = 1
client_addr = "0.0.0.0"
ui = true
node_name = "consul-server"
connect {
  enabled = true
}
ports {
  grpc = 8502
}

config_entries {
  bootstrap = [
    {
      kind = "proxy-defaults"
      name = "global"
      config {
        protocol                   = "http"
        envoy_prometheus_bind_addr = "0.0.0.0:9102"
      }
    }
  ]
}

ui_config {
  enabled          = true
  metrics_provider = "prometheus"
  metrics_proxy {
    base_url = "http://10.19.215.48:9090"
  }
}

为了简化consul部署这里设置成consul server 集群只需要1个节点就行了,生产环境建议要部署3-5个consul server 节点。

再编写一个consul.hcl文件,作为每个consul节点的通用配置:

datacenter = "dc1"
data_dir = "/opt/consul"
// encrypt = "fARB1df3e3SNcR4DGwGj5VbpTjoYWiFTVJkd4cJcB9o="
// ca_file = "/etc/consul.d/consul-agent-ca.pem"
// cert_file = "/etc/consul.d/dc1-server-consul-0.pem"
// key_file = "/etc/consul.d/dc1-server-consul-0-key.pem"
verify_incoming = false
verify_outgoing = false
verify_server_hostname = false
client_addr = "0.0.0.0"
//client节点专属配置
retry_join = ["10.19.215.45"]

以上两个配置文件均放置到/etc/consul.d目录下。接下来在consul server所在服务器上运行consul agent -config-dir=/etc/consul.d/,consul server启动成功。

虚机A部署consul client

把consul二进制文件放到/usr/bin目录下,然后再/etc/consul.d目录下新建文件client.hcl:

node_name = "consul-client"
connect {
  enabled = true
}
ports {
  grpc = 8502
}

再把之前的consul.hcl复制到/etc/consul.d目录下,然后运行consul agent -config-dir=/etc/consul.d/

虚机B部署consul client

步骤与之前一样,不过client.hcl内容得改个node_name:

node_name = "consul-client2"
connect {
  enabled = true
}
ports {
  grpc = 8502
}

之后我们访问http://10.19.215.45:8500/就能看到有3个node了。

准备Spring Cloud微服务

这里就不贴代码了,直接去github.com/FunnyYish/c… 下载。

需要说明一下这两个微服务额外依赖了我自己写的jar包:

<dependency>
	<groupId>com.dys.consul</groupId>
	<artifactId>service-mesh</artifactId>
	<version>0.1.1.RELEASE</version>
</dependency>

这个包目前尚未发布到任何仓库,读者要使用时需要去 github.com/FunnyYish/s… 下载代码并mvn install到本地仓库使用。这个jar包使用时还需要额外配置一些配置项:

#启用sidecar
spring.cloud.consul.discovery.sidecar=true
#声明所依赖的上游微服务server本地端口为1234
spring.cloud.consul.discovery.upstream.server=1234

以上配置的效果会使得restTemplate.getForObject("http://server/home", String.class);这行代码运行时请求127.0.0.1:1234。

部署微服务

这块没什么好说的,将这两个微服务编译打包后,务必按照前文部署架构运行在虚机A、B中。但是此时它们之间仍旧不能互通,因为sidecar尚未运行。

部署sidecar

archive.tetratelabs.io/envoy/envoy… ,在这里能找到各个版本的envoy制品。下载1.18.3这个版本,解压后放置在/usr/bin目录下。

在虚机A中运行consul connect envoy -sidecar-for client-82 -admin-bind localhost:19001,注意client-82是服务实例id,这个在consul后台管理界面中可以看到。也可以通过spring cloud 框架指定。

然后再在虚机B中运行consul connect envoy -sidecar-for server-81 -admin-bind localhost:19001

接着我们就可以测试服务调用了。向client微服务所在端口82/call发起调用可以发现调用成功。

通过consul控制链路通断

访问consul控制台http://10.19.215.45:8500/ui/dc1/services/client/intentions,可以在UI界面上操作新建一条intention:

219d143fa5cec6b849d86827d528261f4cdd3965a44f94dab9595785541a80fa.png

监控

consul只提供了非常有限的监控UI支持,如果要看详细的还是得上grafana。consul service mesh监控的原理是 prometheus配置抓取任务指向每个envoy,consul再配置从prometheus获取监控数据。

再准备一台服务器安装prometheus,配置如下:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  # - "first.rules"
  # - "second.rules"

scrape_configs:
  - job_name: client-sidecar
    static_configs:
      - targets: ["10.19.215.69:9102"]
  - job_name: server-sidecar
    static_configs:
      - targets: ["10.19.215.62:9102"]

注意前文consul server的配置文件里有个URL是http://10.19.215.48:9090,这个地址指向的就是刚才部署的prometheus。读者请根据自身IP修改这个配置项。值得注意的是在k8s下consul应该是提供了某种工具实现自动配置prometheus抓取各个envoy监控数据,无需手动配置。这块后续再补充。