DockOne微信分享(八十四):Docker在B站的实施之路
生活随笔
收集整理的這篇文章主要介紹了
DockOne微信分享(八十四):Docker在B站的实施之路
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
本文講的是DockOne微信分享(八十四):Docker在B站的實施之路【編者的話】B站一直在關(guān)注Docker的發(fā)展,去年成功在核心SLB(Tengine)集群上實施了Docker。今年我們對比了各種Docker實施方案后選擇了Mesos。結(jié)合CI&CD,打通了整個業(yè)務(wù)的Docker上線流程,并實現(xiàn)了全自動擴(kuò)縮容。這次結(jié)合我們的實施之路,分享一下遇到的重點與難點:
自研Docker網(wǎng)絡(luò)插件的介紹; Bili PaaS平臺中的CD實現(xiàn)與優(yōu)化; 應(yīng)用全自動擴(kuò)縮容的實現(xiàn)方案; Nginx動態(tài)Upstream跟Docker的配合使用。
B站一直在關(guān)注Docker的發(fā)展,去年成功在核心SLB(Tengine)集群上實施了Docker,規(guī)模不大但訪問量大、沒有CI & CD的工作。隨著業(yè)務(wù)量的增長,應(yīng)用擴(kuò)縮需求越來越多。但在沒有docker標(biāo)準(zhǔn)化的情況下,應(yīng)用擴(kuò)容需要擴(kuò)服務(wù)器,操作繁重。同時為了減少因測試、線上環(huán)境不一致導(dǎo)致的問題,我們計劃將業(yè)務(wù)全部Docker化,并配合CI & CD,打通整個業(yè)務(wù)上線流程,達(dá)到秒級動態(tài)擴(kuò)縮容。
下面是我們的實施之路,整體架構(gòu)圖如下:
Bridger:Docker分配私有IP,本機通過bridge跟容器通信。不同宿主機如果要通信需要iptables映射端口。隨著容器的增多,端口管理會很混亂,iptables規(guī)則也越來越多。
Host:使用宿主機的網(wǎng)絡(luò),不同容器不能監(jiān)聽相同端口。
None:Docker不給容器分配網(wǎng)絡(luò),手動分配。
正當(dāng)我們無法選定Docker的網(wǎng)絡(luò)方案時,發(fā)現(xiàn)最新的Docker 1.12版本提供了另外兩種網(wǎng)絡(luò)驅(qū)動:Overlay和Macvlan。
Overlay:在原來的TCP/IP數(shù)據(jù)包基礎(chǔ)上再封裝成UDP的數(shù)據(jù)包傳輸。當(dāng)網(wǎng)絡(luò)出現(xiàn)問題需要抓包時,會比較麻煩。而且,Overlay依靠服務(wù)器的CPU來解UDP數(shù)據(jù)包,會導(dǎo)致Docker網(wǎng)絡(luò)性能非常不穩(wěn)定,性能損耗比較嚴(yán)重,在生產(chǎn)環(huán)境中難以使用。
Macvlan:在交換機上配置VLAN,然后在宿主機上配置物理網(wǎng)卡,使其接收對應(yīng)的VLAN。Docker在創(chuàng)建Network時driver指定Macvlan。對Docker的Macvlan網(wǎng)絡(luò)進(jìn)行壓測,跑在Macvlan網(wǎng)絡(luò)的容器比跑在host網(wǎng)絡(luò)的容器性能損失10~15%左右,但總體性能很穩(wěn)定,沒有抖動。這是能接受的。
基于Macvlan,我們開發(fā)了自己的IPAM Driver Plugin—底層基于Consul。
Docker在創(chuàng)建Macvlan網(wǎng)絡(luò)時,驅(qū)動指定為自己研發(fā)的Consul。Consul中會記錄free和used的IP。如下圖:
IPAM Driver在每臺宿主機上都存在,通過Socket的方式暴露給Docker調(diào)用。Docker在創(chuàng)建容器時,IPAM Plugin會從Consul申請一個free的IP地址。刪除容器時,IPAM Plugin會釋放這個IP到Consul。因為所有宿主機上的IPAM Plugin連接到的是同一個Consul,就保證了所有容器的IP地址唯一性。
我們用IPAM Plugin遇到的問題:
1)?Consul IPAM Plugin在每臺宿主機上都存在,通過Socket方式調(diào)用,目前使用容器啟動。
當(dāng)Docker daemon重啟加載Network時,因為容器還未啟動,會找不到Consul IPAM Plugin的Socket文件,導(dǎo)致Docker daemon會去重試請求IPAM,延長daemon的啟動時間,報錯如下:
level=warning?msg="Unable?to?locate?plugin:?consul,?retrying?in?1s" level=warning?msg="Unable?to?locate?plugin:?consul,?retrying?in?2s" level=warning?msg="Unable?to?locate?plugin:?consul,?retrying?in?4s" level=warning?msg="Unable?to?locate?plugin:?consul,?retrying?in?8s" level=warning?msg="Failed?to?retrieve?ipam?driver?for?network?\"vlan1062\"
解決方案:Docker識別Plugin的方式有三種:
sock files are UNIX domain sockets. spec files are text files containing a URL, such as unix:///other.sock or tcp://localhost:8080. json files are text files containing a full json specification for the plugin.
最早我們是通過.sock的方式識別IPAM Plugin。現(xiàn)在通過.spec文件的方式調(diào)用非本地的IPAM Plugin。這樣Docker daemon在重啟時就不受IPAM Plugin的影響。
2)?在通過Docker network rm 刪除用Consul IPAM創(chuàng)建的網(wǎng)絡(luò)時,會把網(wǎng)關(guān)地址釋放給Consul,下次創(chuàng)建容器申請IP時會獲取到網(wǎng)關(guān)的IP,導(dǎo)致網(wǎng)關(guān)IP地址沖突。
解決方案:在刪除容器釋放IP時,檢測下IP地址,如果是網(wǎng)關(guān)IP,則不允許添加到Consul的free列表。
基于以上背景,我們剛開始選型的時候,測試過Docker 1.11 + Swarm 和Docker 1.12集成的SwarmKit。Docker 1.11 + Swarm網(wǎng)絡(luò)沒有Macvlan驅(qū)動,而Docker 1.12集成的SwarmKit只能使用Overlay網(wǎng)絡(luò),Overlay的性能太差。最終我們采用了Docker 1.12 + Mesos。
第一步: build出想要的war,并把war包保存到固定的目錄。第二步:build docker鏡像,會自動發(fā)現(xiàn)前面build出的war包,并通過寫好的Dockerfile build鏡像,鏡像名即為應(yīng)用名。鏡像構(gòu)建成功后會push到我們的私有倉庫。每次鏡像構(gòu)建都會打上一個tag,tag即為發(fā)布版本號。后續(xù)我們計劃把CI從jenkins獨立出來,通過自建的Paas平臺來build war包和鏡像。
我們自研了基于Docker的PaaS平臺(持續(xù)開發(fā)中)。該平臺的功能主要包括信息錄入、應(yīng)用部署、監(jiān)控、容器管理、應(yīng)用擴(kuò)縮等。CD就在Paa上。
當(dāng)要部署一個新的業(yè)務(wù)系統(tǒng)時,要先在Paas系統(tǒng)上錄入應(yīng)用相關(guān)信息,比如基礎(chǔ)鏡像地址、容器資源配置、容器數(shù)量、網(wǎng)絡(luò)、健康檢查等
CD時,需要選擇鏡像的版本號,即上文提到的tag
我們同時支持控制迭代速度,即迭代比例的設(shè)置
這個設(shè)置是指,每次迭代20%的容器,同時存活的容器不能低于迭代前的100%。
我們遇到的問題:控制迭代比例。
Marathon有兩個參數(shù)控制迭代比例:
假如有個Java應(yīng)用通過Tomcat部署,分配了四個容器,默認(rèn)配置下迭代,Marathon可以同時啟動四個新的容器,啟動成功后刪除四個老的容器。四個新的Tomcat容器在對外提供服務(wù)的瞬間,因為請求量太大,需要立即擴(kuò)線程數(shù)預(yù)熱,導(dǎo)致剛進(jìn)來的請求處理時間延長甚至超時(B站因為請求量大,請求設(shè)置的超時時間很短,在這種情況下,請求會504超時)。
解決方法:
對于請求量很大需要預(yù)熱的應(yīng)用,嚴(yán)格控制迭代比例,比如設(shè)置maximumOverCapacity為0.1,則迭代時只能同時新建10%的容器,這10%的容器啟動成功并刪除對應(yīng)老的容器后才會再新建10%的容器繼續(xù)迭代。
對于請求量不大的應(yīng)用,可適當(dāng)調(diào)大maximumOverCapacity,加快迭代速度。
自動擴(kuò)縮容依賴總架構(gòu)圖中的幾個組件:Monitoring Agent、Nginx+UpSync+Consul、Marathon Hook、Bili PaaS。
Monitor Agent:我們自研了Docker的監(jiān)控Agent,封裝成容器,部署在每臺Docker宿主機上,通過docker stats的接口獲取容器的CPU、內(nèi)存、IO等信息,信息錄入InfluxDB,并在Grafana展示。
Bili PaaS:應(yīng)用在錄入PaaS平臺時可以選擇擴(kuò)縮容的規(guī)則,比如:平均CPU > 300% OR MEM > 4G。PaaS平臺定時輪詢判斷應(yīng)用的負(fù)載情況,如果達(dá)到擴(kuò)容規(guī)則,就按一定的比例增加節(jié)點。本質(zhì)上是調(diào)用Marathon的API進(jìn)行擴(kuò)縮。
Marathon Hook:通過Marathon提供的/v2/events接口監(jiān)聽Marathon的事件流。當(dāng)在Bili PaaS平臺手動擴(kuò)容或觸發(fā)規(guī)則自動擴(kuò)容時,Bili Paas平臺會調(diào)用Marathon的API。Marathon的每個操作都會產(chǎn)生事件,通過/v2/events接口暴露出來。Marathon Hook程序會把所有容器的信息注冊到Consul中。當(dāng)Marathon刪除或創(chuàng)建容器時,Marathon Hook就會更新Consul中的Docker容器信息,保證Consul中的信息和Marathon中的信息是一致的,并且是最新的。
Nginx+UpSync+Consul:當(dāng)Marathon擴(kuò)容完成時,新容器的IP:PORT一定要加到SLB(Tengine/Nginx)的Upstream中,并reload SLB后才能對外提供服務(wù)。但Tengine/Nginx reload時性能會下降。為了避免頻繁reload SLB導(dǎo)致的性能損耗,我們使用了動態(tài)Upstream:Nginx + UpSync + Consul。Upsync是Weibo開源的一個模塊,使用Consul保存Upstream的server信息。Nginx啟動時會從Consul獲取最新的Upstream server信息,同時Nginx會建立一個TCP連接hook到Consul,當(dāng)Consul里的數(shù)據(jù)有變更時會立即通知到Nginx,Nginx的worker進(jìn)程更新自己的Upstream server信息。整個過程不需要reload nginx。注意:UpSync的功能是動態(tài)更新upstream server,當(dāng)有vhost的變更時,還是需要reload nginx。
我們遇到的問題:
1)?Nginx + UpSync 在reload時會產(chǎn)生shutting down。因為Nginx Hook到Consul的鏈接不能及時斷開。曾在GitHub上因這個問題提過issue,作者回復(fù)已解決。個人測試發(fā)現(xiàn)shuttding down還是存在。并且UpSync和Tengine的http upstream check模塊不能同時編譯。
解決方案:Tengine + Dyups。我們正在嘗試把Nginx + Dyups替換為Tengine + Dyups。Dyups的弊端就是Upstream信息是保存在內(nèi)存里的。Reload/Restart Tengine時就會丟失。需要自己同步Upstream信息到磁盤中。基于此,我們對Tengine + Dyups做了封裝,由一個代理進(jìn)程Hook Consul,發(fā)現(xiàn)有更時則主動更新Tengine,并提供了Web管理界面。目前正在內(nèi)部測試中。
2)Docker Hook —> Marathon Hook,最早我們是去Hook Docker的events。這需要在每臺宿主機上起一個Hook服務(wù)。當(dāng)應(yīng)用迭代時,Docker會立即產(chǎn)生一個create container的事件。Hook程序監(jiān)控到后去更新Consul,然后Consul通知Nginx去更新。導(dǎo)致問題就是:容器里的服務(wù)還沒啟動成功(比如Tomcat),就已經(jīng)對外提供服務(wù)了。這會導(dǎo)致很多請求失敗,產(chǎn)生重啟請求。
解決方案:Marathon Hook。Marathon中有一個health check的配置。如下圖
我們規(guī)定所有的Web服務(wù)必須提供一個Health Check接口,這個接口隨著服務(wù)一同起來,這個接口任何非200的http code都代表應(yīng)用異常。Marathon剛啟動容器時,顯示此容器的Health狀態(tài)是uknow。當(dāng)Health Check成功時,Marathon顯示此容器的Health狀態(tài)Healthy,并會產(chǎn)生一個事件。Marathon Hook程序通過Hook這個事件,就能準(zhǔn)確捕獲容器中應(yīng)用啟動成功的時間,并更新Consul,同步Nginx,對外提供服務(wù)。
3)Marathon Failover后會丟失command health check,通過Marathon給容器添加Health Check時,有三種方式可以選擇:HTTP TCP COMMAND
當(dāng)使用HTTP TCP時,Check是由Marathon發(fā)起的,無法選擇Check時的端口,Marathon會用自己分配的PORT進(jìn)行Check。實際上我們并未使用marathon映射的端口。我們選擇了COMMAND方式,在容器內(nèi)部發(fā)起curl請求來判斷容器里的應(yīng)用狀態(tài)。當(dāng)Marathon發(fā)生failover后,會丟失COMMAND health check,所有容器狀態(tài)都顯示unknow。需要重啟或者迭代應(yīng)用才能恢復(fù)。
A:目前的自動擴(kuò)容是針對應(yīng)用的。Mesos Agent擴(kuò)容時,先把物理機信息錄入PaaS平臺,手動在PaaS平臺點擊擴(kuò)容,后臺會調(diào)用Ansible,分鐘快速級擴(kuò)Mesos Agent。 Q:現(xiàn)在是確定Nginx+UpSync+Upsteam check是無法一起用的么?貴公司的Nginx版本是多少哇?
A:測試過Nginx 1.8和1.10,確認(rèn)無法一同編譯。我們用的最多的Nginx(SLB)是Tengine 2.1.1,部署在Docker上。 Q:既然是封裝, 那底層用Mesos比Kubernets并沒有太大的靈活性吧?
A:對于PaaS平臺,目前我們希望的只需要資源調(diào)度這個功能,其他功能我們還是希望可以自己實現(xiàn),而Mesos是專注于調(diào)度資源,而且已經(jīng)歷經(jīng)了大量級的考驗。而Kubernetes目前提供的很多服務(wù),我們并不需要,所以選擇了Mesos。 Q:容器是采用Monitor Agent監(jiān)控,那容器內(nèi)的呢?也還是內(nèi)部埋點?還是EFK嗎?監(jiān)控是采用Prometheus嗎?
A:Prometheus沒有使用,我們是用自己的監(jiān)控Agent -> InfluxDB。容器內(nèi)有多種監(jiān)控方式。有用ELK,也有其他埋點,比如StatsD,基于Dapper論文實現(xiàn)的全鏈路追蹤。 Q:網(wǎng)絡(luò)選型這塊,還調(diào)研過其他網(wǎng)絡(luò)方案嗎?譬如Calico、Weave等,為什么會選用Macvlan?
A:我們的選型第一步是先選擇標(biāo)準(zhǔn)的,要從CoreOS主導(dǎo)的cni還是Docker官方主導(dǎo)cnm里面選擇,目前由于我們?nèi)萜鞣桨高€是走的Docker,所以選擇了cnm,那從cnm的標(biāo)準(zhǔn)里面的選擇基本是:1. 基于XVLAN的Overlay;2. 基于三層路由的Calico;3. 基于二層隔離的Macvlan,實際以上的方案我們都調(diào)研使用過,基于希望盡量簡單的原則最終選型還是Macvlan。 Q:Bili PaaS平臺,自動擴(kuò)容和手動擴(kuò)容,應(yīng)用多的是哪種方式?自動擴(kuò)容后,資源會重調(diào)度么?是否會中斷已有業(yè)務(wù)呢?
A:用的更多的是根據(jù)制定好的策略,自動擴(kuò)容。通過Nginx 動態(tài)Upstream對外提供服務(wù),不會中斷業(yè)務(wù)。 Q:關(guān)于日志收集每個容器里都跑一個Logstash嗎?好像ELK不能搜索展示上下文的啊?
A:容器里面沒有跑Logstash。目前是在遠(yuǎn)端的Logstash集群上監(jiān)聽一個UDP端口,應(yīng)用直接把日志推送到Logstash的UDP端口,然后Logstash把日志推送到Kafka,Kafka的消費者有兩個,一個是Elasticsearch,一個是HDFS。一般用ELK足以。需要詳細(xì)日志時,由運維通過HDFS查詢。 Q:我想請教下Nginx的一些動態(tài)配置文件是封裝在容器內(nèi)部了?還是通過volume的方式掛載了?有沒有配置中心類似的服務(wù)?這塊想了解下是怎么實現(xiàn)的?
A:Nginx的Upstream是從Consul動態(tài)獲取生成在本地的,通過Volume掛載,持久化到宿主機。有配置中心。業(yè)務(wù)Docker化時,就會推動業(yè)務(wù)配置接配置中心,Docker中不在保存業(yè)務(wù)依賴的配置。
B站一直在關(guān)注Docker的發(fā)展,去年成功在核心SLB(Tengine)集群上實施了Docker,規(guī)模不大但訪問量大、沒有CI & CD的工作。隨著業(yè)務(wù)量的增長,應(yīng)用擴(kuò)縮需求越來越多。但在沒有docker標(biāo)準(zhǔn)化的情況下,應(yīng)用擴(kuò)容需要擴(kuò)服務(wù)器,操作繁重。同時為了減少因測試、線上環(huán)境不一致導(dǎo)致的問題,我們計劃將業(yè)務(wù)全部Docker化,并配合CI & CD,打通整個業(yè)務(wù)上線流程,達(dá)到秒級動態(tài)擴(kuò)縮容。
下面是我們的實施之路,整體架構(gòu)圖如下:
為什么選擇Mesos?
Kubernetes太重,功能繁多。我們主要看中Mesos的調(diào)度功能,且輕量更易維護(hù)。另外和我們選擇了Macvlan的網(wǎng)絡(luò)有關(guān)。Docker網(wǎng)絡(luò)選擇
Docker自帶的網(wǎng)絡(luò)都不能滿足我們的需求。Bridger:Docker分配私有IP,本機通過bridge跟容器通信。不同宿主機如果要通信需要iptables映射端口。隨著容器的增多,端口管理會很混亂,iptables規(guī)則也越來越多。
Host:使用宿主機的網(wǎng)絡(luò),不同容器不能監(jiān)聽相同端口。
None:Docker不給容器分配網(wǎng)絡(luò),手動分配。
正當(dāng)我們無法選定Docker的網(wǎng)絡(luò)方案時,發(fā)現(xiàn)最新的Docker 1.12版本提供了另外兩種網(wǎng)絡(luò)驅(qū)動:Overlay和Macvlan。
Overlay:在原來的TCP/IP數(shù)據(jù)包基礎(chǔ)上再封裝成UDP的數(shù)據(jù)包傳輸。當(dāng)網(wǎng)絡(luò)出現(xiàn)問題需要抓包時,會比較麻煩。而且,Overlay依靠服務(wù)器的CPU來解UDP數(shù)據(jù)包,會導(dǎo)致Docker網(wǎng)絡(luò)性能非常不穩(wěn)定,性能損耗比較嚴(yán)重,在生產(chǎn)環(huán)境中難以使用。
Macvlan:在交換機上配置VLAN,然后在宿主機上配置物理網(wǎng)卡,使其接收對應(yīng)的VLAN。Docker在創(chuàng)建Network時driver指定Macvlan。對Docker的Macvlan網(wǎng)絡(luò)進(jìn)行壓測,跑在Macvlan網(wǎng)絡(luò)的容器比跑在host網(wǎng)絡(luò)的容器性能損失10~15%左右,但總體性能很穩(wěn)定,沒有抖動。這是能接受的。
基于Macvlan,我們開發(fā)了自己的IPAM Driver Plugin—底層基于Consul。
Docker在創(chuàng)建Macvlan網(wǎng)絡(luò)時,驅(qū)動指定為自己研發(fā)的Consul。Consul中會記錄free和used的IP。如下圖:
IPAM Driver在每臺宿主機上都存在,通過Socket的方式暴露給Docker調(diào)用。Docker在創(chuàng)建容器時,IPAM Plugin會從Consul申請一個free的IP地址。刪除容器時,IPAM Plugin會釋放這個IP到Consul。因為所有宿主機上的IPAM Plugin連接到的是同一個Consul,就保證了所有容器的IP地址唯一性。
我們用IPAM Plugin遇到的問題:
1)?Consul IPAM Plugin在每臺宿主機上都存在,通過Socket方式調(diào)用,目前使用容器啟動。
當(dāng)Docker daemon重啟加載Network時,因為容器還未啟動,會找不到Consul IPAM Plugin的Socket文件,導(dǎo)致Docker daemon會去重試請求IPAM,延長daemon的啟動時間,報錯如下:
level=warning?msg="Unable?to?locate?plugin:?consul,?retrying?in?1s" level=warning?msg="Unable?to?locate?plugin:?consul,?retrying?in?2s" level=warning?msg="Unable?to?locate?plugin:?consul,?retrying?in?4s" level=warning?msg="Unable?to?locate?plugin:?consul,?retrying?in?8s" level=warning?msg="Failed?to?retrieve?ipam?driver?for?network?\"vlan1062\"
解決方案:Docker識別Plugin的方式有三種:
最早我們是通過.sock的方式識別IPAM Plugin。現(xiàn)在通過.spec文件的方式調(diào)用非本地的IPAM Plugin。這樣Docker daemon在重啟時就不受IPAM Plugin的影響。
2)?在通過Docker network rm 刪除用Consul IPAM創(chuàng)建的網(wǎng)絡(luò)時,會把網(wǎng)關(guān)地址釋放給Consul,下次創(chuàng)建容器申請IP時會獲取到網(wǎng)關(guān)的IP,導(dǎo)致網(wǎng)關(guān)IP地址沖突。
解決方案:在刪除容器釋放IP時,檢測下IP地址,如果是網(wǎng)關(guān)IP,則不允許添加到Consul的free列表。
基于以上背景,我們剛開始選型的時候,測試過Docker 1.11 + Swarm 和Docker 1.12集成的SwarmKit。Docker 1.11 + Swarm網(wǎng)絡(luò)沒有Macvlan驅(qū)動,而Docker 1.12集成的SwarmKit只能使用Overlay網(wǎng)絡(luò),Overlay的性能太差。最終我們采用了Docker 1.12 + Mesos。
CI & CD
對于CI,我們采用了目前公司中正在大量使用的Jenkins。 Jenkins通過Pipeline分為多個step,step 1 build出要構(gòu)建war包。Step 2 build Docker 鏡像并push到倉庫中。第一步: build出想要的war,并把war包保存到固定的目錄。第二步:build docker鏡像,會自動發(fā)現(xiàn)前面build出的war包,并通過寫好的Dockerfile build鏡像,鏡像名即為應(yīng)用名。鏡像構(gòu)建成功后會push到我們的私有倉庫。每次鏡像構(gòu)建都會打上一個tag,tag即為發(fā)布版本號。后續(xù)我們計劃把CI從jenkins獨立出來,通過自建的Paas平臺來build war包和鏡像。
我們自研了基于Docker的PaaS平臺(持續(xù)開發(fā)中)。該平臺的功能主要包括信息錄入、應(yīng)用部署、監(jiān)控、容器管理、應(yīng)用擴(kuò)縮等。CD就在Paa上。
當(dāng)要部署一個新的業(yè)務(wù)系統(tǒng)時,要先在Paas系統(tǒng)上錄入應(yīng)用相關(guān)信息,比如基礎(chǔ)鏡像地址、容器資源配置、容器數(shù)量、網(wǎng)絡(luò)、健康檢查等
CD時,需要選擇鏡像的版本號,即上文提到的tag
我們同時支持控制迭代速度,即迭代比例的設(shè)置
這個設(shè)置是指,每次迭代20%的容器,同時存活的容器不能低于迭代前的100%。
我們遇到的問題:控制迭代比例。
Marathon有兩個參數(shù)控制迭代比例:
- minimumHealthCapacity(Optional. Default: 1.0)處于health狀態(tài)的最少容器比例;
- maximumOverCapacity(Optional. Default: 1.0)可同時迭代的容器比例。
假如有個Java應(yīng)用通過Tomcat部署,分配了四個容器,默認(rèn)配置下迭代,Marathon可以同時啟動四個新的容器,啟動成功后刪除四個老的容器。四個新的Tomcat容器在對外提供服務(wù)的瞬間,因為請求量太大,需要立即擴(kuò)線程數(shù)預(yù)熱,導(dǎo)致剛進(jìn)來的請求處理時間延長甚至超時(B站因為請求量大,請求設(shè)置的超時時間很短,在這種情況下,請求會504超時)。
解決方法:
對于請求量很大需要預(yù)熱的應(yīng)用,嚴(yán)格控制迭代比例,比如設(shè)置maximumOverCapacity為0.1,則迭代時只能同時新建10%的容器,這10%的容器啟動成功并刪除對應(yīng)老的容器后才會再新建10%的容器繼續(xù)迭代。
對于請求量不大的應(yīng)用,可適當(dāng)調(diào)大maximumOverCapacity,加快迭代速度。
動態(tài)擴(kuò)縮容
節(jié)假日或做活動時,為了應(yīng)對臨時飆高的QPS,需要對應(yīng)用臨時擴(kuò)容。或者當(dāng)監(jiān)控到某個業(yè)務(wù)的平均資源使用率超過一定限制時,自動擴(kuò)容。我們的擴(kuò)容方式有兩種:1、手動擴(kuò)容;2、制定一定的規(guī)則,當(dāng)觸發(fā)到規(guī)則時,自動擴(kuò)容。我們的Bili PaaS平臺同時提供了這兩種方式,底層是基于Marathon的Scale API。這里著重講解下基于規(guī)則的自動擴(kuò)縮容。自動擴(kuò)縮容依賴總架構(gòu)圖中的幾個組件:Monitoring Agent、Nginx+UpSync+Consul、Marathon Hook、Bili PaaS。
Monitor Agent:我們自研了Docker的監(jiān)控Agent,封裝成容器,部署在每臺Docker宿主機上,通過docker stats的接口獲取容器的CPU、內(nèi)存、IO等信息,信息錄入InfluxDB,并在Grafana展示。
Bili PaaS:應(yīng)用在錄入PaaS平臺時可以選擇擴(kuò)縮容的規(guī)則,比如:平均CPU > 300% OR MEM > 4G。PaaS平臺定時輪詢判斷應(yīng)用的負(fù)載情況,如果達(dá)到擴(kuò)容規(guī)則,就按一定的比例增加節(jié)點。本質(zhì)上是調(diào)用Marathon的API進(jìn)行擴(kuò)縮。
Marathon Hook:通過Marathon提供的/v2/events接口監(jiān)聽Marathon的事件流。當(dāng)在Bili PaaS平臺手動擴(kuò)容或觸發(fā)規(guī)則自動擴(kuò)容時,Bili Paas平臺會調(diào)用Marathon的API。Marathon的每個操作都會產(chǎn)生事件,通過/v2/events接口暴露出來。Marathon Hook程序會把所有容器的信息注冊到Consul中。當(dāng)Marathon刪除或創(chuàng)建容器時,Marathon Hook就會更新Consul中的Docker容器信息,保證Consul中的信息和Marathon中的信息是一致的,并且是最新的。
Nginx+UpSync+Consul:當(dāng)Marathon擴(kuò)容完成時,新容器的IP:PORT一定要加到SLB(Tengine/Nginx)的Upstream中,并reload SLB后才能對外提供服務(wù)。但Tengine/Nginx reload時性能會下降。為了避免頻繁reload SLB導(dǎo)致的性能損耗,我們使用了動態(tài)Upstream:Nginx + UpSync + Consul。Upsync是Weibo開源的一個模塊,使用Consul保存Upstream的server信息。Nginx啟動時會從Consul獲取最新的Upstream server信息,同時Nginx會建立一個TCP連接hook到Consul,當(dāng)Consul里的數(shù)據(jù)有變更時會立即通知到Nginx,Nginx的worker進(jìn)程更新自己的Upstream server信息。整個過程不需要reload nginx。注意:UpSync的功能是動態(tài)更新upstream server,當(dāng)有vhost的變更時,還是需要reload nginx。
我們遇到的問題:
1)?Nginx + UpSync 在reload時會產(chǎn)生shutting down。因為Nginx Hook到Consul的鏈接不能及時斷開。曾在GitHub上因這個問題提過issue,作者回復(fù)已解決。個人測試發(fā)現(xiàn)shuttding down還是存在。并且UpSync和Tengine的http upstream check模塊不能同時編譯。
解決方案:Tengine + Dyups。我們正在嘗試把Nginx + Dyups替換為Tengine + Dyups。Dyups的弊端就是Upstream信息是保存在內(nèi)存里的。Reload/Restart Tengine時就會丟失。需要自己同步Upstream信息到磁盤中。基于此,我們對Tengine + Dyups做了封裝,由一個代理進(jìn)程Hook Consul,發(fā)現(xiàn)有更時則主動更新Tengine,并提供了Web管理界面。目前正在內(nèi)部測試中。
2)Docker Hook —> Marathon Hook,最早我們是去Hook Docker的events。這需要在每臺宿主機上起一個Hook服務(wù)。當(dāng)應(yīng)用迭代時,Docker會立即產(chǎn)生一個create container的事件。Hook程序監(jiān)控到后去更新Consul,然后Consul通知Nginx去更新。導(dǎo)致問題就是:容器里的服務(wù)還沒啟動成功(比如Tomcat),就已經(jīng)對外提供服務(wù)了。這會導(dǎo)致很多請求失敗,產(chǎn)生重啟請求。
解決方案:Marathon Hook。Marathon中有一個health check的配置。如下圖
我們規(guī)定所有的Web服務(wù)必須提供一個Health Check接口,這個接口隨著服務(wù)一同起來,這個接口任何非200的http code都代表應(yīng)用異常。Marathon剛啟動容器時,顯示此容器的Health狀態(tài)是uknow。當(dāng)Health Check成功時,Marathon顯示此容器的Health狀態(tài)Healthy,并會產(chǎn)生一個事件。Marathon Hook程序通過Hook這個事件,就能準(zhǔn)確捕獲容器中應(yīng)用啟動成功的時間,并更新Consul,同步Nginx,對外提供服務(wù)。
3)Marathon Failover后會丟失command health check,通過Marathon給容器添加Health Check時,有三種方式可以選擇:HTTP TCP COMMAND
當(dāng)使用HTTP TCP時,Check是由Marathon發(fā)起的,無法選擇Check時的端口,Marathon會用自己分配的PORT進(jìn)行Check。實際上我們并未使用marathon映射的端口。我們選擇了COMMAND方式,在容器內(nèi)部發(fā)起curl請求來判斷容器里的應(yīng)用狀態(tài)。當(dāng)Marathon發(fā)生failover后,會丟失COMMAND health check,所有容器狀態(tài)都顯示unknow。需要重啟或者迭代應(yīng)用才能恢復(fù)。
Q&A
Q:你好 問下貴公司的自動擴(kuò)容是針對應(yīng)用的吧 有沒有針對Mesos資源池監(jiān)控并做Mesos Agent的擴(kuò)容?A:目前的自動擴(kuò)容是針對應(yīng)用的。Mesos Agent擴(kuò)容時,先把物理機信息錄入PaaS平臺,手動在PaaS平臺點擊擴(kuò)容,后臺會調(diào)用Ansible,分鐘快速級擴(kuò)Mesos Agent。 Q:現(xiàn)在是確定Nginx+UpSync+Upsteam check是無法一起用的么?貴公司的Nginx版本是多少哇?
A:測試過Nginx 1.8和1.10,確認(rèn)無法一同編譯。我們用的最多的Nginx(SLB)是Tengine 2.1.1,部署在Docker上。 Q:既然是封裝, 那底層用Mesos比Kubernets并沒有太大的靈活性吧?
A:對于PaaS平臺,目前我們希望的只需要資源調(diào)度這個功能,其他功能我們還是希望可以自己實現(xiàn),而Mesos是專注于調(diào)度資源,而且已經(jīng)歷經(jīng)了大量級的考驗。而Kubernetes目前提供的很多服務(wù),我們并不需要,所以選擇了Mesos。 Q:容器是采用Monitor Agent監(jiān)控,那容器內(nèi)的呢?也還是內(nèi)部埋點?還是EFK嗎?監(jiān)控是采用Prometheus嗎?
A:Prometheus沒有使用,我們是用自己的監(jiān)控Agent -> InfluxDB。容器內(nèi)有多種監(jiān)控方式。有用ELK,也有其他埋點,比如StatsD,基于Dapper論文實現(xiàn)的全鏈路追蹤。 Q:網(wǎng)絡(luò)選型這塊,還調(diào)研過其他網(wǎng)絡(luò)方案嗎?譬如Calico、Weave等,為什么會選用Macvlan?
A:我們的選型第一步是先選擇標(biāo)準(zhǔn)的,要從CoreOS主導(dǎo)的cni還是Docker官方主導(dǎo)cnm里面選擇,目前由于我們?nèi)萜鞣桨高€是走的Docker,所以選擇了cnm,那從cnm的標(biāo)準(zhǔn)里面的選擇基本是:1. 基于XVLAN的Overlay;2. 基于三層路由的Calico;3. 基于二層隔離的Macvlan,實際以上的方案我們都調(diào)研使用過,基于希望盡量簡單的原則最終選型還是Macvlan。 Q:Bili PaaS平臺,自動擴(kuò)容和手動擴(kuò)容,應(yīng)用多的是哪種方式?自動擴(kuò)容后,資源會重調(diào)度么?是否會中斷已有業(yè)務(wù)呢?
A:用的更多的是根據(jù)制定好的策略,自動擴(kuò)容。通過Nginx 動態(tài)Upstream對外提供服務(wù),不會中斷業(yè)務(wù)。 Q:關(guān)于日志收集每個容器里都跑一個Logstash嗎?好像ELK不能搜索展示上下文的啊?
A:容器里面沒有跑Logstash。目前是在遠(yuǎn)端的Logstash集群上監(jiān)聽一個UDP端口,應(yīng)用直接把日志推送到Logstash的UDP端口,然后Logstash把日志推送到Kafka,Kafka的消費者有兩個,一個是Elasticsearch,一個是HDFS。一般用ELK足以。需要詳細(xì)日志時,由運維通過HDFS查詢。 Q:我想請教下Nginx的一些動態(tài)配置文件是封裝在容器內(nèi)部了?還是通過volume的方式掛載了?有沒有配置中心類似的服務(wù)?這塊想了解下是怎么實現(xiàn)的?
A:Nginx的Upstream是從Consul動態(tài)獲取生成在本地的,通過Volume掛載,持久化到宿主機。有配置中心。業(yè)務(wù)Docker化時,就會推動業(yè)務(wù)配置接配置中心,Docker中不在保存業(yè)務(wù)依賴的配置。
以上內(nèi)容根據(jù)2016年9月27日晚微信群分享內(nèi)容整理。分享人武安闖,Bilibili 運維工程師,目前主要負(fù)責(zé)B站運維和業(yè)務(wù)Docker化的實施。一直關(guān)注于Docker的發(fā)展,Docker與Mesos結(jié)合的容器平臺實施?。DockOne每周都會組織定向的技術(shù)分享,歡迎感興趣的同學(xué)加微信:liyingjiesz,進(jìn)群參與,您有想聽的話題或者想分享的話題都可以給我們留言。
原文發(fā)布時間為:2016-09-29
本文作者:武安闖
本文來自云棲社區(qū)合作伙伴Dockerone.io,了解相關(guān)信息可以關(guān)注Dockerone.io。
原文標(biāo)題:DockOne微信分享(八十四):Docker在B站的實施之路
總結(jié)
以上是生活随笔為你收集整理的DockOne微信分享(八十四):Docker在B站的实施之路的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux DHCP
- 下一篇: Kubernetes排错:用容器的元数据