摘要: 將容器應用部署到集羣時,其服務地址,即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實現了一樣的功能。