Consul 注册服务和健康检查(script检查) - Part.3
Consul提供的一个主要功能就是提供一个可用服务的列表,进行服务注册和发现。这在服务做负载均衡时非常有用,因为前端应用不再直接访问服务(可能访问到不可用的节点,例如节点宕机或者服务进程异常退出),而是先请求consul获取可用的服务列表,然后再访问列表中服务,从而提高了服务的可用性。Consul则负责维护这个服务列表,并对列表中的服务进行健康检查。
这篇文章将介绍如何注册服务和健康检查。注册服务和健康检查有两种方式:通过配置文件 和 通过Http API,这篇文章仅介绍通过配置文件的方式注册。
健康检查的类型分为好几种,常用的有:script检查,通过执行一段脚本检查,功能最强大,但是要手写脚本;http检查,通过发起http Get请求检查;docker检查,检查docker容器;TCP检查,检查TCP是否连接;gRPC检查,通过gRPC的健康协议进行检查。
这篇文章仅就最常用的script检查和http检查快速的过一遍流程。
注册服务(控制台类型服务/Script健康检查)
编写服务定义
服务定义说明了当前节点(定义服务的节点)具有哪些服务。在注册服务的同时,可以直接添加对该服务的健康检查方法。关于健康检查的详细官方文档在这里:https://www.consul.io/docs/agent/checks.html。
通过编写脚本进行健康检查是最灵活的,缺点是你需要编写一些检查的代码(通常是shell脚本或者python脚本)。
假设我在某个节点(比如我的工作电脑mac_work上)运行了一个控制台程序,该程序提供RPC服务。有两种方式来验证它是否工作:
- 访问RPC服务,看是否能正常调用;
- 查看服务进程是否存在。
对于方式1,优点是比较全面,缺点是要编写访问该RPC服务的代码,而且这个代码不够通用。
对于方式2,优点是编写的代码简单,而且相对通用一些,缺点是可能不准确,进程存在,但是服务无法访问(例如程序BUG导致的死锁,或者是其他原因导致的异常,虽然进程无法响应但又不会退出)。
这里简单起见,我们采用方式2。我们在consul的主目录(我的是/Users/zhangzy/linux/consul)下创建一个config文件夹保存配置,一个scripts文件夹保存健康检查的脚本,服务的定义文件放置在config文件夹下。
定义服务的脚本可以很复杂也可以很简单,更多的含义可以参考https://www.consul.io/docs/agent/services.html,这里仅给出范例和主要选项的说明:
创建一个services.json保存至配置目录(启动consul时指定的目录,在我的mac_work这台电脑是:/Users/zhangzy/linux/consul/config):
{ "services":[ { "id": "datacollect.server1", "name":"datacollect.server", "tags": ["grpc primary"], "port": 9101, "meta":{ "key1":"value1", "key2":"value2" }, "enable_tag_override": false, "checks":[ { "args":["python", "/Users/zhangzy/linux/consul/scripts/check_proc.py", "datacollect.server", "1"], "interval": "10s", "timeout": "2s" } ] } ] }
checks.args节点中的的datacollect.server是要检查的控制台程序。你可以使用任意一个控制台程序,哪怕只是输出一个helloworld。但注意这个控制台程序必须是长期运行的服务类型。
几个重要的配置说明一下:
- id: 必须唯一。可以不填,不填时使用name作为id。
- name: 可以不唯一,当多个相同的服务做负载均衡时,相同功能的服务应当配置相同的name。当name不唯一时,需要提供id。
- tags: 一些说明性的标签
- checks.args: 脚本检查的命令行参数,这里出现的datacollect.server是要检查的进程名称,和前面配置项中的services.name没有任何关系,只是如果进程名称和这里定义的服务名称统一更方便管理一些。
有时候,同一台机器会跑多个相同名称的进程,此时这里check_proc.py就会无法区分,因为其调用的pgrep会返回多个结果。一个简单的解决方案是将应用程序重命名成类似datacollect.server1、datacollect.server2 这样,然后再配置健康检查的脚本。
编写健康检查的脚本
接下来,编写check_proc.py。对于script检查而言,通过返回码说明检查的结果:
- Exit code 0 - 通过
- Exit code 1 - 警告
- Exit code 其他 - 失败
import subprocess import sys if __name__ == '__main__': if len(sys.argv) != 3: print "not enough arguments, usage: <process_name> <process_count>" sys.exit(2) process = sys.argv[1] count = int(sys.argv[2]) try: out_bytes = subprocess.check_output("pgrep -fa " + process, shell=True) except subprocess.CalledProcessError as e: out_bytes = e.output print "process not exists" sys.exit(2) found = 0 out_bytes lines = out_bytes.decode('utf-8').split("\n") list = [] for line in lines: if process in line and "python" not in line: list.append(line) found +=1 if found == 0: #fail print "process not exists" sys.exit(2) if found > count: #warning print "too many running, need {}, have {}. \n".format(count, found) + ";\n".join(list) sys.exit(1) if found < count: print "too few running, need {}, have {}. \n".format(count, found)+ ";\n".join(list) sys.exit(1) print list[0] sys.exit(0) #pass
这个检查接受2个参数,第1个参数是启动的命令(或者是应用程序名),第2个参数是预期个数。当实际个数为0时返回失败,大于0但不等于预期个数返回警告,等于预期个数时返回成功。
因为我们是通过python启动检查脚本的,这个命令中也包含了要检查的进程datacollect.server,因此在返回的结果中会连这个检查进程也包含进去,所以在后面需要额外剔除一下。
更新Consul配置
有两个办法可以让consul重新加载配置,比较简单的方法是执行consul reload:
$ ./consul reload Configuration reload triggered
还有一个方法就是发送SIGHUP信号量,即 kill -1 [PID]。
之后再次打开Web UI,可以看到已经有了健康检查和结果输出:
这里直接输出了服务的进程ID,因为我本机运行了2个同名的服务应用,因此这里输出了两个id。此时简单的处理办法是不要使用相同的应用名称启动服务。
分别定义服务和健康检查
在上面的配置中,我们将健康检查的规则写在了服务定义中,除此以外,也可以分开来单独定义。在config文件夹下再创建一个checks.json文件夹,写入内容如下:
{ "checks": [ { "id": "chk_service1", "name": "chk_service1", "notes": "检查服务 datacollect.server1 是否运行", "args": ["python", "/Users/zhangzy/linux/consul/scripts/check_proc.py", "datacollect.server1", "1"], "interval": "10s", "service_id": "datacollect.server1" }, { "id": "chk_service2", "name": "chk_service2", "notes": "检查服务 datacollect.server2 是否运行", "args": ["python", "/Users/zhangzy/linux/consul/scripts/check_proc.py", "datacollect.server2", "1"], "interval": "10s", "service_id": "datacollect.server2" } ] }
几个主要的配置项目含义如下:
- id: 节点上必须是唯一的,可以不设置,此时将会使用name作为id。
- name: 必须提供,可以不唯一,当name不唯一时,必须提供唯一id。建议name唯一,不设置id。
- notes: 检查的说明。
- servie_id: 检查的服务名称。
修改config/service.json,将其中的checks部分删除。此时我同时开启了两个服务datacollect.server1和datacollect.server2,所以服务定义如下:
{ "services":[ { "id": "datacollect.server1", "name":"datacollect.server", "tags": ["grpc primary"], "port": 9101, "meta":{ "key1":"value1", "key2":"value2" }, "enable_tag_override": false }, { "id": "datacollect.server2", "name":"datacollect.server", "tags": ["grpc primary"], "port": 9102, "meta":{ "key1":"value1", "key2":"value2" }, "enable_tag_override": false } ] }
这里两个服务的name相同,因为它们提供的是相同的服务。
使用consul reload重新加载配置,在Web UI可以看到这样的结果:
总结
这篇文章中,我们看了如何使用配置文件定义consul的服务和健康检查。这里的服务是一个console控制台应用,使用编写script的方式进行健康检查。本来在这篇文章中我想将http服务器的服务定义和健康检查也写进来,但考虑到篇幅已经很长,那么就在后续文章中介绍了。
感谢阅读,希望这篇文章能给你带来帮助!