服务发现在SOA(Service-Oriented Architecture)架构中是一个很重要的概念,是支撑大规模 SOA 的核心服务,在应用Docker容器集群的实践中也是非常重要的功能。对于Docker容器之间跨主机访问这个难题,服务发现是目前较为实用的解决方案。
服务发现的功能是管理一个集群中的进程或服务之间的通信,涉及到服务列表,在服务列表中注册服务,并且能够在服务列表中查找并连接服务。服务发现的基本思想是任何一个应用的实例能够以编程的方式获取当前环境的细节,这是为了让新的实例可以嵌入到现有的应用环境而不需要人工干预。当有任何进程监听TCP或UDP端口时,服务发现组件都应该能够察觉,并且能够根据名字查找到并连接上这个端口。复杂系统的服务发现组件要提供更多的功能,例如,服务元数据存储、健康监控、多种查询和实时更新等。总的来说,服务发现提供了一种协调机制,方便服务的发布和查找。
设计目标是减少或消除组件之间的“手动”的连接。当你把你的应用程序推送进生产的时候,所有的这些事情都可以配置:数据库服务器的主机和端口,REST服务的 URL等等,在一个高可扩展的架构中,这些连接可以动态改变。一个新的后端可以被添加,一个数据库节点可以被停止。你的应用需要适应这种动态环境。普遍原则是:当启动的时候,一个服务的实例必须注册进配置服务器。当停止的时候(完美停止或是 Crash 了),节点必须从配置服务中移除掉。注册后,其他服务可以在配置服务器中搜索到提供制度服务的实例列表(主机和端口)。
工作过程:每一个服务发现工具都会提供一套API,使得组件可以用其来设置或搜索数据。正是如此,对于每一个组件,服务发现的地址要么强制编码到程序或容器内部,要么在运行时以参数形式提供。通常来说,发现服务用键值对形式实现,采用标准http协议交互。
工作方式:当每一个服务启动上线之后,他们通过发现工具来注册自身信息。它记录了一个相关组件若想使用某服务时的全部必要信息。例如,一个MySQL数据库服务会在这注册它运行的ip和端口,如有必要,登录时的用户名和密码也会留下。
当一个服务的消费者上线时,它能够在预设的终端查询该服务的相关信息。然后它就可以基于查到的信息与其需要的组件进行交互。负载均衡就是一个很好的例子,它可以通过查询服务发现得到各个后端节点承受的流量数,然后根据这个信息来调整配置。
etcd: 这是CoreOS的创建者提供的工具,面向容器和宿主机提供服务发现和全局配置存储功能。它在每个宿主机上有基于http协议的API和命令行的客户端。
Zookeeper: 最早做服务发现的开源项目之一,起源于Hadoop,帮助在Hadoop集群中维护各种组件。它非常成熟、可靠,被许多大公司(YouTube、eBay、雅虎等)使用。其数据存储的格式类似于文件系统,如果运行在一个服务器集群中,Zookeeper实现了跨所有节点共享配置状态。
consul: 这个服务发现平台有很多高级的特性,使得它脱颖而出,例如:配置健康检查、ACL功能HAProxy配置等等。
Consul是强一致性的数据存储,与Zookeeper和etcd不一样,Consul内嵌实现了服务发现系统,所以这样就不需要构建自己的系统或使用第三方系统,另外,Consul还包括节点健康检查和运行在其上的服务。
总结:
服务发现工具使得Docker容器可以适应它们当前所处环境并嵌入现有的组件。这是实现提供方便、容易扩展和部署的功能的一个重要的先决条件,该功能通过允许组件跟踪和应对他们所在环境变化来实现。
==============================
Consul是基于GO语言开发的开源工具,主要面向分布式、服务化的系统提供服务注册、服务发现和配置管理的功能。Consul的功能都很实用,其中包括:服务注册/发现、健康检查、Key/Value存储、多数据中心和分布式一致性保证等特性。Consul本身只是一个二进制的可执行文件,所以安装和部署都非常简单,只需要从官网下载后,在执行对应的启动脚本即可。
Consul特性
为什么微服务架构下就需要做服务注册和服务发现呢?微服务的目标就是要将原来大一统的系统架构,拆分成细粒度的按功能职责分成的小系统,这样就会出现很多小的系统,部署的节点也会随之增加。试想一下,如果没有一个统一的服务组件来管理各系统间的列表,微服务架构是很难落地实现的。
Consul提供的服务注册/发现功能在数据强一致性和分区容错性上都有非常好的保证,但在集群可用性下就会稍微差一些(相比Euerka来说)。
Consul采用了一致性算法Raft来保证服务列表数据在数据中心中各Server下的强一致性,这样能保证同一个数据中心下不管某一台Server Down了,请求从其他Server中同样也能获取的最新的服务列表数据。数据强一致性带来的副作用是当数据在同步或者Server在选举Leader过程中,会出现集群不可用。
Consul支持多数据中心(Data Center),多个数据中心之间通过Gossip协议进行数据同步。多数据中心的好处是当某个数据中心出现故障时,其他数据中心可以继续提供服务,提升了可用性。
Consul支持基本硬件资源方面的检查,如:CPU、内存、硬盘等
Consul支持Key/Value存储功能,可以将Consul作为配置中心使用,可以将一些公共配置信息配置到Consul,然后通过Consul提供的 HTTP API来获取对应Key的Value。
======================================
实验环境准备
Consul:分布式、高可用的,服务发现和配置的工具,数据中心
Registrator:负责收集dockerhost上,容器服务的信息,并且发送给consul
Consul-template:根据编辑好的模板生成新的nginx配置文件,并且负责加载nginx配置文件
| 主机名 | IP地址 | 所需组件
|
| docker01|192.168.100.205 |consul,consul-template,nginx
| docker02|192.168.100.105 |consul, registrator
| docker03|192.168.100.134 |consul, registrator
注:实验环境关闭防火墙、禁用SElinux
[root@docker01 ~]# rz //上传压缩包
[root@docker01 ~]# unzip consul_1.5.1_linux_amd64.zip //解包,解压后会得到一个命令
[root@docker01 ~]# mv consul /usr/local/bin/ // 移动到命令存放路径
[root@docker01 ~]# chmod +x /usr/local/bin/consul //赋予执行权限
[root@docker01 ~]# nohup consul agent -server -bootstrap -ui -data-dir=/var/lib/consul-data -bind=192.168.1.205 -client=0.0.0.0 -node=master &
//nohup: 忽略输入并把输出追加到"nohup.out"
//执行命令后,会提示该信息,并占用终端,按两下回车键即可,
//运行上述命令后,会在当前目录下生成一个名为“nohup.out”的文件,其存放的是consul服务的运行日志
//执行上述命令后,consul就放到后台运行了,并返回其PID号,可以通过“jobs -l”命令进行查看
上述命令的相关参数解释如下:
-server:添加一个服务;
-bootstrap:一般在server单节点的时候使用,自选举为leader;
-ui:开启内部的web界面;
-bind:指定开启服务的IP(就是本机IP咯);
-client:指定服务的客户端(一般此处为任意);
-node:在集群内部通信使用的名称,默认是主机名。
开启的端口作用如下:
8300:集群节点;
8301:集群内部访问的端口;
8302:跨数据中心之间的通信;
8500:http_ui;
8600:DNS
[root@docker01 ~]# consul info //可以看到这个群集的leader及版本信息
//如:leader_addr = 192.168.100.205:8300
[root@docker01 ~]# consul members //查看consul集群内成员的信息
至此,客户端可以访问docker01的8500端口进行验证:
docker02配置如下:
[root@docker02 ~]# docker run -d --name consul -p 8301:8301 -p 8301:8301/udp -p 8500:8500 -p 8600:8600 -p 8600:8600/udp --restart=always progrium/consul -join 192.168.100.205 -advertise 192.168.100.105 -client 0.0.0.0 -node=node01
//上述命令中,“-join”是指定leader的IP地址(也就是docker01);“-advertise”是指定自己本身的IP地址
docker03配置如下:
[root@docker03 ~]# docker run -d --name consul -p 8301:8301 -p 8301:8301/udp -p 8500:8500 -p 8600:8600 -p 8600:8600/udp --restart=always progrium/consul -join 192.168.100.205 -advertise 192.168.100.134 -client 0.0.0.0 -node=node02
//上述命令中,“-join”是指定leader的IP地址(也就是docker01);“-advertise”是指定自己本身的IP地址
//注意:node名称在consul群集中,必须唯一
至此,执行“consul members”命令即可查看到docker02及docker03的信息:
[root@docker01 ~]# consul members
Node Address Status Type Build Protocol DC Segment
master 192.168.100.205:8301 alive server 1.5.1 2 dc1
node01 192.168.100.105:8301 alive client 0.5.2 2 dc1
node02 192.168.100.134:8301 alive client 0.5.2 2 dc1
浏览器访问consul服务,验证集群信息:
http://192.168.100.205:8500
registrator是一个能自动发现docker container提供的服务,并在后端服务注册中心注册服务或取消服务的工具,后端注册中心支持conusl、etcd、skydns2、zookeeper等。
docker02配置如下:
[root@docker02 ~]# docker run -d --name registrator -v /var/run/docker.sock:/tmp/docker.sock --restart always gliderlabs/registrator consul://192.168.100.105:8500
//上述命令的作用是将收集的容器信息发送给本机的8500端口来显示
docker03配置如下:
[root@docker03 ~]# docker run -d --name registrator -v /var/run/docker.sock:/tmp/docker.sock --restart always gliderlabs/registrator consul://192.168.100.134:8500
//上述命令的作用是将收集的容器信息发送给本机的8500端口来cha显示
浏览器测试访问,这里我只访问了node1,这里就不访问node2了:
yum -y install gcc openssl openssl-devel zlib zlib-devel pcre pcre-devel
tar zxf nginx-1.14.0.tar.gz -C /usr/src/
useradd -M -s /sbin/nologin nginx
cd /usr/src/nginx-1.14.0/
./configure --user=nginx --group=nginx --with-http_stub_status_module --with-http_realip_module --with-pcre --with-http_ssl_module && make && make install
ln -s /usr/local/nginx/sbin/* /usr/local/sbin/
nginx -t
nginx
注:consul-template的作用就是将收集到的信息(把registrator收集到容器的信息)写入template模板中,并且最终写入Nginx的配置文件中。
[root@docker01 ~]# rz //上传包
[root@docker01 ~]# unzip consul-template_0.19.5_linux_amd64.zip //解包
[root@docker01 ~]# mv consul-template /usr/local/bin/ //移动到命令搜索路径
[root@docker01 ~]# chmod +x /usr/local/bin/consul-template //赋予执行权限
[root@docker01 ~]# cd /usr/local/nginx/
//在Nginx安装目录下,编写模板供consul-template命令工具使用,并且配置Nginx反向代理
[root@docker01 nginx]# mkdir consul
[root@docker01 nginx]# cd consul/
[root@docker01 consul]# vim nginx.ctmpl
upstream http_backend {
{{range service "nginx"}}
server {{ .Address }}:{{ .Port }};
{{ end }}
}
server {
listen 8000;
server_name localhost;
location / {
proxy_pass http://http_backend;
}
}
//编辑完成后,保存退出即可
[root@docker01 consul]# vim ../conf/nginx.conf //在主配置文件中进行调用生成的vhost.conf文件
include /usr/local/nginx/consul/*.conf;
} //在配置文件末尾的花括号上方写入“include”配置,进行调用vhost.conf文件
[root@docker01 consul]# nginx -s reload //重启nginx服务
[root@docker01 consul]# nohup consul-template -consul-addr 192.168.100.205:8500 -template "/usr/local/nginx/consul/nginx.ctmpl:/usr/local/nginx/consul/vhost.conf:/usr/local/sbin/nginx -s reload" &
//同样也是将这条命令放入后台执行,否则会占用前台的终端
//这条命令的作用就是将本机收集到的信息,生成一个vhost.conf的文件,将命令放入后台才能保证实时发现同步并更新
配置至此,docker02或者docker03上一旦有任何Nginx相关的容器以后台“-d”的运行方式运行,都会被添加到反向代理中来,进行调度,一旦容器发生意外关闭,则可以自动从反向代理配置文件中剔除。
现在可以在docker02、和docker03上分别运行两台Nginx容器,其容器名称依次为web01、web02…,其网页文件依次为:this is web01 test、this is web02 test…
为其准备不同的网页文件的目的就是方便客户端访问时区分访问的是哪台容器。
由于其配置过程类似,我这里就写出一个运行Nginx容器的过程,其他照做即可。
配置示例如下(运行web01并修改其首页文件):
[root@docker02 ~]# docker run -d -P --name web01 nginx
[root@docker02 ~]# docker exec -it web01 /bin/bash
root@1aaf578ec6f7:/# echo "this is a web01 test." > /usr/share/nginx/html/index.html
注:在docker02及docker03运行四个Nginx容器后(必须以后台运行的方式,也就是说在运行时必须有“-d”选项),那么,此时访问docker01的8000端口,就会循环访问到这四个容器提供的网页文件,如下:
[root@docker01 consul]# curl 192.168.100.205:8000
this is a web01 test.
[root@docker01 consul]# curl 192.168.100.205:8000
this is a web02 test.
[root@docker01 consul]# curl 192.168.100.205:8000
this is a web03 test.
[root@docker01 consul]# curl 192.168.100.205:8000
this is a web04 test.
[root@docker01 consul]# curl 192.168.100.205:8000
this is a web01 test.
[root@docker01 consul]# curl 192.168.100.205:8000
this is a web02 test.
//并且查看以下文件,会看到其中的配置
[root@docker01 consul]# cat /usr/local/nginx/consul/vhost.conf //以下web池中的server都是基于编写的模板自动生成的
upstream http_backend {
server 192.168.100.105:32768;
server 192.168.100.105:32769;
server 192.168.100.134:32768;
server 192.168.100.134:32769;
}
server {
listen 8000;
server_name localhost;
location / {
proxy_pass http://http_backend;
}
}
//由于consul-template是在后台运行的,所以,只要检测到容器的变化,就会动态修改上述文件
//并且重启Nginx服务,使更改生效
至此,consul+registrator+docker实时服务发现就配置完成了