当我们聊Consul服务发现的时候我们在聊什么
导读:随着微服务概念深入人心,越来越多的解决方案选择使用微服务架构,这类架构的共同点是服务数量多,因此种类繁多的服务之间如何互相访问就变成了一个很现实的问题。目前比较流行的分布式存储有:Consul, etcd, ZooKeeper。今天主要分享下如何基于Consul来实现服务发现。
金烨 / 数人云运维工程师
早期做Java开发,后转职为运维开发,曾就职于仙掌软件、金山西山居,现就职于数人云。负责数人云平台Mesos/Marathon/Docker环境维护、运维自动化建设。
关于服务发现及Consul
将应用部署到集群时,服务IP或端口是动态分配的,用户访问时不知道后端的IP及端口在访问这些服务时,使用一定的机制动态发现服务的IP及端口就是服务发现(Service Discovery)。
Consul是一个服务发现和配置管理的工具,是具有分布式、高可用、高度可扩展的服务。
Consul客户端可以提供其他服务的服务发现功能,使用dns和http,服务发现问题很容易被解决。
使用Consul可以提供任意数量的健康检查功能,无论是服务状态(web服务器返回 http 200)还是本地节点的信息(内存利用率超过90%),这些信息都可以由操作员进行设置,并由服务发现组件将服务流量从不健康的组件中移开。
Consul的kv存储功能,通过http api可以很容易完成动态配置管理、协调服务、主选举、功能标记等服务。
Consul支持多数据中心感知,可以支撑任意数量的区域无需复杂配置。
server
服务发现、健康检查、数据一致性存储,leader选举机制,所以奇数部署
agent
健康检查
正常退出(至关重要)
节点通过信号正常关闭,接到信号的Consul会通知其他节点,正常离开后上面的服务和健康检查将被移除
异常退出
没有通过信号正常关闭的节点,其他节点会由于没有接到信号,认为这个节点还是正常的,也不会移除其服务和健康检查。本文主要分享服务发现和健康检查两个特性。
为了更加方便快捷的部署,采用Consul进行docker化的方式实现,Consul本身安装很简单,只安装二进制的包即可使用。首先从组件下载页找到合适系统的二进制包下载,下载后将二进制的包拷贝安装在对应的可被执行的目录里,如:~/bin 或者 /usr/local/bin。Consul 还有web_ui,需要单独下载,并在启动指定目录即可。
集群运行Consul可以运行client和server 两种节点模式,每个数据中心必须有一个server节点,但如果高可用,需要部署3个以上。其它Consul全部运行在client模式。客户端连接会有一个非常轻的注册过程,运行健康检查并发给server节点。client运行在每个节点上。ansible批量部署Consul server (agent与server类似)
Consul server ansible模板片段
Consul agent ansible模板片段
上面启动的信息中每台主机启动的参数不同的主要是IP,所以使用ansbile的时候就用到了一个小技巧,在ansible的hosts配置文件中给每条主机信息添加一个变量 ansible_ssh_host,在ansible运行中直接引用hosts配置中的变量,其它相对来说比较固定的参数,填写到ansible的全局变量文件ansible/inventory/group_vars/all中,或执行playbook时传入就可以了。
ansible hosts配置示例
base-Consul-master ansible role 片段
base-Consul-master playbook
调用playbook的执行脚本
调用playbook是可以指定主机或主机组
部署Consul master
指定ansible role中的tag 执行任务
Consul中的服务可以在启动前就定义 ,提前写好定义服务的配置文件放置到 Consul配置目录Consul.d中, 如果这种方式需要更新,则需要重启或发送SIGHUP信号给Consul ,不太方便操作,因此推荐使用Consul api的方式来注册服务。注册服务的信息主要包含id, name, port, address, check每个服务可以由多个ip可以有多个端口的服务组成。需要注意的是:每个服务可以有多个check,check可以是端口检查、http检查、脚本检查。其中检查脚本通常是自由做任何事情,以确定检查的状态。唯一的限制是,该退出代码都必须遵守此约定:
Exit code 0 - 检查结果是 passing
Exit code 1 - 检查结果是 warning
其它 Exit code - 检查结果是 failing
注册服务通常是通过agent来注册,因为服务的健康检查与所在Consul agent是绑定的,如果 Consul agent所有宿主机挂掉,就认为这台主机上的服务也是挂的。
Consul健康检查参考:
注册的服务有问题,需要删除调用deregistry接口
删除一个服务
删除一个检查
agent api 参考:注意:
同一个服务 每个实例的id唯一
同一个服务 每个检查项的id唯一
Consul启动后,并且服务集群状态正常,就可以对外提供DNS或者HTTP API来查询服务了。
http api 通过Consul server查询服务列表信息通过Consul server查询单个服务信息
通过agent查询服务服务
只查询健康的服务
(不加passing参数会包含warning的服务,不包含critical的服务)
agent操作api参考 https://www.consul.io/docs/agent/http/agent.html
Consul dns服务发现Consul自带一套dns 服务,所以可以处理简单的服务发现。通过dig命令查询Consul dns中的服务解析, critical的服务不会解析
Consul默认的dns服务端口是8600,常用的dns服务默认的端口为53,所以如果使用8600,不太方便,而且Consul 0.7.0版本后才支持修改默认Consul dns端口,就算可以修改Consul dns端口,但又可能会跟其它的dns服务冲突。
可以使用centos7自带的服务dnsmasp来转发Consul dns和其它dns服务在/etc/dnsmasq.d/dns-hosts.conf中添加 Consul dns地址
配置NetworkManager
在resolv.conf中添加search service.Consul和默认的dns server指定到本机,
这样即可直接在宿主机使用dns访问到Consul中的服务了,而且可以使用短域名访问,例如:
registrator通过在容器启动时检查容器来自动注册和注销任何Docker容器的服务。registrator支持自动注册的服务包括Consul,etcd和SkyDNS 2github地址:
直接使用官方镜像即可。
docker run方法
服务自动注册的id name check等需要在被注册的服务启动环境变量指定。例如:
docker run -d --net host -p 5014:5014 -e SERVICE_5014_ID=cadivisor -e SERVICE_5014_NAME=cadivisor cadivisor
注意:使用Consul做服务发现一般为host模式启动的服务,端口是固定的。由于端口问题,一台宿主机也只部署一个实例。host模式一般不添加 -p 或 --publish参数,但registrator 的默认注册规则,是有--publish都会自动注册到Consul, 所以host 模式 就算是不需要端口映射,也需要添加--publish参考指定端口,这样registrator 就会把服务注册到Consul了。
参考 :
haproxy做为PaaS平台服务的负载均衡服务,对外服务;配置backend服务时,配置的是Consul中的服务域名。
这里有个坑,原来使用haproxy 1.5版本, 后端服务使用域名时,启动后只解析一次(和nginx类似),这时如果解析到的服务挂掉,访问haproxy页面时会503。查询官网得知haproxy 1.6支持了动态dns 域名解析的配置,后升级为haproxy 1.6。
下面是动态DNS解析相关的配置内容:
用户访问haproxy就会动态解析访问后端Consul中的服务。
数人云最初的Mysql主从切换是基于 haproxy+keepalived 来做高可用的。这种机制下,Mysql主从切换的简单需求需要引入2个开源组件,架构上也非常复杂。为了减轻架构复杂度和可维护性,将主从切换改为了使用Consul进行主从切换。Mysql是安装了两台,一主一从,设置权限时,主是读写的,从是普通用户只读权限。通过将服务注册到Consul来做健康检查,Mysql Master和Slave注册四个服务到Consul, 如下代码:
正常情况mysql-vip.service.Consul解析到 主mysql ip。这里分两种情况切换到从解析:— 主Mysql挂掉后,ip解析到从mysql ip,mysql-vip 切换到从Mysql IP 。— 主库的Consul 挂掉也会导致服务无法解析,即使 mysql-master正常,如果Consul挂掉也会被激活切换。
请注意:因为Mysql从是只读的,相当于降级服务。
接下来再分享一下Consul实践中遇到的一些坑。
初期进行http api 调试测试时,注册了一些服务到Consul, 后来想删除就调用,ip:8500/v1/catalog/deregister 接口进行删除,调用该接口返回成功。但过一会儿去web ui查询, 发现刚才删除的服务还在,后来查询官方文档,原来server端catalog和 agent端都有deregister接口,我们的服务都是通过agent注册的,所以在catalog 删除不生效,只能通过agent的 deregistry接口来删除。
测试Consul server服务的高可用,先停止其中一台Consul server节点的容器,并清理Consul 的持久化数据,其它服务的Consul节点正常,达到预期结果,后来尝试恢复这个Consul server,恢复后发现从Consul ui查发现 少了一些服务节点,查询过后,正好是清理数据的这台Consul server上的服务在 Consul 中消失了,因此又重新在这个节点进行相应的服务注册才真正恢复正常。
健康检查脚本中使用grep检查结果内容,如果匹配则正常,不匹配为异常,例如: echo result|grep ok,有一次,一个服务节点异常,grep也没有匹配,当时认为这样就不会被解析了,后来发现这个服务ip还是会被解析,查询问题发现grep 不匹配的error code 为1 ,code 1 只是warning ,还是会被解析,所以修改检查脚本内容,如果不匹配,手动返回error code 2,之后这个异常服务才不解析。
在数人云的PaaS平台发布的服务,一般服务容器名称都是随机的,registrator 默认的自动注册规则是把容器名注册为 Consul中的service id,这样在Consul就有可能同一个服务被注册多次,实际上是同 个服务不同实例。所以需要在启动容器的环境变量中指定SERVICE_xxx_NAME ,这样registrator注册的时候就不会同是有多个相同的服务但server name 的不同 ,同理,如果需要consul 中的 service id 保持 致,则需要启动容 时指定SERVICE_xxx_ID。
registrator 默认 自动注册的服务在consul中 带健康检查的,所以容器实例挂掉,consul的 页面上是看不 出的,会导致consul dns解析到异常的容器实例 上,所以需要在启动容 时需 要添加类似下面的环境变量。
部署客户环境时遇到一个诡异的问题,有两个节点总是异常,健康检查这两个节点时好时坏,大部分时间是坏的,少部分时间显示正常状态。
排查问题时,发现主机之间响应时间很快,Consul server 与Consul agnet 之间的Consul端口都是可以通的。
查询Consul server日志,看到 server 日志中有error
以为是有端口漏开放了,telnet 测试后,发现Consul server到agent都是通的,谷歌下这个错误,发现有可能是因为没有开通udp端口导致的,后来又使用nmap测试Consul相关的udp端口是不是开放的,测试后发现udp 果然没有开通。于是就向客户申请开通相应的udp端口,开通后,再进行测试,发现问题还是存在。
这就不得不再想其它办法,后来又查询异常的Consul agent节点日志发现,发现Consul agent 也报类似的错误,乍一看跟server端的日志是一样的,但仔细研究发现关键问题,异常节点的agent报了很多访问其它agent节点8301端口的 time out的日志:
比起另外两个同样流行的分布式存储etcd和ZooKeeper, Consul从设计上就考虑到了很多服务发现的需求, 比如说健康检查, 服务注册, DNS等。所以基于Consul来实现服务发现的功能还是有很多的想象空间的。
Q1:感谢老师!Consul, etcd, ZooKeeper能比较一下吗?为什么没有选用后两者呢?
A1:ZooKeeper 处理数据 不支持 resf api , 需要操作的时候比较繁琐,功能也比较单一,etcd 服务功能也相对单一,需要依赖其它 组件 配合使用Consul 本身自带服务发现、健康检查 、dns解析 功能相对来说强大一些,不需要依赖其它的组件就可能实现很多功能
Q2:就服务发现和zk相比,会有多大的延迟?
A2: 就服务发现来说,Consul 支持 api 和 dns 两个方式获取 服务, dns 解析基本无消耗。 api 操作的话,具体的数据我没有进行严格测试,但zk是java服务本身需要的内存比较多,java gc 垃圾回收也会影响一些性能。Consul 是go 语言开发性能会有一些优势。
Q3: 这里问一个不是很相关的问题,老师怎么看待Docker和微服务更配这样的说法?
A3: 以前是所有功能集成到一个服务中,微服务本身需要拆分服务,需要部署、更新、维护的服务数量会几何增加,如果是传统的方式运维的话,操作起来就是个挑战,Docker化之后,部署、迁移、交付都会变简单。
Q4:Consul最大承受多大规模,有瓶颈吗?
Q4: 我没有针对 Consul服务 做过压力测试,我们一般是会对服务进行压力测试,就现在我们部署的集群规模而言,支持几百来的Consul 节点是没有问题的。理论上可以更多。
Q5:内部的web服务怎么注册?用后台脚本周期性地检测web服务正常后,再请求接口注册服务吗?失败取消
A5: 如果 使用了 registrator ,docker服务启动时,添加相应的环境变量,就会自动注册到Consul,也会自动删除,不过registrator偶尔异常情况会有不同步的现象,我们在 registrator 启动时添加同步参数定时同步,如果没有使用 registrator ,就需要自己处理注册和删除的逻辑,例如我们使用marathon /mesos 做为调试的核心基础组件,可以监听marathon 的事件获取 容器实例 启动在哪,删除了哪些,就可以相应对 Consul做操作Consul 也支持异常服务定时清理,在注册添加时添加DeregisterCriticalServiceAfter 参数 就可以
Q6:“每个数据中心必须有一个server节点,但如果高可用,需要部署3个以上”为啥一定要三个以上
A6: Consul server 节点也分为 leader 和 follower 节点,leader 选举一般为基数选举,1个就不是高可用了,所以最少3个,遵循2N+1 原则,支持宕机 N 个节点。
Q7: Consul能够支撑多大的高并发系统?
A7: 这个问题跟上面的问题稍有些重合,其实高并发是指的业务,业务一般在 启动、停止、迁移 、或业务之间相互访问时 Consul 才会有些数据交互 ,Consul的健康检查 和 访问都是分摊到每个宿主机上, Consul 的压力 对比业务来说可以忽略不计。
--------- 活动推荐 ----------
一年之计在于春,
数人云邀请到了架构&运维的前辈们来分享自己的成长轨迹以及技术发展方向,
或许会给在路上的你提供一些帮助,
坚持一下就能看到诗和远方。