當當網Docker應用實踐
隨著Docker成為當下熱門的容器技術,各大公司以及中小團隊都開始選擇Docker來進行應用部署,從原有部署方式遷移到Docker方式過程中難免會遇到各種問題,本次分享主要介紹當當網個性化推薦組應用Docker進行應用部署以及小團隊試水Docker的的若干經驗,分享主要包括以下幾點:
- 現有應用Docker化的過程Docker化的過程
- 結合Jenkins的自動化構建
- 使用Mesos和Marathon的自動化部署與集群管理
大家好,非常高興能有這樣的機會跟大家交流。
我是當當網個性化推薦組項目負責人,我們主要是負責數據分析和后臺算法支持,產出的推薦結果供其他部門使用。
今天的內容主要分為兩部分,一部分講講對于一個小團隊來說,打算使用docker的可行性以及具體的使用方法。另一部分講一下在這過程中遇到的一些問題以及需要注意的一些情況。
我先介紹一下我們的推薦系統,跟一般的電商都是一樣的,如果瀏覽一個商品頁,下面就會推薦一些和這個商品相關的其他商品,我點開一個熱水壺,就會推薦其他的廚房用品。我們當當推薦的流量差不多是每天五千萬請求,一共21臺機器。
下面簡單介紹一下我們推薦系統的架構。首先是基礎數據,基礎數據是生成推薦數據所使用的一些基本數據,包括商品信息、用戶信息、用戶行為信息等等。這些數據通過推薦算法與策略在每天運行的日作業上生成一份數據供推薦后臺使用,同時還有一份實時數據。實時數據就是可能有促銷,手機專享價等價格數據。把這些數據一起放在后臺里面,前臺請求商品的推薦信息,后臺反饋對應的一組推薦品。
推薦后臺日志也很重要,因為我們需要分析日志,日志里面包括用戶什么時候點擊的推薦商品,什么時候請求的推薦后臺,點擊率,曝光率,都是從日志數據里面出的,后臺通過Flume把日志推到Kafka和Hdfs上,供后續離線數據分析和實時監控分析使用。同時還可以實時監控后臺狀態的功能,包括響應時間、請求數等,每一分鐘更新一個點畫曲線出來。
因為后臺這邊對我們來說,比較偏向數據和算法,后臺這塊,對于開發測試部署這塊,尤其是測試和部署的時候,總是要拿出一個人專門跟著測試和部署,因為流程上可能有一些問題,所以就想用Docker了。想用Docker主要解決上線環境不同,配置不同的問題。
還有因為我們是電商,618、雙11總會搞活動,之前大促時候擴容加機器也要提前加,把數定下來,運維部署之后數量就定下來了,如果到時候發現機器又不夠了,再加機器時間要花費很多。基于這兩點,我們打算用Docker試一下。
同時,使用Docker給我們帶來的便利性還存在以下幾方面,對于開發來說,可以使用root權限來方便的開發運行程序;對于測試來說,他們拿到的Docker鏡像就包含了所有運行依賴的環境,這就保證了一致性;對運維來說,能夠使用統一的指令來做相關的運維工作。
推薦后臺這塊,我們已經有一個成形的程序,包括配置、數據等。擺在我們面前的問題是原來的程序,我們應該怎么把它放在容器里,也就是現有程序的Docker化。Docker化這個問題,要是把程序放在容器里的話,這個程序至少應該是無狀態的,不能依賴于宿主機的一切環境,如目錄、IP。之前的推薦后臺,推薦數據每天更新,達到50G,數據更新作業就要依賴自己IP和數據目錄。從開發到測試到上線,都是很麻煩的。如果我們用Docker的話,在開發這塊,以前如果用正常開發的話,root是不讓用的,用Docker就可以。測試的時候,測試的同事經常問我們為什么程序起不來,一看是數據庫配置錯了,如果用Docker的話,環境都打包在一起,這個是沒有問題的。運維是手動用腳本上21臺,我們單拿出個人跟著,因為每次上線有很多東西要修改,運維不了解的話,有很多事需要我們去做。
我們給后臺設計這么一個簡單的容器的結構,下面的Base image是CentOS 7,上面這層裝了些必要的一些程序和工具,最上面這層才是我們推薦后臺的程序鏡像。除了上面這些我們自己應用鏡像以外,下面其他的鏡像越小越好,消耗的資源越少越好。如果想讓它再小的話,可以用CoreOS等,或者自己定制都可以。我們的應用分了4個鏡像,數據鏡像、推薦后臺本身程序鏡像、日志鏡像、收集日志用的Flume鏡像。
整個結構定好了以后,就要看看把程序跑在Docker里面和跑在物理機上相比,性能上有沒有很大的差距。如果有的話,這個東西肯定不能上。測試來看,放在物理機上和放在容器里運行,性能基本上是差不多的。因為我們推薦后臺是IO密集型的應用,對網絡來說流量不是那么大,所以差的不是太多。
用了Docker以后,就想把整個生態的東西都用起來,如果都用起來的話,能讓代碼流水線管理,提高開發效率。這是我們現在用的流程圖。開發完代碼,把代碼推到GitLab,Jenkins用Webhook發現有新代碼推過來,把代碼拿來編譯一下,然后如果測試通過會到打成鏡像并推到測試鏡像庫,同時發郵件給開發和測試。測試這邊收到郵件就會把容器運行起來,這里編排用的是DockerCompose,如果那邊測試通過,把鏡像再提交到線上鏡像庫,同時通知運維用DockerCompose做相同的事情。
鏡像庫用的也是原生的,這個是僅供其他人查詢的頁面,不用登錄Docker的機器就可以查詢鏡像的相關信息。
Jenkins從2.0開始有 Pipeline as Code,可以用類似寫代碼的方式把流程定義出來,同時還提供了對Docker默認的支持,比以前用起來舒服很多。Build成功就Test,然后Push到鏡像庫然后發郵件,這是比較簡單的流程。
編排使用Docker Compose,因為有4個鏡像一起上線。把推薦后臺、數據、日志鏡像、Flume鏡像啟動命令寫到配置文件中用docker-compose up命令就可以啟動了。
由于我們推薦后臺里面有數據容器,剛開始的時候,數據有50多個G,拉數據鏡像啟動再加載的話要二十分鐘,對于用Docker集群做管理的話有點不現實。所以我們一步步往外摘數據。像商品數據,拿圖書來說,一個PID后面有價格、作者,評論數等等,我們會把這些比較結構化的數據放到ES里面,現在實際上已經把推薦數據放到ES里面了,其他的商品數據正在慢慢往外摘。
如果我們把前面說到的所有數據都摘掉,都不在本地保存,也不在運行的時候加載,那么下一步就可以上Mesos+Marathon。Mesos+Marathon可能大家都常用,這里使用的是一個最簡單的結構:Mesos+Marathon的集群,后面用自帶的Marathon_LB,Marathon_LB是服務發現和負載均衡的工具。如果想從前端訪問推薦后臺的話,都是定好的一個IP或者一個域名用LVS訪問。但是用Mesos+Maratho時起來的實例都是隨機IP和端口的,這里就要用Marathon lb做服務發現,同時有負載均衡的功能。Marathon_LB也可以部多臺,然后前面加一級LVS。
下面說一下,想把現有的程序Docker化,我們需要注意哪些東西?首先就是鏡像構建這塊,一定要從Dockerfile生成,這樣做最大的好處是可以通過Dockerfile“閱讀”鏡像。在后續的協作、升級維護等方面會帶來巨大的便利。如果不從Dockerfile生成,以后更新、回滾是很麻煩的。我用Dockerfile生成的時候,其他的人可以通過Dockerfile或者鏡像就可以閱讀這個鏡像是怎么來的。哪個鏡像是base鏡像,中間加了哪些軟件,運行什么東西。
二是避免依賴過深。不要在基礎鏡像上加太多產生其他的鏡像,我覺得這塊最多是三四層。一層是base景像再往上是工具、中間件這樣的,再往上一層就是你自己的程序,再多就比較亂了。
三是鏡像里面部署的應用程序,包括對應的代碼都必須有對應的Git,雖然Docker自己有一個記錄歷史更改的功能,但是代碼對應的Git必須也要有。
鏡像管理這塊,雖然我們應用的是原生的,但是這幾項只要跟數據有關系的都要考慮。單點問題,暫時我們用的是一臺機器,不是很安全,對應的解決方案可以考慮DRBD、分布式存儲以及云存儲。性能問題,主要是下載加速,目前可用的解決方案是通過HTTP反向代理緩存來加速Layer的下載。權限問題可以用Nginx LUA提供一個簡單快速的實現方案
發布方面,我們使用Docker,和傳統的發布流程相比,Docker最大的好處是不需要考慮外部依賴,利用容器的自包含的特點,我們可以將發布回滾流程標準化和產品化。而傳統的發布和回滾,需要casebycase去針對不同應用做升級回滾的方案。要做到基于Docker的發布,鏡像的生成必須堅持自動化,否則會發現升級比傳統的方法更麻煩。因此在現實中我們也發現很多人將代碼目錄放到主機目錄映射到容器內,這樣做破壞了Docker的自包含特性,解決的辦法是堅持應用鏡像更新自動化。
日志管理的話,如果把日志放在容器里面,由于容器是無狀態的,所以存儲在容器內的日志會隨著容器的銷毀而消失。你要把日志實時保存下來,或者把日志放在宿主機,但是放在宿主機的話,有點違反Docker化不依賴宿主機的任何環境的要求。建議放一些日志收集工具如Logstash或者Flume等。
如果用Docker配置管理,手工修改容器內的配置,再新建啟動的時候,配置文件就沒有了。我們要看一下有沒有其他的方式去配置容器或者是配置容器里面運行程序的參數。第一個就是配置文件,每一個版本配置文件都要做一個容器,這是最簡單的方法。設置環境變量,指定容器運行時的環境變量,然后應用再去用環境變量。配置中心,用數據庫或者ZooKeeper,把配置放在里面做配置中心都可以。
網絡管理這塊,從Docker1.9開始,出了一個自定義的網絡類型,而且不再推薦容器使用默認的bridge網卡,它的存在僅僅是為了兼容早期設計。還有Host,Host網絡中容器和主機共享網絡命名空間,不同容器需要做好端口規劃,防止端口沖突。1.9的自定義網絡,以前Docker就一個網絡docker0,現在如果用自定義的話,可以建多個像docker0這樣的網絡,可以連接和隔離容器之間的通信。
應用Docker時發現的問題,一開始用Docker的時候,實際上我們的機器從內核到系統版本都不符合,這個要運維配合升級。如果自己團隊想用Docker的話,這些東西都是要注意的。包括運行環境、內核版本、操作系統的發行版本,這些都是有要求的。
鏡像載荷要求,如果一個現有的比較成熟的架構的應用,要放到Docker里面是必須要考慮如何Docker化的。原有程序是不是依賴宿主機的環境,包括用不用本機的IP,本地的目錄。最開始的我們后臺下載數據的時候是需要往我們開發的數據中心里面發送自己的IP,數據中心才會往IP對應的機器推數據,類似這樣的問題還是需要考慮的和修改。
還有就是我們的推薦后臺遇到的問題,是數據過大,數據50G。當時折中的方法,先往上扔一個空數據容器,后臺第一次啟動的時候,如果容器里面沒有數據的話,自己就先下載,這也是一個折中的辦法。
多實例運行,我們的機器可夠跑多個實例的,啟動的時候每個實例的占的內存和CPU都要考慮一下。
鏡像管理、版本控制。如果提交一個鏡像,提交時tag必須要有比較嚴格的規定,例如格式定義等,否則會比較混亂。
我們在正常物理機的情況下,ulimit的參數,直接可以改配置文件,或者直接運行一下指令修改。但是在Docker里面你不能用。就要在docker run是用--ulimit 來指定,如果使用默認的參數,要注意下CentOS6和7是不同的。
DNS,默認的是8.8.8.8和8.8.4.4,有的時候這個DNS訪問就不好使了。如果有需要還是要手動用--dns來指定內部IP這塊,如果原先的程序是依賴本地IP做一些事情的話,自己獲取的話,用Bridge是內部IP,有這個需求的應用必須要注意一下。
latest tag,版本控制,運行鏡像的時候,不要用latest,也不要把后面的tag省去。latest今天運行的是這個版本,第二天推了一個新的,latest就可能被覆蓋了,有可能會注意不到這個問題。
Q:你提到數據量過大,扔一個空的,能不能具體介紹一下?還要下載數據?
A:這個是這樣的,正常來說是把數據放到鏡像里面起來以后,link到其他的容器里面。但是我先直接扔一個空的數據鏡像,link的目錄都是指定好的,后臺啟動的時候,先檢測容器是不是空的,如果是空的,從數據服務器往這里同步,這是最開始的時候折中的辦法。
Q:同步完應該也是50多G吧?
A:因為數據每天都要更新,如果每天更新50多個G的鏡像的話,我們的鏡像庫也要每天往里放50個G,單獨更新數據的話,包括用的空間,傳輸數據量來說要好很多。
Q:還有一個問題,ES是怎么使用的?
A:我們拿它當數據存儲用,存儲結構化的數據。
Q:選Marathon和Mesos的時候,有沒有評價過Kubernetes?
A:因為Mesos比較成熟,Kubernetes也不好拉鏡像,而且我們數據分析用Spark也是運行在Mesos上,比較熟悉,而且混合負載也比較好。
Q::容器網絡用的是什么?
A:是用它自己的Bridge。
來自:http://dockone.io/article/1718