文章大多来自于阅读 https://yq.aliyun.com/articles/231714 后,自己的整理。
(这里先解释自己读到的原理,纸上得来终觉浅,下次实践时再做补充。)
现在流行微服务,注重 单一职责 的原则,那服务被打散了,如何知道所调用的服务是否存在、是否正常以及在哪里(IP:port)就成为了一种问题。
(其实还不理解 consul 和 containpilot 的具体区别,下次了解了区别之后再在这里补充吧。)
服务的发现与注册有两种模式:客户端模式 与 服务端模式,两者的区别主要在于注册的位置(注册库)在客户端还是在服务端。
客户端模式.png 服务端模式.png它的工作原理是这样一个流程:
- Register:当新加进来一个服务实例(同一个服务的不同实例)时,服务实例会注册到注册库(Service Registry)中
- Query:当客户端发起一个 request 时,服务发现组件的客户端会去注册库中查询服务的信息
- Request:发生在第二步找到可用的服务配置(可能有多个实例)之后,会进行真实的服务访问(多个实例时选择某一个就实现了负载均衡)
服务注册中心(Service Registry)
把服务集中在一个地方管理,目前有三种方式:
- consul:提供了一站式的解决方案,存储服务信息,健康检查,DNS解析一应俱全,甚至提供了个简单的UI工具
- etcd:是一个 KV 的分布式存储工具
- zookeeper: 相对繁琐,不多介绍了
所以,consul 的基本功能是存储服务信息,服务的配置信息还有应用的配置信息都可以放在这里,它也提供了 RESTful 的 api 实现查询,还自带了健康检查和 DNS 解析的功能。
但即便这样,还免不了人工参与的方式,所以,这就引入了 containerpilot 来解决这一问题。(consul 的具体使用目前还不了解,后面了解了再补充)
containerpilot
containerpilot 做到了服务的发现与注册完全不用人工干预,只需要一些配置即可。
具体是这样:
- 所有 containerpilot 相关的配置放在一个固定的目录下面,例如
/etc/containerpilot
- 在容器启动的时候执行脚本从依赖关系文件(例如
serviceDependencies.json
)中读取出服务列表,并执行 containerpilot 的可执行文件(原作者说删除 socket 文件,这个我还不理解,后面补充) - containerpilot 读取配置文件 config.json5,通过模板语法,在不同的条件下根据 环境变量 激活不同的job
重要的部分有这几步:
- 连接到 consul,将当前服务注册进去,第一次生成依赖服务的地址 services.json
- 在
containerpilot
与 consul 建立通信后,获取依赖服务的列表,通过 consul-template 将服务的信息渲染到预定义好的模板文件中 - watch在consul中依赖的服务,当检测到更新的时候给服务发送 SIGHUP 的信号提示服务重新读取依赖服务的最新地址 services.json
- 通过调用 ping 命令对服务进行健康检查,在服务不健康甚至是容器异常退出的时候会将 consul 中服务的状态标记为不可用(不会解注册)
环境变量
containerpilot 是通过环境变量实现服务的注册与发现的
配置依赖服务
被托管服务如果依赖于其他服务,需在项目中添加 serviceDependencies.json 文件,构建 docker 镜像时把这个文件复制到 WORKDIR 下,示例如下:
{
"service1": ["service2", "service3"], // “SERVICE_NAME”: ["other_service_name"]
"service2": ["service4"]
}
上面例子表示服务1会调用(依赖于)服务2和服务3。
对于不使用服务注册,只使用服务发现的服务,可以用 main
来作 SERVICE_NAME
示例:
{
"main": ["service2", "service3"],
}
containerpilot
会根据 SERVICE_NAME
选取需要监控的依赖服务,比如设置 SERVICE_NAME
为 service1 ,则将监控 service2 和 service3,当依赖服务实例发生变化时,会更新 /etc/containerpilot/services.json
,生成的内容示例如下:
{
"service2": [
"1.1.1.1:80",
"2.2.2.2:80"
],
"service3": [
"3.3.3.3:80",
"4.4.4.4:80"
]
}
key 为依赖的服务名,value 为该服务的实例地址数组。
被托管服务要获取该文件的更新可通过如下方式:
- 静态语言中,可以设置环境变量
SERVICES_SIGNAL_CHANGED
,然后代码中处理SIGHUP
信号,收到信号时重新读取该文件。 - 动态语言如 PHP 不好处理信号的,可以每次处理请求时都重新读取该文件。
启动 containerpilot
原文章中有如下示例脚本
#!/bin/bash
# 假设外部已传入 CONSUL_JOIN_ADDR 环境变量
# 假设运行在 k8s 上,则 CONSUL_ADVERTISE 使用默认值即可
export SERVICE_NAME="test"
export SERVICE_PORT=80
export SERVICE_COMMAND="myservice args"
# containerpilot 默认使用健康检查命令 ./ping ,即需要把 ping 文件放在 WORKDIR 下,并在启动 containerpilot 前切换到 WORKDIR
# WORKDIR 默认为 /
cd /
# 启动 containerpilot
exec /etc/containerpilot/containerpilot.sh
网友评论