基於Docker+Consul+Registrator+Nodejs實現服務治理(一)
前言
服務治理分為兩篇文章,上篇介紹用Docker+Consul+Registrator實現服務註冊,下篇介紹使用Nodejs實現服務發現。
服務治理
服務治理是微服務中最基礎也是最核心的功能。 在剛開始構建微服務的時候,服務並不是特別的多,可以通過靜態配置方式來完成服務調用。比如A服務調用B服務某個業務,為了保證B服務的可用性,不管採用服務端負載還是客戶端的負載,都需要手動維護B服務的實例的清單。隨著業務越來越複雜,功能越來越多,我們的靜態配置就會變得越來越難,越來越難維護、擴展。所以需要尋求一種機制,讓每個服務能動態的創建地址,同時調用方要能獲取到這些信息、且感知地址的動態變化。
為了解決微服務維護實例問題,產生了大量的服務治理框架和產品。這些產品與框架的實現都是圍繞服務註冊與服務發現機制來實現服務治理的。
先看整體流程:
註冊中心:每個服務提供者向註冊中心登記自己提供的服務,將服務名與主機IP、埠等一些附加信息告知服務中心,註冊中心按服務名分類組織服務清單。如A服務運行在192.168.1.82:3000,192.168.1.83:3000實例上。那麼維護的內容如下:
同時註冊中心也還會以心跳的方式去檢查服務是否可用,如果不可用,則從服務實例中剔除。
服務消費者:在微服務的治理框架,服務之間的調用不再通過具體實例地址訪問,而是向服務名發起調用實現。如上述例子,在註冊中心註冊A服務後,訪問A服務的的調用方法就變為http://A服務/xxxx
,通過以下的步驟,在真正發起請求時,把A服務替換為服務實例地址。
- 服務消費者從服務消費者從訂閱註冊中心獲取A服務所有實例地址;
- 根據獲取的實例地址通過負載均衡(後續有時間會寫文章詳細說明)的策略獲取合適的Ip地址與埠,假設獲取到的實例地址為:192.168.1.82:3000;
- 把A服務地址替換為192.168.1.82:3000。
核心功能已介紹完成,下面我們就開始構建我們的註冊中心。
技術說明
Registrator:一個由Go語言編寫的,針對docker使用的,通過檢查本機容器進程在線或者停止運行狀態,去註冊服務的工具。所以我們要做的實驗,所有的工具都是在docker上運行的,就是因為registrator是通過檢查docker容器的狀態來判斷服務狀態的,這樣就和我們的代碼實現完全解耦了,對上層透明化,無感知。 它有如下特點:
- 通過docker socket直接監聽容器event,根據容器啟動/停止等event來註冊/註銷服務;
- 每個容器的每個exposed埠對應不同的服務;
- 支持可插拔的registry backend,默認支持Consul, etcd and SkyDNS;
- 自身也是docker化的,可以容器方式啟動;
- 用戶可自定義配置,如服務TTL(time-to-live)、服務名稱、服務tag等。
Consul:採用Go開發的高可用的服務註冊與配置服務,具體內容請參考官網:consul,本文的註冊中心採用Consul實現。
Docker:耳熟能詳,不解釋。
示例
示例中包含3台consul node構成consul cluster,1台registrator監控服務,3台service web 提供服務。 具體架構如下:
利用Registrator來監控每個web server的狀態,當有新的service web加入的時候,registator會把service web註冊到consul cluster,當web server下線的時,reigstrator也會通知consul cluster下線服務,整個過程自動化的,無須人工干預。
示例環境: 系統:macos; docker: 17.09.1-ce; docker-compose:1.17.1。
1. 首先搭建consul cluster、Registrator監控
在示例目錄下,創建模板文件docker-compose.yml
,源碼見:docker-compose.consul.yml
version: 3.0services: # consul server,對外暴露的ui介面為8500,可通過ui直接訪問consulserver,並且配置在2台consul伺服器的情況下集群才起作用 consulserver: image: progrium/consul:latest hostname: consulserver ports: - "8300" - "8400" - "8500:8500" - "53" command: -server -ui-dir /ui -data-dir /tmp/consul --bootstrap-expect=2 # consul server1在consul server服務起來後,加入集群中 consulserver1: image: progrium/consul:latest hostname: consulserver1 depends_on: - "consulserver" ports: - "8300" - "8400" - "8500" - "53" command: -server -data-dir /tmp/consul -join consulserver # consul server2在consul server服務起來後,加入集群中 consulserver2: image: progrium/consul:latest hostname: consulserver2 depends_on: - "consulserver" ports: - "8300" - "8400" - "8500" - "53" command: -server -data-dir /tmp/consul -join consulserver # 監聽容器中暴露的埠,埠發生變化,通知註冊中心作出相應處理 registrator: image: gliderlabs/registrator:master hostname: registrator depends_on: - "consulserver" volumes: - "/var/run/docker.sock:/tmp/docker.sock" command: -internal consul://consulserver:8500
進入模板目錄,運行 docker-compose up -d
啟動服務。在瀏覽器輸入http://127.0.0.1:8500/ui/#/dc1/nodes
,可以看到consul server 服務起來了。
三台consul server 對應ip分別為:
2. 搭建service web服務
創建新的目錄,創建docker-compose.yml
文件,源碼見:docker-compose.web.yml
version: 3.0# 啟動node-service-web節點服務services: web: image: windavid/node-service-test-web environment: SERVICE_3000_NAME: service-web ports: - "3000"
image為windavid/node-service-test-web
是我用nodejs實現,主要功能為獲取本地ip地址,加這個功能是為方便後面服務發現測試。代碼參考:app.js
運行docker-compose -f docker-compose.web.yml up -d --scale web=3
啟動服務,其中--scale web=3
表示啟動3台伺服器,可根據實際情況進行擴展。
此時可以看到3台service-web
已註冊到consul cluster中了。
至此我們的服務已經搭建完成,下一步我們簡單驗證服務註冊的功能。
3. 驗證服務註冊功能
驗證service-web服務下線情況
- 找到測試要下線的web,例如我們要下線nodeservicetestweb_web_1。
運行docker ps
命令,3台service-web信息:
- 下線nodeservicetestweb_web_1。
運行docker stop 6c7701d39184
,下線nodeservicetestweb_web_1,發現ip為172.22.0.7
伺服器信息已經從consul cluster中移除了 。
經過驗證,下載服務後,consul cluster會把相應的服務對應的信息移除
驗證consul cluster可用性
- 停止consul cluster主節點,本文中的leader節點為:consulserver。
運行docker ps
命令,3台consul server對應容器Id以及name:
運行docker stop be9508b34527
,暫停consulserver(leader)節點後,http://127.0.0.1:8500/ui 已經不能訪問了,這是因為我們對外只暴露了consulserver的8500埠,consulserver1,consulserver2沒有對外暴露可訪問的埠。 雖然通過UI的方式無法查看consul cluster 的狀態,不過我們可以進入容器查看集群的狀態。
運行docker logs afaed8bfb66a
查看日誌可得到雖然暫停consulserver服務,但是 consulserver1與consulserver2會進行重新選舉,consulserver1被選為主節點。
2018/04/20 12:22:50 [INFO] raft: Node at 172.22.0.5:8300 [Leader] entering Leader state 2018/04/20 12:22:50 [INFO] consul: cluster leadership acquired 2018/04/20 12:22:50 [INFO] raft: pipelining replication to peer 172.22.0.4:8300 2018/04/20 12:22:50 [INFO] raft: pipelining replication to peer 172.22.0.2:8300 2018/04/20 12:22:50 [INFO] consul: New leader elected: consulserver1
- 驗證service-web是否是可訪問
運行docker exec -it afaed8bfb66a /bin/bash
進入到consulserver1容器中,查看下service-web註冊的服務是否可以用。 在consulserver1容器中運行命令curl 127.0.0.1:8500/v1/catalog/service/service-web
發現服務的節點仍然存在。結果如下:
[{"Node":"consulserver","Address":"172.22.0.2","ServiceID":"registrator:nodeservicetestweb_web_2:3000","ServiceName":"service-web","ServiceTags":null,"ServiceAddress":"172.22.0.6","ServicePort":3000},{"Node":"consulserver","Address":"172.22.0.2","ServiceID":"registrator:nodeservicetestweb_web_3:3000","ServiceName":"service-web","ServiceTags":null,"ServiceAddress":"172.22.0.8","ServicePort":3000}]
經驗證consul節點是高可用,不會單個節點問題,而影響集群功能。
總結 && 參考
通過上述的示例,我們已經在單機環境下服務註冊功能,並驗證consul cluster高可用性。當然本示例比較簡單,只是在單機環境下,且不涉及到consul client的情況。建議大家在多機器情況也嘗試配置下。
參考:
https://www.jianshu.com/p/f8746b81d65d
https://www.jianshu.com/p/a4c04a3eeb57
https://segmentfault.com/a/1190000007601338
https://blog.csdn.net/socho/article/details/75434733
https://www.consul.io/docs/guides/index.html
後續
下篇介紹使用Nodejs實現服務發現。
推薦閱讀: