摘要: 将容器应用部署到集群时,其服务地址,即IP和端口, 是由集群系统动态分配的。那么,当咱们须要访问这个服务时,如何肯定它的地址呢?这时,就须要服务发现(Service Discovery)了。本文将以Nginx的部署为例,介绍服务发现的原理与实践。html
做者: KiwenLau前端
日期: 2016-09-04node
Nginx做为网页服务器,功能与Apache一致。使用Docker, 能够快速部署Nginx。python
sudo docker pull nginx:1.10
sudo docker run -d \ -p 8080:80 \ nginx:1.10
其中,-d选项表示Nginx容器在后台运行,-p选项表示主机的8080端口映射为容器的80端口。nginx
使用浏览器服务Nginxgit
http://192.168.59.1:8080github
或者使用curl命令访问Nginx, 其返回结果为html文件。docker
curl 192.168.59.1:8080
Nginx的服务地址是192.168.59.1:8080,其中,192.168.59.1是运行Nginx容器的主机的IP,而8080是Nginx提供服务的端口。json
可知,将容器应用部署到单个主机上时,服务的IP即为运行容器的主机IP,而服务的端口能够经过-p选项手动指定,这时服务地址至关因而静态分配的,所以不存在服务发现的问题。然而,当咱们将容器应用部署到多个节点的集群时呢?后端
首先,能够按照基于Docker搭建多节点Mesos/Marathon介绍的方法,快速搭建3个节点的Mesos/Marathon集群。部署Nginx时,能够不使用服务发现,也可使用Marathon LB提供服务发现。经过对比两种方式,能够更好地理解服务发现。
1. 不使用服务发现
Nginx定义(nginx1.json):
{ "id": "nginx1", "cpus": 0.2, "mem": 20.0, "instances": 1, "healthChecks": [{ "path": "/" }], "container": { "type": "DOCKER", "docker": { "image": "nginx:1.10", "network": "BRIDGE", "portMappings": [{ "containerPort": 80, "hostPort": 0, "protocol": "tcp" }] } } }
其中,instances为1,表示仅部署单个Nginx容器; hostPort为0,表示Nginx容器绑定的主机端口由Marathon随机分配。
部署Nginx:
curl -Ss \ -X POST \ -H "Content-Type: application/json" \ --data "@nginx1.json" \ http://127.0.0.1:8080/v2/apps | python2.7 -mjson.tool
这时,Nginx容器可能运行node2(192.168.59.2)上,也可能运行在node3(192.168.59.3)上,所以Nginx服务的IP是没法事先肯定的。而Nginx容器绑定的主机端口由Marathon随机分配,也不肯定。
固然,服务端口能够经过hostPort指定,可是这样作并不合适,由于有可能会发生端口冲突。当集群中运行了很是多不一样的服务时,静态分配端口是不现实的,也限制了集群的灵活性与扩展性。
在Slave节点上使用docker ps命令能够获取Nginx服务的IP与端口。
node2(192.168.59.2)
sudo docker ps | grep nginx b863d407b880 nginx:1.10 "nginx -g 'daemon off" 15 minutes ago Up 15 minutes 443/tcp, 0.0.0.0:31575->80/tcp mesos-d34d0b5b-c3b1-4020-9bb2-bb8582252bf3-S0.d2de6d05-9751-4fbe-af10-d7e35e9e6c7b
node3(192.168.59.3)
sudo docker ps | grep nginx
可知Nginx服务的IP与端口分别为192.168.59.2和31575,即Nginx的服务地址为:http://192.168.59.2:31575
每次从新部署Nginx时,其IP和端口会发生变化,这就意味着每次都要手动去查询服务地址,这很不方便,且没法将部署任务自动化。在容器集群中,一般须要运行很是多不一样的应用,这就意味着服务发现是容器集群系统的必备功能。
2. 使用Marathon LB提供服务发现
Marathon LB是Marathon的服务发现系统。Marathon LB经过使用Haproxy实现了代理服务器的功能。
经过使用Marathon LB能够配置服务的固定端口,而服务的IP就是运行Marathon LB的节点IP。Marathon LB会监听Marathon的调度事件,获取容器实际运行的IP与端口,而后更新Haproxy的配置文件。所以,当从新部署Nginx时,咱们仍然能够经过固定的IP与端口访问该服务。
Nginx定义(nginx2.json):
{ "id": "nginx2", "labels": { "HAPROXY_GROUP": "external" }, "cpus": 0.2, "mem": 20.0, "instances": 1, "healthChecks": [{ "path": "/" }], "container": { "type": "DOCKER", "docker": { "image": "nginx:1.10", "network": "BRIDGE", "portMappings": [{ "containerPort": 80, "hostPort": 0, "servicePort": 10000, "protocol": "tcp" }] } } }
其中,nginx2.json只有HAPROXY_GROUP与servicePort两处修改。HAPROXY_GROUP为external,表示Nginx将使用分组为external的Marathon LB作服务发现。servicePort为10000,表示Nginx将使用Marathon LB节点的10000端口提供服务。
部署Nginx:
curl -Ss \ -X POST \ -H "Content-Type: application/json" \ --data "@nginx2.json" \ http://127.0.0.1:8080/v2/apps | python2.7 -mjson.tool
这时,Nginx服务的IP为运行Marathon LB的节点IP,即192.168.59.1,而Nginx服务的端口为servicePort指定的端口,即10000。所以,Nginx的服务地址为:http://192.168.59.1:10000。而Nginx容器的实际地址为http://192.168.59.2:31270,Marathon LB使用Haproxy做为代理服务器转发的服务请求。Marathon LB会监听Marathon的调度事件,获取容器实际运行的IP与端口,而后更新Haproxy的配置文件。下面即为Marathon LB自动生成的Haproxy配置文件:
frontend nginx2_10000 bind *:10000 mode http use_backend nginx2_10000 backend nginx2_10000 balance roundrobin mode http option forwardfor http-request set-header X-Forwarded-Port %[dst_port] http-request add-header X-Forwarded-Proto https if { ssl_fc } option httpchk GET / timeout check 20s server 192_168_59_2_31270 192.168.59.2:31270 check inter 60s fall 4
可知Haproxy中,niginx服务的前端(frontend)地址为: *:10000,然后端(backend)地址为: 192.168.59.2:31270。Haproxy负责将服务请求转发到Nginx容器。
当咱们从新部署Nginx时,Nginx容器的IP和端口会发生变化,Marathon LB会更新Haproxy的配置文件,所以咱们仍然能够经过http://192.168.59.1:10000访问服务。
所以,Marathon LB的任务就是发现服务的地址(IP和端口),而后用户就不用每次手动查询了。bamboo与nixy实现了一样的功能。