本文源自3月30『高效開發運維』微信群的在線分享,轉載請在文章開頭註明來自『高效開發運維』公眾號。加群學習請關注『高效開發運維』公眾號,並點擊菜單中的「加群學習」或直接回復「加群」。
隨著微服務概念深入人心,越來越多的解決方案選擇使用微服務架構,這類架構的共同點是服務數量多,因此種類繁多的服務之間如何互相訪問就變成了一個很現實的問題。目前比較流行的分散式存儲有:Consul, etcd, ZooKeeper。今天主要分享下如何基於Consul來實現服務發現。
關於服務發現及Consul
將應用部署到集群時,服務IP或埠是動態分配的,用戶訪問時不知道後端的IP及埠在訪問這些服務時,使用一定的機制動態發現服務的IP及埠就是服務發現(Service Discovery)。
Consul是一個服務發現和配置管理的工具,是具有分散式、高可用、高度可擴展的服務。
Consul的特性服務發現
Consul客戶端可以提供其他服務的服務發現功能,使用dns和http,服務發現問題很容易被解決。
監控檢查
使用Consul可以提供任意數量的健康檢查功能,無論是服務狀態(web伺服器返回 http 200)還是本地節點的信息(內存利用率超過90%),這些信息都可以由操作員進行設置,並由服務發現組件將服務流量從不健康的組件中移開。
key/value存儲
Consul的kv存儲功能,通過http api可以很容易完成動態配置管理、協調服務、主選舉、功能標記等服務。
跨中心部署
Consul支持多數據中心感知,可以支撐任意數量的區域無需複雜配置。
節點節點類型
server
服務發現、健康檢查、數據一致性存儲,leader選舉機制,所以奇數部署
agent
健康檢查
Consul節點退出
正常退出(至關重要)
節點通過信號正常關閉,接到信號的Consul會通知其他節點,正常離開後上面的服務和健康檢查將被移除
異常退出
沒有通過信號正常關閉的節點,其他節點會由於沒有接到信號,認為這個節點還是正常的,也不會移除其服務和健康檢查。本文主要分享服務發現和健康檢查兩個特性。
為了更加方便快捷的部署,採用Consul進行docker化的方式實現,Consul本身安裝很簡單,只安裝二進位的包即可使用。首先從組件下載頁找到合適系統的二進位包下載,下載后將二進位的包拷貝安裝在對應的可被執行的目錄里,如:~/bin 或者 /usr/local/bin。Consul 還有web_ui,需要單獨下載,並在啟動指定目錄即可。
dockerfile
集群運行
Consul可以運行client和server 兩種節點模式,每個數據中心必須有一個server節點,但如果高可用,需要部署3個以上。其它Consul全部運行在client模式。客戶端連接會有一個非常輕的註冊過程,運行健康檢查併發給server節點。client運行在每個節點上。
ansible批量部署Consul server (agent與server類似)Consul server ansible模板片段
Consul agent ansible模板片段
上面啟動的信息中每台主機啟動的參數不同的主要是IP,所以使用ansbile的時候就用到了一個小技巧,在ansible的hosts配置文件中給每條主機信息添加一個變數 ansible_ssh_host,在ansible運行中直接引用hosts配置中的變數,其它相對來說比較固定的參數,填寫到ansible的全局變數文件ansible/inventory/group_vars/all中,或執行playbook時傳入就可以了。
ansible hosts配置示例
base-Consul-master ansible role 片段
base-Consul-master playbook
調用playbook的執行腳本
調用playbook是可以指定主機或主機組
部署Consul master
指定ansible role中的tag 執行任務
ansible批量註冊服務健康檢查到ConsulConsul服務定義
Consul中的服務可以在啟動前就定義 ,提前寫好定義服務的配置文件放置到 Consul配置目錄Consul.d中, 如果這種方式需要更新,則需要重啟或發送SIGHUP信號給Consul ,不太方便操作,因此推薦使用Consul api的方式來註冊服務。註冊服務的信息主要包含id, name, port, address, check每個服務可以由多個ip可以有多個埠的服務組成。需要注意的是:每個服務可以有多個check,check可以是埠檢查、http檢查、腳本檢查。其中檢查腳本通常是自由做任何事情,以確定檢查的狀態。唯一的限制是,該退出代碼都必須遵守此約定:
Exit code 0 - 檢查結果是 passing
Exit code 1 - 檢查結果是 warning
其它 Exit code - 檢查結果是 failing
註冊服務通常是通過agent來註冊,因為服務的健康檢查與所在Consul agent是綁定的,如果 Consul agent所有宿主機掛掉,就認為這台主機上的服務也是掛的。
Consul健康檢查參考:
ansible註冊服務及健康檢查到Consul agent模板reg_Consul.sh片段註冊的服務有問題,需要刪除調用deregistry介面
刪除一個服務
刪除一個檢查
agent api 參考:注意:
同一個服務 每個實例的id唯一
同一個服務 每個檢查項的id唯一
服務發現
Consul啟動后,並且服務集群狀態正常,就可以對外提供DNS或者HTTP API來查詢服務了。
http api通過Consul server查詢服務列表信息
通過Consul server查詢單個服務信息
通過agent查詢服務服務
只查詢健康的服務
(不加passing參數會包含warning的服務,不包含critical的服務)
agent操作api參考 https://www.consul.io/docs/agent/http/agent.html
Consul dns服務發現
Consul自帶一套dns 服務,所以可以處理簡單的服務發現。通過dig命令查詢Consul dns中的服務解析, critical的服務不會解析
使用dnsmasp轉發Consul dns
Consul默認的dns服務埠是8600,常用的dns服務默認的埠為53,所以如果使用8600,不太方便,而且Consul 0.7.0版本后才支持修改默認Consul dns埠,就算可以修改Consul dns埠,但又可能會跟其它的dns服務衝突。
可以使用centos7自帶的服務dnsmasp來轉發Consul dns和其它dns服務在/etc/dnsmasq.d/dns-hosts.conf中添加 Consul dns地址
配置NetworkManager
在resolv.conf中添加search service.Consul和默認的dns server指定到本機,
這樣即可直接在宿主機使用dns訪問到Consul中的服務了,而且可以使用短域名訪問,例如:
使用registrator實現自動註冊服務
registrator通過在容器啟動時檢查容器來自動註冊和註銷任何Docker容器的服務。registrator支持自動註冊的服務包括Consul,etcd和SkyDNS 2github地址:
直接使用官方鏡像即可。
docker run方法
服務自動註冊的id name check等需要在被註冊的服務啟動環境變數指定。例如:
docker run -d --net host -p 5014:5014 -e SERVICE_5014_ID=cadivisor -e SERVICE_5014_NAME=cadivisor cadivisor
注意:使用Consul做服務發現一般為host模式啟動的服務,埠是固定的。由於埠問題,一台宿主機也只部署一個實例。host模式一般不添加 -p 或 --publish參數,但registrator 的默認註冊規則,是有--publish都會自動註冊到Consul, 所以host 模式 就算是不需要埠映射,也需要添加--publish參考指定埠,這樣registrator 就會把服務註冊到Consul了。
參考 :
haproxy配合Consul dns實現服務發現
haproxy做為PaaS平台服務的負載均衡服務,對外服務;配置backend服務時,配置的是Consul中的服務域名。
這裡有個坑,原來使用haproxy 1.5版本, 後端服務使用域名時,啟動后只解析一次(和nginx類似),這時如果解析到的服務掛掉,訪問haproxy頁面時會503。查詢官網得知haproxy 1.6支持了動態dns 域名解析的配置,后升級為haproxy 1.6。
下面是動態DNS解析相關的配置內容:
用戶訪問haproxy就會動態解析訪問後端Consul中的服務。
基於Consul dns,實現mysql高可用切換
數人云最初的Mysql主從切換是基於 haproxy+keepalived 來做高可用的。這種機制下,Mysql主從切換的簡單需求需要引入2個開源組件,架構上也非常複雜。為了減輕架構複雜度和可維護性,將主從切換改為了使用Consul進行主從切換。Mysql是安裝了兩台,一主一從,設置許可權時,主是讀寫的,從是普通用戶只讀許可權。通過將服務註冊到Consul來做健康檢查,Mysql Master和Slave註冊四個服務到Consul, 如下代碼:
正常情況mysql-vip.service.Consul解析到 主mysql ip。這裡分兩種情況切換到從解析:— 主Mysql掛掉后,ip解析到從mysql ip,mysql-vip 切換到從Mysql IP 。— 主庫的Consul 掛掉也會導致服務無法解析,即使 mysql-master正常,如果Consul掛掉也會被激活切換。
請注意:因為Mysql從是只讀的,相當於降級服務。
接下來再分享一下Consul實踐中遇到的一些坑。
Consul實踐遇到的坑調用deregistry介面刪除Consul服務后,詭異的出現了
初期進行http api 調試測試時,註冊了一些服務到Consul, 後來想刪除就調用,ip:8500/v1/catalog/deregister 介面進行刪除,調用該介面返回成功。但過一會兒去web ui查詢, 發現剛才刪除的服務還在,後來查詢官方文檔,原來server端catalog和 agent端都有deregister介面,我們的服務都是通過agent註冊的,所以在catalog 刪除不生效,只能通過agent的 deregistry介面來刪除。
清理Consul持久化數據異常
測試Consul server服務的高可用,先停止其中一台Consul server節點的容器,並清理Consul 的持久化數據,其它服務的Consul節點正常,達到預期結果,後來嘗試恢復這個Consul server,恢復后發現從Consul ui查發現 少了一些服務節點,查詢過後,正好是清理數據的這台Consul server上的服務在 Consul 中消失了,因此又重新在這個節點進行相應的服務註冊才真正恢復正常。
健康檢查狀態碼的坑
健康檢查腳本中使用grep檢查結果內容,如果匹配則正常,不匹配為異常,例如: echo result|grep ok,有一次,一個服務節點異常,grep也沒有匹配,當時認為這樣就不會被解析了,後來發現這個服務ip還是會被解析,查詢問題發現grep 不匹配的error code 為1 ,code 1 只是warning ,還是會被解析,所以修改檢查腳本內容,如果不匹配,手動返回error code 2,之後這個異常服務才不解析。
使用registrator自動註冊的坑
在數人云的PaaS平台發布的服務,一般服務容器名稱都是隨機的,registrator 默認的自動註冊規則是把容器名註冊為 Consul中的service id,這樣在Consul就有可能同一個服務被註冊多次,實際上是同 個服務不同實例。所以需要在啟動容器的環境變數中指定SERVICE_xxx_NAME ,這樣registrator註冊的時候就不會同是有多個相同的服務但server name 的不同 ,同理,如果需要consul 中的 service id 保持 致,則需要啟動容 時指定SERVICE_xxx_ID。
registrator 默認 自動註冊的服務在consul中 帶健康檢查的,所以容器實例掛掉,consul的 頁面上是看不 出的,會導致consul dns解析到異常的容器實例 上,所以需要在啟動容 時需 要添加類似下面的環境變數。
Consul agent節點異常
部署客戶環境時遇到一個詭異的問題,有兩個節點總是異常,健康檢查這兩個節點時好時壞,大部分時間是壞的,少部分時間顯示正常狀態。
排查問題時,發現主機之間響應時間很快,Consul server 與Consul agnet 之間的Consul埠都是可以通的。
查詢Consul server日誌,看到 server 日誌中有error
以為是有埠漏開放了,telnet 測試后,發現Consul server到agent都是通的,谷歌下這個錯誤,發現有可能是因為沒有開通udp埠導致的,後來又使用nmap測試Consul相關的udp埠是不是開放的,測試后發現udp 果然沒有開通。於是就向客戶申請開通相應的udp埠,開通后,再進行測試,發現問題還是存在。
這就不得不再想其它辦法,後來又查詢異常的Consul agent節點日誌發現,發現Consul agent 也報類似的錯誤,乍一看跟server端的日誌是一樣的,但仔細研究發現關鍵問題,異常節點的agent報了很多訪問其它agent節點8301埠的 time out的日誌:
才發現agent不僅訪問server, 還需要訪問同級的其它agent 8301埠進行健康檢查,後來開放8301所有Consul節點的埠訪問后才正常。
比起另外兩個同樣流行的分散式存儲etcd和ZooKeeper, Consul從設計上就考慮到了很多服務發現的需求, 比如說健康檢查, 服務註冊, DNS等。所以基於Consul來實現服務發現的功能還是有很多的想象空間的。
QA環節
Q1:感謝老師!Consul, etcd, ZooKeeper能比較一下嗎?為什麼沒有選用后兩者呢?
A1:ZooKeeper 處理數據 不支持 resf api , 需要操作的時候比較繁瑣,功能也比較單一,etcd 服務功能也相對單一,需要依賴其它 組件 配合使用Consul 本身自帶服務發現、健康檢查 、dns解析 功能相對來說強大一些,不需要依賴其它的組件就可能實現很多功能
Q2:就服務發現和zk相比,會有多大的延遲?
A2: 就服務發現來說,Consul 支持 api 和 dns 兩個方式獲取 服務, dns 解析基本無消耗。 api 操作的話,具體的數據我沒有進行嚴格測試,但zk是java服務本身需要的內存比較多,java gc 垃圾回收也會影響一些性能。Consul 是go 語言開發性能會有一些優勢。
Q3: 這裡問一個不是很相關的問題,老師怎麼看待Docker和微服務更配這樣的說法?
A3: 以前是所有功能集成到一個服務中,微服務本身需要拆分服務,需要部署、更新、維護的服務數量會幾何增加,如果是傳統的方式運維的話,操作起來就是個挑戰,Docker化之後,部署、遷移、交付都會變簡單。
Q4:Consul最大承受多大規模,有瓶頸嗎?
Q4: 我沒有針對 Consul服務 做過壓力測試,我們一般是會對服務進行壓力測試,就現在我們部署的集群規模而言,支持幾百來的Consul 節點是沒有問題的。理論上可以更多。
Q5:內部的web服務怎麼註冊?用後台腳本周期性地檢測web服務正常后,再請求介面註冊服務嗎?失敗取消
A5: 如果 使用了 registrator ,docker服務啟動時,添加相應的環境變數,就會自動註冊到Consul,也會自動刪除,不過registrator偶爾異常情況會有不同步的現象,我們在 registrator 啟動時添加同步參數定時同步,如果沒有使用 registrator ,就需要自己處理註冊和刪除的邏輯,例如我們使用marathon /mesos 做為調試的核心基礎組件,可以監聽marathon 的事件獲取 容器實例 啟動在哪,刪除了哪些,就可以相應對 Consul做操作Consul 也支持異常服務定時清理,在註冊添加時添加DeregisterCriticalServiceAfter 參數 就可以
Q6:「每個數據中心必須有一個server節點,但如果高可用,需要部署3個以上」為啥一定要三個以上
A6: Consul server 節點也分為 leader 和 follower 節點,leader 選舉一般為基數選舉,1個就不是高可用了,所以最少3個,遵循2N+1 原則,支持宕機 N 個節點。
Q7: Consul能夠支撐多大的高併發系統?
A7: 這個問題跟上面的問題稍有些重合,其實高併發是指的業務,業務一般在 啟動、停止、遷移 、或業務之間相互訪問時 Consul 才會有些數據交互 ,Consul的健康檢查 和 訪問都是分攤到每個宿主機上, Consul 的壓力 對比業務來說可以忽略不計。
作者介紹
劉金燁,數人云運維工程師。早期做 Java 開發,後轉職為運維開發,曾就職於仙掌軟體、金山西山居,現就職於數人云。負責數人雲平台 Mesos/Marathon/Docker 環境維護、運維自動化建設。